diff --git a/.gitignore b/.gitignore index e69de29bb..557507983 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,6 @@ +linux-*.tar.bz2 +patch-*.bz2 +clog +*.rpm +kernel-2.6.*/ +/patch-2.6.32.26.bz2 diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..46e734236 --- /dev/null +++ b/Makefile @@ -0,0 +1,269 @@ +# Makefile for source rpm: kernel +SPECFILE := kernel.spec + +# use noarch for make prep instead of the current CPU +# noarch creates and checks all config files not just the current one, +# in addition "i386" isn't a valid kernel target +PREPARCH = noarch + +# we only check the .sign signatures +UPSTREAM_CHECKS = sign + +.PHONY: help +help: +%: + @echo "Try fedpkg $@ or something like that" + @exit 1 + +include Makefile.config + +ifndef KVERSION +KVERSION := $(shell awk '$$1 == "%define" && $$2 == "base_sublevel" { \ + print "2.6." $$3 \ + }' $(SPECFILE)) +endif + +prep: + fedpkg -v prep --arch=$(PREPARCH) + +extremedebug: + @perl -pi -e 's/# CONFIG_DEBUG_PAGEALLOC is not set/CONFIG_DEBUG_PAGEALLOC=y/' config-nodebug + +debug: + @perl -pi -e 's/# CONFIG_SLUB_DEBUG_ON is not set/CONFIG_SLUB_DEBUG_ON=y/' config-nodebug + @perl -pi -e 's/# CONFIG_LOCK_STAT is not set/CONFIG_LOCK_STAT=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_STACK_USAGE is not set/CONFIG_DEBUG_STACK_USAGE=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_SLAB is not set/CONFIG_DEBUG_SLAB=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_MUTEXES is not set/CONFIG_DEBUG_MUTEXES=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_RT_MUTEXES is not set/CONFIG_DEBUG_RT_MUTEXES=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_RWSEMS is not set/CONFIG_DEBUG_RWSEMS=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_LOCK_ALLOC is not set/CONFIG_DEBUG_LOCK_ALLOC=y/' config-nodebug + @perl -pi -e 's/# CONFIG_PROVE_LOCKING is not set/CONFIG_PROVE_LOCKING=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_SPINLOCK is not set/CONFIG_DEBUG_SPINLOCK=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_VM is not set/CONFIG_DEBUG_VM=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_SLEEP_IN_IRQ is not set/CONFIG_DEBUG_SLEEP_IN_IRQ=y/' config-nodebug + @perl -pi -e 's/# CONFIG_FAULT_INJECTION is not set/CONFIG_FAULT_INJECTION=y/' config-nodebug + @perl -pi -e 's/# CONFIG_FAILSLAB is not set/CONFIG_FAILSLAB=y/' config-nodebug + @perl -pi -e 's/# CONFIG_FAIL_PAGE_ALLOC is not set/CONFIG_FAIL_PAGE_ALLOC=y/' config-nodebug + @perl -pi -e 's/# CONFIG_FAIL_IO_TIMEOUT is not set/CONFIG_FAIL_IO_TIMEOUT=y/' config-nodebug + @perl -pi -e 's/# CONFIG_FAIL_MAKE_REQUEST is not set/CONFIG_FAIL_MAKE_REQUEST=y/' config-nodebug + @perl -pi -e 's/# CONFIG_FAULT_INJECTION_DEBUG_FS is not set/CONFIG_FAULT_INJECTION_DEBUG_FS=y/' config-nodebug + @perl -pi -e 's/# CONFIG_FAULT_INJECTION_STACKTRACE_FILTER is not set/CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_SG is not set/CONFIG_DEBUG_SG=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_WRITECOUNT is not set/CONFIG_DEBUG_WRITECOUNT=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_OBJECTS is not set/CONFIG_DEBUG_OBJECTS=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_OBJECTS_FREE is not set/CONFIG_DEBUG_OBJECTS_FREE=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_OBJECTS_TIMERS is not set/CONFIG_DEBUG_OBJECTS_TIMERS=y/' config-nodebug + @perl -pi -e 's/# CONFIG_X86_PTDUMP is not set/CONFIG_X86_PTDUMP=y/' config-nodebug + @perl -pi -e 's/# CONFIG_CAN_DEBUG_DEVICES is not set/CONFIG_CAN_DEBUG_DEVICES=y/' config-nodebug + @perl -pi -e 's/# CONFIG_MODULE_FORCE_UNLOAD is not set/CONFIG_MODULE_FORCE_UNLOAD=y/' config-nodebug + @perl -pi -e 's/# CONFIG_SYSCTL_SYSCALL_CHECK is not set/CONFIG_SYSCTL_SYSCALL_CHECK=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_NOTIFIERS is not set/CONFIG_DEBUG_NOTIFIERS=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DMA_API_DEBUG is not set/CONFIG_DMA_API_DEBUG=y/' config-nodebug + @perl -pi -e 's/# CONFIG_PM_TEST_SUSPEND is not set/CONFIG_PM_TEST_SUSPEND=y/' config-generic + @perl -pi -e 's/# CONFIG_BOOT_TRACER is not set/CONFIG_BOOT_TRACER=y/' config-generic + @perl -pi -e 's/# CONFIG_B43_DEBUG is not set/CONFIG_B43_DEBUG=y/' config-generic + @perl -pi -e 's/# CONFIG_B43LEGACY_DEBUG is not set/CONFIG_B43LEGACY_DEBUG=y/' config-generic + @perl -pi -e 's/# CONFIG_MMIOTRACE is not set/CONFIG_MMIOTRACE=y/' config-nodebug + @perl -pi -e 's/CONFIG_STRIP_ASM_SYMS=y/# CONFIG_STRIP_ASM_SYMS is not set/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_CREDENTIALS is not set/CONFIG_DEBUG_CREDENTIALS=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set/CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y/' config-nodebug + @perl -pi -e 's/# CONFIG_ACPI_DEBUG is not set/CONFIG_ACPI_DEBUG=y/' config-nodebug + @perl -pi -e 's/# CONFIG_EXT4_DEBUG is not set/CONFIG_EXT4_DEBUG=y/' config-nodebug + @perl -pi -e 's/# CONFIG_DEBUG_PERF_USE_VMALLOC is not set/CONFIG_DEBUG_PERF_USE_VMALLOC=y/' config-nodebug + @perl -pi -e 's/# CONFIG_JBD2_DEBUG is not set/CONFIG_JBD2_DEBUG=y/' config-nodebug + + @# just in case we're going from extremedebug -> debug + @perl -pi -e 's/CONFIG_DEBUG_PAGEALLOC=y/# CONFIG_DEBUG_PAGEALLOC is not set/' config-nodebug + + @perl -pi -e 's/CONFIG_NR_CPUS=256/CONFIG_NR_CPUS=512/' config-x86_64-generic + + @perl -pi -e 's/^%define debugbuildsenabled 1/%define debugbuildsenabled 0/' kernel.spec + @perl -pi -e 's/^%define rawhide_skip_docs 0/%define rawhide_skip_docs 1/' kernel.spec + +release: + @perl -pi -e 's/CONFIG_SLUB_DEBUG_ON=y/# CONFIG_SLUB_DEBUG_ON is not set/' config-nodebug + @perl -pi -e 's/CONFIG_LOCK_STAT=y/# CONFIG_LOCK_STAT is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_STACK_USAGE=y/# CONFIG_DEBUG_STACK_USAGE is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_SLAB=y/# CONFIG_DEBUG_SLAB is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_MUTEXES=y/# CONFIG_DEBUG_MUTEXES is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_RT_MUTEXES=y/# CONFIG_DEBUG_RT_MUTEXES is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_RWSEMS=y/# CONFIG_DEBUG_RWSEMS is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_LOCK_ALLOC=y/# CONFIG_DEBUG_LOCK_ALLOC is not set/' config-nodebug + @perl -pi -e 's/CONFIG_PROVE_LOCKING=y/# CONFIG_PROVE_LOCKING is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_SPINLOCK=y/# CONFIG_DEBUG_SPINLOCK is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_VM=y/# CONFIG_DEBUG_VM is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_SLEEP_IN_IRQ=y/# CONFIG_DEBUG_SLEEP_IN_IRQ is not set/' config-nodebug + @perl -pi -e 's/CONFIG_FAULT_INJECTION=y/# CONFIG_FAULT_INJECTION is not set/' config-nodebug + @perl -pi -e 's/CONFIG_FAILSLAB=y/# CONFIG_FAILSLAB is not set/' config-nodebug + @perl -pi -e 's/CONFIG_FAIL_PAGE_ALLOC=y/# CONFIG_FAIL_PAGE_ALLOC is not set/' config-nodebug + @perl -pi -e 's/CONFIG_FAIL_IO_TIMEOUT=y/# CONFIG_FAIL_IO_TIMEOUT is not set/' config-nodebug + @perl -pi -e 's/CONFIG_FAIL_MAKE_REQUEST=y/# CONFIG_FAIL_MAKE_REQUEST is not set/' config-nodebug + @perl -pi -e 's/CONFIG_FAULT_INJECTION_DEBUG_FS=y/# CONFIG_FAULT_INJECTION_DEBUG_FS is not set/' config-nodebug + @perl -pi -e 's/CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y/# CONFIG_FAULT_INJECTION_STACKTRACE_FILTER is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_SG=y/# CONFIG_DEBUG_SG is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_WRITECOUNT=y/# CONFIG_DEBUG_WRITECOUNT is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_OBJECTS=y/# CONFIG_DEBUG_OBJECTS is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_OBJECTS_FREE=y/# CONFIG_DEBUG_OBJECTS_FREE is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_OBJECTS_TIMERS=y/# CONFIG_DEBUG_OBJECTS_TIMERS is not set/' config-nodebug + @perl -pi -e 's/CONFIG_X86_PTDUMP=y/# CONFIG_X86_PTDUMP is not set/' config-nodebug + @perl -pi -e 's/CONFIG_CAN_DEBUG_DEVICES=y/# CONFIG_CAN_DEBUG_DEVICES is not set/' config-nodebug + @perl -pi -e 's/CONFIG_MODULE_FORCE_UNLOAD=y/# CONFIG_MODULE_FORCE_UNLOAD is not set/' config-nodebug + @perl -pi -e 's/CONFIG_SYSCTL_SYSCALL_CHECK=y/# CONFIG_SYSCTL_SYSCALL_CHECK is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_NOTIFIERS=y/# CONFIG_DEBUG_NOTIFIERS is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DMA_API_DEBUG=y/# CONFIG_DMA_API_DEBUG is not set/' config-nodebug + @perl -pi -e 's/CONFIG_PM_TEST_SUSPEND=y/#\ CONFIG_PM_TEST_SUSPEND\ is\ not\ set/' config-generic + @perl -pi -e 's/CONFIG_BOOT_TRACER=y/#\ CONFIG_BOOT_TRACER\ is\ not\ set/' config-generic + @perl -pi -e 's/CONFIG_B43_DEBUG=y/# CONFIG_B43_DEBUG is not set/' config-generic + @perl -pi -e 's/CONFIG_B43LEGACY_DEBUG=y/# CONFIG_B43LEGACY_DEBUG is not set/' config-generic + @perl -pi -e 's/CONFIG_MMIOTRACE=y/# CONFIG_MMIOTRACE is not set/' config-nodebug + @perl -pi -e 's/# CONFIG_STRIP_ASM_SYMS is not set/CONFIG_STRIP_ASM_SYMS=y/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_CREDENTIALS=y/# CONFIG_DEBUG_CREDENTIALS is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y/# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set/' config-nodebug + @perl -pi -e 's/CONFIG_ACPI_DEBUG=y/# CONFIG_ACPI_DEBUG is not set/' config-nodebug + @perl -pi -e 's/CONFIG_EXT4_DEBUG=y/# CONFIG_EXT4_DEBUG is not set/' config-nodebug + @perl -pi -e 's/CONFIG_DEBUG_PERF_USE_VMALLOC=y/# CONFIG_DEBUG_PERF_USE_VMALLOC is not set/' config-nodebug + @perl -pi -e 's/CONFIG_JBD2_DEBUG=y/# CONFIG_JBD2_DEBUG is not set/' config-nodebug + + @perl -pi -e 's/CONFIG_DEBUG_PAGEALLOC=y/# CONFIG_DEBUG_PAGEALLOC is not set/' config-debug + @perl -pi -e 's/CONFIG_DEBUG_PAGEALLOC=y/# CONFIG_DEBUG_PAGEALLOC is not set/' config-nodebug + + @perl -pi -e 's/CONFIG_NR_CPUS=512/CONFIG_NR_CPUS=256/' config-x86_64-generic + + @perl -pi -e 's/^%define debugbuildsenabled 0/%define debugbuildsenabled 1/' kernel.spec + @perl -pi -e 's/^%define rawhide_skip_docs 1/%define rawhide_skip_docs 0/' kernel.spec + +rhel: + @perl -pi -e 's/# CONFIG_PPC_64K_PAGES is not set/CONFIG_PPC_64K_PAGES=y/' config-powerpc64 + @perl -pi -e 's/CONFIG_NR_CPUS=256/CONFIG_NR_CPUS=512/' config-x86_64-generic + +reconfig: + @rm -f kernel-*-config + @VERSION=$(KVERSION) make -f Makefile.config configs + @scripts/reconfig.sh + +unused-kernel-patches: + @for f in *.patch; do if [ -e $$f ]; then (egrep -q "^Patch[[:digit:]]+:[[:space:]]+$$f" $(SPECFILE) || echo "Unused: $$f") && egrep -q "^ApplyPatch[[:space:]]+$$f|^ApplyOptionalPatch[[:space:]]+$$f" $(SPECFILE) || echo "Unapplied: $$f"; fi; done + +# since i386 isn't a target... +compile compile-short: DIST_DEFINES += --target $(shell uname -m) + +# 'make local' also needs to build the noarch firmware package +local: noarch + +# +# Hacks for building vanilla (unpatched) kernel rpms. +# Use "make vanilla-TARGET" like "make TARGET" (make vanilla-scratch-build). +# +vanilla-%: $(SPECFILE:.spec=-vanilla.spec) + @$(MAKE) $* SPECFILE=$< + +$(SPECFILE:.spec=-vanilla.spec): $(SPECFILE) + @rm -f $@ + (echo %define nopatches 1; cat $<) > $@ + +#scratch-build: NAME = $(shell rpm $(RPM_DEFINES) $(DIST_DEFINES) -q --qf "%{NAME}\n" --specfile $(SPECFILE)| head -1) +#scratch-build: test-srpm +# $(BUILD_CLIENT) build $(BUILD_FLAGS) --scratch $(TARGET) \ +# $(SRCRPMDIR)/$(NAME)-$(VERSION)-$(RELEASE).src.rpm + +# Dismal kludge for building via brew from cvs after "make vanilla-tag". +ifdef BEEHIVE_SRPM_BUILD +export CHECKOUT_TAG ?= $(shell sed s/^.// CVS/Tag) +tag-pattern = $(TAG_NAME)-$(TAG_VERSION)-0_%_$(TAG_RELEASE) +ifeq (,$(filter-out $(tag-pattern),$(CHECKOUT_TAG))) +variant := $(patsubst $(tag-pattern),%,$(CHECKOUT_TAG)) +srpm: SPECFILE := $(wildcard $(SPECFILE:.spec=-$(variant).spec) \ + $(SPECFILE:.spec=.t.$(variant).spec)) +srpm beehive-sprm: RELEASE := 0.$(variant).$(RELEASE) +endif +endif + +# +# Hacks for building kernel rpms from upstream code plus local GIT branches. +# Use "make git/BRANCH/TARGET" like "make TARGET". +# Use "make git/BRANCH-fedora/TARGET" to include Fedora patches on top. +# +ifndef GIT_SPEC +git/%: + @$(MAKE) GIT_SPEC=$(subst /,-,$(*D)) git-$(*F) +else +git-%: $(SPECFILE:.spec=.t.$(GIT_SPEC).spec) + @$(MAKE) GIT_SPEC= $* SPECFILE=$< +endif + +# +# Your git-branches.mk file can define GIT_DIR, e.g.: +# GIT_DIR = ${HOME}/kernel/.git +# Make sure GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL are also set +# or your rpm changelogs will look like crap. +# +# For each branch it can define a variable branch-BRANCH or tag-BRANCH +# giving the parent of BRANCH to diff against in a separate patch. If +# the parent is unknown, it will use $(branch-upstream) defaulting to +# "refs/remotes/upstream/master". +# +# Defining tag-BRANCH means the tag corresponds to an upstream patch in +# the sources file, so that is used instead of generating a patch with +# git. If there is no tag-upstream defined, it will figure out a vNNN +# tag or vNNN-gitN pseudo-tag from the last patch in the sources file. +# For example: +# tag-some-hacks = v2.6.21-rc5 +# branch-more-hacks = some-hacks +# Leads to patches: +# git diff v2.6.21-rc5..more-hacks > linux-2.6.21-rc5-some-hacks.patch +# git diff some-hacks..more-hacks > linux-2.6.21-rc5-more-hacks.patch +# Whereas having no git-branches.mk at all but doing +# "make GIT_DIR=... git/mybranch/test-srpm" does: +# id=`cat patch-2.6.21-rc5-git4.id` # auto-fetched via upstream file +# git diff $id..upstream > linux-2.6.21-rc5-git4-upstream.patch +# git diff upstream..mybranch > linux-2.6.21-rc5-git4-mybranch.patch +# If the upstream patch (or any branch patch) is empty it's left out. +# +git-branches.mk:; +-include git-branches.mk + +branch-upstream ?= refs/remotes/upstream/master + +ifdef GIT_DIR +export GIT_DIR +export GIT_AUTHOR_NAME +export GIT_AUTHOR_EMAIL +gen-patches ?= gen-patches + +ifndef havespec +$(SPECFILE:.spec=.t.%-fedora.spec): $(SPECFILE) $(gen-patches) FORCE + ./$(gen-patches) --fedora < $< > $@ $(gen-patches-args) +$(SPECFILE:.spec=.t.%.spec): $(SPECFILE) $(gen-patches) FORCE + ./$(gen-patches) < $< > $@ $(gen-patches-args) +.PRECIOUS: $(SPECFILE:.spec=.t.%.spec) $(SPECFILE:.spec=.t.%-fedora.spec) +endif + +spec-%: $(SPECFILE:.spec=.t.%.spec) ; +$(SPECFILE):; +FORCE:; + +branch-of-* = $(firstword $(head-$*) $*) +gen-patches-args = --name $* v$(KVERSION) $(call heads,$(branch-of-*)) +define heads +$(if $(tag-$1),$(filter-out v$(KVERSION),$(tag-$1)),\ + $(call heads,$(firstword $(branch-$1) $(branch-upstream)))) $1 +endef + +files-%-fedora: + @echo $(SPECFILE:.spec=.t.$*-fedora.spec) + @$(call list-patches,$(branch-of-*)) +files-%: + @echo $(SPECFILE:.spec=.t.$*.spec) + @$(call list-patches,$(branch-of-*)) +define list-patches +$(if $(tag-$1),version=$(patsubst v%,%,$(tag-$1)),\ + $(call list-patches,$(firstword $(branch-$1) $(branch-upstream)))); \ +echo linux-$${version}-$(patsubst refs/remotes/%/master,%,$1).patch +endef + +ifndef tag-$(branch-upstream) +tag-$(branch-upstream) := $(shell \ + sed -n 's/^.* *//;s/\.bz2$$//;s/patch-/v/;/^v/h;$${g;p}' sources) +endif +endif diff --git a/Makefile.config b/Makefile.config new file mode 100644 index 000000000..53812fa9b --- /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/README.txt b/README.txt new file mode 100644 index 000000000..482f8ea5b --- /dev/null +++ b/README.txt @@ -0,0 +1,67 @@ + + Kernel package tips & tricks. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The kernel is one of the more complicated packages in the distro, and +for the newcomer, some of the voodoo in the spec file can be somewhat scary. +This file attempts to document some of the magic. + + +Speeding up make prep +--------------------- +The kernel is nearly 500MB of source code, and as such, 'make prep' +takes a while. The spec file employs some trickery so that repeated +invocations of make prep don't take as long. Ordinarily the %prep +phase of a package will delete the tree it is about to untar/patch. +The kernel %prep keeps around an unpatched version of the tree, +and makes a symlink tree clone of that clean tree and than applies +the patches listed in the spec to the symlink tree. +This makes a huge difference if you're doing multiple make preps a day. +As an added bonus, doing a diff between the clean tree and the symlink +tree is slightly faster than it would be doing two proper copies of the tree. + + +build logs. +----------- +There's a convenience helper script in scripts/grab-logs.sh +that will grab the build logs from koji for the kernel version reported +by make verrel + + +config heirarchy. +----------------- +Instead of having to maintain a config file for every arch variant we build on, +the kernel spec uses a nested system of configs. At the top level, is +config-generic. Add options here that should be present in every possible +config on all architectures. +Beneath this are per-arch overrides. For example config-x86-generic add +additional x86 specific options, and also _override_ any options that were +set in config-generic. +There exist two additional overrides, config-debug, and config-nodebug, +which override -generic, and the per-arch overrides. It is documented +further below. + +debug options. +-------------- +This is a little complicated, as the purpose & meaning of this changes +depending on where we are in the release cycle. +If we are building for a current stable release, 'make release' has +typically been run already, which sets up the following.. +- Two builds occur, a 'kernel' and a 'kernel-debug' flavor. +- kernel-debug will get various heavyweight debugging options like + lockdep etc turned on. + +If we are building for rawhide, 'make debug' has been run, which changes +the status quo to: +- We only build one kernel 'kernel' +- The debug options from 'config-debug' are always turned on. +This is done to increase coverage testing, as not many people actually +run kernel-debug. + +To add new debug options, add an option to _both_ config-debug and config-nodebug, +and also new stanzas to the Makefile 'debug' and 'release' targets. + +Sometimes debug options get added to config-generic, or per-arch overrides +instead of config-[no]debug. In this instance, the options should have no +discernable performance impact, otherwise they belong in the debug files. + diff --git a/acpi-ec-add-delay-before-write.patch b/acpi-ec-add-delay-before-write.patch new file mode 100644 index 000000000..af49cccbd --- /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 000000000..ac68e7115 --- /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 ++ ++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 ++ * ++ * Copyright (C) 2006 James McKenzie ++ * Copyright (C) 2008 Greg Kroah-Hartman ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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/add-support-for-ricoh-e822-sdhci.patch b/add-support-for-ricoh-e822-sdhci.patch new file mode 100644 index 000000000..93bc45993 --- /dev/null +++ b/add-support-for-ricoh-e822-sdhci.patch @@ -0,0 +1,141 @@ +diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c +index e035664..3d7f790 100644 +--- a/drivers/mmc/host/sdhci-pci.c ++++ b/drivers/mmc/host/sdhci-pci.c +@@ -86,7 +86,30 @@ static int ricoh_probe(struct sdhci_pci_chip *chip) + if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG || + chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY) + chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; ++ return 0; ++} ++ ++static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) ++{ ++ slot->host->caps = ++ ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT) ++ & SDHCI_TIMEOUT_CLK_MASK) | ++ ++ ((0x21 << SDHCI_CLOCK_BASE_SHIFT) ++ & SDHCI_CLOCK_BASE_MASK) | + ++ SDHCI_TIMEOUT_CLK_UNIT | ++ SDHCI_CAN_VDD_330 | ++ SDHCI_CAN_DO_SDMA; ++ return 0; ++} ++ ++static int ricoh_mmc_resume(struct sdhci_pci_chip *chip) ++{ ++ /* Apply a delay to allow controller to settle */ ++ /* Otherwise it becomes confused if card state changed ++ during suspend */ ++ msleep(500); + return 0; + } + +@@ -95,6 +118,15 @@ static const struct sdhci_pci_fixes sdhci_ricoh = { + .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR, + }; + ++static const struct sdhci_pci_fixes sdhci_ricoh_mmc = { ++ .probe_slot = ricoh_mmc_probe_slot, ++ .resume = ricoh_mmc_resume, ++ .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | ++ SDHCI_QUIRK_CLOCK_BEFORE_RESET | ++ SDHCI_QUIRK_NO_CARD_NO_RESET | ++ SDHCI_QUIRK_MISSING_CAPS ++}; ++ + static const struct sdhci_pci_fixes sdhci_ene_712 = { + .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | + SDHCI_QUIRK_BROKEN_DMA, +@@ -307,6 +339,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = { + }, + + { ++ .vendor = PCI_VENDOR_ID_RICOH, ++ .device = 0x843, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, ++ }, ++ ++ { ++ .vendor = PCI_VENDOR_ID_RICOH, ++ .device = 0xe822, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, ++ }, ++ ++ { + .vendor = PCI_VENDOR_ID_ENE, + .device = PCI_DEVICE_ID_ENE_CB712_SD, + .subvendor = PCI_ANY_ID, +diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c +index c279fbc..7d4bff0 100644 +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -1147,6 +1147,11 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ++ if (ios->bus_width == MMC_BUS_WIDTH_8) ++ ctrl |= SDHCI_CTRL_8BITBUS; ++ else ++ ctrl &= ~SDHCI_CTRL_8BITBUS; ++ + if (ios->bus_width == MMC_BUS_WIDTH_4) + ctrl |= SDHCI_CTRL_4BITBUS; + else +@@ -1678,7 +1683,8 @@ int sdhci_add_host(struct sdhci_host *host) + host->version); + } + +- caps = sdhci_readl(host, SDHCI_CAPABILITIES); ++ caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : ++ sdhci_readl(host, SDHCI_CAPABILITIES); + + if (host->quirks & SDHCI_QUIRK_FORCE_DMA) + host->flags |= SDHCI_USE_SDMA; +diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h +index ce5f1d7..b5e2761 100644 +--- a/drivers/mmc/host/sdhci.h ++++ b/drivers/mmc/host/sdhci.h +@@ -70,6 +70,7 @@ + #define SDHCI_CTRL_ADMA1 0x08 + #define SDHCI_CTRL_ADMA32 0x10 + #define SDHCI_CTRL_ADMA64 0x18 ++#define SDHCI_CTRL_8BITBUS 0x20 + + #define SDHCI_POWER_CONTROL 0x29 + #define SDHCI_POWER_ON 0x01 +@@ -125,7 +126,7 @@ + #define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ + SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ + SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ +- SDHCI_INT_DATA_END_BIT | SDHCI_ADMA_ERROR) ++ SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR) + #define SDHCI_INT_ALL_MASK ((unsigned int)-1) + + #define SDHCI_ACMD12_ERR 0x3C +@@ -235,6 +236,10 @@ struct sdhci_host { + /* Controller uses SDCLK instead of TMCLK for data timeouts */ + #define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24) + ++/* RHEL6 miss some quirks */ ++ ++/* Controller is missing device caps. Use caps provided by host */ ++#define SDHCI_QUIRK_MISSING_CAPS (1<<28) + int irq; /* Device IRQ */ + void __iomem * ioaddr; /* Mapped address */ + +@@ -286,6 +291,8 @@ struct sdhci_host { + + struct timer_list timer; /* Timer for timeouts */ + ++ unsigned int caps; /* Alternative capabilities */ ++ + unsigned long private[0] ____cacheline_aligned; + }; + 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 000000000..079260349 --- /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 +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 +Signed-off-by: Chris Mason +--- + +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/config-arm b/config-arm new file mode 100644 index 000000000..0c90cd5a9 --- /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 000000000..0ded62b16 --- /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 000000000..1a949eb01 --- /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 is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +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 000000000..7f49f6160 --- /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 000000000..51e1bc91d --- /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 000000000..26f0fa05a --- /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 000000000..598fbb547 --- /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 000000000..39f9f745d --- /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 000000000..e60f59cdf --- /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 000000000..d9f7983db --- /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 000000000..197ae8378 --- /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 000000000..632983bff --- /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 000000000..fc09d14fd --- /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 000000000..e0f1d1ebc --- /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 000000000..175f57b09 --- /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 000000000..8df0ad44f --- /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 +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 +Signed-off-by: Herbert Xu +--- + 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/crystalhd-2.6.34-staging.patch b/crystalhd-2.6.34-staging.patch new file mode 100644 index 000000000..671bd3911 --- /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 + +--- + 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 ++Naren Sankar ++Jarod Wilson ++Scott Davilla ++Manu Abraham ++ +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 . ++ *******************************************************************/ ++ ++#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 . ++ *******************************************************************/ ++ ++#ifndef _BC_DTS_GLOB_LNX_H_ ++#define _BC_DTS_GLOB_LNX_H_ ++ ++#ifdef __LINUX_USER__ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 . ++ *******************************************************************/ ++ ++#ifndef _BC_DTS_TYPES_H_ ++#define _BC_DTS_TYPES_H_ ++ ++#ifdef __LINUX_USER__ // Don't include these for KERNEL.. ++#include ++#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 . ++ ***************************************************************************/ ++ ++#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 . ++ **********************************************************************/ ++ ++#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 . ++ **********************************************************************/ ++ ++#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 . ++ **********************************************************************/ ++ ++#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 . ++ **********************************************************************/ ++ ++#include ++#include ++#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 . ++ **********************************************************************/ ++ ++#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 . ++ **********************************************************************/ ++ ++#include ++ ++#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 "); ++MODULE_AUTHOR("Prasad Bolisetty "); ++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 . ++ **********************************************************************/ ++ ++#ifndef _CRYSTALHD_LNX_H_ ++#define _CRYSTALHD_LNX_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 . ++ **********************************************************************/ ++ ++#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(¤t->mm->mmap_sem); ++ res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ, ++ 0, dio->pages, NULL); ++ up_read(¤t->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 . ++ **********************************************************************/ ++ ++#ifndef _CRYSTALHD_MISC_H_ ++#define _CRYSTALHD_MISC_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 000000000..26beabfc7 --- /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-resume-force-mode.patch b/drm-i915-resume-force-mode.patch new file mode 100644 index 000000000..3e6c6484b --- /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 + + 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 +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-i915-sanity-check-pread-pwrite.patch b/drm-i915-sanity-check-pread-pwrite.patch new file mode 100644 index 000000000..ca6d2bc82 --- /dev/null +++ b/drm-i915-sanity-check-pread-pwrite.patch @@ -0,0 +1,90 @@ +From ce9d419dbecc292cc3e06e8b1d6d123d3fa813a4 Mon Sep 17 00:00:00 2001 +From: Chris Wilson +Date: Sun, 26 Sep 2010 20:50:05 +0100 +Subject: drm/i915: Sanity check pread/pwrite + +From: Chris Wilson + +commit ce9d419dbecc292cc3e06e8b1d6d123d3fa813a4 upstream. + +Move the access control up from the fast paths, which are no longer +universally taken first, up into the caller. This then duplicates some +sanity checking along the slow paths, but is much simpler. +Tracked as CVE-2010-2962. + +Reported-by: Kees Cook +Signed-off-by: Chris Wilson +Signed-off-by: Greg Kroah-Hartman +Backported-by: Chuck Ebbert 2.6.32 + +--- + drivers/gpu/drm/i915/i915_gem.c | 28 ++++++++++++++++++++-------- + 1 file changed, 20 insertions(+), 8 deletions(-) + +--- a/drivers/gpu/drm/i915/i915_gem.c ++++ b/drivers/gpu/drm/i915/i915_gem.c +@@ -465,8 +465,15 @@ i915_gem_pread_ioctl(struct drm_device * + */ + if (args->offset > obj->size || args->size > obj->size || + args->offset + args->size > obj->size) { +- drm_gem_object_unreference(obj); +- return -EINVAL; ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (!access_ok(VERIFY_WRITE, ++ (char __user *)(uintptr_t)args->data_ptr, ++ args->size)) { ++ ret = -EFAULT; ++ goto err; + } + + if (i915_gem_object_needs_bit17_swizzle(obj)) { +@@ -478,8 +485,8 @@ i915_gem_pread_ioctl(struct drm_device * + file_priv); + } + ++err: + drm_gem_object_unreference(obj); +- + return ret; + } + +@@ -568,8 +575,6 @@ i915_gem_gtt_pwrite_fast(struct drm_devi + + user_data = (char __user *) (uintptr_t) args->data_ptr; + remain = args->size; +- if (!access_ok(VERIFY_READ, user_data, remain)) +- return -EFAULT; + + + mutex_lock(&dev->struct_mutex); +@@ -928,8 +933,15 @@ i915_gem_pwrite_ioctl(struct drm_device + */ + if (args->offset > obj->size || args->size > obj->size || + args->offset + args->size > obj->size) { +- drm_gem_object_unreference(obj); +- return -EINVAL; ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (!access_ok(VERIFY_READ, ++ (char __user *)(uintptr_t)args->data_ptr, ++ args->size)) { ++ ret = -EFAULT; ++ goto err; + } + + /* We can only do the GTT pwrite on untiled buffers, as otherwise +@@ -963,8 +975,8 @@ i915_gem_pwrite_ioctl(struct drm_device + DRM_INFO("pwrite failed %d\n", ret); + #endif + ++err: + drm_gem_object_unreference(obj); +- + return ret; + } + diff --git a/drm-intel-945gm-stability-fixes.patch b/drm-intel-945gm-stability-fixes.patch new file mode 100644 index 000000000..ff661cf0a --- /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 000000000..069f2769a --- /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 000000000..e7047508d --- /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 000000000..af3fd8f9f --- /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 000000000..c6cac6926 --- /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 000000000..9de59b374 --- /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 000000000..601e200eb --- /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 000000000..299aa6409 --- /dev/null +++ b/drm-nouveau-g80-ctxprog.patch @@ -0,0 +1,10306 @@ +From c771c11d2febd217b9ee11156211296c7aad77cc Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +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 000000000..208400b1a --- /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 000000000..4bc1fc133 --- /dev/null +++ b/drm-nouveau-mutex.patch @@ -0,0 +1,56 @@ +From 967c89306de560a6da9539d24a0d63cb036d58ed Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Tue, 16 Feb 2010 11:14:14 +1000 +Subject: [PATCH] drm/nouveau: use mutex for vbios lock + +Signed-off-by: Ben Skeggs +--- + 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-nva3-noaccel.patch b/drm-nouveau-nva3-noaccel.patch new file mode 100644 index 000000000..505c7240e --- /dev/null +++ b/drm-nouveau-nva3-noaccel.patch @@ -0,0 +1,105 @@ +From 030e105efc9a29c7d34fb59fb0e0a40e54178299 Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Wed, 30 Jun 2010 13:34:05 +1000 +Subject: [PATCH] drm/nouveau: disable acceleration on NVA3/NVA5/NVA8 by default + +There's an GPU lockup problem for which the cause is currently unknown +on these chipsets. + +Until it's resolved, it's better to leave the user with a working system +without acceleration than to have random lockups. + +With this patch, acceleration will be off by default if a known problem +chipset is detected, but can be re-enabled with nouveau.noaccel=0 on +the kernel commandline. + +Signed-off-by: Ben Skeggs + +[ cebbert@redhat.com : Backport to F12 and fix some module parameter descriptions. ] +--- + +--- a/drivers/gpu/drm/nouveau/nouveau_drv.c ++++ b/drivers/gpu/drm/nouveau/nouveau_drv.c +@@ -75,11 +75,11 @@ int nouveau_ignorelid = 0; + int nouveau_ignorelid = 0; + module_param_named(ignorelid, nouveau_ignorelid, int, 0400); + +-MODULE_PARM_DESC(noagp, "Disable all acceleration"); ++MODULE_PARM_DESC(noaccel, "Disable all acceleration"); +-int nouveau_noaccel = 0; ++int nouveau_noaccel = -1; + module_param_named(noaccel, nouveau_noaccel, int, 0400); + +-MODULE_PARM_DESC(noagp, "Disable fbcon acceleration"); ++MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration"); + int nouveau_nofbaccel = 0; + module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400); + +--- a/drivers/gpu/drm/nouveau/nouveau_drv.h ++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h +@@ -493,6 +493,7 @@ enum nouveau_card_type { + + struct drm_nouveau_private { + struct drm_device *dev; ++ bool noaccel; + enum { + NOUVEAU_CARD_INIT_DOWN, + NOUVEAU_CARD_INIT_DONE, +--- a/drivers/gpu/drm/nouveau/nouveau_state.c ++++ b/drivers/gpu/drm/nouveau/nouveau_state.c +@@ -435,7 +435,7 @@ nouveau_card_init(struct drm_device *dev) + if (ret) + goto out_timer; + +- if (nouveau_noaccel) ++ if (dev_priv->noaccel) + engine->graph.accel_blocked = true; + else { + /* PGRAPH */ +@@ -491,10 +491,10 @@ out_display: + out_irq: + drm_irq_uninstall(dev); + out_fifo: +- if (!nouveau_noaccel) ++ if (!dev_priv->noaccel) + engine->fifo.takedown(dev); + out_graph: +- if (!nouveau_noaccel) ++ if (!dev_priv->noaccel) + engine->graph.takedown(dev); + out_fb: + engine->fb.takedown(dev); +@@ -532,7 +532,7 @@ static void nouveau_card_takedown(struct drm_device *dev) + dev_priv->channel = NULL; + } + +- if (!nouveau_noaccel) { ++ if (!dev_priv->noaccel) { + engine->fifo.takedown(dev); + engine->graph.takedown(dev); + } +@@ -691,6 +691,21 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) + NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n", + dev_priv->card_type, reg0); + ++ if (nouveau_noaccel == -1) { ++ switch (dev_priv->chipset) { ++ case 0xa3: ++ case 0xa5: ++ case 0xa8: ++ dev_priv->noaccel = true; ++ break; ++ default: ++ dev_priv->noaccel = false; ++ break; ++ } ++ } else { ++ dev_priv->noaccel = (nouveau_noaccel != 0); ++ } ++ + /* map larger RAMIN aperture on NV40 cards */ + dev_priv->ramin = NULL; + if (dev_priv->card_type >= NV_40) { +-- +1.7.2 + diff --git a/drm-nouveau-safetile-getparam.patch b/drm-nouveau-safetile-getparam.patch new file mode 100644 index 000000000..09f9734d4 --- /dev/null +++ b/drm-nouveau-safetile-getparam.patch @@ -0,0 +1,26 @@ +From 2aa78d8442bd947637ed81da00fd9c22232d2ed0 Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +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 000000000..b04de64a7 --- /dev/null +++ b/drm-nouveau-tvout-disable.patch @@ -0,0 +1,57 @@ +From 1c48b3294c0efe6b28ef84139eb838725bb34ccc Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +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 (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 000000000..6eaa1e10f --- /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 000000000..df12ec8a8 --- /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-upgrayed-fixes.patch b/drm-upgrayed-fixes.patch new file mode 100644 index 000000000..e437dc21f --- /dev/null +++ b/drm-upgrayed-fixes.patch @@ -0,0 +1,1092 @@ +commit 9b86cec953ee8ae21be2183f34b8c8d2fcf14ecb +Author: Alex Deucher +Date: Thu Feb 11 10:47:52 2010 -0500 + + drm/radeon/kms: remove HDP flushes from fence emit (v2) + + r600_ioctl_wait_idle() now handles this. + + v2: update blit fence counts + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie + +commit 817dbb17e5e8fcdfb7bb3d18f3ceb15078dd6050 +Author: Dave Airlie +Date: Wed Mar 3 13:22:20 2010 +1000 + + drm/radeon: Add asic hook for dma copy to r200 cards. + + r200 cards have dma engine which can be used to tranfer data + between vram and system memory. + + r300 dma engine registers match r200 dma engine. Enabling + dma copy for r200 is simple as hooking r200 asic to already + existing function r300_copy_dma. + + Rename r300_dma_copy to r200_dma_copyto reflect that supports + starts from r200 cards. + + v2: Created a new asic object for r200 cards. + + Signed-off-by: Pauli Nieminen + Signed-off-by: Dave Airlie + + Conflicts: + + drivers/gpu/drm/radeon/r300.c + drivers/gpu/drm/radeon/radeon_asic.h + +commit 30988e41d355e2011d1a340d6b889deeceeb447d +Author: Pauli Nieminen +Date: Thu Feb 11 17:55:35 2010 +0000 + + drm/radeon/kms: Create asic structure for r300 pcie cards. + + Setting global asic structure to point to different function + would cause problem in system where is multiple r300 cards + with different bus type. + + r300_asic_pcie is just copy from r300_asic with gart tlb + functions replaced with pcie versions. + + Signed-off-by: Pauli Nieminen + Signed-off-by: Dave Airlie + +commit 0faed138b285ef54c27a6c17016d8ff718c40113 +Author: Jesse Barnes +Date: Fri Feb 12 09:30:00 2010 -0800 + + drm/i915: give up on 8xx lid status + + These old machines more often than not lie about their lid state. So + don't use it to detect LVDS presence, but leave the event handler to + deal with lid open/close, when we might need to reset the mode. + + Fixes kernel bug #15248 + + Signed-off-by: Jesse Barnes + Cc: stable@kernel.org + Signed-off-by: Eric Anholt + +commit 19d2051901dc28cf96d2b29e5f5c5f6725e90105 +Author: Jerome Glisse +Date: Fri Feb 26 19:14:12 2010 +0000 + + drm/radeon/kms: initialize set_surface_reg reg for rs600 asic + + rs600 asic was missing set_surface_reg callback leading to + oops. + + Signed-off-by: Jerome Glisse + Signed-off-by: Dave Airlie + +commit 884d6cda747259e5ba56c00cdc8a5406a45209c9 +Author: Dave Airlie +Date: Thu Feb 11 14:28:58 2010 +1000 + + drm/kms: fix fb_changed = true else statement + + a patch from Roel was wrong, fix this properly, really + if the fb ptrs are different fb changed shuold be true. + + Signed-off-by: Dave Airlie + +commit b6428034e4bc7c9d57d1a7394a4d77fc80737cf4 +Author: Alex Deucher +Date: Tue Feb 23 21:56:12 2010 -0500 + + drm/radeon/kms/atom: fix shr/shl ops + + The whole attribute table is valid for + shr/shl ops. + + Fixes fdo bug 26668 + + Signed-off-by: Alex Deucher + Cc: stable@kernel.org + Signed-off-by: Dave Airlie + +commit 93e49a04b1118c8036ccba855c1b8b095fd6b51b +Author: Maarten Maathuis +Date: Sat Feb 20 03:22:21 2010 +0100 + + drm/ttm: handle OOM in ttm_tt_swapout + + - Without this change I get a general protection fault. + - Also use PTR_ERR where applicable. + + Signed-off-by: Maarten Maathuis + Reviewed-by: Dave Airlie + Acked-by: Thomas Hellstrom + Cc: stable@kernel.org + Signed-off-by: Dave Airlie + +commit 36e437d37c849da2ad35d4ba3c0662d8a06f9f9f +Author: Zhang Rui +Date: Tue Feb 16 04:16:55 2010 -0500 + + ACPI, i915: blacklist Clevo M5x0N bad_lid state + + Wrong Lid state reported. + Need to blacklist this machine for LVDS detection. + + Signed-off-by: Zhang Rui + Signed-off-by: Len Brown + +commit 5bf1252153707877ae62bef73fe00731972ef18b +Author: Adam Jackson +Date: Mon Feb 15 22:15:39 2010 +0000 + + drm/edid: Fix interlaced detailed timings to be frame size, not field. + + cf. https://bugzilla.redhat.com/show_bug.cgi?id=533561 + + Signed-off-by: Adam Jackson + Signed-off-by: Dave Airlie + +commit 268d51cd56bcf861bd668163c1b618c96d8b3701 +Author: Rafael J. Wysocki +Date: Thu Feb 18 23:06:27 2010 +0100 + + i915 / PM: Fix hibernate regression caused by suspend/resume splitting + + Commit 84b79f8d2882b0a84330c04839ed4d3cefd2ff77 (drm/i915: Fix crash + while aborting hibernation) attempted to fix a regression introduced + by commit cbda12d77ea590082edb6d30bd342a67ebc459e0 (drm/i915: + implement new pm ops for i915), but it went too far trying to split + the freeze/suspend and resume/thaw parts of the code. As a result, + it introduced another regression, which only is visible on some systems. + + Fix the problem by merging i915_drm_suspend() with + i915_drm_freeze() and moving some code from i915_resume() + into i915_drm_thaw(), so that intel_opregion_free() and + intel_opregion_init() are also executed in the freeze and thaw code + paths, respectively. + + Signed-off-by: Rafael J. Wysocki + Reported-and-tested-by: Pedro Ribeiro + Tested-by: Tino Keitel + Acked-by: Eric Anholt + Signed-off-by: Linus Torvalds + +commit 8d205562d42d397086d829ba4b8f3911c3dca682 +Author: Francisco Jerez +Date: Sat Feb 20 07:30:15 2010 +1000 + + drm/ttm: fix caching problem on non-PAT systems. + + http://bugzilla.kernel.org/show_bug.cgi?id=15328 + + This fixes a serious regression on AGP/non-PAT systems, where + pages were ending up in the wrong state and slowing down the + whole system. + + [airlied: taken this from the bug as the other option is to revert + the change which caused it]. + + Tested-by: John W. Linville (in bug). + Signed-off-by: Dave Airlie + +commit 7a2bb26f41ceb9b17805dcae93311004e03bd1d0 +Author: Dave Airlie +Date: Sat Feb 20 09:17:18 2010 +1000 + + drm/radeon: bump the UMS driver version number to indicate rv740 fix + + This lets UMS userspace know the rv740 fix is in. For KMS we can + consider the kernel release to be the v2.0.0 release so we don't need the + bump there. + + Signed-off-by: Dave Airlie + +commit 211dc7044003e3ed8c7a2e2b646bf6937b921960 +Author: Jerome Glisse +Date: Thu Feb 18 13:13:29 2010 +0000 + + drm/radeon/kms: free fence IB if it wasn't emited at IB free time + + If at IB free time fence wasn't emited that means the IB wasn't + scheduled because an error occured somewhere, thus we can free + then fence and mark the IB as free. + + Signed-off-by: Jerome Glisse + Signed-off-by: Dave Airlie + +commit b2449d6d4de19459d616011fbed87aaa485f5e71 +Author: Alex Deucher +Date: Fri Feb 19 16:07:02 2010 -0500 + + drm/radeon/rv740: fix backend setup + + This patch fixes occlusion queries and rendering errors + on rv740 boards. Hardcoding the backend map is not an optimal + solution, but a better fix is being worked on. + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie + +commit 79b5531abdad5c266e87a5c35975e5a92f4aba20 +Author: Alex Deucher +Date: Fri Feb 19 02:13:56 2010 -0500 + + drm/radeon/kms: fix shared ddc detection + + Just compare the i2c id since the i2c structs + may be slighly different. + + Fixes fdo bug 26616. + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie + +commit d2ffb93351a506acdfede450778964eb862e63b7 +Author: Alex Deucher +Date: Thu Feb 18 14:14:58 2010 -0500 + + drm/radeon/kms/rs600: add connector quirk + + rs600 board lists DVI port as HDMI. + + Fixes fdo bug 26605 + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie +diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c +index 7d0f00a..f2aaf39 100644 +--- a/drivers/gpu/drm/drm_crtc_helper.c ++++ b/drivers/gpu/drm/drm_crtc_helper.c +@@ -836,11 +836,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) + mode_changed = true; + } else if (set->fb == NULL) { + mode_changed = true; +- } else if ((set->fb->bits_per_pixel != +- set->crtc->fb->bits_per_pixel) || +- set->fb->depth != set->crtc->fb->depth) +- fb_changed = true; +- else ++ } else + fb_changed = true; + } + +diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c +index f41e91c..f97e7c4 100644 +--- a/drivers/gpu/drm/drm_edid.c ++++ b/drivers/gpu/drm/drm_edid.c +@@ -598,6 +598,50 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev, + return mode; + } + ++/* ++ * EDID is delightfully ambiguous about how interlaced modes are to be ++ * encoded. Our internal representation is of frame height, but some ++ * HDTV detailed timings are encoded as field height. ++ * ++ * The format list here is from CEA, in frame size. Technically we ++ * should be checking refresh rate too. Whatever. ++ */ ++static void ++drm_mode_do_interlace_quirk(struct drm_display_mode *mode, ++ struct detailed_pixel_timing *pt) ++{ ++ int i; ++ static const struct { ++ int w, h; ++ } cea_interlaced[] = { ++ { 1920, 1080 }, ++ { 720, 480 }, ++ { 1440, 480 }, ++ { 2880, 480 }, ++ { 720, 576 }, ++ { 1440, 576 }, ++ { 2880, 576 }, ++ }; ++ static const int n_sizes = ++ sizeof(cea_interlaced)/sizeof(cea_interlaced[0]); ++ ++ if (!(pt->misc & DRM_EDID_PT_INTERLACED)) ++ return; ++ ++ for (i = 0; i < n_sizes; i++) { ++ if ((mode->hdisplay == cea_interlaced[i].w) && ++ (mode->vdisplay == cea_interlaced[i].h / 2)) { ++ mode->vdisplay *= 2; ++ mode->vsync_start *= 2; ++ mode->vsync_end *= 2; ++ mode->vtotal *= 2; ++ mode->vtotal |= 1; ++ } ++ } ++ ++ mode->flags |= DRM_MODE_FLAG_INTERLACE; ++} ++ + /** + * drm_mode_detailed - create a new mode from an EDID detailed timing section + * @dev: DRM device (needed to create new mode) +@@ -680,8 +724,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, + + drm_mode_set_name(mode); + +- if (pt->misc & DRM_EDID_PT_INTERLACED) +- mode->flags |= DRM_MODE_FLAG_INTERLACE; ++ drm_mode_do_interlace_quirk(mode, pt); + + if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { + pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; +diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c +index 79beffc..cf4cb3e 100644 +--- a/drivers/gpu/drm/i915/i915_drv.c ++++ b/drivers/gpu/drm/i915/i915_drv.c +@@ -176,6 +176,8 @@ MODULE_DEVICE_TABLE(pci, pciidlist); + + static int i915_drm_freeze(struct drm_device *dev) + { ++ struct drm_i915_private *dev_priv = dev->dev_private; ++ + pci_save_state(dev->pdev); + + /* If KMS is active, we do the leavevt stuff here */ +@@ -191,17 +193,12 @@ static int i915_drm_freeze(struct drm_device *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; ++ ++ return 0; + } + + static int i915_suspend(struct drm_device *dev, pm_message_t state) +@@ -221,8 +218,6 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state) + if (error) + return error; + +- i915_drm_suspend(dev); +- + if (state.event == PM_EVENT_SUSPEND) { + /* Shut down the device */ + pci_disable_device(dev->pdev); +@@ -237,6 +232,10 @@ static int i915_drm_thaw(struct drm_device *dev) + struct drm_i915_private *dev_priv = dev->dev_private; + int error = 0; + ++ i915_restore_state(dev); ++ ++ intel_opregion_init(dev, 1); ++ + /* KMS EnterVT equivalent */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + mutex_lock(&dev->struct_mutex); +@@ -263,10 +262,6 @@ static int i915_resume(struct drm_device *dev) + + pci_set_master(dev->pdev); + +- i915_restore_state(dev); +- +- intel_opregion_init(dev, 1); +- + return i915_drm_thaw(dev); + } + +@@ -423,8 +418,6 @@ static int i915_pm_suspend(struct device *dev) + if (error) + return error; + +- i915_drm_suspend(drm_dev); +- + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + +@@ -464,13 +457,8 @@ 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; ++ return i915_drm_freeze(drm_dev); + } + + const struct dev_pm_ops i915_pm_ops = { +diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c +index b1d0acb..93031a7 100644 +--- a/drivers/gpu/drm/i915/intel_lvds.c ++++ b/drivers/gpu/drm/i915/intel_lvds.c +@@ -636,6 +636,13 @@ static const struct dmi_system_id bad_lid_status[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "PC-81005"), + }, + }, ++ { ++ .ident = "Clevo M5x0N", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."), ++ DMI_MATCH(DMI_BOARD_NAME, "M5x0N"), ++ }, ++ }, + { } + }; + +@@ -648,8 +655,15 @@ static const struct dmi_system_id bad_lid_status[] = { + */ + static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector) + { ++ struct drm_device *dev = connector->dev; + enum drm_connector_status status = connector_status_connected; + ++ /* ACPI lid methods were generally unreliable in this generation, so ++ * don't even bother. ++ */ ++ if (IS_I8XX(dev)) ++ return connector_status_connected; ++ + if (!dmi_check_system(bad_lid_status) && !acpi_lid_open()) + status = connector_status_disconnected; + +diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c +index 592ce91..4362f82 100644 +--- a/drivers/gpu/drm/radeon/atom.c ++++ b/drivers/gpu/drm/radeon/atom.c +@@ -882,8 +882,6 @@ static void atom_op_shl(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(ctx, attr, ptr); +@@ -898,8 +896,6 @@ static void atom_op_shr(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(ctx, attr, ptr); +diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c +index ff1e0cd..1146c99 100644 +--- a/drivers/gpu/drm/radeon/r200.c ++++ b/drivers/gpu/drm/radeon/r200.c +@@ -31,6 +31,7 @@ + #include "radeon_reg.h" + #include "radeon.h" + ++#include "r100d.h" + #include "r200_reg_safe.h" + + #include "r100_track.h" +@@ -79,6 +80,51 @@ static int r200_get_vtx_size_0(uint32_t vtx_fmt_0) + return vtx_size; + } + ++int r200_copy_dma(struct radeon_device *rdev, ++ uint64_t src_offset, ++ uint64_t dst_offset, ++ unsigned num_pages, ++ struct radeon_fence *fence) ++{ ++ uint32_t size; ++ uint32_t cur_size; ++ int i, num_loops; ++ int r = 0; ++ ++ /* radeon pitch is /64 */ ++ size = num_pages << PAGE_SHIFT; ++ num_loops = DIV_ROUND_UP(size, 0x1FFFFF); ++ r = radeon_ring_lock(rdev, num_loops * 4 + 64); ++ if (r) { ++ DRM_ERROR("radeon: moving bo (%d).\n", r); ++ return r; ++ } ++ /* Must wait for 2D idle & clean before DMA or hangs might happen */ ++ radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0)); ++ radeon_ring_write(rdev, (1 << 16)); ++ for (i = 0; i < num_loops; i++) { ++ cur_size = size; ++ if (cur_size > 0x1FFFFF) { ++ cur_size = 0x1FFFFF; ++ } ++ size -= cur_size; ++ radeon_ring_write(rdev, PACKET0(0x720, 2)); ++ radeon_ring_write(rdev, src_offset); ++ radeon_ring_write(rdev, dst_offset); ++ radeon_ring_write(rdev, cur_size | (1 << 31) | (1 << 30)); ++ src_offset += cur_size; ++ dst_offset += cur_size; ++ } ++ radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0)); ++ radeon_ring_write(rdev, RADEON_WAIT_DMA_GUI_IDLE); ++ if (fence) { ++ r = radeon_fence_emit(rdev, fence); ++ } ++ radeon_ring_unlock_commit(rdev); ++ return r; ++} ++ ++ + static int r200_get_vtx_size_1(uint32_t vtx_fmt_1) + { + int vtx_size, i, tex_size; +diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c +index 43b55a0..6516cd6 100644 +--- a/drivers/gpu/drm/radeon/r300.c ++++ b/drivers/gpu/drm/radeon/r300.c +@@ -198,50 +198,6 @@ void r300_fence_ring_emit(struct radeon_device *rdev, + radeon_ring_write(rdev, RADEON_SW_INT_FIRE); + } + +-int r300_copy_dma(struct radeon_device *rdev, +- uint64_t src_offset, +- uint64_t dst_offset, +- unsigned num_pages, +- struct radeon_fence *fence) +-{ +- uint32_t size; +- uint32_t cur_size; +- int i, num_loops; +- int r = 0; +- +- /* radeon pitch is /64 */ +- size = num_pages << PAGE_SHIFT; +- num_loops = DIV_ROUND_UP(size, 0x1FFFFF); +- r = radeon_ring_lock(rdev, num_loops * 4 + 64); +- if (r) { +- DRM_ERROR("radeon: moving bo (%d).\n", r); +- return r; +- } +- /* Must wait for 2D idle & clean before DMA or hangs might happen */ +- radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0 )); +- radeon_ring_write(rdev, (1 << 16)); +- for (i = 0; i < num_loops; i++) { +- cur_size = size; +- if (cur_size > 0x1FFFFF) { +- cur_size = 0x1FFFFF; +- } +- size -= cur_size; +- radeon_ring_write(rdev, PACKET0(0x720, 2)); +- radeon_ring_write(rdev, src_offset); +- radeon_ring_write(rdev, dst_offset); +- radeon_ring_write(rdev, cur_size | (1 << 31) | (1 << 30)); +- src_offset += cur_size; +- dst_offset += cur_size; +- } +- radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0)); +- radeon_ring_write(rdev, RADEON_WAIT_DMA_GUI_IDLE); +- if (fence) { +- r = radeon_fence_emit(rdev, fence); +- } +- radeon_ring_unlock_commit(rdev); +- return r; +-} +- + void r300_ring_start(struct radeon_device *rdev) + { + unsigned gb_tile_config; +diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c +index 91f5af9..526b3ec 100644 +--- a/drivers/gpu/drm/radeon/r600.c ++++ b/drivers/gpu/drm/radeon/r600.c +@@ -1797,8 +1797,6 @@ void r600_fence_ring_emit(struct radeon_device *rdev, + 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); +- 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); +diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c +index ec49dad..de8bbbc 100644 +--- a/drivers/gpu/drm/radeon/r600_blit_kms.c ++++ b/drivers/gpu/drm/radeon/r600_blit_kms.c +@@ -576,9 +576,9 @@ 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 += 12; /* fence emit for VB IB */ ++ ring_size += 10; /* fence emit for VB IB */ + ring_size += 5; /* done copy */ +- ring_size += 12; /* fence emit for done copy */ ++ ring_size += 10; /* fence emit for done copy */ + r = radeon_ring_lock(rdev, ring_size); + if (r) + return r; +diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c +index 6d5a711..75bcf35 100644 +--- a/drivers/gpu/drm/radeon/r600_cp.c ++++ b/drivers/gpu/drm/radeon/r600_cp.c +@@ -1428,9 +1428,12 @@ static void r700_gfx_init(struct drm_device *dev, + + gb_tiling_config |= R600_BANK_SWAPS(1); + +- backend_map = r700_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes, +- dev_priv->r600_max_backends, +- (0xff << dev_priv->r600_max_backends) & 0xff); ++ if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV740) ++ backend_map = 0x28; ++ else ++ backend_map = r700_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes, ++ dev_priv->r600_max_backends, ++ (0xff << dev_priv->r600_max_backends) & 0xff); + gb_tiling_config |= R600_BACKEND_MAP(backend_map); + + cc_gc_shader_pipe_config = +diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h +index 05ee1ae..afb3ddb 100644 +--- a/drivers/gpu/drm/radeon/radeon_asic.h ++++ b/drivers/gpu/drm/radeon/radeon_asic.h +@@ -43,7 +43,7 @@ void radeon_atom_set_memory_clock(struct radeon_device *rdev, uint32_t mem_clock + void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); + + /* +- * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 ++ * r100,rv100,rs100,rv200,rs200 + */ + extern int r100_init(struct radeon_device *rdev); + extern void r100_fini(struct radeon_device *rdev); +@@ -120,6 +120,51 @@ static struct radeon_asic r100_asic = { + .ioctl_wait_idle = NULL, + }; + ++/* ++ * r200,rv250,rs300,rv280 ++ */ ++extern int r200_copy_dma(struct radeon_device *rdev, ++ uint64_t src_offset, ++ uint64_t dst_offset, ++ unsigned num_pages, ++ struct radeon_fence *fence); ++static struct radeon_asic r200_asic = { ++ .init = &r100_init, ++ .fini = &r100_fini, ++ .suspend = &r100_suspend, ++ .resume = &r100_resume, ++ .vga_set_state = &r100_vga_set_state, ++ .gpu_reset = &r100_gpu_reset, ++ .gart_tlb_flush = &r100_pci_gart_tlb_flush, ++ .gart_set_page = &r100_pci_gart_set_page, ++ .cp_commit = &r100_cp_commit, ++ .ring_start = &r100_ring_start, ++ .ring_test = &r100_ring_test, ++ .ring_ib_execute = &r100_ring_ib_execute, ++ .irq_set = &r100_irq_set, ++ .irq_process = &r100_irq_process, ++ .get_vblank_counter = &r100_get_vblank_counter, ++ .fence_ring_emit = &r100_fence_ring_emit, ++ .cs_parse = &r100_cs_parse, ++ .copy_blit = &r100_copy_blit, ++ .copy_dma = &r200_copy_dma, ++ .copy = &r100_copy_blit, ++ .get_engine_clock = &radeon_legacy_get_engine_clock, ++ .set_engine_clock = &radeon_legacy_set_engine_clock, ++ .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, ++}; ++ + + /* + * r300,r350,rv350,rv380 +@@ -138,11 +183,6 @@ extern int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t + extern uint32_t rv370_pcie_rreg(struct radeon_device *rdev, uint32_t reg); + extern void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); + extern void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes); +-extern int r300_copy_dma(struct radeon_device *rdev, +- uint64_t src_offset, +- uint64_t dst_offset, +- unsigned num_pages, +- struct radeon_fence *fence); + static struct radeon_asic r300_asic = { + .init = &r300_init, + .fini = &r300_fini, +@@ -162,7 +202,45 @@ static struct radeon_asic r300_asic = { + .fence_ring_emit = &r300_fence_ring_emit, + .cs_parse = &r300_cs_parse, + .copy_blit = &r100_copy_blit, +- .copy_dma = &r300_copy_dma, ++ .copy_dma = &r200_copy_dma, ++ .copy = &r100_copy_blit, ++ .get_engine_clock = &radeon_legacy_get_engine_clock, ++ .set_engine_clock = &radeon_legacy_set_engine_clock, ++ .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, ++}; ++ ++ ++static struct radeon_asic r300_asic_pcie = { ++ .init = &r300_init, ++ .fini = &r300_fini, ++ .suspend = &r300_suspend, ++ .resume = &r300_resume, ++ .vga_set_state = &r100_vga_set_state, ++ .gpu_reset = &r300_gpu_reset, ++ .gart_tlb_flush = &rv370_pcie_gart_tlb_flush, ++ .gart_set_page = &rv370_pcie_gart_set_page, ++ .cp_commit = &r100_cp_commit, ++ .ring_start = &r300_ring_start, ++ .ring_test = &r100_ring_test, ++ .ring_ib_execute = &r100_ring_ib_execute, ++ .irq_set = &r100_irq_set, ++ .irq_process = &r100_irq_process, ++ .get_vblank_counter = &r100_get_vblank_counter, ++ .fence_ring_emit = &r300_fence_ring_emit, ++ .cs_parse = &r300_cs_parse, ++ .copy_blit = &r100_copy_blit, ++ .copy_dma = &r200_copy_dma, + .copy = &r100_copy_blit, + .get_engine_clock = &radeon_legacy_get_engine_clock, + .set_engine_clock = &radeon_legacy_set_engine_clock, +@@ -206,7 +284,7 @@ static struct radeon_asic r420_asic = { + .fence_ring_emit = &r300_fence_ring_emit, + .cs_parse = &r300_cs_parse, + .copy_blit = &r100_copy_blit, +- .copy_dma = &r300_copy_dma, ++ .copy_dma = &r200_copy_dma, + .copy = &r100_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, +@@ -255,7 +333,7 @@ static struct radeon_asic rs400_asic = { + .fence_ring_emit = &r300_fence_ring_emit, + .cs_parse = &r300_cs_parse, + .copy_blit = &r100_copy_blit, +- .copy_dma = &r300_copy_dma, ++ .copy_dma = &r200_copy_dma, + .copy = &r100_copy_blit, + .get_engine_clock = &radeon_legacy_get_engine_clock, + .set_engine_clock = &radeon_legacy_set_engine_clock, +@@ -314,7 +392,7 @@ static struct radeon_asic rs600_asic = { + .fence_ring_emit = &r300_fence_ring_emit, + .cs_parse = &r300_cs_parse, + .copy_blit = &r100_copy_blit, +- .copy_dma = &r300_copy_dma, ++ .copy_dma = &r200_copy_dma, + .copy = &r100_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, +@@ -322,6 +400,8 @@ static struct radeon_asic rs600_asic = { + .set_memory_clock = &radeon_atom_set_memory_clock, + .set_pcie_lanes = NULL, + .set_clock_gating = &radeon_atom_set_clock_gating, ++ .set_surface_reg = r100_set_surface_reg, ++ .clear_surface_reg = r100_clear_surface_reg, + .bandwidth_update = &rs600_bandwidth_update, + .hpd_init = &rs600_hpd_init, + .hpd_fini = &rs600_hpd_fini, +@@ -360,8 +440,8 @@ static struct radeon_asic rs690_asic = { + .fence_ring_emit = &r300_fence_ring_emit, + .cs_parse = &r300_cs_parse, + .copy_blit = &r100_copy_blit, +- .copy_dma = &r300_copy_dma, +- .copy = &r300_copy_dma, ++ .copy_dma = &r200_copy_dma, ++ .copy = &r200_copy_dma, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, +@@ -412,7 +492,7 @@ static struct radeon_asic rv515_asic = { + .fence_ring_emit = &r300_fence_ring_emit, + .cs_parse = &r300_cs_parse, + .copy_blit = &r100_copy_blit, +- .copy_dma = &r300_copy_dma, ++ .copy_dma = &r200_copy_dma, + .copy = &r100_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, +@@ -455,7 +535,7 @@ static struct radeon_asic r520_asic = { + .fence_ring_emit = &r300_fence_ring_emit, + .cs_parse = &r300_cs_parse, + .copy_blit = &r100_copy_blit, +- .copy_dma = &r300_copy_dma, ++ .copy_dma = &r200_copy_dma, + .copy = &r100_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, +diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c +index 2dcda61..4d88315 100644 +--- a/drivers/gpu/drm/radeon/radeon_atombios.c ++++ b/drivers/gpu/drm/radeon/radeon_atombios.c +@@ -206,6 +206,15 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, + *connector_type = DRM_MODE_CONNECTOR_DVID; + } + ++ /* Asrock RS600 board lists the DVI port as HDMI */ ++ if ((dev->pdev->device == 0x7941) && ++ (dev->pdev->subsystem_vendor == 0x1849) && ++ (dev->pdev->subsystem_device == 0x7941)) { ++ if ((*connector_type == DRM_MODE_CONNECTOR_HDMIA) && ++ (supported_device == ATOM_DEVICE_DFP3_SUPPORT)) ++ *connector_type = DRM_MODE_CONNECTOR_DVID; ++ } ++ + /* a-bit f-i90hd - ciaranm on #radeonhd - this board has no DVI */ + if ((dev->pdev->device == 0x7941) && + (dev->pdev->subsystem_vendor == 0x147b) && +diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c +index 2381885..65f8194 100644 +--- a/drivers/gpu/drm/radeon/radeon_connectors.c ++++ b/drivers/gpu/drm/radeon/radeon_connectors.c +@@ -780,7 +780,7 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect + * connected and the DVI port disconnected. If the edid doesn't + * say HDMI, vice versa. + */ +- if (radeon_connector->shared_ddc && connector_status_connected) { ++ if (radeon_connector->shared_ddc && (ret == connector_status_connected)) { + struct drm_device *dev = connector->dev; + struct drm_connector *list_connector; + struct radeon_connector *list_radeon_connector; +@@ -1060,8 +1060,7 @@ radeon_add_atom_connector(struct drm_device *dev, + return; + } + if (radeon_connector->ddc_bus && i2c_bus->valid) { +- if (memcmp(&radeon_connector->ddc_bus->rec, i2c_bus, +- sizeof(struct radeon_i2c_bus_rec)) == 0) { ++ if (radeon_connector->ddc_bus->rec.i2c_id == i2c_bus->i2c_id) { + radeon_connector->shared_ddc = true; + shared_ddc = true; + } +diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c +index 768b150..767aed8 100644 +--- a/drivers/gpu/drm/radeon/radeon_device.c ++++ b/drivers/gpu/drm/radeon/radeon_device.c +@@ -329,21 +329,22 @@ int radeon_asic_init(struct radeon_device *rdev) + case CHIP_RS100: + case CHIP_RV200: + case CHIP_RS200: ++ rdev->asic = &r100_asic; ++ break; + case CHIP_R200: + case CHIP_RV250: + case CHIP_RS300: + case CHIP_RV280: +- rdev->asic = &r100_asic; ++ rdev->asic = &r200_asic; + break; + case CHIP_R300: + case CHIP_R350: + case CHIP_RV350: + case CHIP_RV380: +- rdev->asic = &r300_asic; +- if (rdev->flags & RADEON_IS_PCIE) { +- rdev->asic->gart_tlb_flush = &rv370_pcie_gart_tlb_flush; +- rdev->asic->gart_set_page = &rv370_pcie_gart_set_page; +- } ++ if (rdev->flags & RADEON_IS_PCIE) ++ rdev->asic = &r300_asic_pcie; ++ else ++ rdev->asic = &r300_asic; + break; + case CHIP_R420: + case CHIP_R423: +diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h +index e137852..c57ad60 100644 +--- a/drivers/gpu/drm/radeon/radeon_drv.h ++++ b/drivers/gpu/drm/radeon/radeon_drv.h +@@ -106,9 +106,10 @@ + * 1.29- R500 3D cmd buffer support + * 1.30- Add support for occlusion queries + * 1.31- Add support for num Z pipes from GET_PARAM ++ * 1.32- fixes for rv740 setup + */ + #define DRIVER_MAJOR 1 +-#define DRIVER_MINOR 31 ++#define DRIVER_MINOR 32 + #define DRIVER_PATCHLEVEL 0 + + enum radeon_cp_microcode_version { +diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c +index 694799f..6579eb4 100644 +--- a/drivers/gpu/drm/radeon/radeon_ring.c ++++ b/drivers/gpu/drm/radeon/radeon_ring.c +@@ -100,6 +100,8 @@ void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib) + if (tmp == NULL) { + return; + } ++ if (!tmp->fence->emited) ++ radeon_fence_unref(&tmp->fence); + mutex_lock(&rdev->ib_pool.mutex); + tmp->free = true; + mutex_unlock(&rdev->ib_pool.mutex); +diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c +index 5943d56..0302167 100644 +--- a/drivers/gpu/drm/radeon/rv770.c ++++ b/drivers/gpu/drm/radeon/rv770.c +@@ -549,9 +549,12 @@ static void rv770_gpu_init(struct radeon_device *rdev) + + gb_tiling_config |= BANK_SWAPS(1); + +- backend_map = r700_get_tile_pipe_to_backend_map(rdev->config.rv770.max_tile_pipes, +- rdev->config.rv770.max_backends, +- (0xff << rdev->config.rv770.max_backends) & 0xff); ++ if (rdev->family == CHIP_RV740) ++ backend_map = 0x28; ++ else ++ backend_map = r700_get_tile_pipe_to_backend_map(rdev->config.rv770.max_tile_pipes, ++ rdev->config.rv770.max_backends, ++ (0xff << rdev->config.rv770.max_backends) & 0xff); + gb_tiling_config |= BACKEND_MAP(backend_map); + + cc_gc_shader_pipe_config = +diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c +index e2123af..a759170 100644 +--- a/drivers/gpu/drm/ttm/ttm_tt.c ++++ b/drivers/gpu/drm/ttm/ttm_tt.c +@@ -196,14 +196,15 @@ 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) ++ enum ttm_caching_state c_old, ++ enum ttm_caching_state c_new) + { + int ret = 0; + + if (PageHighMem(p)) + return 0; + +- if (get_page_memtype(p) != -1) { ++ if (c_old != tt_cached) { + /* p isn't in the default caching state, set it to + * writeback first to free its current memtype. */ + +@@ -212,16 +213,17 @@ static inline int ttm_tt_set_page_caching(struct page *p, + return ret; + } + +- if (c_state == tt_wc) ++ if (c_new == tt_wc) + ret = set_memory_wc((unsigned long) page_address(p), 1); +- else if (c_state == tt_uncached) ++ else if (c_new == tt_uncached) + ret = set_pages_uc(p, 1); + + return ret; + } + #else /* CONFIG_X86 */ + static inline int ttm_tt_set_page_caching(struct page *p, +- enum ttm_caching_state c_state) ++ enum ttm_caching_state c_old, ++ enum ttm_caching_state c_new) + { + return 0; + } +@@ -254,7 +256,9 @@ static int ttm_tt_set_caching(struct ttm_tt *ttm, + for (i = 0; i < ttm->num_pages; ++i) { + cur_page = ttm->pages[i]; + if (likely(cur_page != NULL)) { +- ret = ttm_tt_set_page_caching(cur_page, c_state); ++ ret = ttm_tt_set_page_caching(cur_page, ++ ttm->caching_state, ++ c_state); + if (unlikely(ret != 0)) + goto out_err; + } +@@ -268,7 +272,7 @@ out_err: + for (j = 0; j < i; ++j) { + cur_page = ttm->pages[j]; + if (likely(cur_page != NULL)) { +- (void)ttm_tt_set_page_caching(cur_page, ++ (void)ttm_tt_set_page_caching(cur_page, c_state, + ttm->caching_state); + } + } +@@ -476,7 +480,7 @@ static int ttm_tt_swapin(struct ttm_tt *ttm) + 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, +@@ -495,8 +499,10 @@ static int ttm_tt_swapin(struct ttm_tt *ttm) + + 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; +@@ -519,7 +525,7 @@ static int ttm_tt_swapin(struct ttm_tt *ttm) + 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) +@@ -531,6 +537,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistant_swap_storage) + 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); +@@ -553,7 +560,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistant_swap_storage) + 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; +@@ -565,9 +572,10 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistant_swap_storage) + 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); +@@ -591,5 +599,5 @@ out_err: + if (!persistant_swap_storage) + fput(swap_storage); + +- return -ENOMEM; ++ return ret; + } diff --git a/drm-upgrayedd.patch b/drm-upgrayedd.patch new file mode 100644 index 000000000..2530bece3 --- /dev/null +++ b/drm-upgrayedd.patch @@ -0,0 +1,85406 @@ +commit 8f6aa16e3a85c732763458c07db65035e75e5906 +Author: Jerome Glisse +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 + Signed-off-by: Dave Airlie + +commit 3a84c7bf19624ef6c5927eaf742adf346be1f34d +Author: Jerome Glisse +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 + Signed-off-by: Dave Airlie + +commit c4716196dd78a7adfd400612d1c8465bf0bd3fb5 +Author: Dave Airlie +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 + +commit 0f5980d95024ff0cf2e495d977db9c26e1a6765b +Author: Dave Airlie +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 + +commit b1bd3bb9f2f5522415497a075313c8a2ba1cc16c +Author: Dave Airlie +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 + +commit ce2440648005a532bffd3fbe8590a085d8869620 +Author: Alex Deucher +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 + Signed-off-by: Dave Airlie + +commit 03b6599497a283874236163c63f8f50045e5f8f1 +Author: Alex Deucher +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 + Signed-off-by: Dave Airlie + +commit 92d87a03788e038b3028653e736d4c51090317ac +Author: Alex Deucher +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 + Signed-off-by: Dave Airlie + +commit b3ba69acb361aa6396ad9e55c557fb9b47a0f43e +Author: Dave Airlie +Date: Wed Feb 17 10:34:58 2010 +1000 + + drm: backport 2.6.33-rc8 drm + +commit ffff5f5e2d9de68dc454ec85bafa9da94f10f204 +Author: Dave Chinner +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 + Acked-by: Dave Airlie + Acked-by: Artem Bityutskiy + Signed-off-by: Linus Torvalds +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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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, + } else + memset(kdata, 0, _IOC_SIZE(cmd)); + +- 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 ++#include + #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<scale || ++ ~mode->valid_norms & 1<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 "); ++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<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 ++#include + #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 + #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 = -1; + 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#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 + { + enum drm_connector_status status = 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 ++ * ++ * 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 + +-#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 ++#include ++#include ++#include ++ ++#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 ++ * ++ * 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 ++ * ++ * Register locations derived from NVClock by Roderick Colenbrander ++ */ ++ ++#include ++ ++#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, <h); ++ 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, <h); ++ 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 ++ * Ben Skeggs ++ * Jeremy Kolb ++ */ ++ ++#include "drmP.h" ++ ++#include "nouveau_drm.h" ++#include "nouveau_drv.h" ++#include "nouveau_dma.h" ++ ++#include ++ ++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 ++ ++#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 ++ * ++ * 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 ++ */ ++ ++#include ++ ++#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 ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++#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, ®p->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, ®p->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 ++#include ++#include ++#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 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 ++ ++#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. ++ * ++ * \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 ++ */ ++ ++#include "drmP.h" ++#include "drm.h" ++#include "nouveau_drm.h" ++#include "nouveau_drv.h" ++#include "nouveau_reg.h" ++#include ++ ++/* 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 ++ */ ++ ++ ++#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 ++ */ ++ ++#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 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; ++ ++ /* 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 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 ++ ++#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 ++#include "drmP.h" ++#include "drm.h" ++#include "drm_sarea.h" ++#include "drm_crtc_helper.h" ++#include ++ ++#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 = ®p->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 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 ++ * 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, ®, &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, ®, &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 + #include ++#include + + #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 + #include + ++#include ++#include ++#include ++#include ++ + #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 + #include + #include ++#include + #include + #include + #include +@@ -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 +-#include +-#include +-#include ++#include ++#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, >t_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, >t_addr); + if (r) { + DRM_ERROR("Failed to pin GTT object %d\n", i); + goto out_cleanup; + } + +- r = radeon_object_kmap(gtt_obj[i], >t_map); ++ r = radeon_bo_kmap(gtt_obj[i], >t_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], >t_map); ++ r = radeon_bo_kmap(gtt_obj[i], >t_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(>t_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(>t_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 + */ ++/* 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 ++#include ++#include ++ ++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 ++ */ ++ ++#include "ttm/ttm_lock.h" ++#include "ttm/ttm_module.h" ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++/** @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 ++#include ++#include ++#include ++#include ++ ++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 ++ ++#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 ++#include + #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 . 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 +-#include /* 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 ++typedef unsigned int drm_handle_t; + +-#define DRM_MAJOR 226 +-#define DRM_MAX_MINOR 15 ++#else /* One of the BSDs */ ++ ++#include ++#include ++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 +-#include +- + #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 +-#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 ++#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 ++#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 ++ */ ++ ++#ifndef _TTM_EXECBUF_UTIL_H_ ++#define _TTM_EXECBUF_UTIL_H_ ++ ++#include "ttm/ttm_bo_api.h" ++#include ++ ++/** ++ * 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 ++ */ ++ ++/** @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 ++#include ++ ++/** ++ * 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 ++ */ ++/** @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 ++#include "drm_hashtab.h" ++#include ++#include ++ ++/** ++ * 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 ++#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 ++ ++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 ++#include ++#include ++#include ++#include ++ ++/** ++ * 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 . 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/find-provides b/find-provides new file mode 100755 index 000000000..b28d1028f --- /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 " +__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 000000000..4a8db58f1 --- /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 + #include + #include ++#include + + #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 000000000..01515f7b4 --- /dev/null +++ b/fix-ima-null-ptr-deref.patch @@ -0,0 +1,54 @@ +From: J. R. Okajima +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 +Signed-off-by: James Morris +--- + +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 000000000..49c6ce8be --- /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 000000000..8ecc995c2 --- /dev/null +++ b/git-bluetooth.patch @@ -0,0 +1,3344 @@ +commit b1fb06830dc870d862f7f80e276130c0ab84d59f +Author: Wei Yongjun +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 + Signed-off-by: Marcel Holtmann + +commit 7585b97a48180f754ebdade1be94092e36bef365 +Author: Wei Yongjun +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 + Signed-off-by: Marcel Holtmann + +commit 2ae9a6be5f476f3512839a4d11a8f432bfd2914c +Author: Dave Young +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 + 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: [] __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:[] 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] [] ? trace_hardirqs_on_caller+0x72/0x1c0 + [ 2845.743742] [] ? lock_acquire+0x76/0xa0 + [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 + [ 2845.743742] [] ? _spin_lock_irqsave+0x45/0x80 + [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 + [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 + [ 2845.743742] [] ? skb_queue_purge+0x14/0x20 + [ 2845.743742] [] ? hci_conn_del+0x10a/0x1c0 [bluetooth] + [ 2845.743742] [] ? l2cap_disconn_ind+0x59/0xb0 [l2cap] + [ 2845.743742] [] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth] + [ 2845.743742] [] ? hci_event_packet+0x5f8/0x31c0 [bluetooth] + [ 2845.743742] [] ? sock_def_readable+0x59/0x80 + [ 2845.743742] [] ? _read_unlock+0x1d/0x20 + [ 2845.743742] [] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth] + [ 2845.743742] [] ? trace_hardirqs_on+0xb/0x10 + [ 2845.743742] [] ? hci_rx_task+0x2ba/0x490 [bluetooth] + [ 2845.743742] [] ? tasklet_action+0x31/0xc0 + [ 2845.743742] [] ? tasklet_action+0x4c/0xc0 + [ 2845.743742] [] ? __do_softirq+0xa7/0x170 + [ 2845.743742] [] ? ack_apic_level+0x5c/0x1c0 + [ 2845.743742] [] ? do_softirq+0x57/0x60 + [ 2845.743742] [] ? irq_exit+0x7c/0x90 + [ 2845.743742] [] ? do_IRQ+0x4b/0x90 + [ 2845.743742] [] ? irq_exit+0x75/0x90 + [ 2845.743742] [] ? common_interrupt+0x2c/0x34 + [ 2845.743742] [] ? mwait_idle+0x4f/0x70 + [ 2845.743742] [] ? cpu_idle+0x65/0xb0 + [ 2845.743742] [] ? 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 ff 86 c4 00 00 00 89 f0 e8 56 a9 ff ff 85 c0 0f 85 6e 03 00 + [ 2845.743742] EIP: [] __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] [] warn_slowpath+0x86/0xa0 + [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 + [ 2845.743742] [] ? up+0x14/0x40 + [ 2845.743742] [] ? release_console_sem+0x31/0x1e0 + [ 2845.743742] [] ? _spin_lock_irqsave+0x6b/0x80 + [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 + [ 2845.743742] [] ? _read_lock_irqsave+0x40/0x80 + [ 2845.743742] [] ? release_console_sem+0x1c2/0x1e0 + [ 2845.743742] [] ? up+0x14/0x40 + [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 + [ 2845.743742] [] ? __mutex_unlock_slowpath+0x97/0x160 + [ 2845.743742] [] ? mutex_trylock+0xb3/0x180 + [ 2845.743742] [] ? mutex_unlock+0x8/0x10 + [ 2845.743742] [] smp_call_function_many+0x151/0x200 + [ 2845.743742] [] ? stop_this_cpu+0x0/0x40 + [ 2845.743742] [] smp_call_function+0x21/0x30 + [ 2845.743742] [] native_smp_send_stop+0x1e/0x50 + [ 2845.743742] [] panic+0x55/0x110 + [ 2845.743742] [] oops_end+0xb8/0xc0 + [ 2845.743742] [] die+0x4f/0x70 + [ 2845.743742] [] do_page_fault+0x269/0x610 + [ 2845.743742] [] ? do_page_fault+0x0/0x610 + [ 2845.743742] [] error_code+0x77/0x7c + [ 2845.743742] [] ? __lock_acquire+0x6c/0xa80 + [ 2845.743742] [] ? trace_hardirqs_on_caller+0x72/0x1c0 + [ 2845.743742] [] lock_acquire+0x76/0xa0 + [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 + [ 2845.743742] [] _spin_lock_irqsave+0x45/0x80 + [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 + [ 2845.743742] [] skb_dequeue+0x1d/0x70 + [ 2845.743742] [] skb_queue_purge+0x14/0x20 + [ 2845.743742] [] hci_conn_del+0x10a/0x1c0 [bluetooth] + [ 2845.743742] [] ? l2cap_disconn_ind+0x59/0xb0 [l2cap] + [ 2845.743742] [] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth] + [ 2845.743742] [] hci_event_packet+0x5f8/0x31c0 [bluetooth] + [ 2845.743742] [] ? sock_def_readable+0x59/0x80 + [ 2845.743742] [] ? _read_unlock+0x1d/0x20 + [ 2845.743742] [] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth] + [ 2845.743742] [] ? trace_hardirqs_on+0xb/0x10 + [ 2845.743742] [] hci_rx_task+0x2ba/0x490 [bluetooth] + [ 2845.743742] [] ? tasklet_action+0x31/0xc0 + [ 2845.743742] [] tasklet_action+0x4c/0xc0 + [ 2845.743742] [] __do_softirq+0xa7/0x170 + [ 2845.743742] [] ? ack_apic_level+0x5c/0x1c0 + [ 2845.743742] [] do_softirq+0x57/0x60 + [ 2845.743742] [] irq_exit+0x7c/0x90 + [ 2845.743742] [] do_IRQ+0x4b/0x90 + [ 2845.743742] [] ? irq_exit+0x75/0x90 + [ 2845.743742] [] common_interrupt+0x2c/0x34 + [ 2845.743742] [] ? mwait_idle+0x4f/0x70 + [ 2845.743742] [] cpu_idle+0x65/0xb0 + [ 2845.743742] [] 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] [] warn_slowpath+0x86/0xa0 + [ 2845.743742] [] ? warn_slowpath+0x10/0xa0 + [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 + [ 2845.743742] [] ? up+0x14/0x40 + [ 2845.743742] [] ? release_console_sem+0x31/0x1e0 + [ 2845.743742] [] ? _spin_lock_irqsave+0x6b/0x80 + [ 2845.743742] [] ? trace_hardirqs_off+0xb/0x10 + [ 2845.743742] [] ? _read_lock_irqsave+0x40/0x80 + [ 2845.743742] [] ? release_console_sem+0x1c2/0x1e0 + [ 2845.743742] [] ? up+0x14/0x40 + [ 2845.743742] [] smp_call_function_single+0x8e/0x110 + [ 2845.743742] [] ? stop_this_cpu+0x0/0x40 + [ 2845.743742] [] ? cpumask_next_and+0x1f/0x40 + [ 2845.743742] [] smp_call_function_many+0x11a/0x200 + [ 2845.743742] [] ? stop_this_cpu+0x0/0x40 + [ 2845.743742] [] smp_call_function+0x21/0x30 + [ 2845.743742] [] native_smp_send_stop+0x1e/0x50 + [ 2845.743742] [] panic+0x55/0x110 + [ 2845.743742] [] oops_end+0xb8/0xc0 + [ 2845.743742] [] die+0x4f/0x70 + [ 2845.743742] [] do_page_fault+0x269/0x610 + [ 2845.743742] [] ? do_page_fault+0x0/0x610 + [ 2845.743742] [] error_code+0x77/0x7c + [ 2845.743742] [] ? __lock_acquire+0x6c/0xa80 + [ 2845.743742] [] ? trace_hardirqs_on_caller+0x72/0x1c0 + [ 2845.743742] [] lock_acquire+0x76/0xa0 + [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 + [ 2845.743742] [] _spin_lock_irqsave+0x45/0x80 + [ 2845.743742] [] ? skb_dequeue+0x1d/0x70 + [ 2845.743742] [] skb_dequeue+0x1d/0x70 + [ 2845.743742] [] skb_queue_purge+0x14/0x20 + [ 2845.743742] [] hci_conn_del+0x10a/0x1c0 [bluetooth] + [ 2845.743742] [] ? l2cap_disconn_ind+0x59/0xb0 [l2cap] + [ 2845.743742] [] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth] + [ 2845.743742] [] hci_event_packet+0x5f8/0x31c0 [bluetooth] + [ 2845.743742] [] ? sock_def_readable+0x59/0x80 + [ 2845.743742] [] ? _read_unlock+0x1d/0x20 + [ 2845.743742] [] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth] + [ 2845.743742] [] ? trace_hardirqs_on+0xb/0x10 + [ 2845.743742] [] hci_rx_task+0x2ba/0x490 [bluetooth] + [ 2845.743742] [] ? tasklet_action+0x31/0xc0 + [ 2845.743742] [] tasklet_action+0x4c/0xc0 + [ 2845.743742] [] __do_softirq+0xa7/0x170 + [ 2845.743742] [] ? ack_apic_level+0x5c/0x1c0 + [ 2845.743742] [] do_softirq+0x57/0x60 + [ 2845.743742] [] irq_exit+0x7c/0x90 + [ 2845.743742] [] do_IRQ+0x4b/0x90 + [ 2845.743742] [] ? irq_exit+0x75/0x90 + [ 2845.743742] [] common_interrupt+0x2c/0x34 + [ 2845.743742] [] ? mwait_idle+0x4f/0x70 + [ 2845.743742] [] cpu_idle+0x65/0xb0 + [ 2845.743742] [] 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 + Signed-off-by: Marcel Holtmann + +commit 2526d3d8b2f671a7d36cc486af984052cd5a690f +Author: Marcel Holtmann +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 + Signed-off-by: Marcel Holtmann + +commit 37e62f5516cfb210e64fe53457932df4341b0ad1 +Author: Marcel Holtmann +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 + +commit 2a517ca687232adc8f14893730644da712010ffc +Author: Marcel Holtmann +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 + +commit 8bf4794174659b06d43cc5e290cd384757374613 +Author: Marcel Holtmann +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 + +commit d5f2d2be68876f65dd051b978a7b66265fde9ffd +Author: Marcel Holtmann +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 + +commit 96a3183322cba1a2846771b067c99b9d6f481263 +Author: Marcel Holtmann +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 + +commit 00ae4af91d8c5b6814e2bb3bfaaf743845f989eb +Author: Marcel Holtmann +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 + +commit 2950f21acb0f6b8fcd964485c2ebf1e06545ac20 +Author: Marcel Holtmann +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 + +commit f29972de8e7476706ab3c01304a505e7c95d9040 +Author: Marcel Holtmann +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 + +commit e1027a7c69700301d14db03d2e049ee60c4f92df +Author: Marcel Holtmann +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 + +commit 435fef20acfc48f46476abad55b0cd3aa47b8365 +Author: Marcel Holtmann +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 + +commit 6a8d3010b313d99adbb28f1826fac0234395bb26 +Author: Marcel Holtmann +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 + +commit 984947dc64f82bc6cafa4d84ba1a139718f634a8 +Author: Marcel Holtmann +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 + +commit 657e17b03c80bec817975984d221bef716f83558 +Author: Marcel Holtmann +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 + +commit 0684e5f9fb9e3f7e168ab831dfca693bcb44805b +Author: Marcel Holtmann +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 + +commit efc7688b557dd1be10eead7399b315efcb1dbc74 +Author: Marcel Holtmann +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 + +commit 255c76014af74165428e7aa16414b857e2bdccf2 +Author: Marcel Holtmann +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 + +commit 43c2e57f94c15744495fee564610aa24602b3824 +Author: Marcel Holtmann +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 + +commit 6e1031a40029492c10509e8c3dcac9b611438ccb +Author: Jaikumar Ganesh +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 + Signed-off-by: Marcel Holtmann + +commit 34a55eda483e8177c9044f93fd2c9107f02bf1c7 +Author: Andre Haupt +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 + Signed-off-by: Marcel Holtmann + +commit dd2efd03b49d56ae795c71335bc7358022514c32 +Author: Dave Young +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 + 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 + Signed-off-by: Marcel Holtmann + +commit 5f9018af004fa8635bbbe3ab2dc61e8a686edfaa +Author: Marcel Holtmann +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 + +commit 0588d94fd7e414367a7ae517569d2222441c255f +Author: Marcel Holtmann +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 + +commit f62e4323ab43c59e7cd7f72c1eb392d7c767ce5a +Author: Marcel Holtmann +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 + +commit 8c84b83076b5062f59b6167cdda90d9e5124aa71 +Author: Marcel Holtmann +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 + + Signed-off-by: Marcel Holtmann + +commit 9f2c8a03fbb3048cf38b158f87aa0c3c09bca084 +Author: Marcel Holtmann +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 + +commit 2af6b9d518ddfbc4d6990d5f9c9b1a05341c1cef +Author: Marcel Holtmann +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 + +commit 8c1b235594fbab9a13240a1dac12ea9fd99b6440 +Author: Marcel Holtmann +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 + +commit c89b6e6bda4c8021195778f47567d0cc9dbfe7ec +Author: Marcel Holtmann +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 + + Signed-off-by: Marcel Holtmann + +commit 71aeeaa1fd88fe7446391e0553336f0e0c2cfe6a +Author: Marcel Holtmann +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 + +commit f66dc81f44d918ee1aa1a9d821bb2f25c7592bc0 +Author: Marcel Holtmann +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 + +commit bb23c0ab824653be4aa7dfca15b07b3059717004 +Author: Marcel Holtmann +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 + +commit c4f912e155504e94dd4f3d63c378dab0ff03dbda +Author: Marcel Holtmann +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 + +commit d58daf42d29a3a4a4d4be46cf47ceee096789680 +Author: Marcel Holtmann +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 + +commit 91aa35a5aa3540223066bf6b51c935418c63a35d +Author: Victor Shcherbatyuk +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 + Signed-off-by: Marcel Holtmann + +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 + #include + +-#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 + +-#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 + #include + +-#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 + #include + +-#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 000000000..e69de29bb diff --git a/git-linus.diff b/git-linus.diff new file mode 100644 index 000000000..e69de29bb diff --git a/hda_intel-prealloc-4mb-dmabuffer.patch b/hda_intel-prealloc-4mb-dmabuffer.patch new file mode 100644 index 000000000..c80f11d9d --- /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 000000000..a5c7e922c --- /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 + + #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 000000000..329651076 --- /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/inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch b/inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch new file mode 100644 index 000000000..3a2703c48 --- /dev/null +++ b/inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch @@ -0,0 +1,105 @@ +From 9c6594941166d85bbf7f3bb4a79d01cb3d572eac Mon Sep 17 00:00:00 2001 +From: Nelson Elhage +Date: Wed, 3 Nov 2010 16:35:41 +0000 +Subject: inet_diag: Make sure we actually run the same bytecode we audited. + +We were using nlmsg_find_attr() to look up the bytecode by attribute when +auditing, but then just using the first attribute when actually running +bytecode. So, if we received a message with two attribute elements, where only +the second had type INET_DIAG_REQ_BYTECODE, we would validate and run different +bytecode strings. + +Fix this by consistently using nlmsg_find_attr everywhere. + +Signed-off-by: Nelson Elhage +Signed-off-by: Thomas Graf +Signed-off-by: David S. Miller +--- + net/ipv4/inet_diag.c | 27 ++++++++++++++++----------- + 1 files changed, 16 insertions(+), 11 deletions(-) + +diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c +index a706a47..6fe360f 100644 +--- a/net/ipv4/inet_diag.c ++++ b/net/ipv4/inet_diag.c +@@ -489,9 +489,11 @@ static int inet_csk_diag_dump(struct sock *sk, + { + struct inet_diag_req *r = NLMSG_DATA(cb->nlh); + +- if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { ++ if (nlmsg_attrlen(cb->nlh, sizeof(*r))) { + struct inet_diag_entry entry; +- struct rtattr *bc = (struct rtattr *)(r + 1); ++ const struct nlattr *bc = nlmsg_find_attr(cb->nlh, ++ sizeof(*r), ++ INET_DIAG_REQ_BYTECODE); + struct inet_sock *inet = inet_sk(sk); + + entry.family = sk->sk_family; +@@ -511,7 +513,7 @@ static int inet_csk_diag_dump(struct sock *sk, + entry.dport = ntohs(inet->dport); + entry.userlocks = sk->sk_userlocks; + +- if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry)) ++ if (!inet_diag_bc_run(nla_data(bc), nla_len(bc), &entry)) + return 0; + } + +@@ -526,9 +528,11 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, + { + struct inet_diag_req *r = NLMSG_DATA(cb->nlh); + +- if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { ++ if (nlmsg_attrlen(cb->nlh, sizeof(*r))) { + struct inet_diag_entry entry; +- struct rtattr *bc = (struct rtattr *)(r + 1); ++ const struct nlattr *bc = nlmsg_find_attr(cb->nlh, ++ sizeof(*r), ++ INET_DIAG_REQ_BYTECODE); + + entry.family = tw->tw_family; + #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) +@@ -547,7 +551,7 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, + entry.dport = ntohs(tw->tw_dport); + entry.userlocks = 0; + +- if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry)) ++ if (!inet_diag_bc_run(nla_data(bc), nla_len(bc), &entry)) + return 0; + } + +@@ -617,7 +621,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, + struct inet_diag_req *r = NLMSG_DATA(cb->nlh); + struct inet_connection_sock *icsk = inet_csk(sk); + struct listen_sock *lopt; +- struct rtattr *bc = NULL; ++ const struct nlattr *bc = NULL; + struct inet_sock *inet = inet_sk(sk); + int j, s_j; + int reqnum, s_reqnum; +@@ -637,8 +641,9 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, + if (!lopt || !lopt->qlen) + goto out; + +- if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { +- bc = (struct rtattr *)(r + 1); ++ if (nlmsg_attrlen(cb->nlh, sizeof(*r))) { ++ bc = nlmsg_find_attr(cb->nlh, sizeof(*r), ++ INET_DIAG_REQ_BYTECODE); + entry.sport = inet->num; + entry.userlocks = sk->sk_userlocks; + } +@@ -671,8 +676,8 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, + &ireq->rmt_addr; + entry.dport = ntohs(ireq->rmt_port); + +- if (!inet_diag_bc_run(RTA_DATA(bc), +- RTA_PAYLOAD(bc), &entry)) ++ if (!inet_diag_bc_run(nla_data(bc), ++ nla_len(bc), &entry)) + continue; + } + +-- +1.7.3.2 + diff --git a/ipc-shm-fix-information-leak-to-user.patch b/ipc-shm-fix-information-leak-to-user.patch new file mode 100644 index 000000000..b23ad439d --- /dev/null +++ b/ipc-shm-fix-information-leak-to-user.patch @@ -0,0 +1,30 @@ +From: Vasiliy Kulikov +Date: Sat, 30 Oct 2010 14:22:49 +0000 (+0400) +Subject: ipc: shm: fix information leak to userland +X-Git-Tag: v2.6.37-rc1~24 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=3af54c9bd9e6f14f896aac1bb0e8405ae0bc7a44 + +ipc: shm: fix information leak to userland + +The shmid_ds structure is copied to userland with shm_unused{,2,3} +fields unitialized. It leads to leaking of contents of kernel stack +memory. + +Signed-off-by: Vasiliy Kulikov +Acked-by: Al Viro +Cc: stable@kernel.org +Signed-off-by: Linus Torvalds +--- + +diff --git a/ipc/shm.c b/ipc/shm.c +index fd658a1..7d3bb22 100644 +--- a/ipc/shm.c ++++ b/ipc/shm.c +@@ -479,6 +479,7 @@ static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ + { + struct shmid_ds out; + ++ memset(&out, 0, sizeof(out)); + ipc64_perm_to_ipc_perm(&in->shm_perm, &out.shm_perm); + out.shm_segsz = in->shm_segsz; + out.shm_atime = in->shm_atime; diff --git a/ipc-zero-struct-memory-for-compat-fns.patch b/ipc-zero-struct-memory-for-compat-fns.patch new file mode 100644 index 000000000..b682c7df0 --- /dev/null +++ b/ipc-zero-struct-memory-for-compat-fns.patch @@ -0,0 +1,73 @@ +From: Dan Rosenberg +Date: Wed, 27 Oct 2010 22:34:17 +0000 (-0700) +Subject: ipc: initialize structure memory to zero for compat functions +X-Git-Tag: v2.6.37-rc1~85^2~50 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=03145beb455cf5c20a761e8451e30b8a74ba58d9 + +ipc: initialize structure memory to zero for compat functions + +This takes care of leaking uninitialized kernel stack memory to +userspace from non-zeroed fields in structs in compat ipc functions. + +Signed-off-by: Dan Rosenberg +Cc: Manfred Spraul +Cc: Arnd Bergmann +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + +diff --git a/ipc/compat.c b/ipc/compat.c +index 9dc2c7d..845a287 100644 +--- a/ipc/compat.c ++++ b/ipc/compat.c +@@ -241,6 +241,8 @@ long compat_sys_semctl(int first, int second, int third, void __user *uptr) + struct semid64_ds __user *up64; + int version = compat_ipc_parse_version(&third); + ++ memset(&s64, 0, sizeof(s64)); ++ + if (!uptr) + return -EINVAL; + if (get_user(pad, (u32 __user *) uptr)) +@@ -421,6 +423,8 @@ long compat_sys_msgctl(int first, int second, void __user *uptr) + int version = compat_ipc_parse_version(&second); + void __user *p; + ++ memset(&m64, 0, sizeof(m64)); ++ + switch (second & (~IPC_64)) { + case IPC_INFO: + case IPC_RMID: +@@ -594,6 +598,8 @@ long compat_sys_shmctl(int first, int second, void __user *uptr) + int err, err2; + int version = compat_ipc_parse_version(&second); + ++ memset(&s64, 0, sizeof(s64)); ++ + switch (second & (~IPC_64)) { + case IPC_RMID: + case SHM_LOCK: +diff --git a/ipc/compat_mq.c b/ipc/compat_mq.c +index d8d1e9f..380ea4f 100644 +--- a/ipc/compat_mq.c ++++ b/ipc/compat_mq.c +@@ -53,6 +53,9 @@ asmlinkage long compat_sys_mq_open(const char __user *u_name, + void __user *p = NULL; + if (u_attr && oflag & O_CREAT) { + struct mq_attr attr; ++ ++ memset(&attr, 0, sizeof(attr)); ++ + p = compat_alloc_user_space(sizeof(attr)); + if (get_compat_mq_attr(&attr, u_attr) || + copy_to_user(p, &attr, sizeof(attr))) +@@ -127,6 +130,8 @@ asmlinkage long compat_sys_mq_getsetattr(mqd_t mqdes, + struct mq_attr __user *p = compat_alloc_user_space(2 * sizeof(*p)); + long ret; + ++ memset(&mqstat, 0, sizeof(mqstat)); ++ + if (u_mqstat) { + if (get_compat_mq_attr(&mqstat, u_mqstat) || + copy_to_user(p, &mqstat, sizeof(mqstat))) diff --git a/iwlwifi-fix-internal-scan-race.patch b/iwlwifi-fix-internal-scan-race.patch new file mode 100644 index 000000000..18b315601 --- /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: +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 ) + 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 +To: linville@tuxdriver.com +Cc: linux-wireless@vger.kernel.org, ipw3945-devel@lists.sourceforge.net, Reinette Chatre +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 + +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 +--- + 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 000000000..2e00f00ad --- /dev/null +++ b/iwlwifi-fix-scan-races.patch @@ -0,0 +1,139 @@ +commit 88be026490ed89c2ffead81a52531fbac5507e01 +Author: Johannes Berg +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 + Signed-off-by: Johannes Berg + Signed-off-by: Reinette Chatre + +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 000000000..9ac9971c2 --- /dev/null +++ b/iwlwifi-manage-QoS-by-mac-stack.patch @@ -0,0 +1,100 @@ +From: Stanislaw Gruszka +To: kernel@lists.fedoraproject.org, "John W. Linville" +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 +--- + 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, ¶ms) && local->ops->conf_tx) ++ if (drv_conf_tx(local, queue, ¶ms)) + 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 000000000..0b69e44f5 --- /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 000000000..74345dceb --- /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: +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 ) + 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 ; 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 ; 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 ; 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 ; + 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 ; + 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 +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 , + "John W. Linville" +X-BeenThere: kernel@lists.fedoraproject.org +X-Mailman-Version: 2.1.9 +Precedence: list +List-Id: "Fedora kernel development." +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-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 +--- + 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 000000000..f1adf0fd0 --- /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 +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 + Signed-off-by: Reinette Chatre + +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 000000000..e9f16239e --- /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 +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 + Signed-off-by: Reinette Chatre + Signed-off-by: John W. Linville + +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 000000000..52bdd9fb4 --- /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 +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 +Signed-off-by: Wey-Yi Guy +Signed-off-by: Reinette Chatre + +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 000000000..e3b4b5cac --- /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 +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 +Signed-off-by: Wey-Yi Guy +Signed-off-by: Reinette Chatre + +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 000000000..1e0189e0e --- /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 +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 + Signed-off-by: Reinette Chatre + Signed-off-by: John W. Linville + +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 000000000..f83e12abc --- /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 +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 + Signed-off-by: Reinette Chatre + Signed-off-by: John W. Linville + +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 000000000..db132ee7b --- /dev/null +++ b/iwlwifi_-add-internal-short-scan-support-for-3945.patch @@ -0,0 +1,85 @@ +commit 4f4d4088b05155d4904e29d5c00316395ce32f27 +Author: Wey-Yi Guy +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 + Signed-off-by: Reinette Chatre + +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 000000000..587c5ff3c --- /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 000000000..60037acae --- /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 +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 +Signed-off-by: Wey-Yi Guy +Signed-off-by: Reinette Chatre +--- + 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 000000000..75d8a4df5 --- /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 +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 +Signed-off-by: Wey-Yi Guy +Signed-off-by: Reinette Chatre + +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 000000000..adc3b246e --- /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 000000000..0faa50585 --- /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 +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 + Signed-off-by: Reinette Chatre + Signed-off-by: John W. Linville + +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 000000000..a2c3789b7 --- /dev/null +++ b/kernel.spec @@ -0,0 +1,4581 @@ +# 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 + +# baserelease defines which build revision of this kernel version we're +# building. We used to call this fedora_build, but the magical name +# baserelease is matched by the rpmdev-bumpspec tool, which you should use. +# +# We used to have some extra magic weirdness to bump this automatically, +# but now we don't. Just use: rpmdev-bumpspec -c 'comment for changelog' +# When changing base_sublevel below or going from rc to a final kernel, +# reset this by hand to 1 (or to 0 and then use rpmdev-bumpspec). +# scripts/rebase.sh should be made to do that for you, actually. +# +%global baserelease 175 +%global fedora_build %{baserelease} + +# 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 26 +# 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 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 +# It uses any kernel__conflicts and kernel__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 +Patch1811: drm-upgrayed-fixes.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 +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-i915-sanity-check-pread-pwrite.patch + +Patch1850: drm-nouveau-g80-ctxprog.patch +Patch1851: drm-nouveau-tvout-disable.patch +Patch1852: drm-nouveau-safetile-getparam.patch +Patch1853: drm-nouveau-kconfig.patch +Patch1854: drm-nouveau-mutex.patch +Patch1855: drm-nouveau-update.patch +Patch1856: drm-nouveau-d620.patch +Patch1857: drm-nouveau-nva3-noaccel.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 + +# NFSv4 +Patch3050: linux-2.6-nfsd4-proots.patch +Patch3051: linux-2.6-nfs4-callback-hidden.patch + +# btrfs +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 +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 + +# 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 + +# 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 +Patch12923: mac80211-explicitly-disable-enable-QoS.patch + +# l2tp: fix oops in pppol2tp_xmit (#607054) +Patch13030: l2tp-fix-oops-in-pppol2tp_xmit.patch + +Patch14050: crypto-add-async-hash-testing.patch + +Patch14200: net-do-not-check-capable-if-kernel.patch + +# rhbz#596475 +Patch14226: add-support-for-ricoh-e822-sdhci.patch + +Patch14300: ipc-zero-struct-memory-for-compat-fns.patch +Patch14301: ipc-shm-fix-information-leak-to-user.patch + +Patch14302: inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch +Patch14307: netlink-make-nlmsg_find_attr-take-a-const-ptr.patch + +Patch14303: posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch + +Patch14305: tty-make-tiocgicount-a-handler.patch +Patch14306: tty-icount-changeover-for-other-main-devices.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--debuginfo package. +# %%kernel_debuginfo_package +# +%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--devel package. +# %%kernel_devel_package +# +%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- and its -devel and -debuginfo too. +# %%define variant_summary The Linux kernel compiled for +# %%kernel_variant_package [-n ] +# +%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 + +# %{vanillaversion} : the full version name, e.g. 2.6.35-rc6-git3 +# %{kversion} : the base version, e.g. 2.6.34 + +# Use kernel-%{kversion}%{?dist} as the top-level directory name +# so we can prep different trees within a single git directory. + +# Build a list of the other top-level kernel tree directories. +# This will be used to hardlink identical vanilla subdirs. +sharedirs=$(find "$PWD" -maxdepth 1 -type d -name 'kernel-2.6.*' \ + | grep -x -v "$PWD"/kernel-%{kversion}%{?dist}) ||: + +if [ ! -d kernel-%{kversion}%{?dist}/vanilla-%{vanillaversion} ]; then + + if [ -d kernel-%{kversion}%{?dist}/vanilla-%{kversion} ]; then + + # The base vanilla version already exists. + cd kernel-%{kversion}%{?dist} + + # Any vanilla-* directories other than the base one are stale. + for dir in vanilla-*; do + [ "$dir" = vanilla-%{kversion} ] || rm -rf $dir & + done + + else + + rm -f pax_global_header + # Look for an identical base vanilla dir that can be hardlinked. + for sharedir in $sharedirs ; do + if [[ ! -z $sharedir && -d $sharedir/vanilla-%{kversion} ]] ; then + break + fi + done + if [[ ! -z $sharedir && -d $sharedir/vanilla-%{kversion} ]] ; then +%setup -q -n kernel-%{kversion}%{?dist} -c -T + cp -rl $sharedir/vanilla-%{kversion} . + else +%setup -q -n kernel-%{kversion}%{?dist} -c + mv linux-%{kversion} vanilla-%{kversion} + fi + + fi + +%if "%{kversion}" != "%{vanillaversion}" + + for sharedir in $sharedirs ; do + if [[ ! -z $sharedir && -d $sharedir/vanilla-%{vanillaversion} ]] ; then + break + fi + done + if [[ ! -z $sharedir && -d $sharedir/vanilla-%{vanillaversion} ]] ; then + + cp -rl $sharedir/vanilla-%{vanillaversion} . + + else + + # Need to apply patches to the base vanilla version. + 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 all vanilla dirs, just change to the top-level directory. + cd kernel-%{kversion}%{?dist} + +fi + +# Now build the fedora kernel tree. +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 + +# xfs + +# btrfs +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 + +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-upgrayed-fixes.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 +# CVE-2010-2962 +ApplyPatch drm-i915-sanity-check-pread-pwrite.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 +ApplyPatch drm-nouveau-nva3-noaccel.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 + +# 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 + +# 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 + +# l2tp: fix oops in pppol2tp_xmit (#607054) +ApplyPatch l2tp-fix-oops-in-pppol2tp_xmit.patch + +# add tests for crypto async hashing (#571577) +ApplyPatch crypto-add-async-hash-testing.patch + +# rhbz #598796 +ApplyPatch net-do-not-check-capable-if-kernel.patch + +# rhbz#596475 +ApplyPatch add-support-for-ricoh-e822-sdhci.patch + +# rhbz#648658 (CVE-2010-4073) +ApplyPatch ipc-zero-struct-memory-for-compat-fns.patch + +# rhbz#648656 (CVE-2010-4072) +ApplyPatch ipc-shm-fix-information-leak-to-user.patch + +# rhbz#651264 (CVE-2010-3880) +ApplyPatch inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch +ApplyPatch netlink-make-nlmsg_find_attr-take-a-const-ptr.patch + +# rhbz#656264 +ApplyPatch posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch + +# CVE-2010-4077, CVE-2010-4075 (rhbz#648660, #648663) +ApplyPatch tty-make-tiocgicount-a-handler.patch +ApplyPatch tty-icount-changeover-for-other-main-devices.patch + +# END OF PATCH APPLICATIONS ==================================================== +%endif + +# Any further pre-build tree manipulations happen here. + +chmod +x scripts/checkpatch.pl + +touch .scmversion + +# 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 [] +# +%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 [] +# 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 ] [-r ] +# 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 +# +%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] +# +%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 +* Fri Nov 26 2010 Kyle McMartin 2.6.32.26-175 +- netlink-make-nlmsg_find_attr-take-a-const-ptr.patch: quiet build warning + the INET_DIAG fix caused. + +* Fri Nov 26 2010 Kyle McMartin +- Plug stack leaks in tty/serial drivers. (#648663, #648660) + +* Tue Nov 23 2010 Kyle McMartin +- zero struct memory in ipc compat (CVE-2010-4073) (#648658) +- zero struct memory in ipc shm (CVE-2010-4072) (#648656) +- fix logic error in INET_DIAG bytecode auditing (CVE-2010-3880) (#651264) +- posix-cpu-timers: workaround to suppress the problems with mt exec + (rhbz#656264) + +* Mon Nov 22 2010 Kyle McMartin 2.6.32.26-174 +- Linux 2.6.32.26 + +* Sat Nov 20 2010 Chuck Ebbert 2.6.32.26-173.rc1 +- Linux 2.6.32.26-rc1 +- Comment out upstreamed patches: + kvm-mmu-fix-conflict-access-permissions-in-direct-sp.patch + +* Fri Oct 29 2010 Chuck Ebbert 2.6.32.25-172 +- Linux 2.6.32.25 + +* Fri Oct 22 2010 Chuck Ebbert 2.6.32.25-171.rc1 +- Linux 2.6.32.25-rc1 +- Comment out patches merged upstream: + execve-improve-interactivity-with-large-arguments.patch + execve-make-responsive-to-sigkill-with-large-arguments.patch + setup_arg_pages-diagnose-excessive-argument-size.patch + xen-fix-typo-in-xen-irq-fix.patch + r8169-fix-dma-allocations.patch + skge-quirk-to-4gb-dma.patch +- drm-i915-sanity-check-pread-pwrite.patch: backport fix for + CVE-2010-2962 + +* Thu Oct 14 2010 Kyle McMartin +- rhbz447489: skge-quirk-to-4gb-dma.patch +- rhbz629158: r8169-fix-dma-allocations.patch +- rhbz596475: add-support-for-ricoh-e822-sdhci.patch + +* Mon Sep 27 2010 Chuck Ebbert 2.6.32.23-170 +- Linux 2.6.32.23 +- Drop merged patches: + aio-check-for-multiplication-overflow-in-do_io_submit.patch + inotify-fix-inotify-oneshot-support.patch + inotify-send-IN_UNMOUNT-events.patch + irda-correctly-clean-up-self-ias_obj-on-irda_bind-failure.patch + keys-fix-bug-in-keyctl_session_to_parent-if-parent-has-no-session-keyring.patch + keys-fix-rcu-no-lock-warning-in-keyctl_session_to_parent.patch +- Fix typo in Xen patch from 2.6.32.22. + +* Mon Sep 20 2010 Chuck Ebbert 2.6.32.22-169 +- Linux 2.6.32.22 +- Drop merged patches: + 01-compat-make-compat_alloc_user_space-incorporate-the-access_ok-check.patch + 02-compat-test-rax-for-the-system-call-number-not-eax.patch + 03-compat-retruncate-rax-after-ia32-syscall-entry-tracing.patch + hid-01-usbhid-initialize-interface-pointers-early-enough.patch + hid-02-fix-suspend-crash-by-moving-initializations-earlier.patch + alsa-seq-oss-fix-double-free-at-error-path-of-snd_seq_oss_open.patch + tracing-do-not-allow-llseek-to-set_ftrace_filter.patch + +* Tue Sep 14 2010 Chuck Ebbert 2.6.32.21-168 +- Fix three CVEs: + CVE-2010-3080: /dev/sequencer open failure is not handled correctly + CVE-2010-2960: keyctl_session_to_parent NULL deref system crash + CVE-2010-3079: ftrace NULL pointer dereference + +* Tue Sep 14 2010 Chuck Ebbert +- Mitigate DOS with large argument lists. + +* Tue Sep 14 2010 Kyle McMartin +- x86_64: plug compat syscalls holes. (CVE-2010-3081, CVE-2010-3301) + upgrading is highly recommended. +- aio: check for multiplication overflow in do_io_submit. (CVE-2010-3067) + +* Mon Sep 06 2010 Kyle McMartin +- Backport two fixes from Eric Paris to resolve #598796 which avoids a + capability check if the request comes from the kernel. + +* Thu Sep 02 2010 Chuck Ebbert 2.6.32.21-167 +- irda-correctly-clean-up-self-ias_obj-on-irda_bind-failure.patch (CVE-2010-2954) + +* Fri Aug 27 2010 Chuck Ebbert 2.6.32.21-166 +- Linux 2.6.32.21 + +* Wed Aug 25 2010 Chuck Ebbert 2.6.32.21-165.rc1 +- Linux 2.6.32.21-rc1 +- Drop merged patches: + drm-i915-add-reclaimable-to-page-allocations.patch + drm-i915-fix-hibernate-memory-corruption.patch +- Fix up execshield and DRM upgrade patches to apply after 2.6.32.21 + +* Sat Aug 21 2010 Chuck Ebbert 2.6.32.20-164 +- Linux 2.6.32.20 +- Drop merged patches: + mm-fix-page-table-unmap-for-stack-guard-page-properly.patch + mm-fix-up-some-user-visible-effects-of-the-stack-guard-page.patch + +* Wed Aug 18 2010 Chuck Ebbert 2.6.32.19-163 +- Bump version. + +* Tue Aug 17 2010 Kyle McMartin +- Touch .scmversion in the kernel top level to prevent scripts/setlocalversion + from recursing into our fedpkg git tree and trying to decide whether the + kernel git is modified (obviously not, since it's a tarball.) Fixes make + local. + +* Tue Aug 17 2010 Chuck Ebbert 2.6.32.19-162 +- Fix fallout from the stack guard page fixes. + (mm-fix-page-table-unmap-for-stack-guard-page-properly.patch, + mm-fix-up-some-user-visible-effects-of-the-stack-guard-page.patch) + +* Sat Aug 14 2010 Chuck Ebbert 2.6.32.19-161 +- Linux 2.6.32.19 + +* Fri Aug 13 2010 Chuck Ebbert 2.6.32.19-160.rc1 +- Linux 2.6.32.19-rc1 +- Comment out patches merged upstream: + linux-2.6-btrfs-fix-acl.patch + crypto-testmgr-add-null-test-for-aesni.patch + ext4-make-sure-the-move_ext-ioctl-can-t-overwrite-append-only-files.patch + ext4-fix-freeze-deadlock-under-io.patch +- Fix linux-2.6-usb-wwan-update.patch to apply after 2.6.32.19 + +* Tue Aug 10 2010 Chuck Ebbert 2.6.32.18-159 +- Linux 2.6.32.18 +- Backport nouveau noaccel fix for nva3+ cards from f13. +- ext4-fix-freeze-deadlock-under-io.patch: + Fix deadlock caused by patch in 2.6.32.17 + (0036-ext4-don-t-return-to-userspace-after-freezing-the-fs.patch) + +* Tue Aug 10 2010 Chuck Ebbert 2.6.32.18-158.rc1 +- Bring back drm-upgrayed-fixes.patch, dropped in the + 2.6.32.16 update. (#620955) +- Revert upstream DRM stable fix we already have: + drm-i915-give-up-on-8xx-lid-status.patch + +* Sat Aug 07 2010 Chuck Ebbert +- Linux 2.6.32.18-rc1 +- Revert DRM patches from -stable we already have: + drm-i915-Fix-LVDS-presence-check + drm-i915-parse-child-device-from-vbt.patch +- Comment out patches merged in -stable: + xfs-prevent-swapext-from-operating-on-write-only-files.patch + cifs-fix-dns-resolver.patch + +* Fri Aug 06 2010 Chuck Ebbert 2.6.32.17-157 +- Fix USB HID initialization (#592785) + +* Mon Aug 02 2010 Chuck Ebbert 2.6.32.17-156 +- Linux 2.6.32.17 +- Drop the patches commented out for -rc1 except ssb_check_for_sprom.patch + +* Mon Aug 02 2010 Chuck Ebbert 2.6.32.17-155.rc1 +- Linux 2.6.32.17-rc1 +- Comment out patches merged upstream: + linux-2.6-ext4-quota-metadata-reservation.patch + ext4-issue-discard-operation-before-releasing-blocks.patch + mac80211-do-not-wipe-out-old-supported-rates.patch + mac80211-fix-supported-rates-IE-if-AP-doesnt-give-us-its-rates.patch + iwlwifi-cancel-scan-watchdog-in-iwl_bg_abort_scan.patch + sched-fix-over-scheduling-bug.patch + ethtool-fix-buffer-overflow.patch + cifs-fix-malicious-redirect-problem-in-the-dns-lookup-code.patch + usb-obey-the-sysfs-power-wakeup-setting.patch +- Revert -stable patches we already have: + drm-i915-enable-low-power-render-writes-on-gen3-hardware.patch + drm-i915-define-mi_arb_state-bits.patch +- Comment out due to conflicts with -stable: + ssb_check_for_sprom.patch + +* Tue Jul 27 2010 Chuck Ebbert 2.6.32.16-154 +- xfs-prevent-swapext-from-operating-on-write-only-files.patch: + CVE-2010-2266 + +* Tue Jul 27 2010 Jarod Wilson 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 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 2.6.32.16-151 +- Fix ext4 metadata vs. quota reservation bug (#608770) + +* Fri Jul 23 2010 Chuck Ebbert 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 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 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 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 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 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 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 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 2.6.32.16-142 +- Drop Intel Moorestown support. + +* Wed Jul 07 2010 Jarod Wilson 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 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 2.6.32.16-139 +- Linux 2.6.32.16 + +* Fri Jul 02 2010 Chuck Ebbert 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 2.6.32.15-137 +- Linux 2.6.32.15 + +* Mon Jun 28 2010 Chuck Ebbert 2.6.32.14-136 +- Drop unused ro-nx patches. +- Add support for sky2 Optima chip. + +* Wed Jun 23 2010 Kyle McMartin 2.6.32.14-135 +- l2tp: fix oops in pppol2tp_xmit (rhbz#607054) + +* Tue Jun 15 2010 Kyle McMartin 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 2.6.32.14-133 +- iwlwifi: cancel scan watchdog in iwl_bg_abort_scan (#604264) + +* Mon Jun 14 2010 Kyle McMartin 2.6.32.14-132 +- Add in ACL fixes to btrfs from CVE-2010-2071. + +* Sun Jun 13 2010 Kyle McMartin 2.6.32.14-131 +- mac80211/iwlwifi fix connections to some APs (rhbz#558002) + patches from sgruszka@. + +* Wed Jun 2 2010 John W. Linville +- iwlwifi: update supported PCI_ID list for 5xx0 series (#599153) + +* Thu May 27 2010 Chuck Ebbert 2.6.32.14-127 +- CVE-2010-1437: keyrings: find_keyring_by_name() can gain the freed keyring + +* Wed May 26 2010 Chuck Ebbert 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 +- iwlwifi: recalculate average tpt if not current (#588021) + +* Mon May 24 2010 John W. Linville +- iwlwifi: recover_from_tx_stall (#589777) +- iwlwifi: fix scan races (#592011) +- iwlwifi: fix internal scan race (#592011) + +* Tue May 18 2010 Kyle McMartin +- btrfs: check for read permission on src file in the clone ioctl + (rhbz#593226) + +* Mon May 17 2010 Matthew Garrett +- thinkpad-acpi-fix-backlight.patch: Fix backlight control on some recent + Thinkpads + +* Fri May 14 2010 Jarod Wilson +- Update previous kworld patch w/the one that actually works + correctly w/o constant rf tracking filter recalibration + +* Thu May 13 2010 Jarod Wilson 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 +- Completely drop patches merged in 2.6.32.13 + +* Wed May 12 2010 Chuck Ebbert 2.6.32.13-118 +- Linux 2.6.32.13 + +* Wed May 12 2010 Chuck Ebbert 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 2.6.32.12-116 +- utrace update (#590954) + +* Fri Apr 30 2010 John W. Linville 2.6.32.12-115 +- Revert "ath9k: fix lockdep warning when unloading module" + +* Tue Apr 27 2010 Chuck Ebbert 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 2.6.32.12-113 +- Fix possible data corruption with ext4 mounted with -o discard + +* Tue Apr 27 2010 Chuck Ebbert 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 2.6.32.12-111 +- Linux 2.6.32.12 + +* Fri Apr 23 2010 Chuck Ebbert 2.6.32.12-110.rc2 +- Linux 2.6.32.12-rc2 +- Drop -rc1 workarounds + +* Thu Apr 22 2010 Chuck Ebbert 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 2.6.32.11-108 +- ACPI embedded controller fixes from Fedora 13 (#532161) + +* Wed Apr 21 2010 Matthew Garrett +- thinkpad-acpi-add-x100e.patch: Add EC path for Thinkpad X100 + +* Wed Apr 21 2010 Chuck Ebbert 2.6.32.11-106 +- libata-fix-accesses-at-LBA28-boundary.patch + +* Tue Apr 20 2010 John W. Linville 2.6.32.11-105 +- mac80211: fix deferred hardware scan requests + +* Fri Apr 16 2010 John W. Linville 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 +- Fix autoloading of phy modules (#525966) + +* Mon Apr 12 2010 John W. Linville 2.6.32.11-102 +- patches from Intel to address intermittent firmware failures with iwlagn + +* Tue Apr 07 2010 Chuck Ebbert +- Disable async RAID4/5/6 processing (#575402) + +* Tue Apr 06 2010 Chuck Ebbert +- Build eeepc-laptop driver for x86_64 (F13#565582) +- Build all of the DVB frontend drivers (F13#578755) + +* Mon Apr 05 2010 Jarod Wilson 2.6.32.11-99 +- Fix oops in lirc_it87 driver (#579270) +- Support more imon 0xffdc key combinations + +* Sat Apr 03 2010 Chuck Ebbert 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 +- Fix quota WARN_ON for ext4 (#521914) + +* Thu Apr 01 2010 Matthew Garrett +- 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 2.6.32.10-94 +- Avoid null pointer dereference introduced by 'ssb: check for sprom' (#577463) + +* Mon Mar 29 2010 John W. Linville 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 2.6.32.10-91 +- nouveau: fix display issues on Dell D620 laptops + +* Mon Mar 22 2010 Jarod Wilson 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 +- Fix tg3 poll controller to not oops (bz 574969) + +* Fri Mar 19 2010 John W. Linville 2.6.32.10-88 +- Revise "ssb: check for sprom" (#533746) + +* Fri Mar 19 2010 Jarod Wilson 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 2.6.32.10-85 +- ssb: check for sprom (#533746) + +* Thu Mar 18 2010 Neil Horman +- Remove TIPC from config (bz 574800) + +* Wed Mar 17 2010 John W. Linville 2.6.32.10-83 +- b43: Rewrite DMA Tx status handling sanity checks + +* Tue Mar 16 2010 Jarod Wilson +- 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 +- iwlwifi-silence-tfds-in-queue-message.patch: + don't spam the log when the problem keeps happening + +* Tue Mar 16 2010 Jarod Wilson 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 +- Linux 2.6.32.10 + +* Mon Mar 15 2010 Jarod Wilson 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 2.6.32.10-75.rc1 +- Make the perf package require libdwarf; fix up description (#568309) + +* Sun Mar 14 2010 Chuck Ebbert +- Fix regression in tg3 driver (#571638) + +* Sat Mar 13 2010 Chuck Ebbert +- 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 +- add add aditional pci-id for xvr-500 +- sparc stack alignment fix + +* Wed Mar 10 2010 Tom "spot" Callaway +- cleanup and re-enable sparc selinux mprotect fix + +* Wed Mar 03 2010 Dave Airlie 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 +- Include examples.txt in the perf package (#569506) + +* Mon Mar 01 2010 Dave Jones +- Don't own /usr/src/kernels any more, it's now owned by filesystem. (#569438) + +* Sat Feb 27 2010 Chuck Ebbert 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 2.6.32.9-66 +- TCP: fix broken RTO calculation causing high CPU load (#567530) + +* Thu Feb 25 2010 Ben Skeggs 2.6.32.9-65 +- nouveau: DP fix for cards with version 0x20 DP tables + +* Tue Feb 23 2010 Chuck Ebbert 2.6.32.9-64 +- Linux 2.6.32.9 + +* Tue Feb 23 2010 Ben Skeggs 2.6.32.9-63.rc1 +- nouveau: fix pre-nv17 output detection regression, support for GF8 IGPs + +* Mon Feb 22 2010 Kyle McMartin +- coredump-uid-pipe-check.patch: ditto from F-13/ + +* Mon Feb 22 2010 Chuck Ebbert 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 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 2.6.32.8-59 +- ice1712-fix-revo71-mixer-names.patch: fix mixer names for + monty. (rhbz#566565) + +* Wed Feb 17 2010 Chuck Ebbert 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 2.6.32.8-57 +- CVE-2010-0623 kernel: local DoS via futex_lock_pi + +* Wed Feb 17 2010 Chuck Ebbert +- CVE-2009-4537 kernel: r8169 issue reported at 26c3 + (fix taken from Red Hat/CentOS 5.4) + +* Wed Feb 17 2010 Ben Skeggs 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 +- 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 +- 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 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 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 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 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 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 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 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 2.6.32.8-44.rc1 +- Fix networking bug in 2.6.32.8-rc1 + +* Thu Feb 04 2010 Chuck Ebbert 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 +- Fix another conntrack issue pointed out by jcm. +- Fix utrace header. (rhbz#561536) + +* Wed Feb 03 2010 Kyle McMartin +- 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 2.6.32.7-40 +- Disable graphics DMAR unit on Cantiga (#538163) + +* Mon Feb 01 2010 Dave Airlie 2.6.32.7-39 +- Add two input quirks for HP and eGalax touchscreens. + +* Sat Jan 30 2010 Chuck Ebbert 2.6.32.7-38 +- Fix possible oops in bio-integrity code. + +* Thu Jan 28 2010 Chuck Ebbert 2.6.32.7-37 +- Linux 2.6.32.7 + +* Mon Jan 25 2010 Kyle McMartin 2.6.32.6-36 +- fix gspca mismerge. + +* Mon Jan 25 2010 Kyle McMartin 2.6.32.6-35 +- stable update 2.6.32.6 +- rebase gspca crud. + +* Mon Jan 25 2010 Dave Jones 2.6.32.5-34 +- Disable CONFIG_X86_CPU_DEBUG + +* Sun Jan 24 2010 Hans de Goede 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 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 2.6.32.4-31 +- fix dracut depends + +* Thu Jan 21 2010 John W. Linville 2.6.32.4-30 +- Report meaningful values for MCS rates through wireless extensions + +* Wed Jan 20 2010 Kyle McMartin 2.6.32.4-29 +- add appleir usb driver + +* Mon Jan 18 2010 Kyle McMartin 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 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 2.6.32.3-26 +- Don't generate i915 TV hotplug interrupts ever ever ever. + +* Thu Jan 14 2010 Josef Bacik 2.6.32.3-25 +- fix null pointer dereference in btrfs acl code + +* Wed Jan 13 2010 Dave Airlie 2.6.32.3-24 +- drm fixes from upstream - mostly printk stupids + integrate radeon s/r fix + +* Wed Jan 13 2010 Dave Airlie 2.6.32.3-23 +- crystalhd fix build on powerpc + +* Wed Jan 13 2010 Dave Airlie 2.6.32.3-22 +- bring back offb handoff patch - fixes G5 + nouveau + +* Wed Jan 13 2010 Dave Airlie 2.6.32.3-21 +- fix regression in radeon s/r - hangs on suspend + +* Wed Jan 13 2010 Dave Airlie 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 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 2.6.32.3-18 +- Add Broadcom Crystal HD driver from staging + +* Tue Jan 12 2010 Ben Skeggs 2.6.32.3-17 +- add nouveau to staging Kconfig + +* Tue Jan 12 2010 Ben Skeggs 2.6.32.3-16 +- nouveau: fix nvac noaccel patch, not sure what happened there! + +* Tue Jan 12 2010 Ben Skeggs 2.6.32.3-15 +- nouveau: patches and firmware from F12 that aren't in upstream kernel + +* Mon Jan 11 2010 Dave Airlie 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 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 2.6.32.3-12 +- Re-enable ATM_HE (#545289) + +* Fri Jan 08 2010 Chuck Ebbert 2.6.32.3-11 +- Add another symbol to look for when generating modules.block + +* Thu Jan 07 2010 Chuck Ebbert 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 2.6.32.3-9 +- Change configs to build the acerhdf driver again, requires + setting CONFIG_HWMON=y + +* Wed Jan 06 2010 Chuck Ebbert 2.6.32.3-8 +- Linux 2.6.32.3 + +* Wed Jan 06 2010 Chuck Ebbert 2.6.32.3-7.rc2 +- Remove obsolete config options (generated .config files are + unchanged.) + +* Wed Jan 06 2010 Chuck Ebbert 2.6.32.3-6.rc2 +- Linux 2.6.32.3-rc2 + +* Tue Jan 05 2010 Chuck Ebbert 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 2.6.32.2-4 +- Fix WMI driver oopses and a memory leak. + +* Tue Jan 05 2010 Chuck Ebbert 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 +- Drop some of the spinlock/vm taint patches. dump_stack() already does same. + +* Fri Jan 1 2010 Kyle McMartin 2.6.32.2-1 +- Rebased F-12 to 2.6.32, still needs forward porting of patches. + +* Thu Dec 24 2009 Kyle McMartin 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 2.6.32.2-14 +- Linux 2.6.32.2 +- dropped upstream patches. + +* Fri Dec 18 2009 Roland McGrath - 2.6.32.1-13 +- minor utrace update + +* Thu Dec 17 2009 Matthew Garrett 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 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 2.6.32.1-10 +- utrace update, now testing the utrace-based ptrace! + +* Mon Dec 14 2009 Kyle McMartin 2.6.32.1-9 +- 2.6.32.1 +- ext4 patches and more... + +* Wed Dec 09 2009 Kyle McMartin 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 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 2.6.32-6 +- Copy fix for #540580 from F-12. + +* Tue Dec 08 2009 Kyle McMartin 2.6.32-5 +- new rpm changes: + - %{PACKAGE_VERSION} -> %{version} + - %{PACKAGE_RELEASE} -> %{release} + +* Tue Dec 08 2009 Kyle McMartin 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 2.6.32-3 +- Allow userspace to adjust kvmclock offset (#530389) + +* Mon Dec 7 2009 Steve Dickson 2.6.32-2 +- Updated the NFS4 pseudo root code to the latest release. + +* Thu Dec 03 2009 Kyle McMartin 2.6.32-1 +- Linux 2.6.32 + +* Wed Dec 02 2009 Kyle McMartin 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 2.6.32-0.64.rc8.git2 +- ath9k: add fixes suggested by upstream maintainer + +* Wed Dec 02 2009 David Woodhouse 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 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 2.6.32-0.61.rc8.git2 +- fix-9p-fscache.patch: fix build. + +* Mon Nov 30 2009 Kyle McMartin 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 +- drm-i915-fix-sync-to-vbl-when-vga-is-off.patch: add, (rhbz#541670) + +* Sun Nov 29 2009 Kyle McMartin +- linux-2.6-sysrq-c.patch: drop, was made consistent upstream. + +* Sat Nov 28 2009 Jarod Wilson 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 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 +- Install vmlinux.id file in kernel-devel rpm. + +* Fri Nov 20 2009 Chuck Ebbert 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 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 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 2.6.32-0.49.rc7.git2 +- 2.6.32-rc7-git2 + +* Mon Nov 16 2009 Dave Airlie 2.6.32-0.48.rc7.git1 +- backout gpg change now that koji is fixed + +* Sun Nov 15 2009 Chuck Ebbert 2.6.32-0.47.rc7.git1 +- Buildrequire gpg + +* Sun Nov 15 2009 Chuck Ebbert +- Fix oops in VIA Padlock driver. + +* Sun Nov 15 2009 Chuck Ebbert +- Linux 2.6.32-rc7-git1 + +* Fri Nov 13 2009 Chuck Ebbert +- Linux 2.6.32-rc7 + +* Thu Nov 05 2009 Jarod Wilson +- Add --with dbgonly rpmbuild option to build only debug kernels + +* Wed Nov 04 2009 Kyle McMartin +- Make JBD2_DEBUG a toggleable config option. + +* Wed Nov 04 2009 Kyle McMartin 2.6.32-0.39.rc6.git0 +- 2.6.32-rc6, fix for NULL ptr deref in cfg80211. + +* Mon Nov 02 2009 Kyle McMartin 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 +- 2.6.32-rc5-git5 + +* Tue Oct 27 2009 John W. Linville +- Disable build of prism54 module + +* Tue Oct 27 2009 Dave Airlie +- Get dd command line args correct. + +* Mon Oct 26 2009 Dave Jones +- Make a 20MB initramfs file so rpm gets its diskspace calculations right. (#530778) + +* Sat Oct 23 2009 Chuck Ebbert +- 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 2.6.32-0.33.rc5.git1 +- 2.6.32-rc5-git1 + +* Fri Oct 16 2009 Chuck Ebbert +- 2.6.32-rc5 +- New config option: CONFIG_VMXNET3=m + +* Wed Oct 14 2009 Chuck Ebbert +- 2.6.32-rc4-git4 + +* Wed Oct 14 2009 Steve Dickson +- Updated the NFS v4 pseudo root patch so it will apply +- Fixed hang during NFS installs (bz 528537) + +* Wed Oct 14 2009 Peter Jones +- 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 +- 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 +- Build perf manpages properly. + +* Tue Oct 13 2009 Dave Airlie +- cleanup some of drm vga arb bits that are upstream + +* Mon Oct 12 2009 Jarod Wilson +- 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 2.6.32-0.24.rc4.git0 +- Last-minute USB fix from upstream. + +* Sun Oct 11 2009 Chuck Ebbert +- Fix lirc build after header changes. +- Fix bug in lirc interrupt processing. + +* Sun Oct 11 2009 Chuck Ebbert +- Fix up multiple definition of debug options + (EXT4_DEBUG, DEBUG_FORCE_WEAK_PER_CPU) + +* Sun Oct 11 2009 Chuck Ebbert +- 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 +- 2.6.32-rc3-git3 + +* Thu Oct 08 2009 Ben Skeggs +- ppc: compile nvidiafb as a module only, nvidiafb+nouveau = bang! (rh#491308) + +* Wed Oct 07 2009 Dave Jones +- Enable FUNCTION_GRAPH_TRACER on x86-64. + +* Wed Oct 07 2009 Dave Jones +- Disable CONFIG_IRQSOFF_TRACER on srostedt's recommendation. + (Adds unwanted overhead when not in use). + +* Sun Oct 04 2009 Kyle McMartin 2.6.32-0.17.rc3.git0 +- 2.6.32-rc3 (bah, rebase script didn't catch it.) + +* Sun Oct 04 2009 Kyle McMartin +- 2.6.32-rc1-git7 +- [x86,x86_64] ACPI_PROCESSOR_AGGREGATOR=m + +* Mon Sep 28 2009 Kyle McMartin +- 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 +- sick of rejects. + +* Mon Sep 28 2009 Chuck Ebbert +- 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 +- Must now make mrproper after each config pass, due to Kbuild + stashing away the $ARCH variable. + +* Sun Sep 27 2009 Kyle McMartin +- 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 +- %ghost the dracut initramfs file. + +* Thu Sep 17 2009 Hans de Goede +- Now that we have %%post generation of dracut images we do not need to + Require dracut-kernel anymore + +* Thu Sep 17 2009 Chuck Ebbert +- Disable drm-nouveau too -- it won't build without other + drm updates. + +* Wed Sep 16 2009 Roland McGrath +- Remove workaround for gcc bug #521991, now fixed. + +* Tue Sep 15 2009 Kyle McMartin +- 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 +- 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 +- Added support for -o v4 mount parsing + +* Fri Sep 11 2009 Dave Jones +- Apply NX/RO to modules + +* Fri Sep 11 2009 Dave Jones +- Mark kernel data section as NX + +* Fri Sep 11 2009 Ben Skeggs +- nouveau: bring in Matthew Garret's initial switchable graphics support + +* Fri Sep 11 2009 Ben Skeggs +- 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 +- 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 +- drm-nouveau.patch: add some scaler-only modes for LVDS, GEM/TTM fixes + +* Wed Sep 09 2009 Dennis Gilmore 2.6.31-2 +- touch the dracut initrd file when using %%{with_dracut} + +* Wed Sep 09 2009 Chuck Ebbert 2.6.31-1 +- Linux 2.6.31 + +* Wed Sep 09 2009 Chuck Ebbert +- Enable VXpocket and PDaudioCF PCMCIA sound drivers. + +* Wed Sep 09 2009 Hans de Goede +- 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 +- Updated the NFS4 pseudo root code to the latest release. + +* Wed Sep 09 2009 Justin M. Forbes +- Revert virtio_blk to rotational mode. (#509383) + +* Wed Sep 09 2009 Dave Airlie 2.6.31-0.219.rc9.git +- uggh lost nouveau bits in page flip + +* Wed Sep 09 2009 Dave Airlie 2.6.31-0.218.rc9.git2 +- fix r600 oops with page flip patch (#520766) + +* Wed Sep 09 2009 Ben Skeggs +- drm-nouveau.patch: fix display resume on pre-G8x chips + +* Wed Sep 09 2009 Ben Skeggs +- drm-nouveau.patch: add getparam to know using tile_flags is ok for scanout + +* Wed Sep 09 2009 Chuck Ebbert +- 2.6.31-rc9-git2 + +* Wed Sep 9 2009 Roland McGrath 2.6.31-0.214.rc9.git1 +- compile with -fno-var-tracking-assignments, work around gcc bug #521991 + +* Wed Sep 09 2009 Dave Airlie 2.6.31-0.213.rc9.git1 +- fix two bugs in r600 kms, fencing + mobile lvds + +* Tue Sep 08 2009 Ben Skeggs 2.6.31-0.212.rc9.git1 +- drm-nouveau.patch: fix ppc build + +* Tue Sep 08 2009 Ben Skeggs 2.6.31-0.211.rc9.git1 +- drm-nouveau.patch: more misc fixes + +* Tue Sep 08 2009 Dave Airlie 2.6.31-0.210.rc9.git1 +- drm-page-flip.patch: rebase again + +* Tue Sep 08 2009 Dave Airlie 2.6.31-0.209.rc9.git1 +- drm-next.patch: fix r600 signal interruption return value + +* Tue Sep 08 2009 Ben Skeggs 2.6.31-0.208.rc9.git1 +- drm-nouveau.patch: latest upstream + rebase onto drm-next + +* Tue Sep 08 2009 Dave Airlie 2.6.31-0.207.rc9.git1 +- drm-vga-arb.patch: update to avoid lockdep + add r600 support + +* Tue Sep 08 2009 Dave Airlie 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 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 2.6.31-0.204.rc9 +- 2.6.31-rc9 + +* Fri Sep 04 2009 Chuck Ebbert 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 +- drm-ddc-caching-bug.patch: Empty the connector's mode list when it's + disconnected. + +* Thu Sep 03 2009 Jarod Wilson +- Update hdpvr and lirc_zilog drivers for 2.6.31 i2c + +* Thu Sep 03 2009 Justin M.Forbes +- Fix xen guest with stack protector. (#508120) +- Small kvm fixes. + +* Wed Sep 02 2009 Adam Jackson 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 +- Add missing scriptlet dependancy. (#520788) + +* Tue Sep 01 2009 Adam Jackson +- Make DRM less chatty about EDID failures. No one cares. + +* Tue Sep 01 2009 Chuck Ebbert +- 2.6.31-rc8-git2 +- Blank out drm-intel-next: entire contents are now upstream. + +* Tue Sep 01 2009 Dave Jones +- Make firmware buildarch noarch. (Suggested by drago01 on irc) + +* Tue Sep 01 2009 Jarod Wilson +- 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 +- 2.6.31-rc8-git1 +- Drop linux-2.6-inotify-accounting.patch, merged upstream. + +* Sun Aug 30 2009 Jarod Wilson +- fix lirc_imon oops on older devices w/o tx ctrl ep (#520008) + +* Fri Aug 28 2009 Eric Paris 2.6.31-0.190.rc8 +- fix inotify length accounting and send inotify events + +* Fri Aug 28 2009 David Woodhouse +- Enable Solos DSL driver + +* Fri Aug 28 2009 Chuck Ebbert +- 2.6.31-rc8 + +* Thu Aug 27 2009 Chuck Ebbert 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 +- drm-rv710-ucode-fix.patch: Treat successful microcode load on RV710 as, + you know, success. (#519718) + +* Thu Aug 27 2009 Chuck Ebbert +- 2.6.31-rc7-git5 +- Drop patch linux-2.6-ima-leak.patch, now merged upstream. + +* Wed Aug 26 2009 Jarod Wilson +- Fix up hdpvr ir enable patch for use w/modular i2c (David Engel) + +* Wed Aug 26 2009 Eric Paris +- fix iint_cache leak in IMA code + drop the ima=0 patch + +* Wed Aug 26 2009 Justin M. Forbes +- Fix munlock with KSM (#516909) +- Re-enable KSM + +* Wed Aug 26 2009 Chuck Ebbert +- 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 +- drm-intel-next.patch: Update, various output setup fixes. + +* Wed Aug 26 2009 David Woodhouse +- Make WiMAX modular (#512070) + +* Tue Aug 25 2009 Kyle McMartin +- allow-disabling-ima.diff: debugging patch... adds ima=0 kernel + param to disable initialization of IMA. + +* Tue Aug 25 2009 Ben Skeggs 2.6.31-0.174.rc7.git2 +- drm-nouveau.patch: upstream update, pre-nv50 tv-out + misc fixes + +* Tue Aug 25 2009 Chuck Ebbert 2.6.31-0.173.rc7.git2 +- Fix Xen boot (#508120) + +* Tue Aug 25 2009 Dave Airlie +- pull in drm-next tree + rebase around it + +* Mon Aug 24 2009 Chuck Ebbert +- 2.6.31-rc7-git2 + +* Mon Aug 24 2009 Chuck Ebbert +- 2.6.31-rc7-git1 + +* Sat Aug 22 2009 Chuck Ebbert +- 2.6.31-rc7 + +* Thu Aug 20 2009 Mark McLoughlin +- Disable LZMA for xen (#515831) + +* Thu Aug 20 2009 Chuck Ebbert +- 2.6.31-rc6-git5 +- Fix up drm-r600-kms.patch +- Drop fix-perf-make-man-failure.patch + +* Wed Aug 19 2009 Chuck Ebbert +- 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 +- 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 +- 2.6.31-rc6-git3 + +* Mon Aug 17 2009 Dave Jones 2.6.31-0.161.rc6.git2 +- 2.6.31-rc6-git2 + +* Mon Aug 17 2009 Chuck Ebbert +- Stop generating the (unused) ppc64-kdump.config file. + +* Mon Aug 17 2009 Jarod Wilson +- Add new lirc driver for built-in ENE0100 device on some laptops + +* Sun Aug 16 2009 Kyle McMartin 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 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 2.6.31-0.155.rc6 +- 2.6.31-rc6 + +* Wed Aug 12 2009 Kyle McMartin +- fix perf. +- move perf to perf.$ver instead of perf-$ver... + +* Wed Aug 12 2009 Dennis Gilmore +- 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 +- 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 +- Update drm-intel-next patch + +* Tue Aug 11 2009 Dennis Gilmore - 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 +- Enable config IMA + +* Tue Aug 11 2009 Ben Skeggs +- nouveau: various cleanups and fixes + more sanity checking in dma paths + +* Mon Aug 10 2009 Jarod Wilson +- 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 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 +- btusb autosuspend: fix build on !CONFIG_PM by stubbing out + suspend/resume methods. + +* Sat Aug 08 2009 Dennis Gilmore 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 +- Apply KSM updates from upstream + +* Fri Aug 07 2009 Hans de Goede +- When building a dracut generic initrd tell new-kernel-pkg to use that + instead of running mkinitrd + +* Fri Aug 07 2009 Dave Airlie 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 2.6.31-0.138.rc5.git3 +- Fix kvm virtio_blk errors (#514901) + +* Thu Aug 06 2009 Adam Jackson +- Hush DRM vblank warnings, they're constant (and harmless) under DRI2. + +* Thu Aug 06 2009 Dave Airlie 2.6.31.0.134.rc5.git3 +- fixup vga arb warning at startup and handover between gpus + +* Thu Aug 06 2009 Kyle McMartin 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 2.6.31.0.132.rc5.git3 +- revert-ftrace-powerpc-snafu.patch - fix ppc build + +* Wed Aug 05 2009 Ben Skeggs +- nouveau: respect nomodeset + +* Wed Aug 05 2009 Chuck Ebbert +- Fix /usr/sbin/perf script. (#515494) + +* Wed Aug 05 2009 Dave Jones +- Fix shift in pci cacheline size printk. + +* Wed Aug 05 2009 Dave Airlie 2.6.31.0.128.rc5.git3 +- 2.6.31-rc5-git3 +- drop cpufreq + set memory fixes + +* Wed Aug 05 2009 Dave Airlie +- Add Jeromes initial r600 kms work. +- rebase arb patch + +* Tue Aug 04 2009 Kyle McMartin +- 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 +- nouveau: more code share between nv50/ +- update VGA arb patches again + +* Mon Aug 03 2009 Adam Jackson +- 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 2.6.31-0.122.rc5.git2 +- 2.6.31-rc5-git2 + +* Mon Aug 03 2009 Adam Jackson +- (Attempt to) fix watermark setup on Intel 9xx parts. + +* Mon Aug 03 2009 Jarod Wilson +- make usbhid driver ignore all recent SoundGraph iMON devices, so the + lirc_imon driver can grab them instead + +* Mon Aug 03 2009 Dave Airlie +- update VGA arb patches + +* Sat Aug 01 2009 David Woodhouse 2.6.31-0.118.rc5 +- Fix boot failures on ppc32 (#514010, #505071) + +* Fri Jul 31 2009 Kyle McMartin 2.6.31-0.117.rc5 +- Linux 2.6.31-rc5 + +* Fri Jul 31 2009 Matthew Garrett +- linux-2.6-dell-laptop-rfkill-fix.patch: Fix up Dell rfkill + +* Fri Jul 31 2009 Ben Skeggs +- nouveau: build against 2.6.31-rc4-git6, fix script parsing on some G8x chips + +* Thu Jul 30 2009 Chuck Ebbert +- 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 +- 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 +- nouveau: another DCB 1.5 entry, G80 corruption fixes, small +- fix VGA ARB + kms + +* Wed Jul 29 2009 Dave Jones +- Add support for dracut. (Harald Hoyer) + +* Wed Jul 29 2009 Ben Skeggs +- drm-nouveau.patch: nv50/nva0 tiled scanout fixes, nv40 kms fixes + +* Wed Jul 29 2009 Chuck Ebbert +- Linux 2.6.31-rc4-git3 +- Drop linux-2.6-ecryptfs-overflow-fixes.patch, merged upstream now. + +* Wed Jul 29 2009 Dave Airlie +- update VGA arb patches + +* Tue Jul 28 2009 Adam Jackson +- Remove the pcspkr modalias. If you're still living in 1994, load it + by hand. + +* Tue Jul 28 2009 Eric Sandeen 2.6.31-0.102.rc4.git2 +- Fix eCryptfs overflow issues (CVE-2009-2406, CVE-2009-2407) + +* Tue Jul 28 2009 Kyle McMartin 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 +- 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 +- 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 +- 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 +- update vga arb code + +* Mon Jul 27 2009 Matthew Garrett +- drm-intel-pm.patch - Add runtime PM for Intel graphics + +* Fri Jul 24 2009 Kristian Høgsberg +- 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 +- Enable CONFIG_DEBUG_KOBJECT in debug kernels. (#513606) + +* Thu Jul 23 2009 Kyle McMartin +- perf BuildRequires binutils-devel now. + +* Thu Jul 23 2009 Justin M. Forbes +- Add KSM support + +* Thu Jul 23 2009 Kyle McMartin 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 +- We have to override the new %%install behavior because, well... the kernel is special. + +* Wed Jul 22 2009 Dave Jones +- 2.6.31-rc3-git5 + +* Wed Jul 22 2009 Ben Skeggs 2.6.31-0.82.rc3.git4 +- Enable KMS for nouveau + +* Wed Jul 22 2009 Ben Skeggs +- Update nouveau from upstream (initial suspend/resume + misc bugfixes) + +* Mon Jul 20 2009 Adam Jackson +- Disable VGA arbiter patches for a moment + +* Mon Jul 20 2009 Adam Jackson +- Revive 4k framebuffers for intel gen3 + +* Mon Jul 20 2009 Dave Jones 2.6.31-0.78.rc3.git4 +- Enable CONFIG_RTC_HCTOSYS (#489494) + +* Mon Jul 20 2009 Dave Jones 2.6.31-0.77.rc3.git4 +- Don't build 586 kernels any more. + +* Sun Jul 19 2009 Dave Jones 2.6.31-0.75.rc3.git4 +- build a 'full' package on i686 (Bill Nottingham) + +* Sun Jul 19 2009 Dave Jones 2.6.31-0.74.rc3.git4 +- 2.6.31-rc3-git4 + +* Sat Jul 18 2009 Matthew Garrett +- 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 +- 2.6.31-rc3-git3 + +* Thu Jul 16 2009 Matthew Garrett +- linux-2.6-defaults-aspm.patch - default ASPM to on for PCIe >= 1.1 hardware + +* Thu Jul 16 2009 Dave Airlie 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 2.6.31-0.68-rc3 +- 2.6.31-rc3 +- config changes: + - RTL8192SU is not set, (staging) + +* Mon Jul 13 2009 Kyle McMartin 2.6.31-0.67.rc2.git9 +- 2.6.31-rc2-git9 +- config changes: + - BLK_DEV_OSD=m + +* Mon Jul 13 2009 Ben Skeggs +- drm-nouveau.patch: update from upstream + +* Fri Jul 10 2009 Chuck Ebbert +- 2.6.31-rc2-git6 +- Drop dmadebug-spinlock patch -- merged upstream. + +* Fri Jul 10 2009 Dave Jones 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 +- 2.6.31-rc2-git5 + +* Thu Jul 09 2009 Dave Jones 2.6.31-0.62.rc2.git4 +- Use correct spinlock initialization in dma-debug + +* Thu Jul 09 2009 Chuck Ebbert 2.6.31-0.61.rc2.git4 +- 2.6.31-rc2-git4 + +* Thu Jul 09 2009 Jarod Wilson +- 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 +- Enable a bunch of debugging options that were missed somehow. + +* Wed Jul 08 2009 Kyle McMartin +- Bump NR_CPUS on x86_64 to 512. + +* Wed Jul 08 2009 Adam Jackson +- drm-no-gem-on-i8xx.patch: Drop, intel 2D driver requires GEM now. This + should be entertaining. + +* Wed Jul 08 2009 Kyle McMartin +- First cut of /usr/sbin/perf wrapper script and 'perf' + subpackage. + +* Wed Jul 08 2009 Kyle McMartin 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 +- Added NFSD v4 dynamic pseudo root patch which allows + NFS v3 exports to be mounted by v4 clients. + +* Tue Jul 07 2009 Jarod Wilson +- See if we can't make lirc_streamzap behave better... (#508952) + +* Tue Jul 07 2009 Chuck Ebbert 2.6.31-0.47.rc2.git2 +- 2.6.31-rc2-git2 + +* Tue Jul 07 2009 Jarod Wilson +- Make lirc_i2c actually work with 2.6.31 i2c + +* Mon Jul 06 2009 Chuck Ebbert +- Use LZMA for kernel compression on X86. + +* Mon Jul 06 2009 Jarod Wilson +- 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 2.6.31-0.42.rc2 +- 2.6.31-rc2 + +* Sat Jul 04 2009 Chuck Ebbert +- 2.6.31-rc1-git11 + +* Fri Jul 03 2009 Hans de Goede +- Disable v4l1 ov511 and quickcam_messenger drivers (obsoleted by + v4l2 gspca subdrivers) + +* Thu Jul 02 2009 Kyle McMartin 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 2.6.31-0.38.rc1.git7 +- 2.6.31-rc1-git7 + +* Tue Jun 30 2009 Dave Jones 2.6.31-0.37.rc1.git5 +- Disable kmemleak. Way too noisy, and not finding any real bugs. + +* Tue Jun 30 2009 Ben Skeggs +- drm-nouveau.patch: match upstream + +* Mon Jun 29 2009 Chuck Ebbert 2.6.31-0.35.rc1.git5 +- 2.6.31-rc1-git5 +- CONFIG_LEDS_LP3944=m + +* Mon Jun 29 2009 Chuck Ebbert +- Try to fix the dm overlay bug for real (#505121) + +* Sat Jun 27 2009 Ben Skeggs 2.6.31-0.33.rc1.git2 +- drm-nouveau.patch: fix conflicts from 2.6.31-rc1-git2 + +* Fri Jun 26 2009 Dave Jones 2.6.31-0.31.rc1.git2 +- Further improvements to kmemleak + +* Fri Jun 26 2009 Dave Jones 2.6.31-0.30.rc1.git2 +- 2.6.31-rc1-git2 + +* Fri Jun 26 2009 Ben Skeggs +- drm-nouveau.patch: latest upstream + reenable + +* Thu Jun 25 2009 Dave Jones 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 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 +- Move perf to /usr/libexec/perf-$KernelVer. + +* Wed Jun 24 2009 Kyle McMartin +- config changes: + - generic: + - CONFIG_SCSI_DEBUG=m (was off, requested by davidz) + +* Wed Jun 24 2009 Dave Jones 2.6.31-0.22.rc0.git22 +- 2.6.30-git22 + +* Tue Jun 23 2009 Dave Jones 2.6.31-0.22.rc0.git20 +- 2.6.30-git20 + +* Mon Jun 22 2009 Kyle McMartin 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 +- 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 2.6.31-0.21.rc0.git18 +- Add patch to possibly fix the pktlen problem on via-velocity. + +* Sun Jun 21 2009 Dave Jones 2.6.31-0.20.rc0.git18 +- 2.6.30-git18 + VIA crypto & mmc patches now upstream. + +* Sun Jun 21 2009 Dave Jones +- Determine cacheline sizes in a generic manner. + +* Sun Jun 21 2009 Chuck Ebbert 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 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 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 +- sched-introduce-SCHED_RESET_ON_FORK-scheduling-policy-flag.patch: add, + queued in tip/sched/core (ca94c442535a44d508c99a77e54f21a59f4fc462) + +* Fri Jun 19 2009 Kyle McMartin 2.6.31.0.15.rc0.git14 +- Fix up ptrace, hopefully. Builds on x86_64 at least. + +* Fri Jun 19 2009 Chuck Ebbert +- linux-2.6-tip.git-203abd67b75f7714ce98ab0cdbd6cfd7ad79dec4.patch + Fixes oops on boot with qemu (#507007) + +* Fri Jun 19 2009 Kyle McMartin 2.6.31-0.13.rc0.git14 +- 2.6.30-git14 + +* Fri Jun 19 2009 Chuck Ebbert +- 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 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 +- 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 +- Update via-sdmmc driver + +* Mon Jun 15 2009 Jarod Wilson +- Update lirc patches w/new imon hotness + +* Fri Jun 12 2009 Chuck Ebbert +- Update VIA temp sensor and mmc drivers. + +* Fri Jun 12 2009 John W. Linville 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 +- 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 2.6.30-1 +- Linux 2.6.30 rebase. + +* Tue Jun 09 2009 John W. Linville +- Clean-up some wireless bits in config-generic + +* Tue Jun 09 2009 Chuck Ebbert +- Add support for ACPI P-states on VIA processors. +- Disable the e_powersaver driver. + +* Tue Jun 09 2009 Chuck Ebbert +- Linux 2.6.30-rc8-git6 + +* Fri Jun 05 2009 Chuck Ebbert +- Linux 2.6.30-rc8-git1 + +* Wed Jun 03 2009 Kyle McMartin +- Linux 2.6.30-rc8 + +* Tue Jun 2 2009 Roland McGrath +- utrace update (fixes stap PR10185) + +* Tue Jun 02 2009 Dave Jones +- For reasons unknown, RT2X00 driver was being built-in. + Make it modular. + +* Tue Jun 02 2009 Dave Jones +- 2.6.30-rc7-git5 + +* Sat May 30 2009 Dave Jones +- 2.6.30-rc7-git4 + +* Thu May 28 2009 Dave Jones +- 2.6.30-rc7-git2 + +* Tue May 26 2009 Dave Jones +- Various cpufreq patches from git. + +* Tue May 26 2009 Dave Jones +- 2.6.30-rc7-git1 + +* Mon May 25 2009 Kyle McMartin +- rds-only-on-64-bit-or-x86.patch: drop patch, issue is fixed upstream. + +* Sat May 23 2009 Dave Jones +- 2.6.30-rc7 + +* Thu May 21 2009 Dave Jones +- 2.6.30-rc6-git6 + +* Wed May 20 2009 Chuck Ebbert +- Enable Divas (formerly Eicon) ISDN drivers on x86_64. (#480837) + +* Wed May 20 2009 Dave Jones +- 2.6.30-rc6-git5 + +* Mon May 18 2009 Dave Jones +- 2.6.30-rc6-git3 + +* Sun May 17 2009 Dave Jones +- 2.6.30-rc6-git2 + +* Sat May 16 2009 Dave Jones +- 2.6.30-rc6 + +* Mon May 11 2009 Kyle McMartin +- Linux 2.6.30-rc5-git1 + +* Fri May 08 2009 Kyle McMartin +- Linux 2.6.30-rc5 + +* Fri May 08 2009 Kyle McMartin +- Linux 2.6.30-rc4-git4 + +* Wed May 06 2009 Kyle McMartin +- 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 +- Fix ext4 corruption on partial write into prealloc block + +* Thu Apr 30 2009 Kyle McMartin +- 2.6.30-rc4 + +* Wed Apr 29 2009 Dave Jones +- 2.6.30-rc3-git6 + +* Tue Apr 28 2009 Dave Jones +- 2.6.30-rc3-git4 + +* Tue Apr 28 2009 Chuck Ebbert +- Make the kernel-vanilla package buildable again. +- Allow building with older versions of RPM. + +* Tue Apr 28 2009 Neil Horman +- Backport missing snmp stats (bz 492391) + +* Tue Apr 28 2009 Chuck Ebbert 2.6.30-0.72.rc3.git3 +- Drop unused exports from the iwl3945 driver. + +* Tue Apr 28 2009 Chuck Ebbert +- Linux 2.6.30-rc3-git3 + +* Mon Apr 27 2009 Dave Jones +- 2.6.30-rc3-git2 + +* Sun Apr 26 2009 Chuck Ebbert 2.6.30-0.68.rc3.git1 +- Linux 2.6.30-rc3-git1 + +* Wed Apr 22 2009 Dave Jones 2.6.30-0.67.rc3 +- Disable SYSFS_DEPRECATED on ia64 + +* Wed Apr 22 2009 Kyle McMartin +- Linux 2.6.30-rc3 +- PROC_VMCORE=y: Exports the dump image of crashed + kernel in ELF format + +* Wed Apr 22 2009 Neil Horman +- 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 +- Don't include the modules.*.bin files in the RPM package. + +* Tue Apr 21 2009 Dave Jones +- 2.6.30-rc2-git7 + +* Mon Apr 20 2009 Dave Jones +- Various s390x config tweaks. (#496596, #496601, #496605, #496607) + +* Mon Apr 20 2009 Dave Jones +- 2.6.30-rc2-git6 + +* Sat Apr 18 2009 Chuck Ebbert +- Set CONFIG_UEVENT_HELPER_PATH to the empty string (#496296) + +* Fri Apr 17 2009 Dave Jones +- 2.6.30-rc2-git3 + +* Thu Apr 16 2009 Kyle McMartin 2.6.30-0.58.rc2.git1 +- 2.6.30-rc2-git1 + +* Wed Apr 15 2009 Kyle McMartin 2.6.30-0.57.rc2 +- 2.6.30-rc2 + +* Tue Apr 14 2009 Kyle McMartin +- 2.6.30-rc1-git7 +- CONFIG_TOUCHSCREEN_AD7879_I2C=m +- CONFIG_STRIP_ASM_SYMS=y, off for -debug + +* Mon Apr 13 2009 Kyle McMartin +- ppc-fix-parport_pc.patch: add from linuxppc-dev@ + +* Mon Apr 13 2009 Kyle McMartin +- execshield: fix build (load_user_cs_desc is 32-bit only in tlb.c) + +* Sun Apr 12 2009 Kyle McMartin +- 2.6.30-rc1-git5 +- revert-fix-modules_install-via-nfs.patch: reverted upstream + +* Thu Apr 09 2009 Kyle McMartin +- actually drop utrace-ftrace from srpm. + +* Thu Apr 09 2009 Kyle McMartin +- 2.6.30-rc1-git2 +- CONFIG_IGBVF=m +- CONFIG_NETFILTER_XT_TARGET_LED=m + +* Thu Apr 09 2009 Dave Jones +- Bring back the /dev/crash driver. (#492803) + +* Wed Apr 08 2009 Dave Jones +- disable MMIOTRACE in non-debug builds (#494584) + +* Wed Apr 08 2009 Kyle McMartin 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 +- utrace update, drop unfinished utrace-ftrace + +* Tue Apr 07 2009 Kyle McMartin +- 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 +- Enable CONFIG_CIFS_STATS (#494545) + +* Mon Apr 06 2009 Kyle McMartin +- linux-2.6-execshield.patch: rebase for 2.6.30 + +* Mon Apr 06 2009 Kyle McMartin +- 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 +- linux-2.6-btrfs-fix-umount-hang.patch: fix umount hang on btrfs + +* Thu Apr 02 2009 Kyle McMartin +- fix-ppc-debug_kmap_atomic.patch: fix build failures on ppc. + +* Thu Apr 02 2009 Kyle McMartin +- Linux 2.6.29-git9 + +* Tue Mar 31 2009 Kyle McMartin +- rds-only-on-64-bit-or-x86.patch: add +- at76-netdev_ops.patch: add + +* Tue Mar 31 2009 Kyle McMartin +- Linux 2.6.29-git8 +- linux-2.6-net-fix-another-gro-bug.patch: upstream. + +* Tue Mar 31 2009 Eric Sandeen +- add fiemap.h to kernel-headers +- build ext4 (and jbd2 and crc16) into the kernel + +* Tue Mar 31 2009 Kyle McMartin +- Linux 2.6.29-git7 +- fix-staging-at76.patch: pull patch from linux-wireless to fix... + +* Mon Mar 30 2009 Kyle McMartin 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 +- Make the .shared-srctree file a list so more than two checkouts + can share source files. + +* Mon Mar 30 2009 Chuck Ebbert +- Separate PAT fixes that are headed for -stable from our out-of-tree ones. + +* Mon Mar 30 2009 Dave Jones +- Make io schedulers selectable at boot time again. (#492817) + +* Mon Mar 30 2009 Dave Jones +- Add a strict-devmem=0 boot argument (#492803) + +* Mon Mar 30 2009 Adam Jackson +- linux-2.6.29-pat-fixes.patch: Fix PAT/GTT interaction + +* Mon Mar 30 2009 Mauro Carvalho Chehab +- some fixes of troubles caused by v4l2 subdev conversion + +* Mon Mar 30 2009 Mark McLoughlin 2.6.29-21 +- Fix guest->remote network stall with virtio/GSO (#490266) + +* Mon Mar 30 2009 Ben Skeggs +- 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 +- more v4l/dvb updates: v4l subdev conversion and some driver improvements + +* Sun Mar 29 2009 Chuck Ebbert +- More fixes for ALSA hardware pointer updating. + +* Sat Mar 28 2009 Mauro Carvalho Chehab +- 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 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 +- 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 +- 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 +- linux-2.6-relatime-by-default.patch: Backport relatime code from 2.6.30 + +* Thu Mar 26 2009 Dave Jones +- Check for modesetting enabled before forcing mode on 915. (#490336) + +* Thu Mar 26 2009 Dave Jones +- Set kernel-PAE as default in grub. (#487578) + +* Thu Mar 26 2009 Dave Jones +- Enable CONFIG_MOUSE_PS2_ELANTECH (#492163) + +* Thu Mar 26 2009 Kyle McMartin +- linux-2.6-v4l-pvrusb2-fixes.patch: fix build for uncle steve. + +* Thu Mar 26 2009 Mauro Carvalho Chehab +- 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 +- remove duplicated Cinergy T2 entry at config-generic + +* Wed Mar 25 2009 Neil Horman +- Add dropmonitor/dropwatch protocol from 2.6.30 + +* Wed Mar 25 2009 Kyle McMartin +- 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 +- drm-nouveau.patch: idle channels better before destroying them + +* Tue Mar 24 2009 Kyle McMartin +- Disable DMAR by default until suspend & resume is fixed. + +* Tue Mar 24 2009 Josef Bacik +- fsync replay fixes for btrfs + +* Mon Mar 23 2009 Dave Jones +- 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/l2tp-fix-oops-in-pppol2tp_xmit.patch b/l2tp-fix-oops-in-pppol2tp_xmit.patch new file mode 100644 index 000000000..bf076487c --- /dev/null +++ b/l2tp-fix-oops-in-pppol2tp_xmit.patch @@ -0,0 +1,78 @@ +From: James Chapman +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: [] 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:[] 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: + [] ? ppp_push+0x459/0x50e [ppp_generic] + [] ? ppp_xmit_process+0x3b6/0x430 [ppp_generic] + [] ? ppp_start_xmit+0x10d/0x120 [ppp_generic] + [] ? dev_hard_start_xmit+0x21f/0x2b2 + [] ? sch_direct_xmit+0x48/0x10e + [] ? dev_queue_xmit+0x263/0x3a6 + [] ? ip_finish_output+0x1f7/0x221 + [] ? ip_forward_finish+0x2e/0x30 + [] ? ip_rcv_finish+0x295/0x2a9 + [] ? netif_receive_skb+0x3e9/0x404 + [] ? e1000_clean_rx_irq+0x253/0x2fc [e1000e] + [] ? e1000_clean+0x63/0x1fc [e1000e] + [] ? sched_clock_local+0x15/0x11b + [] ? net_rx_action+0x96/0x195 + [] ? __do_softirq+0xaa/0x151 + [] ? do_softirq+0x31/0x3c + [] ? irq_exit+0x26/0x58 + [] ? do_IRQ+0x78/0x89 + [] ? common_interrupt+0x29/0x30 + [] ? native_safe_halt+0x2/0x3 + [] ? default_idle+0x55/0x75 + [] ? c1e_idle+0xd2/0xd5 + [] ? 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: [] pppol2tp_xmit+0x341/0x4da [pppol2tp] SS:ESP 0068:f70a9cac +CR2: 000000000000000c + +Signed-off-by: James Chapman +Signed-off-by: David S. Miller +--- + +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 000000000..3e2085193 --- /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 000000000..32096d463 --- /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 ++ ++/* ++ * 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 000000000..f093a4023 --- /dev/null +++ b/linux-2.6-autoload-wmi.patch @@ -0,0 +1,244 @@ +From: Matthew Garrett +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 (). + +Signed-off-by: Matthew Garrett +Tested-by: Carlos Corbacho +Acked-by: Carlos Corbacho +Signed-off-by: Len Brown +--- + +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 + #include + #include ++#include + #include + #include + #include +@@ -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 000000000..441213ca0 --- /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 +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 + Signed-off-by: John W. Linville + +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 ++#include + + #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-build-nonintconfig.patch b/linux-2.6-build-nonintconfig.patch new file mode 100644 index 000000000..e88e0ea1e --- /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 000000000..a18e38ba9 --- /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 000000000..34c08ce47 --- /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 000000000..5669f7aaf --- /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 ++#include ++#include ++ ++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 + EXPORT_SYMBOL(unw_init_running); + ++#include ++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 ++#include ++#include ++ ++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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 000000000..24f665ca6 --- /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 +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 000000000..447419c08 --- /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 +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 000000000..cc7747d1f --- /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 ++#include ++#include ++ + /* 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 000000000..ee367d45a --- /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 +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 000000000..2fcd9b956 --- /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 000000000..af883b0d3 --- /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 000000000..15c1af0c5 --- /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 000000000..49b832d2c --- /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 000000000..fad6a53b2 --- /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 000000000..07f2670c7 --- /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 000000000..336788c97 --- /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 + #include + #include ++#include + #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 000000000..2632af455 --- /dev/null +++ b/linux-2.6-driver-level-usb-autosuspend.diff @@ -0,0 +1,62 @@ +commit 7d0d20a25c6f477fb198b85510c78156d7d7c5af +Author: Matthew Garrett +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 + +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 000000000..5a3391cde --- /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 000000000..7e75341bb --- /dev/null +++ b/linux-2.6-enable-btusb-autosuspend.patch @@ -0,0 +1,18 @@ +commit 8e962bd41a2cbf7f0e55191a757b87f793a725a8 +Author: Matthew Garrett +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 000000000..8f0edb20a --- /dev/null +++ b/linux-2.6-execshield.patch @@ -0,0 +1,988 @@ +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 + #include + #include ++#include + + 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 . 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(¤t->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(¤t->mm->page_table_lock); ++ if (limit >= TASK_SIZE) ++ limit = -1UL; ++ current->mm->context.exec_limit = limit; ++ } ++ set_user_cs(¤t->mm->context.user_cs, limit); ++ ++ desc1 = ¤t->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 + #include + #include + #include +@@ -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 + #include + ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + + #include + #include +@@ -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,9 @@ static inline void + { + struct vm_area_struct *next; + ++ if (vma->vm_flags & VM_EXEC) ++ arch_add_exec_range(mm, vma->vm_end); ++ + vma->vm_prev = prev; + if (prev) { + next = prev->vm_next; +@@ -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 + #include + #include ++#include + #include + #include + ++#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-firewire-git-pending.patch b/linux-2.6-firewire-git-pending.patch new file mode 100644 index 000000000..e05471f1b --- /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 000000000..685808133 --- /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 +-#include +-#include +-#include ++#include ++#include + #include +-#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + #include + #include ++#include + #include +-#include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++ + #include + #include +-#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, ®ion) < 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, ®ion); ++ 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 + #include + #include ++#include + #include + #include ++#include + #include + #include + #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 + #include + #include ++#include + #include + + 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 + * +@@ -18,21 +20,25 @@ + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +-#include +-#include + #include +-#include ++#include ++#include ++#include + #include ++#include ++#include + +-#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 ++#include ++ ++#include ++ + 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(®isters, +- ®isters_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(®isters, ®isters_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 + #include + +-#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 000000000..1471ef1bb --- /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 000000000..257cc5642 --- /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 100644 index 000000000..7fd536026 --- /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 000000000..74558e69f --- /dev/null +++ b/linux-2.6-input-fix-toshiba-hotkeys.patch @@ -0,0 +1,278 @@ +commit 61a2aa30877a6e2be1d3fb3a71385e1f741819d7 +Author: Matthew Garrett +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 + +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 + #include + #include ++#include + + #include + +@@ -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 000000000..db38685f0 --- /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 +To: Dave Airlie +Subject: [PATCH] HID: add multi-input quirk for eGalax Touchcontroller + +Signed-off-by: Peter Hutterer +Tested-by: Alfred Broda +--- + 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 000000000..2e6314694 --- /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 000000000..9fac2a8dc --- /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 +Signed-off-by: Justin M. Forbes +--- +--- 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 000000000..94b71f9b1 --- /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 000000000..8fc236836 --- /dev/null +++ b/linux-2.6-nfs4-callback-hidden.patch @@ -0,0 +1,20 @@ +Author: Steve Dickson +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 + +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 000000000..84d589470 --- /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 000000000..8acaee494 --- /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 + +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 000000000..38ec79724 --- /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 000000000..2c423a90a --- /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 +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 +--- + 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 +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 +--- + 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 + #include + #include ++#include + + #include + +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 "); + 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 000000000..ceb6519ab --- /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 000000000..28d55e087 --- /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 000000000..cd1db3e93 --- /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 000000000..175252537 --- /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 000000000..17d67ef64 --- /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 ) ++ * + * 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 000000000..c5997bb6e --- /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 000000000..45ab73331 --- /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 000000000..119a97769 --- /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 +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 + +--- 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 000000000..30d3689c9 --- /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 000000000..7f6c05ae3 --- /dev/null +++ b/linux-2.6-tracehook.patch @@ -0,0 +1,368 @@ +From: Oleg Nesterov + +[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 +Signed-off-by: Roland McGrath +--- + 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 + #include + #include +-#include ++#include + #include + #include + #include +@@ -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, ¤t->blocked, + info); + diff --git a/linux-2.6-upstream-reverts.patch b/linux-2.6-upstream-reverts.patch new file mode 100644 index 000000000..81f7983a4 --- /dev/null +++ b/linux-2.6-upstream-reverts.patch @@ -0,0 +1,2436 @@ +From 7b9c5abee98c54f85bcc04bd4d7ec8d5094c73f4 Mon Sep 17 00:00:00 2001 +From: Jesse Barnes +Date: Fri, 12 Feb 2010 09:30:00 -0800 +Subject: drm/i915: give up on 8xx lid status + +From: Jesse Barnes + +commit 7b9c5abee98c54f85bcc04bd4d7ec8d5094c73f4 upstream. + +These old machines more often than not lie about their lid state. So +don't use it to detect LVDS presence, but leave the event handler to +deal with lid open/close, when we might need to reset the mode. + +Fixes kernel bug #15248 + +Signed-off-by: Jesse Barnes +Signed-off-by: Eric Anholt +Cc: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/i915/intel_lvds.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/gpu/drm/i915/intel_lvds.c ++++ b/drivers/gpu/drm/i915/intel_lvds.c +@@ -648,8 +648,12 @@ static const struct dmi_system_id bad_li + */ + static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector) + { ++ struct drm_device *dev = connector->dev; + enum drm_connector_status status = connector_status_connected; + ++ if (IS_I8XX(dev)) ++ return connector_status_connected; ++ + if (!acpi_lid_open() && !dmi_check_system(bad_lid_status)) + status = connector_status_disconnected; + +From 6363ee6f496eb7e3b3f78dc105e522c7b496089b Mon Sep 17 00:00:00 2001 +From: Zhao Yakui +Date: Tue, 24 Nov 2009 09:48:44 +0800 +Subject: drm/i915: parse child device from VBT + +From: Zhao Yakui + +commit 6363ee6f496eb7e3b3f78dc105e522c7b496089b upstream. + +On some laptops there is no HDMI/DP. But the xrandr still reports +several disconnected HDMI/display ports. In such case the user will be +confused. + >DVI1 disconnected (normal left inverted right x axis y axis) + >DP1 disconnected (normal left inverted right x axis y axis) + >DVI2 disconnected (normal left inverted right x axis y axis) + >DP2 disconnected (normal left inverted right x axis y axis) + >DP3 disconnected (normal left inverted right x axis y axis) + +This patch set is to use the child device parsed in VBT to decide whether +the HDMI/DP/LVDS/TV should be initialized. + +Parse the child device from VBT. + +The device class type is also added for LFP, TV, HDMI, DP output. + +https://bugs.freedesktop.org/show_bug.cgi?id=22785 + +Signed-off-by: Zhao Yakui +Reviewed-by: Adam Jackson +Signed-off-by: Eric Anholt +Acked-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/i915/i915_dma.c | 9 +++++ + drivers/gpu/drm/i915/i915_drv.h | 2 + + drivers/gpu/drm/i915/intel_bios.c | 65 ++++++++++++++++++++++++++++++++++++++ + drivers/gpu/drm/i915/intel_bios.h | 17 +++++++++ + 4 files changed, 93 insertions(+) + +--- a/drivers/gpu/drm/i915/i915_dma.c ++++ b/drivers/gpu/drm/i915/i915_dma.c +@@ -1526,6 +1526,15 @@ int i915_driver_unload(struct drm_device + } + + 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); + } +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -555,6 +555,8 @@ typedef struct drm_i915_private { + 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; + +--- a/drivers/gpu/drm/i915/intel_bios.c ++++ b/drivers/gpu/drm/i915/intel_bios.c +@@ -362,6 +362,70 @@ parse_driver_features(struct drm_i915_pr + dev_priv->render_reclock_avail = true; + } + ++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 +@@ -413,6 +477,7 @@ 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); + + pci_unmap_rom(pdev, bios); +--- a/drivers/gpu/drm/i915/intel_bios.h ++++ b/drivers/gpu/drm/i915/intel_bios.h +@@ -549,4 +549,21 @@ bool intel_init_bios(struct drm_device * + #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_ */ +From 38b3037ee47fbd65a36bc7c39f60a900fbbe3b8e Mon Sep 17 00:00:00 2001 +From: Adam Jackson +Date: Tue, 24 Nov 2009 10:07:00 -0500 +Subject: drm/i915: Fix LVDS presence check + +Combined patches from 2.6.33 for fixing LVDS detection. +7cf4f69d3f4511f443473954456cb91d5514756d + drm/i915: Don't set up the LVDS if it isn't in the BIOS device table. +38b3037ee47fbd65a36bc7c39f60a900fbbe3b8e + drm/i915: Fix LVDS presence check +6e36595a2131e7ed5ee2674be54b2713ba7f0490 + drm/i915: Declare the new VBT parsing functions as static +11ba159288f1bfc1a475c994e598f5fe423fde9d + drm/i915: Don't check for lid presence when detecting LVDS + +Acked-by: Takashi Iwai +Cc: Matthew Garrett +Cc: Adam Jackson +Cc: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/i915/intel_lvds.c | 90 +++++++++++++------------------------- + 1 file changed, 33 insertions(+), 57 deletions(-) + +--- a/drivers/gpu/drm/i915/intel_lvds.c ++++ b/drivers/gpu/drm/i915/intel_lvds.c +@@ -901,64 +901,45 @@ static const struct dmi_system_id intel_ + { } /* 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) ++ * 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 acpi_status +-check_lid_device(acpi_handle handle, u32 level, void *context, +- void **return_value) ++static int lvds_is_present_in_vbt(struct drm_device *dev) + { +- 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; +- } +- +- if (!strncmp(acpi_device_hid(acpi_dev), "PNP0C0D", 7)) +- *lid_present = 1; ++ struct drm_i915_private *dev_priv = dev->dev_private; ++ struct child_device_config *p_child; ++ int i, ret; + +- return AE_OK; +-} ++ if (!dev_priv->child_dev_num) ++ return 1; + +-/** +- * check whether there exists the ACPI LID device by enumerating the ACPI +- * device tree. +- */ +-static int intel_lid_present(void) +-{ +- int lid_present = 0; ++ 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; + +- if (acpi_disabled) { +- /* If ACPI is disabled, there is no ACPI device tree to +- * check, so assume the LID device would have been present. ++ /* The addin_offset should be checked. Only when it is ++ * non-zero, it is regarded as present. + */ +- return 1; ++ if (p_child->addin_offset) { ++ ret = 1; ++ break; ++ } + } +- +- acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, +- ACPI_UINT32_MAX, +- check_lid_device, &lid_present, NULL); +- +- 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; ++ return ret; + } +-#endif + + /** + * intel_lvds_init - setup LVDS connectors on this device +@@ -983,15 +964,10 @@ void intel_lvds_init(struct drm_device * + 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 ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) +From 944001201ca0196bcdb088129e5866a9f379d08c Mon Sep 17 00:00:00 2001 +From: Dave Airlie +Date: Tue, 20 Jul 2010 13:15:31 +1000 +Subject: drm/i915: enable low power render writes on GEN3 hardware. + +From: Dave Airlie + +commit 944001201ca0196bcdb088129e5866a9f379d08c upstream. + +A lot of 945GMs have had stability issues for a long time, this manifested as X hangs, blitter engine hangs, and lots of crashes. + +one such report is at: +https://bugs.freedesktop.org/show_bug.cgi?id=20560 + +along with numerous distro bugzillas. + +This only took a week of digging and hair ripping to figure out. + +Tracked down and tested on a 945GM Lenovo T60, +previously running +x11perf -copypixwin500 +or +x11perf -copywinpix500 +repeatedly would cause the GPU to wedge within 4 or 5 tries, with random busy bits set. + +After this patch no hangs were observed. + +Signed-off-by: Dave Airlie +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/i915/i915_gem.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/gpu/drm/i915/i915_gem.c ++++ b/drivers/gpu/drm/i915/i915_gem.c +@@ -4697,6 +4697,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_I915G(dev) || IS_I915GM(dev) || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(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; + +From 45503ded966c98e604c9667c0b458d40666b9ef3 Mon Sep 17 00:00:00 2001 +From: Keith Packard +Date: Mon, 19 Jul 2010 21:12:35 -0700 +Subject: drm/i915: Define MI_ARB_STATE bits + +From: Keith Packard + +commit 45503ded966c98e604c9667c0b458d40666b9ef3 upstream. + +The i915 memory arbiter has a register full of configuration +bits which are currently not defined in the driver header file. + +Signed-off-by: Keith Packard +Signed-off-by: Dave Airlie +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/i915/i915_reg.h | 64 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 64 insertions(+) + +--- a/drivers/gpu/drm/i915/i915_reg.h ++++ b/drivers/gpu/drm/i915/i915_reg.h +@@ -307,6 +307,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) +From 0725e95ea56698774e893edb7e7276b1d6890954 Mon Sep 17 00:00:00 2001 +From: Bernhard Rosenkraenzer +Date: Wed, 10 Mar 2010 12:36:43 +0100 +Subject: USB: qcserial: add new device ids + +From: Bernhard Rosenkraenzer + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Thu, 3 Dec 2009 17:44:36 -0500 +Subject: drm/edid: Unify detailed block parsing between base and extension blocks + +From: Adam Jackson + +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 +Signed-off-by: Dave Airlie +Cc: maximilian attems +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Wed, 18 Nov 2009 15:15:02 +0800 +Subject: drm/i915: fix gpio register detection logic for BIOS without VBT + +From: Shaohua Li + +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 +Signed-off-by: Zhao Yakui +Signed-off-by: Eric Anholt +[anholt: clarified the commit message a bit] +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Sat, 20 Feb 2010 03:22:21 +0100 +Subject: drm/ttm: handle OOM in ttm_tt_swapout + +From: Maarten Maathuis + +commit 290e55056ec3d25c72088628245d8cae037b30db upstream. + +- Without this change I get a general protection fault. +- Also use PTR_ERR where applicable. + +Signed-off-by: Maarten Maathuis +Reviewed-by: Dave Airlie +Acked-by: Thomas Hellstrom +Signed-off-by: Dave Airlie +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +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 + +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 +Signed-off-by: Hans de Goede +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Mon, 28 Dec 2009 13:15:20 +0800 +Subject: drm/i915: disable TV hotplug status check + +From: Zhenyu Wang + +commit 8fcc501831aa5b37a4a5a8cd9dc965be3cacc599 upstream. + +As we removed TV hotplug, don't check its status ever. + +Reviewed-by: Adam Jackson +Signed-off-by: Zhenyu Wang +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +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 + +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 +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Sun, 14 Feb 2010 19:33:18 +0000 +Subject: drm/radeon: r6xx/r7xx possible security issue, system ram access + +From: Jerome Glisse + +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 +Signed-off-by: Dave Airlie +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +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 + +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 +Signed-off-by: Dave Airlie +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +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 + +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 +Signed-off-by: Dave Airlie +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Tue, 16 Feb 2010 04:16:55 -0500 +Subject: ACPI, i915: blacklist Clevo M5x0N bad_lid state + +From: Zhang Rui + +commit 1379d2fef0ec07c7027a5e89036025ce761470c8 upstream. + +Wrong Lid state reported. +Need to blacklist this machine for LVDS detection. + +Signed-off-by: Zhang Rui +Signed-off-by: Len Brown +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Sun, 31 Jan 2010 07:07:14 +1000 +Subject: drm/radeon/kms: use udelay for short delays + +From: Dave Airlie + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Wed, 25 Nov 2009 13:09:39 +0800 +Subject: drm/i915: Don't wait interruptible for possible plane buffer flush + +From: Zhenyu Wang + +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 +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Tue, 15 Sep 2009 22:57:32 +0200 +Subject: drm/i915: add i915_lp_ring_sync helper + +From: Daniel Vetter + +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 +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Mon, 28 Dec 2009 13:23:36 +0800 +Subject: drm/i915: remove full registers dump debug + +From: Zhenyu Wang + +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 +Signed-off-by: Zhenyu Wang +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + + +--- + 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 +Date: Sun, 7 Feb 2010 16:20:18 +0100 +Subject: drm/i915: Update write_domains on active list after flush. + +From: Daniel Vetter + +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 , 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 +Signed-off-by: Chris Wilson +Tested-by: Adam Lantos +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Tue, 9 Feb 2010 14:14:36 +0000 +Subject: drm/i915: Increase fb alignment to 64k + +From: Chris Wilson + +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 +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Sat, 6 Feb 2010 10:41:53 -0800 +Subject: drm/i915: handle FBC and self-refresh better + +From: Jesse Barnes + +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 +Signed-off-by: Jesse Barnes +Tested-by: Julien Cristau (fd.o #25371) +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Fri, 11 Dec 2009 09:26:10 +0800 +Subject: drm/i915: Add MALATA PC-81005 to ACPI LID quirk list + +From: Zhao Yakui + +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 +Review-by: Jesse Barnes +Tested-by: Hector +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Thu, 21 Jan 2010 15:20:18 +0800 +Subject: drm/i915: Fix the incorrect DMI string for Samsung SX20S laptop + +From: Zhao Yakui + +commit f034b12dbb5749b11e9390e15e93ffa87ece8038 upstream. + +Signed-off-by: Zhao Yakui +Reported-by: Philipp Kohlbecher +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Wed, 6 Jan 2010 13:30:36 +0800 +Subject: drm/i915: Add HP nx9020/SamsungSX20S to ACPI LID quirk list + +From: Zhao Yakui + +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 +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +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 + +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 +Cc: maximilian attems +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Wed, 27 Jan 2010 15:19:08 +0530 +Subject: drm/i915: Disable SR when more than one pipe is enabled + +From: David John + +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 +Signed-off-by: Jesse Barnes +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Mon, 19 Oct 2009 10:08:17 +0900 +Subject: drm/i915: enable self-refresh on 965 + +From: Jesse Barnes + +commit 1dc7546d1a73664e5d117715b214bea9cae5951c upstream. + +Need to calculate the SR watermark and enable it. + +Signed-off-by: Jesse Barnes +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +Date: Mon, 25 Jan 2010 10:35:16 +0800 +Subject: drm/i915: disable hotplug detect before Ironlake CRT detect + +From: Zhenyu Wang + +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 +Signed-off-by: Eric Anholt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 000000000..b7c7f6e0f --- /dev/null +++ b/linux-2.6-usb-uvc-autosuspend.diff @@ -0,0 +1,19 @@ +commit 9d4c919bcfa794c054cc33155c7e3c53ac2c5684 +Author: Matthew Garrett +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 000000000..1077e5bc9 --- /dev/null +++ b/linux-2.6-usb-wwan-update.patch @@ -0,0 +1,1641 @@ +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 + #include + #include ++#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,223 +740,35 @@ 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; + + /* Don't bind network interfaces on Huawei K3765 & K4505 */ + if (serial->dev->descriptor.idVendor == HUAWEI_VENDOR_ID && + (serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K3765 || + serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4505) && + serial->interface->cur_altsetting->desc.bInterfaceNumber == 1) + 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 + #include + #include ++#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 ++ ++ 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 ++ ++ History: see the git log. ++ ++ Work sponsored by: Sigos GmbH, Germany ++ ++ 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 " ++#define DRIVER_DESC "USB Driver for GSM modems" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 000000000..e95ca6ade --- /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 +Signed-off-by: Oleg Nesterov +--- + 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 /* For unlikely. */ + #include /* 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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(¤t->blocked, signr)) { ++ send_sig_info(signr, info, current); ++ return UTRACE_SIGNAL_IGN; ++ } ++ ++ spin_lock_irq(¤t->sighand->siglock); ++ *return_ka = current->sighand->action[signr - 1]; ++ spin_unlock_irq(¤t->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 ++ ++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 + #include + #include +-#include + #include + #include + #include +@@ -24,7 +23,286 @@ + #include + #include + ++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 ++ ++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 +- + 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 000000000..8d1adf8c2 --- /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 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 +--- + 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 @@ ++ ++ ++ ++ ++ ++ The utrace User Debugging Infrastructure ++ ++ ++ ++ ++ utrace concepts ++ ++ Introduction ++ ++ ++ utrace is infrastructure code for tracing ++ and controlling user threads. This is the foundation for writing ++ tracing engines, which can be loadable kernel modules. ++ ++ ++ ++ The basic actors in utrace are the thread ++ and the tracing engine. A tracing engine is some body of code that ++ calls into the <linux/utrace.h> ++ interfaces, represented by a struct ++ utrace_engine_ops. (Usually it's a kernel module, ++ though the legacy ptrace support is a tracing ++ engine that is not in a kernel module.) The interface operates on ++ individual threads (struct task_struct). ++ If an engine wants to treat several threads as a group, that is up ++ to its higher-level code. ++ ++ ++ ++ Tracing begins by attaching an engine to a thread, using ++ utrace_attach_task or ++ utrace_attach_pid. If successful, it returns a ++ pointer that is the handle used in all other calls. ++ ++ ++ ++ ++ Events and Callbacks ++ ++ ++ An attached engine does nothing by default. An engine makes something ++ happen by requesting callbacks via utrace_set_events ++ and poking the thread with utrace_control. ++ The synchronization issues related to these two calls ++ are discussed further below in . ++ ++ ++ ++ Events are specified using the macro ++ UTRACE_EVENT(type). ++ Each event type is associated with a callback in struct ++ utrace_engine_ops. A tracing engine can leave unused ++ callbacks NULL. The only callbacks required ++ are those used by the event flags it sets. ++ ++ ++ ++ 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. ++ ++ ++ ++ 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 ++ kmalloc and the like, but never wait for i/o or ++ for user mode to do something. If you need the thread to wait, use ++ UTRACE_STOP and return from the callback ++ quickly. When your i/o finishes or whatever, you can use ++ utrace_control to resume the thread. ++ ++ ++ ++ The UTRACE_EVENT(SYSCALL_ENTRY) 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 report_syscall_entry callback is ++ special in two ways. For this event, engines are called in reverse of ++ the normal order (this includes the report_quiesce ++ call that precedes a report_syscall_entry 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 UTRACE_STOP (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. ++ ++ ++ ++ ++ Stopping Safely ++ ++ Writing well-behaved callbacks ++ ++ ++ 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 CLONE 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 CLONE ++ tracing callbacks return. (This allows engines tracing the parent to ++ attach to the child.) If a CLONE event callback ++ blocks the parent thread, it also prevents the child thread from ++ running (even to process a SIGKILL). If what you ++ want is to make both the parent and child block, then use ++ utrace_attach_task on the child and then use ++ UTRACE_STOP on both threads. A more crucial ++ problem with blocking in callbacks is that it can prevent ++ SIGKILL from working. A thread that is blocking ++ due to UTRACE_STOP will still wake up and die ++ immediately when sent a SIGKILL, as all threads ++ should. Relying on the utrace ++ infrastructure rather than on private synchronization calls in event ++ callbacks is an important way to help keep tracing robustly ++ noninvasive. ++ ++ ++ ++ ++ Using <constant>UTRACE_STOP</constant> ++ ++ ++ To control another thread and access its state, it must be stopped ++ with UTRACE_STOP. This means that it is ++ stopped and won't start running again while we access it. When a ++ thread is not already stopped, utrace_control ++ returns -EINPROGRESS 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 ++ UTRACE_EVENT(QUIESCE) event bit. To wake up an ++ interruptible wait, use UTRACE_INTERRUPT. ++ ++ ++ ++ As long as some engine has used UTRACE_STOP and ++ not called utrace_control to resume the thread, ++ then the thread will remain stopped. SIGKILL ++ will wake it up, but it will not run user code. When the stop is ++ cleared with utrace_control or a callback ++ return value, the thread starts running again. ++ (See also .) ++ ++ ++ ++ ++ ++ ++ Tear-down Races ++ ++ Primacy of <constant>SIGKILL</constant> ++ ++ Ordinarily synchronization issues for tracing engines are kept fairly ++ straightforward by using UTRACE_STOP. You ask a ++ thread to stop, and then once it makes the ++ report_quiesce callback it cannot do anything else ++ that would result in another callback, until you let it with a ++ utrace_control 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 ++ SIGKILL from making a thread die and release its ++ resources. To preserve this important property of ++ SIGKILL, it as a special case can break ++ UTRACE_STOP like nothing else normally can. This ++ includes both explicit SIGKILL signals and the ++ implicit SIGKILL sent to each other thread in the ++ same thread group by a thread doing an exec, or processing a fatal ++ signal, or making an exit_group system call. A ++ tracing engine can prevent a thread from beginning the exit or exec or ++ dying by signal (other than SIGKILL) 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. ++ ++ ++ ++ Final callbacks ++ ++ The report_reap 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 ++ report_death 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 report_death callback is the natural ++ place to clean up data structures and detach. To facilitate writing ++ such engines robustly, given the asynchrony of ++ SIGKILL, and without error-prone manual ++ implementation of synchronization schemes, the ++ utrace infrastructure provides some special ++ guarantees about the report_death and ++ report_reap 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. ++ ++ ++ ++ Engine and task pointers ++ ++ The first sort of guarantee concerns the core data structures ++ themselves. struct utrace_engine 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 utrace call. Each call to ++ utrace_attach_task or ++ utrace_attach_pid returns an engine pointer with a ++ reference belonging to the caller. You own that reference until you ++ drop it using utrace_engine_put. There is an ++ implicit reference on the engine while it is attached. So if you drop ++ your only reference, and then use ++ utrace_attach_task without ++ UTRACE_ATTACH_CREATE 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 utrace_engine_get. ++ When an engine has been detached, either explicitly with ++ UTRACE_DETACH or implicitly after ++ report_reap, then any references you hold are all ++ that keep the old engine pointer alive. ++ ++ ++ ++ There is nothing a kernel module can do to keep a struct ++ task_struct alive outside of ++ rcu_read_lock. 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. ++ utrace 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 ++ utrace 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 -ESRCH for a detached engine, which tells ++ you that the task pointer you passed could be invalid now. Since ++ utrace_control and ++ utrace_set_events do not block, you can call those ++ inside a rcu_read_lock section and be sure after ++ they don't return -ESRCH that the task pointer is ++ still valid until rcu_read_unlock. The ++ infrastructure never holds task references of its own. Though neither ++ rcu_read_lock nor any other lock is held while ++ making a callback, it's always guaranteed that the struct ++ task_struct and the struct ++ utrace_engine passed as arguments remain valid ++ until the callback function returns. ++ ++ ++ ++ The common means for safely holding task pointers that is available to ++ kernel modules is to use struct pid, which ++ permits put_pid from kernel modules. When using ++ that, the calls utrace_attach_pid, ++ utrace_control_pid, ++ utrace_set_events_pid, and ++ utrace_barrier_pid are available. ++ ++ ++ ++ ++ ++ Serialization of <constant>DEATH</constant> and <constant>REAP</constant> ++ ++ ++ The second guarantee is the serialization of ++ DEATH and REAP event ++ callbacks for a given thread. The actual reaping by the parent ++ (release_task call) can occur simultaneously ++ while the thread is still doing the final steps of dying, including ++ the report_death callback. If a tracing engine ++ has requested both DEATH and ++ REAP event reports, it's guaranteed that the ++ report_reap callback will not be made until ++ after the report_death callback has returned. ++ If the report_death callback itself detaches ++ from the thread, then the report_reap callback ++ will never be made. Thus it is safe for a ++ report_death callback to clean up data ++ structures and detach. ++ ++ ++ ++ Interlock with final callbacks ++ ++ The final sort of guarantee is that a tracing engine will know for sure ++ whether or not the report_death and/or ++ report_reap callbacks will be made for a certain ++ thread. These tear-down races are disambiguated by the error return ++ values of utrace_set_events and ++ utrace_control. Normally ++ utrace_control called with ++ UTRACE_DETACH returns zero, and this means that no ++ more callbacks will be made. If the thread is in the midst of dying, ++ it returns -EALREADY to indicate that the ++ report_death callback may already be in progress; ++ when you get this error, you know that any cleanup your ++ report_death callback does is about to happen or ++ has just happened--note that if the report_death ++ callback does not detach, the engine remains attached until the thread ++ gets reaped. If the thread is in the midst of being reaped, ++ utrace_control returns -ESRCH ++ to indicate that the report_reap 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 ++ report_death or report_reap ++ callback will try to do the same. utrace_detach ++ returns -ESRCH when the struct ++ utrace_engine 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. ++ ++ ++ ++ In the same vein, utrace_set_events 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 -EALREADY 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 the target is already ++ dead or dying. Like utrace_control, it returns ++ -ESRCH 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 ++ utrace_set_events 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. ++ ++ ++ ++ Using <function>utrace_barrier</function> ++ ++ When a thread is safely stopped, calling ++ utrace_control with UTRACE_DETACH ++ or calling utrace_set_events 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 ++ -EINPROGRESS 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 utrace_barrier. This is necessary if the ++ kernel module containing your callback code is going to be unloaded. ++ ++ ++ After using UTRACE_DETACH once, further calls to ++ utrace_control with the same engine pointer will ++ return -ESRCH. In contrast, after getting ++ -EINPROGRESS from ++ utrace_set_events, you can call ++ utrace_set_events again later and if it returns zero ++ then know the old callbacks have finished. ++ ++ ++ Unlike all other calls, utrace_barrier (and ++ utrace_barrier_pid) will accept any engine pointer you ++ hold a reference on, even if UTRACE_DETACH has already ++ been used. After any utrace_control or ++ utrace_set_events call (these do not block), you can ++ call utrace_barrier to block until callbacks have ++ finished. This returns -ESRCH 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 ++ -ERESTARTSYS if its wait is interrupted. ++ ++ ++ ++ ++ ++ ++ ++utrace core API ++ ++ ++ The utrace API is declared in <linux/utrace.h>. ++ ++ ++!Iinclude/linux/utrace.h ++!Ekernel/utrace.c ++ ++ ++ ++Machine State ++ ++ ++ The task_current_syscall function can be used on any ++ valid struct task_struct at any time, and does ++ not even require that utrace_attach_task was used at all. ++ ++ ++ ++ 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 utrace_set_events can ++ request callbacks (except for the DEATH and ++ REAP events). So at any event callback, it is safe to ++ examine current. ++ ++ ++ ++ One task can examine another only after a callback in the target task that ++ returns UTRACE_STOP 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 utrace_control, unless the ++ task dies suddenly. To examine safely, one must use a pair of calls to ++ utrace_prepare_examine and ++ utrace_finish_examine surrounding the calls to ++ struct user_regset functions or direct examination ++ of task data structures. utrace_prepare_examine returns ++ an error if the task is not properly stopped, or is dead. After a ++ successful examination, the paired utrace_finish_examine ++ 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. ++ ++ ++<structname>struct user_regset</structname> ++ ++ ++ The struct user_regset API ++ is declared in <linux/regset.h>. ++ ++ ++!Finclude/linux/regset.h ++ ++ ++ ++ ++ <filename>System Call Information</filename> ++ ++ ++ This function is declared in <linux/ptrace.h>. ++ ++ ++!Elib/syscall.c ++ ++ ++ ++<filename>System Call Tracing</filename> ++ ++ ++ The arch API for system call information is declared in ++ <asm/syscall.h>. ++ 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 ++ report_syscall_entry callback, ++ or any time after that callback has returned UTRACE_STOP. ++ ++ ++!Finclude/asm-generic/syscall.h ++ ++ ++ ++ ++ ++Kernel Internals ++ ++ ++ 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. ++ ++ ++Core Calls In ++ ++ ++ These calls are declared in <linux/tracehook.h>. ++ The core kernel calls these functions at various important places. ++ ++ ++!Finclude/linux/tracehook.h ++ ++ ++ ++Architecture Calls Out ++ ++ ++ An arch that has done all these things sets ++ CONFIG_HAVE_ARCH_TRACEHOOK. ++ This is required to enable the utrace code. ++ ++ ++<filename><asm/ptrace.h></filename> ++ ++ ++ An arch defines these in <asm/ptrace.h> ++ if it supports hardware single-step or block-step features. ++ ++ ++!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 ++ ++ ++ ++ ++ <filename><asm/syscall.h></filename> ++ ++ ++ An arch provides <asm/syscall.h> that ++ defines these as inlines, or declares them as exported functions. ++ These interfaces are described in . ++ ++ ++ ++ ++ ++ <filename><linux/tracehook.h></filename> ++ ++ ++ An arch must define TIF_NOTIFY_RESUME ++ and TIF_SYSCALL_TRACE ++ in its <asm/thread_info.h>. ++ The arch code must call the following functions, all declared ++ in <linux/tracehook.h> and ++ described in : ++ ++ ++ ++ tracehook_notify_resume ++ ++ ++ tracehook_report_syscall_entry ++ ++ ++ tracehook_report_syscall_exit ++ ++ ++ tracehook_signal_handler ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +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 + #include + #include ++#include + + #include + #include +@@ -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 + #include + #include ++#include + 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 ++#include ++#include ++#include ++ ++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 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 ++ ++/* ++ * 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 + #include + #include ++#include + #include + #include + #include +@@ -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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * 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 000000000..196e4da1c --- /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 + +--- +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 "); +@@ -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 000000000..30c50434f --- /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 +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 +--- + 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 + * +@@ -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 "); + 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 000000000..1a26e7eb4 --- /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 ++#include ++#include ++#include ++#include ++#include ++ ++#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(¶ms, 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(¶ms, &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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 "); ++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 ++ ++ 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++ 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#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 ++ ++ 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 ++ 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ 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 ++#include ++ ++#include "cx231xx.h" ++#include ++#include ++ ++#include "xc5000.h" ++#include "dvb_dummy_fe.h" ++ ++MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); ++MODULE_AUTHOR("Srinivasa Deevi "); ++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, ¶ms); ++ } ++ ++ } ++ ++ 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 ++ 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++ 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 ++ ++ 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 ++#include ++ ++/*************************************************************************** ++ * 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 ++ ++ 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 ++ 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ 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 ++ 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 " ++#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=, ++ chip=[,min=,max=] ++ dump registers from to [VIDIOC_DBG_G_REGISTER] ++ -r, --set-register=type=, ++ chip=,reg=,val= ++ set the register [VIDIOC_DBG_S_REGISTER] ++ ++ if type == host, then is the hosts chip ID (default 0) ++ if type == i2cdrv (default), then is the I2C driver name or ID ++ if type == i2caddr, then 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(®->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 ++ 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 ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include ++#include ++#include ++#if defined(CONFIG_VIDEO_CX231XX_DVB) || \ ++ defined(CONFIG_VIDEO_CX231XX_DVB_MODULE) ++#include ++#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 - 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 000000000..322148f41 --- /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 < '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 + + --------------------------- + +-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 +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 ++Copyright (c) 2009 Tobias Lorenz + + + 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:///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/.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, ++ * ++ * 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 ++ ++#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 */ ++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 */ + 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 ++ */ ++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 ++ */ ++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 ++#include + + 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 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "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, ®); ++ 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 ++ * ++ * 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 ++ * ++ * 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 + #include +-#include ++#include + #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 ++ * Copyright (C) 2008, 2009 Michael Krufky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -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 "); + 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 + #include +-#include ++#include + #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 + #include +-#include ++#include + #include + #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 + #include +-#include ++#include + #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 + + /* 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 +- * +- * 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 ++ * 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 () 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 () 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 ++ * ++ * 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 "); ++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 ++ * ++ * 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 +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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, ®->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, ®->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 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "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 ++ Copyright (C) 2008 Devin Heitmueller ++ 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 + #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 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include "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 "); ++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 ++ * ++ * 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 ++#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 ++ * ++ * 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 ++ ++#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 + * + * 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 + +-#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 ++ * ++ * 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 ++#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 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "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 ++ * ++ * 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 ++ * ++ * 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 ++ ++#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 ++ * ++ * 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 ++ * ++ * 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 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "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, ®0[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 ++ * ++ * 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 ++#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 ++ * ++ * 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 ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#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 + #include + #include ++#include + + #include "dmxdev.h" + #include "dvbdev.h" + #include "dvb_demux.h" + #include "dvb_frontend.h" + +-#include + + #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 /* Initdata */ + #include /* request_region */ + #include /* udelay */ +-#include /* outb, outb_p */ +-#include /* copy to/from user */ + #include /* kernel radio structs */ +-#include ++#include /* for KERNEL_VERSION MACRO */ ++#include /* outb, outb_p */ ++#include + #include + +-#include /* 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 /* Initdata */ + #include /* request_region */ + #include /* udelay */ +-#include /* outb, outb_p */ +-#include /* copy to/from user */ + #include /* kernel radio structs */ +-#include ++#include /* for KERNEL_VERSION MACRO */ ++#include /* outb, outb_p */ ++#include + #include + +-#include /* 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 /* Initdata */ + #include /* request_region */ + #include /* udelay */ +-#include /* outb, outb_p */ +-#include /* copy to/from user */ + #include /* V4L2 API defs */ +-#include +-#include + #include + #include ++#include /* outb, outb_p */ ++#include ++#include ++ ++MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); ++MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card."); ++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( irdsin != 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 + #include + #include +-#include +-#include + #include +- + #include /* 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 ++#include ++#include + +-#include +-#include ++MODULE_AUTHOR("Vladimir Shebordaev "); ++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 " ); +-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 /* Initdata */ + #include /* request_region */ + #include /* udelay */ +-#include /* outb, outb_p */ +-#include /* copy to/from user */ + #include /* kernel radio structs */ ++#include /* for KERNEL_VERSION MACRO */ ++#include ++#include /* outb, outb_p */ + #include +-#include +-#include ++#include + +-#include /* 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(>->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(>->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(>->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(>->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(>->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(>->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(>->lock); ++ sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1; ++ mutex_unlock(>->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(>->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(>->lock); + return 0; + } + } + outb_p(q >> 5, port); /* Write bus contents back. */ + udelay(SHORT_DELAY); + +- spin_unlock(&lock); +- verified = port; ++ mutex_unlock(>->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 = >->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 = >->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(>->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(>->vdev, gt); ++ ++ if (video_register_device(>->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 = >->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(>->vdev); ++ v4l2_device_unregister(>->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 + #include + #include +-#include +-#include ++#include /* for KERNEL_VERSION MACRO */ + #include + #include +-#include ++#include ++#include + #include + +-#include /* 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 + #include + #include +-#include +-#include + #include +- + #include + #include +-#include ++#include /* for KERNEL_VERSION MACRO */ ++#include ++#include + #include + ++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 /* 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 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 and ++ * David Ellingsworth ++ * 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 " + #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 /* Initdata */ + #include /* request_region */ + #include /* udelay */ +-#include /* outb, outb_p */ +-#include /* copy to/from user */ + #include /* kernel radio structs */ +-#include ++#include ++#include /* for KERNEL_VERSION MACRO */ ++#include /* outb, outb_p */ ++#include + #include +-#include + +-#include /* 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 /* Initdata */ + #include /* request_region */ + #include /* udelay */ +-#include /* kernel radio structs */ +-#include +-#include + #include +-#include /* outb, outb_p */ +-#include /* copy to/from user */ + #include ++#include /* kernel radio structs */ ++#include /* outb, outb_p */ ++#include ++#include + +-#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 /* Initdata */ + #include /* request_region */ + #include /* udelay */ +-#include /* outb, outb_p */ +-#include /* copy to/from user */ + #include /* kernel radio structs */ +-#include +-#include + #include ++#include /* for KERNEL_VERSION MACRO */ ++#include /* outb, outb_p */ ++#include ++#include + +-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 /* 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 ++ * Copyright (c) 2009 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -29,7 +30,7 @@ + * 2008-01-12 Tobias Lorenz + * Version 1.0.0 + * - First working version +- * 2008-01-13 Tobias Lorenz ++ * 2008-01-13 Tobias Lorenz + * 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 + * Tobias Lorenz + * - 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 " + #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 /* Modules */ + #include /* Initdata */ + #include /* request_region */ +-#include /* udelay */ +-#include /* outb, outb_p */ +-#include /* copy to/from user */ + #include /* kernel radio structs */ +-#include ++#include ++#include /* for KERNEL_VERSION MACRO */ ++#include /* outb, outb_p */ ++#include + #include +-#include + +-#include /* 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 + #include + #include +-#include +-#include ++#include /* for KERNEL_VERSION MACRO */ + #include +-#include ++#include ++#include + #include + +-#include /* 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 /* Modules */ + #include /* Initdata */ + #include /* request_region */ +-#include /* radio card status report */ +-#include +-#include /* outb, outb_p */ +-#include /* copy to/from user */ ++#include /* for KERNEL_VERSION MACRO */ + #include /* kernel radio structs */ +-#include ++#include /* outb, outb_p */ ++#include + #include + +-#include /* 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 /* Initdata */ + #include /* request_region */ + #include /* udelay, msleep */ +-#include /* outb, outb_p */ +-#include /* copy to/from user */ + #include /* kernel radio structs */ +-#include ++#include ++#include /* for KERNEL_VERSION MACRO */ ++#include /* outb, outb_p */ ++#include + #include + +-#include /* 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 + #include + #include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include + + 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 + #include + #include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include + + 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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 + #include + ++/* Analog */ ++#include ++#include ++#include ++ + /* 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 +-#include + #include + #include +-#include ++#include + #include + #include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include ++#include + + 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 + #include + #include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include + + 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 + #include + #include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include + + 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" */ + .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 */ + .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 [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 */ + .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 , + Sven Grothklags */ + .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 + 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 */ + .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 */ + .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 */ + .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 +@@ -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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + /* 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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 +@@ -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 +@@ -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 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 ++ * ++ * 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 ++ * ++ * 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 ++*/ ++ ++/* 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 + #include + +-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 ++#include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include +-#include + #include + #include + #include +@@ -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 ++ * ++ * 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 + #include + #include +-#include ++#include + #include + #include + #include +@@ -33,7 +39,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -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(®->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(®->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 + #include + #include ++#include + #include + + #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 + #include + #include +-#include ++#include + + 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 + #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(®->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(®->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 ++ + 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 + +- +-/* 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 + #include + #include ++#include + #include + #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 + + #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(®->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(®->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 + * +@@ -30,7 +30,7 @@ + #include + #include + +-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 ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * 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 = ®, ++ .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 ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * 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 + + #include "cx23885.h" ++#include "cimax2.h" + + MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); + MODULE_AUTHOR("Steven Toth "); +@@ -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 + ++#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 + #include + +-#ifdef CONFIG_VIDEO_V4L1_COMPAT +-/* Include V4L1 specific functions. Should be removed soon */ +-#include +-#endif +- + MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); + MODULE_AUTHOR("Steven Toth "); + MODULE_LICENSE("GPL"); +@@ -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(®->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(®->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 + #include + +-#include ++#include + #include + #include + #include +@@ -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 ++ * ++ * 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 ++ * ++ * 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 ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * 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 ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * 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 + #include + #include +-#include ++#include + #include + + #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 + #include + +-#ifdef CONFIG_VIDEO_V4L1_COMPAT +-/* Include V4L1 specific functions. Should be removed soon */ +-#include +-#endif +- + MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); + MODULE_AUTHOR("Gerd Knorr [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 + #include + +-#include ++#include + #include + #include + #include +@@ -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 " +- " \n"); ++ " \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 " +- " \n"); ++ " \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= insmod option to " + "workaround that.\n"); + em28xx_errdev("Please send an email with this log to:\n"); +- em28xx_errdev("\tV4L Mailing List \n"); ++ em28xx_errdev("\tV4L Mailing List \n"); + em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash); + em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash); + +@@ -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 "); +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 "); + 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 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 ++ * ++ * 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 "); ++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 + * Copyright (C) 2008 Jim Paris ++ * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr + * + * Based on a prototype written by Mark Ferrell + * USB protocol reverse engineered by Jim Paris +@@ -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 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 ++#include "gspca.h" ++ ++MODULE_AUTHOR("Adam Baker , " ++ "Theodore Kilgore "); ++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 ++#include "gspca.h" ++ ++MODULE_AUTHOR("Theodore Kilgore "); ++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 , " + "Serge A. Suchkov "); +@@ -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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#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<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 ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 + #include + +-#include + /* IndyCam decodes stream of photons into digital image representation ;-) */ +-#include ++#include + #include ++#include ++#include ++#include + + #include "indycam.h" + +@@ -33,6 +35,7 @@ MODULE_VERSION(INDYCAM_MODULE_VERSION); + MODULE_AUTHOR("Mikael Nousiainen "); + 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, ®); ++ switch (ctrl->id) { ++ case V4L2_CID_AUTOGAIN: ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); + 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, ®); ++ case V4L2_CID_EXPOSURE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, ®); + 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, ®); ++ case V4L2_CID_GAIN: ++ ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, ®); + if (ret) + return -EIO; + ctrl->value = (s32)reg; + break; +- case INDYCAM_CONTROL_RED_BALANCE: +- ret = indycam_read_reg(client, INDYCAM_REG_RED_BALANCE, ®); ++ case V4L2_CID_RED_BALANCE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, ®); + if (ret) + return -EIO; + ctrl->value = (s32)reg; + break; +- case INDYCAM_CONTROL_BLUE_BALANCE: +- ret = indycam_read_reg(client, INDYCAM_REG_BLUE_BALANCE, ®); ++ case V4L2_CID_BLUE_BALANCE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); ++ switch (ctrl->id) { ++ case V4L2_CID_AUTOGAIN: ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); + 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 + * Mark Schultz + * Brian Rogers ++ * modified for AVerMedia Cardbus by ++ * Oldrich Jedlicka + * + * 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 + #include + #include +-#include +-#include +-#include ++#include ++#include ++#include ++#include + #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), ® }, +- { c->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val } ++ { client->addr, 0, sizeof(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 +- + /* 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 + #include + #include +-#include + + #include + #include +@@ -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 + #include + #include +-#include + + #include + #include +@@ -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, ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 "); ++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 + #include +-#include + #include + #include + + #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 + #include +-#include ++#include + #include +-#include +-#include ++#include ++#include + #include +-#include ++#include + + + MODULE_AUTHOR("Jonathan Corbet "); + 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, ®->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, ®->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 + #include + #include ++#include ++#include ++#include + #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 ++#include + #include + + #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 + #include + +-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 ++ * Copyright (C) 2004 Aurelien Alleaume ++ * ++ * 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 ++#include ++#include ++#include ++ ++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 ++ * Copyright (C) 2004 Aurelien Alleaume ++ * ++ * 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 + #include + +-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 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 + #include "pvrusb2-hdw.h" + #include "pvrusb2-io.h" ++#include + #include + #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 + #include + #include ++#include + #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 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 +- * +- * 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 +-#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 +- * Copyright (C) 2004 Aurelien Alleaume +- * +- * 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 +-#include +- +-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 +- * Copyright (C) 2004 Aurelien Alleaume +- * +- * 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 + #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=]", +- 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 +-#include +- + 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 +- * Copyright (C) 2004 Aurelien Alleaume +- * +- * 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 +-#include +-#include +- +-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 +- * +- * 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 + #include + +-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 + #include + +-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 +@@ -61,6 +62,9 @@ + #include + #include + #include ++#ifdef CONFIG_USB_PWC_INPUT_EVDEV ++#include ++#endif + #include + #include + +@@ -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 + #include + #include ++#ifdef CONFIG_USB_PWC_INPUT_EVDEV ++#include ++#endif + + #include "pwc-uncompress.h" + #include +@@ -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 + #include + #include +-#include +-#include ++#include ++#include ++#include + #include +-#include ++#include + + MODULE_AUTHOR("Michael Geng "); + 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 + #include + #include +-#include +-#include ++#include ++#include ++#include + #include +-#include ++#include + + MODULE_AUTHOR("Michael Geng "); + 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 + #include + #include +-#include ++#include + #include + #include + #include +@@ -32,15 +32,10 @@ + #include + + #include ++#include ++#include ++#include + +-/* 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 + #include + #include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include + + 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 +- * +- * Slight changes for video timing and attachment output by +- * Wolfgang Scherr +- * +- * Changes by Ronald Bultje +- * - moved over to linux>=2.4.x i2c protocol (1/1/2003) +- * +- * Changes by Michael Hunold +- * - 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-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 +- * +- * Based on saa7111 driver by Dave Perks +- * +- * Copyright (C) 1998 Dave Perks +- * +- * Slight changes for video timing and attachment output by +- * Wolfgang Scherr +- * +- * Changes by Ronald Bultje +- * - 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-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 + #include + #include ++#include + #include + #include +-#include ++#include + #include + #include + +@@ -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, ¶ms, 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, ¶ms, 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 + #include + #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 [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 +- +-#ifdef CONFIG_VIDEO_V4L1_COMPAT +-/* Include V4L1 specific functions. Should be removed soon */ +-#include +-#endif ++#include + + /* ------------------------------------------------------------------ */ + +@@ -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 + #include ++#include + #include + #include + #include +@@ -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 + #include + +-#include +- + #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 + #include + #include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include + + 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 + #include + +-#include +-#include ++#include + #include ++#include ++#include ++#include + + #include "saa7191.h" + +@@ -32,6 +34,7 @@ MODULE_VERSION(SAA7191_MODULE_VERSION); + MODULE_AUTHOR("Mikael Nousiainen "); + 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 + #include + ++/* 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 + #include + #include +-#include ++#include + + #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 + #include + #include +-#include +-#include "tda9840.h" ++#include ++#include + + MODULE_AUTHOR("Michael Hunold "); + 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 + #include + #include +-#include ++#include + #include + + 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 + #include + #include +-#include ++#include ++#include + #include "tea6415c.h" + + MODULE_AUTHOR("Michael Hunold "); +@@ -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 + #include + #include +-#include ++#include ++#include + #include "tea6420.h" + + MODULE_AUTHOR("Michael Hunold "); +@@ -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 + #include + #include +-#include ++#include + + 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 + #include + #include +-#include ++#include + #include + #include + #include +@@ -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 + #include + #include +-#include + #include + #include + #include +@@ -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 + #include + #include +-#include + #include + + #include +@@ -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 + #include + #include +-#include + #include + + #include +@@ -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 + * + * +- * 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 + #include + #include +-#include ++#include + #include + #include + +@@ -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 "); + 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 + + #define __OLD_VIDIOC_ /* To allow fixing old calls */ ++#include + #include + + #ifdef CONFIG_VIDEO_V4L1 +@@ -24,7 +25,7 @@ + #endif + #include + #include +-#include ++#include + + #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 ++ * ++ * v4l2_device/v4l2_subdev conversion by: ++ * Copyright (C) 2009 Hans Verkuil ++ * ++ * 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 + + #include +-#include + + #include +-#include ++#include + #include +-#include + #include + + #include +@@ -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 + #include + #include +-#ifdef CONFIG_VIDEO_V4L1_COMPAT +-/* Include V4L1 specific functions. Should be removed soon */ +-#include +-#endif + #include +-#include +-#include +-#include + #include + #include + #include ++#include ++#include ++#include ++#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 + #include + #include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include + + 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 + #include + #include +-#include ++#include + #include + #include + #include +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 + #include + #include ++#include + #include + + #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, ¶m_nv[0], 0444); + module_param_array(packet_size, uint, ¶m_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 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 + #include + ++#include + #include + + #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 + #include + #include +-#include ++#include + + 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 ++#include + +-//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 ++ + 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 + #include + #include +-#include +-#include ++#include + #include + #include + #include +@@ -47,11 +46,10 @@ + + #include + #include +-#include +-#include + #include +- +-#include ++#include ++#include ++#include + + #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 ). +- +- 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 + #include + #include +-#include ++#include ++#include + #include + #include + + #include +-#include +-#include + #include + #include + +@@ -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 + + #include +-#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 + #include +@@ -79,36 +69,17 @@ + #include + #include + +-#include +-#include + #include + #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 + #include + #include +-#include ++#include + #include + #include + #include +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 + #include + +-/* includes for structures and defines regarding video +- #include */ +- + /* I/O commands, error codes */ + #include +-//#include + + /* v4l API */ +-#include + + /* 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 + #include + +-/* includes for structures and defines regarding video +- #include */ +- + /* I/O commands, error codes */ + #include +-//#include + + /* 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 + #include + +-/* includes for structures and defines regarding video +- #include */ +- + /* I/O commands, error codes */ + #include +-//#include + + /* 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 +- +-#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 +- +-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 + #include + ++#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 */ +- __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 ++ ++/* 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 + ++/* 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 + #include + #include ++#include + + #include /* for vmalloc() */ + #include /* 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 ++#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 + #include /* need __user */ + #ifdef CONFIG_VIDEO_V4L1_COMPAT ++#define __MIN_V4L1 + #include + #else + #include +@@ -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 + #ifdef CONFIG_VIDEO_V4L1_COMPAT ++#define __MIN_V4L1 + #include + #endif + #include +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 ++#include + #include ++#include + + 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 + #include + #include ++#include + #include + #include + +@@ -31,6 +32,13 @@ MODULE_AUTHOR("Jaroslav Kysela "); + 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 000000000..6b325b541 --- /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 "); ++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 ++ * ++ * 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 ++ * (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 "); ++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 ++ * 2009/09/24 Olivier Lorin + * GSPCA by Jean-Francois Moine + * Thanks BUGabundo and Malmostoso for your amazing help! + * +@@ -23,8 +21,8 @@ + #include "gspca.h" + #include "gl860.h" + +-MODULE_AUTHOR("Olivier Lorin "); +-MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver"); ++MODULE_AUTHOR("Olivier Lorin "); ++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 + * + * 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 "); + 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 + * + * 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 + * ++ * Support for the control settings for the CIF cameras is ++ * Copyright (C) 2009 Hans de Goede and ++ * Thomas Kaiser ++ * ++ * Support for the control settings for the VGA cameras is + * Copyright (C) 2009 Theodore Kilgore + * +- * Acknowledgements: ++ * Several previously unsupported cameras are owned and have been tested by ++ * Hans de Goede and ++ * Thomas Kaiser and ++ * Theodore Kilgore and ++ * Edmond Rodriguez and ++ * Aurelien Jacobs + * + * The MR97311A support in gspca/mars.c has been helpful in understanding some + * of the registers in these cameras. + * +- * Hans de Goede and +- * Thomas Kaiser +- * 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 ," + "Theodore Kilgore "); + 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 + * + * 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 ++ * Color fixes by by Orion Sky Lawlor (2/26/2000) ++ * OV7620 fixes by Charl P. Botha ++ * Changes by Claudio Matsuoka + * + * 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 + * Copyright (C) 2008 Jim Paris + * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr +@@ -8,6 +9,10 @@ + * USB protocol reverse engineered by Jim Paris + * 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 ++ * + * 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 ++ * Copyright (C) 2008 Jim Paris ++ * ++ * Based on a prototype written by Mark Ferrell ++ * USB protocol reverse engineered by Jim Paris ++ * 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 "); ++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 ++ * ++ * Separated from Pixart PAC7311 library by Mrton Nmeth ++ * ++ * 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 ++#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 ++ * ++ * 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 ++ * ++ * 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 ++ * Copyright (C) 2009 Jean-Francois Moine ++ * 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 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 ++ * ++ * 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 "); ++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 ++ * ++ * This module is adapted from the in kernel v4l1 w9968cf driver: ++ * ++ * Copyright (C) 2002-2004 by Luca Risolia ++ * ++ * 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 ++ * Copyright (C) 2009-2010 Jean-Francois Moine ++ * 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 , " ++MODULE_AUTHOR("Jean-Francois Moine , " + "Serge A. Suchkov "); + 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 000000000..814bff14b --- /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 ++#include ++#include ++#include ++#include ++#include ++ ++#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(¶ms, 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(¶ms, &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 000000000..057eac781 --- /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 000000000..b35096fa1 --- /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 +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 +--- + 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 000000000..d73c30adc --- /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 000000000..a4a0809e5 --- /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 000000000..c703b8844 --- /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 "); + 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 000000000..f64e9d511 --- /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 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "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 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_COMPAT ++#include ++#endif ++ ++#include ++#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 ++ * 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 ++#include ++#include ++#include ++#include ++ ++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 ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ * USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "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 ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ * USA ++ */ ++ ++#include ++#include "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 ++ * modified for PixelView (BT878P+W/FM) by ++ * Michal Kochanowicz ++ * Christoph Bartelmus ++ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by ++ * Ulrich Mueller ++ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by ++ * Stefan Jahn ++ * modified for inclusion into kernel sources by ++ * Jerome Brock ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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<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 ++ * ++ * ++ * This driver was derived from: ++ * Paul Miller ++ * "lirc_atiusb" module ++ * Vladimir Dergachev 's 2002 ++ * "USB ATI Remote support" (input device) ++ * Adrian Dewhurst 's 2002 ++ * "USB StreamZap remote driver" (LIRC) ++ * Artur Lipowski '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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "lirc_dev.h" ++ ++ ++/* module identification */ ++#define DRIVER_VERSION "0.1" ++#define DRIVER_AUTHOR \ ++ "Jan M. Hochstein " ++#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 ++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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "lirc_dev.h" ++ ++ ++#define MOD_AUTHOR "Venky Raju " ++#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 ++ * ++ * 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 : ++ * 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 ++ * 2009/12/24 James Edwards implemeted support ++ * for ITE8704/ITE8718, on my machine, the DSDT reports 8704, but the ++ * chip identifies as 18. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#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 ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ * USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "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 ++ * ++ * Transmitter support and reception code cleanup. ++ * (C) by Daniel Melander ++ * ++ * Original lirc_mceusb driver for 1st-gen device: ++ * Copyright (c) 2003-2004 Dan Conti ++ * ++ * 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 and Patrick Calhoun ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "lirc_dev.h" ++ ++#define DRIVER_VERSION "1.90" ++#define DRIVER_AUTHOR "Daniel Melander , " \ ++ "Martin Blatter , " \ ++ "Dan Conti " ++#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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#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 ++ ++#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< ++ * Tim Davies ++ * ++ * This driver was derived from: ++ * Venky Raju ++ * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD" ++ * Paul Miller 's 2003-2004 ++ * "lirc_atiusb - USB remote support for LIRC" ++ * Culver Consulting Services '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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "lirc_dev.h" ++ ++ ++#define MOD_AUTHOR "Oliver Stabel , " \ ++ "Tim Davies " ++#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 ++ * Copyright (C) 1998 Trent Piepho ++ * Copyright (C) 1998 Ben Pfaff ++ * Copyright (C) 1999 Christoph Bartelmus ++ * Copyright (C) 2007 Andrei Tanas (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 July 2001 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_LIRC_SERIAL_NSLU2 ++#include ++#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 ++#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 ). ++ * 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 ++ * 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 ++ . */ ++ ++ /* ++ * 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 ++ * ++ * 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 : ++ * added timeout and relaxed pulse detection, removed gap bug ++ * ++ * 2000/12/15 Christoph Bartelmus : ++ * 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 : ++ * added support for StrongARM SA1100 embedded microprocessor ++ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef LIRC_ON_SA1100 ++#include ++#ifdef CONFIG_SA1100_COLLIE ++#include ++#include ++#endif ++#endif ++ ++#include ++ ++#include ++#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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#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 ++ * modified for PixelView (BT878P+W/FM) by ++ * Michal Kochanowicz ++ * Christoph Bartelmus ++ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by ++ * Ulrich Mueller ++ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by ++ * Stefan Jahn ++ * modified for inclusion into kernel sources by ++ * Jerome Brock ++ * modified for Leadtek Winfast PVR2000 by ++ * Thomas Reitmayr (treitmayr@yahoo.com) ++ * modified for Hauppauge PVR-150 IR TX device by ++ * Mark Weaver ++ * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150 ++ * Jarod Wilson ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "lirc_dev.h" ++#include ++ ++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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MOD_AUTHOR "Jarod Wilson " ++#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 ++ * Copyright (C) 2005 Bernhard Rosenkraenzer ++ * Copyright (C) 2005 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. ++ */ ++ ++#include ++#include ++ ++MODULE_AUTHOR("Dmitry Torokhov "); ++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 ++#include ++ ++#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-explicitly-disable-enable-QoS.patch b/mac80211-explicitly-disable-enable-QoS.patch new file mode 100644 index 000000000..2d5454033 --- /dev/null +++ b/mac80211-explicitly-disable-enable-QoS.patch @@ -0,0 +1,358 @@ +From: Stanislaw Gruszka +To: kernel@lists.fedoraproject.org, "John W. Linville" +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 +--- + 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/merge.pl b/merge.pl new file mode 100755 index 000000000..8c318156a --- /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 () { + 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 () { + 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/net-do-not-check-capable-if-kernel.patch b/net-do-not-check-capable-if-kernel.patch new file mode 100644 index 000000000..0669a6cdd --- /dev/null +++ b/net-do-not-check-capable-if-kernel.patch @@ -0,0 +1,682 @@ +commit abbee616fa69a781c6e58249318a7ae6796a3394 +Author: Eric Paris +Date: Thu Nov 5 20:45:52 2009 -0800 + + net: check kern before calling security subsystem + + Before calling capable(CAP_NET_RAW) check if this operations is on behalf + of the kernel or on behalf of userspace. Do not do the security check if + it is on behalf of the kernel. + + Signed-off-by: Eric Paris + Acked-by: Arnaldo Carvalho de Melo + Signed-off-by: David S. Miller + +commit 8b7950c0735ab6228f08e7b19fc0f94c09c7f2ba +Author: Eric Paris +Date: Thu Nov 5 22:18:14 2009 -0800 + + net: pass kern to net_proto_family create function + + The generic __sock_create function has a kern argument which allows the + security system to make decisions based on if a socket is being created by + the kernel or by userspace. This patch passes that flag to the + net_proto_family specific create function, so it can do the same thing. + + Signed-off-by: Eric Paris + Acked-by: Arnaldo Carvalho de Melo + Signed-off-by: David S. Miller + +--- + drivers/isdn/mISDN/socket.c | 2 +- + drivers/net/pppox.c | 3 ++- + include/linux/net.h | 3 ++- + net/appletalk/ddp.c | 3 ++- + net/atm/pvc.c | 3 ++- + net/atm/svc.c | 7 ++++--- + net/ax25/af_ax25.c | 3 ++- + net/bluetooth/af_bluetooth.c | 5 +++-- + net/bluetooth/bnep/sock.c | 3 ++- + net/bluetooth/cmtp/sock.c | 3 ++- + net/bluetooth/hci_sock.c | 3 ++- + net/bluetooth/hidp/sock.c | 3 ++- + net/bluetooth/l2cap.c | 5 +++-- + net/bluetooth/rfcomm/sock.c | 3 ++- + net/bluetooth/sco.c | 3 ++- + net/can/af_can.c | 3 ++- + net/decnet/af_decnet.c | 3 ++- + net/econet/af_econet.c | 3 ++- + net/ieee802154/af_ieee802154.c | 2 +- + net/ipv4/af_inet.c | 5 +++-- + net/ipv6/af_inet6.c | 5 +++-- + net/ipx/af_ipx.c | 3 ++- + net/irda/af_irda.c | 7 ++++--- + net/iucv/af_iucv.c | 3 ++- + net/key/af_key.c | 3 ++- + net/llc/af_llc.c | 5 ++++- + net/netlink/af_netlink.c | 3 ++- + net/netrom/af_netrom.c | 3 ++- + net/packet/af_packet.c | 3 ++- + net/phonet/af_phonet.c | 3 ++- + net/rds/af_rds.c | 3 ++- + net/rose/af_rose.c | 3 ++- + net/rxrpc/af_rxrpc.c | 3 ++- + net/socket.c | 2 +- + net/tipc/socket.c | 6 ++++-- + net/unix/af_unix.c | 3 ++- + net/x25/af_x25.c | 3 ++- + 37 files changed, 83 insertions(+), 46 deletions(-) + +diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c +index feb0fa4..8167346 100644 +--- a/drivers/isdn/mISDN/socket.c ++++ b/drivers/isdn/mISDN/socket.c +@@ -779,7 +779,7 @@ base_sock_create(struct net *net, struct socket *sock, int protocol) + } + + static int +-mISDN_sock_create(struct net *net, struct socket *sock, int proto) ++mISDN_sock_create(struct net *net, struct socket *sock, int proto, int kern) + { + int err = -EPROTONOSUPPORT; + +diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c +index 4f6d33f..a155baf 100644 +--- a/drivers/net/pppox.c ++++ b/drivers/net/pppox.c +@@ -104,7 +104,8 @@ int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) + + EXPORT_SYMBOL(pppox_ioctl); + +-static int pppox_create(struct net *net, struct socket *sock, int protocol) ++static int pppox_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + int rc = -EPROTOTYPE; + +diff --git a/include/linux/net.h b/include/linux/net.h +index 529a093..3a63efd 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -200,7 +200,8 @@ struct proto_ops { + + struct net_proto_family { + int family; +- int (*create)(struct net *net, struct socket *sock, int protocol); ++ int (*create)(struct net *net, struct socket *sock, ++ int protocol, int kern); + struct module *owner; + }; + +diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c +index b1a4290..7c22d90 100644 +--- a/net/appletalk/ddp.c ++++ b/net/appletalk/ddp.c +@@ -1021,7 +1021,8 @@ static struct proto ddp_proto = { + * Create a socket. Initialise the socket, blank the addresses + * set the state. + */ +-static int atalk_create(struct net *net, struct socket *sock, int protocol) ++static int atalk_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + int rc = -ESOCKTNOSUPPORT; +diff --git a/net/atm/pvc.c b/net/atm/pvc.c +index d4c0245..e879725 100644 +--- a/net/atm/pvc.c ++++ b/net/atm/pvc.c +@@ -127,7 +127,8 @@ static const struct proto_ops pvc_proto_ops = { + }; + + +-static int pvc_create(struct net *net, struct socket *sock,int protocol) ++static int pvc_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + if (net != &init_net) + return -EAFNOSUPPORT; +diff --git a/net/atm/svc.c b/net/atm/svc.c +index f90d143..ed096a6 100644 +--- a/net/atm/svc.c ++++ b/net/atm/svc.c +@@ -25,7 +25,7 @@ + #include "signaling.h" + #include "addr.h" + +-static int svc_create(struct net *net, struct socket *sock,int protocol); ++static int svc_create(struct net *net, struct socket *sock, int protocol, int kern); + + /* + * Note: since all this is still nicely synchronized with the signaling demon, +@@ -330,7 +330,7 @@ static int svc_accept(struct socket *sock,struct socket *newsock,int flags) + + lock_sock(sk); + +- error = svc_create(sock_net(sk), newsock,0); ++ error = svc_create(sock_net(sk), newsock, 0, 0); + if (error) + goto out; + +@@ -650,7 +650,8 @@ static const struct proto_ops svc_proto_ops = { + }; + + +-static int svc_create(struct net *net, struct socket *sock,int protocol) ++static int svc_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + int error; + +diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c +index f454607..274d5c0 100644 +--- a/net/ax25/af_ax25.c ++++ b/net/ax25/af_ax25.c +@@ -800,7 +800,8 @@ static struct proto ax25_proto = { + .obj_size = sizeof(struct sock), + }; + +-static int ax25_create(struct net *net, struct socket *sock, int protocol) ++static int ax25_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + ax25_cb *ax25; +diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c +index 8cfb5a8..af074ed 100644 +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -126,7 +126,8 @@ int bt_sock_unregister(int proto) + } + EXPORT_SYMBOL(bt_sock_unregister); + +-static int bt_sock_create(struct net *net, struct socket *sock, int proto) ++static int bt_sock_create(struct net *net, struct socket *sock, int proto, ++ int kern) + { + int err; + +@@ -144,7 +145,7 @@ static int bt_sock_create(struct net *net, struct socket *sock, int proto) + read_lock(&bt_proto_lock); + + if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) { +- err = bt_proto[proto]->create(net, sock, proto); ++ err = bt_proto[proto]->create(net, sock, proto, kern); + bt_sock_reclassify_lock(sock, proto); + module_put(bt_proto[proto]->owner); + } +diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c +index e857628..ee86b31 100644 +--- a/net/bluetooth/bnep/sock.c ++++ b/net/bluetooth/bnep/sock.c +@@ -195,7 +195,8 @@ static struct proto bnep_proto = { + .obj_size = sizeof(struct bt_sock) + }; + +-static int bnep_sock_create(struct net *net, struct socket *sock, int protocol) ++static int bnep_sock_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + +diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c +index 16b0fad..536482f 100644 +--- a/net/bluetooth/cmtp/sock.c ++++ b/net/bluetooth/cmtp/sock.c +@@ -190,7 +190,8 @@ static struct proto cmtp_proto = { + .obj_size = sizeof(struct bt_sock) + }; + +-static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol) ++static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + +diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c +index 75302a9..94a138f 100644 +--- a/net/bluetooth/hci_sock.c ++++ b/net/bluetooth/hci_sock.c +@@ -621,7 +621,8 @@ static struct proto hci_sk_proto = { + .obj_size = sizeof(struct hci_pinfo) + }; + +-static int hci_sock_create(struct net *net, struct socket *sock, int protocol) ++static int hci_sock_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + +diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c +index 37c9d7d..40fac2c 100644 +--- a/net/bluetooth/hidp/sock.c ++++ b/net/bluetooth/hidp/sock.c +@@ -241,7 +241,8 @@ static struct proto hidp_proto = { + .obj_size = sizeof(struct bt_sock) + }; + +-static int hidp_sock_create(struct net *net, struct socket *sock, int protocol) ++static int hidp_sock_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + +diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c +index 8d1c4a9..0fdf477 100644 +--- a/net/bluetooth/l2cap.c ++++ b/net/bluetooth/l2cap.c +@@ -819,7 +819,8 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p + return sk; + } + +-static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol) ++static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + +@@ -831,7 +832,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol) + sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + +- if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW)) ++ if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) + return -EPERM; + + sock->ops = &l2cap_sock_ops; +diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c +index 30a3649..f596455 100644 +--- a/net/bluetooth/rfcomm/sock.c ++++ b/net/bluetooth/rfcomm/sock.c +@@ -323,7 +323,8 @@ static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int + return sk; + } + +-static int rfcomm_sock_create(struct net *net, struct socket *sock, int protocol) ++static int rfcomm_sock_create(struct net *net, struct socket *sock, ++ int protocol, int kern) + { + struct sock *sk; + +diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c +index 5c0685e..2c06950 100644 +--- a/net/bluetooth/sco.c ++++ b/net/bluetooth/sco.c +@@ -430,7 +430,8 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int pro + return sk; + } + +-static int sco_sock_create(struct net *net, struct socket *sock, int protocol) ++static int sco_sock_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + +diff --git a/net/can/af_can.c b/net/can/af_can.c +index 6068321..bcd546f 100644 +--- a/net/can/af_can.c ++++ b/net/can/af_can.c +@@ -114,7 +114,8 @@ static void can_sock_destruct(struct sock *sk) + skb_queue_purge(&sk->sk_receive_queue); + } + +-static int can_create(struct net *net, struct socket *sock, int protocol) ++static int can_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + struct can_proto *cp; +diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c +index 7a58c87..5540230 100644 +--- a/net/decnet/af_decnet.c ++++ b/net/decnet/af_decnet.c +@@ -675,7 +675,8 @@ char *dn_addr2asc(__u16 addr, char *buf) + + + +-static int dn_create(struct net *net, struct socket *sock, int protocol) ++static int dn_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + +diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c +index 0e0254f..b9d5f2f 100644 +--- a/net/econet/af_econet.c ++++ b/net/econet/af_econet.c +@@ -605,7 +605,8 @@ static struct proto econet_proto = { + * Create an Econet socket + */ + +-static int econet_create(struct net *net, struct socket *sock, int protocol) ++static int econet_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + struct econet_sock *eo; +diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c +index cd949d5..40dcb54 100644 +--- a/net/ieee802154/af_ieee802154.c ++++ b/net/ieee802154/af_ieee802154.c +@@ -234,7 +234,7 @@ static const struct proto_ops ieee802154_dgram_ops = { + * set the state. + */ + static int ieee802154_create(struct net *net, struct socket *sock, +- int protocol) ++ int protocol, int kern) + { + struct sock *sk; + int rc; +diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c +index 57737b8..e718ab6 100644 +--- a/net/ipv4/af_inet.c ++++ b/net/ipv4/af_inet.c +@@ -262,7 +262,8 @@ static inline int inet_netns_ok(struct net *net, int protocol) + * Create an inet socket. + */ + +-static int inet_create(struct net *net, struct socket *sock, int protocol) ++static int inet_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + struct inet_protosw *answer; +@@ -325,7 +326,7 @@ lookup_protocol: + } + + err = -EPERM; +- if (answer->capability > 0 && !capable(answer->capability)) ++ if (answer->capability > 0 && !kern && !capable(answer->capability)) + goto out_rcu_unlock; + + err = -EAFNOSUPPORT; +diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c +index e127a32..10776f0 100644 +--- a/net/ipv6/af_inet6.c ++++ b/net/ipv6/af_inet6.c +@@ -95,7 +95,8 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) + return (struct ipv6_pinfo *)(((u8 *)sk) + offset); + } + +-static int inet6_create(struct net *net, struct socket *sock, int protocol) ++static int inet6_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct inet_sock *inet; + struct ipv6_pinfo *np; +@@ -158,7 +159,7 @@ lookup_protocol: + } + + err = -EPERM; +- if (answer->capability > 0 && !capable(answer->capability)) ++ if (answer->capability > 0 && !kern && !capable(answer->capability)) + goto out_rcu_unlock; + + sock->ops = answer->ops; +diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c +index 66c7a20..7a7ac38 100644 +--- a/net/ipx/af_ipx.c ++++ b/net/ipx/af_ipx.c +@@ -1352,7 +1352,8 @@ static struct proto ipx_proto = { + .obj_size = sizeof(struct ipx_sock), + }; + +-static int ipx_create(struct net *net, struct socket *sock, int protocol) ++static int ipx_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + int rc = -ESOCKTNOSUPPORT; + struct sock *sk; +diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c +index dd35641..ef1ac44 100644 +--- a/net/irda/af_irda.c ++++ b/net/irda/af_irda.c +@@ -61,7 +61,7 @@ + + #include + +-static int irda_create(struct net *net, struct socket *sock, int protocol); ++static int irda_create(struct net *net, struct socket *sock, int protocol, int kern); + + static const struct proto_ops irda_stream_ops; + static const struct proto_ops irda_seqpacket_ops; +@@ -839,7 +839,7 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags) + + IRDA_DEBUG(2, "%s()\n", __func__); + +- err = irda_create(sock_net(sk), newsock, sk->sk_protocol); ++ err = irda_create(sock_net(sk), newsock, sk->sk_protocol, 0); + if (err) + return err; + +@@ -1062,7 +1062,8 @@ static struct proto irda_proto = { + * Create IrDA socket + * + */ +-static int irda_create(struct net *net, struct socket *sock, int protocol) ++static int irda_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + struct irda_sock *self; +diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c +index bada1b9..efc05e1 100644 +--- a/net/iucv/af_iucv.c ++++ b/net/iucv/af_iucv.c +@@ -482,7 +482,8 @@ static struct sock *iucv_sock_alloc(struct socket *sock, int proto, gfp_t prio) + } + + /* Create an IUCV socket */ +-static int iucv_sock_create(struct net *net, struct socket *sock, int protocol) ++static int iucv_sock_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + +diff --git a/net/key/af_key.c b/net/key/af_key.c +index 4e98193..8c44f69 100644 +--- a/net/key/af_key.c ++++ b/net/key/af_key.c +@@ -177,7 +177,8 @@ static struct proto key_proto = { + .obj_size = sizeof(struct pfkey_sock), + }; + +-static int pfkey_create(struct net *net, struct socket *sock, int protocol) ++static int pfkey_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id); + struct sock *sk; +diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c +index 7aa4fd1..6f38b8a 100644 +--- a/net/llc/af_llc.c ++++ b/net/llc/af_llc.c +@@ -140,14 +140,17 @@ static struct proto llc_proto = { + + /** + * llc_ui_create - alloc and init a new llc_ui socket ++ * @net: network namespace (must be default network) + * @sock: Socket to initialize and attach allocated sk to. + * @protocol: Unused. ++ * @kern: on behalf of kernel or userspace + * + * Allocate and initialize a new llc_ui socket, validate the user wants a + * socket type we have available. + * Returns 0 upon success, negative upon failure. + */ +-static int llc_ui_create(struct net *net, struct socket *sock, int protocol) ++static int llc_ui_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + int rc = -ESOCKTNOSUPPORT; +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 5a7dcdf..eadedb5 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -428,7 +428,8 @@ static int __netlink_create(struct net *net, struct socket *sock, + return 0; + } + +-static int netlink_create(struct net *net, struct socket *sock, int protocol) ++static int netlink_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct module *module = NULL; + struct mutex *cb_mutex; +diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c +index 7a83495..837e10b 100644 +--- a/net/netrom/af_netrom.c ++++ b/net/netrom/af_netrom.c +@@ -425,7 +425,8 @@ static struct proto nr_proto = { + .obj_size = sizeof(struct nr_sock), + }; + +-static int nr_create(struct net *net, struct socket *sock, int protocol) ++static int nr_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + struct nr_sock *nr; +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 41866eb..e0e3f6c 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -1350,7 +1350,8 @@ static struct proto packet_proto = { + * Create a packet of type SOCK_PACKET. + */ + +-static int packet_create(struct net *net, struct socket *sock, int protocol) ++static int packet_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + struct packet_sock *po; +diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c +index f60c0c2..61bcae9 100644 +--- a/net/phonet/af_phonet.c ++++ b/net/phonet/af_phonet.c +@@ -60,7 +60,8 @@ static inline void phonet_proto_put(struct phonet_protocol *pp) + + /* protocol family functions */ + +-static int pn_socket_create(struct net *net, struct socket *sock, int protocol) ++static int pn_socket_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + struct pn_sock *pn; +diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c +index 98e0538..ca35aad 100644 +--- a/net/rds/af_rds.c ++++ b/net/rds/af_rds.c +@@ -407,7 +407,8 @@ static int __rds_create(struct socket *sock, struct sock *sk, int protocol) + return 0; + } + +-static int rds_create(struct net *net, struct socket *sock, int protocol) ++static int rds_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + +diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c +index 502cce7..f167ed0 100644 +--- a/net/rose/af_rose.c ++++ b/net/rose/af_rose.c +@@ -512,7 +512,8 @@ static struct proto rose_proto = { + .obj_size = sizeof(struct rose_sock), + }; + +-static int rose_create(struct net *net, struct socket *sock, int protocol) ++static int rose_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + struct rose_sock *rose; +diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c +index a86afce..b37e304 100644 +--- a/net/rxrpc/af_rxrpc.c ++++ b/net/rxrpc/af_rxrpc.c +@@ -608,7 +608,8 @@ static unsigned int rxrpc_poll(struct file *file, struct socket *sock, + /* + * create an RxRPC socket + */ +-static int rxrpc_create(struct net *net, struct socket *sock, int protocol) ++static int rxrpc_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct rxrpc_sock *rx; + struct sock *sk; +diff --git a/net/socket.c b/net/socket.c +index 7565536..55c6e4f 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -1216,7 +1216,7 @@ static int __sock_create(struct net *net, int family, int type, int protocol, + /* Now protected by module ref count */ + rcu_read_unlock(); + +- err = pf->create(net, sock, protocol); ++ err = pf->create(net, sock, protocol, kern); + if (err < 0) + goto out_module_put; + +diff --git a/net/tipc/socket.c b/net/tipc/socket.c +index e6d9abf..d00c211 100644 +--- a/net/tipc/socket.c ++++ b/net/tipc/socket.c +@@ -177,6 +177,7 @@ static void reject_rx_queue(struct sock *sk) + * @net: network namespace (must be default network) + * @sock: pre-allocated socket structure + * @protocol: protocol indicator (must be 0) ++ * @kern: caused by kernel or by userspace? + * + * This routine creates additional data structures used by the TIPC socket, + * initializes them, and links them together. +@@ -184,7 +185,8 @@ static void reject_rx_queue(struct sock *sk) + * Returns 0 on success, errno otherwise + */ + +-static int tipc_create(struct net *net, struct socket *sock, int protocol) ++static int tipc_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + const struct proto_ops *ops; + socket_state state; +@@ -1528,7 +1530,7 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags) + + buf = skb_peek(&sk->sk_receive_queue); + +- res = tipc_create(sock_net(sock->sk), new_sock, 0); ++ res = tipc_create(sock_net(sock->sk), new_sock, 0, 0); + if (!res) { + struct sock *new_sk = new_sock->sk; + struct tipc_sock *new_tsock = tipc_sk(new_sk); +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index fc820cd..a1e3c85 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -621,7 +621,8 @@ out: + return sk; + } + +-static int unix_create(struct net *net, struct socket *sock, int protocol) ++static int unix_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + if (protocol && protocol != PF_UNIX) + return -EPROTONOSUPPORT; +diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c +index 7fa9c7a..62c47a4 100644 +--- a/net/x25/af_x25.c ++++ b/net/x25/af_x25.c +@@ -501,7 +501,8 @@ out: + return sk; + } + +-static int x25_create(struct net *net, struct socket *sock, int protocol) ++static int x25_create(struct net *net, struct socket *sock, int protocol, ++ int kern) + { + struct sock *sk; + struct x25_sock *x25; diff --git a/netlink-make-nlmsg_find_attr-take-a-const-ptr.patch b/netlink-make-nlmsg_find_attr-take-a-const-ptr.patch new file mode 100644 index 000000000..5b75ca400 --- /dev/null +++ b/netlink-make-nlmsg_find_attr-take-a-const-ptr.patch @@ -0,0 +1,29 @@ +From 38f1f0db010ac5b981ae06f1fe2fd64095ebb171 Mon Sep 17 00:00:00 2001 +From: Nelson Elhage +Date: Wed, 3 Nov 2010 16:35:40 +0000 +Subject: [PATCH] netlink: Make nlmsg_find_attr take a const nlmsghdr*. + +This will let us use it on a nlmsghdr stored inside a netlink_callback. + +Signed-off-by: Nelson Elhage +Signed-off-by: David S. Miller +--- + include/net/netlink.h | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/include/net/netlink.h b/include/net/netlink.h +index a63b219..c344646 100644 +--- a/include/net/netlink.h ++++ b/include/net/netlink.h +@@ -384,7 +384,7 @@ static inline int nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen, + * + * Returns the first attribute which matches the specified type. + */ +-static inline struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, ++static inline struct nlattr *nlmsg_find_attr(const struct nlmsghdr *nlh, + int hdrlen, int attrtype) + { + return nla_find(nlmsg_attrdata(nlh, hdrlen), +-- +1.7.3.2 + 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 000000000..82f6a9c85 --- /dev/null +++ b/pci-acpi-disable-aspm-if-no-osc.patch @@ -0,0 +1,55 @@ +From: Matthew Garrett +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 +--- + +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 + #include + #include ++#include + #include + #include + #include +@@ -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 000000000..ea91a2554 --- /dev/null +++ b/pci-aspm-dont-enable-too-early.patch @@ -0,0 +1,50 @@ +From: Matthew Garrett +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 +Signed-off-by: Matthew Garrett +Signed-off-by: Jesse Barnes +--- + +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 000000000..ea8980694 --- /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/posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch b/posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch new file mode 100644 index 000000000..92c284909 --- /dev/null +++ b/posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch @@ -0,0 +1,60 @@ +From 9bdade1bc13e547130d2629291758a579722e5d1 Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov +Date: Fri, 5 Nov 2010 16:53:42 +0100 +Subject: posix-cpu-timers: workaround to suppress the problems with mt exec + +posix-cpu-timers.c correctly assumes that the dying process does +posix_cpu_timers_exit_group() and removes all !CPUCLOCK_PERTHREAD +timers from signal->cpu_timers list. + +But, it also assumes that timer->it.cpu.task is always the group +leader, and thus the dead ->task means the dead thread group. + +This is obviously not true after de_thread() changes the leader. +After that almost every posix_cpu_timer_ method has problems. + +It is not simple to fix this bug correctly. First of all, I think +that timer->it.cpu should use struct pid instead of task_struct. +Also, the locking should be reworked completely. In particular, +tasklist_lock should not be used at all. This all needs a lot of +nontrivial and hard-to-test changes. + +Change __exit_signal() to do posix_cpu_timers_exit_group() when +the old leader dies during exec. This is not the fix, just the +temporary hack to hide the problem for 2.6.37 and stable. IOW, +this is obviously wrong but this is what we currently have anyway: +cpu timers do not work after mt exec. + +In theory this change adds another race. The exiting leader can +detach the timers which were attached to the new leader. However, +the window between de_thread() and release_task() is small, we +can pretend that sys_timer_create() was called before de_thread(). + +Signed-off-by: Oleg Nesterov +Signed-off-by: Linus Torvalds +--- + kernel/exit.c | 8 ++++++++ + 1 files changed, 8 insertions(+), 0 deletions(-) + +diff --git a/kernel/exit.c b/kernel/exit.c +index 45102e9..02b7104 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -92,6 +92,14 @@ static void __exit_signal(struct task_struct *tsk) + posix_cpu_timers_exit_group(tsk); + else { + /* ++ * This can only happen if the caller is de_thread(). ++ * FIXME: this is the temporary hack, we should teach ++ * posix-cpu-timers to handle this case correctly. ++ */ ++ if (unlikely(has_group_leader_pid(tsk))) ++ posix_cpu_timers_exit_group(tsk); ++ ++ /* + * If there is any task waiting for the group exit + * then notify it: + */ +-- +1.7.3.2 + diff --git a/scripts/bumpspecfile.py b/scripts/bumpspecfile.py new file mode 100755 index 000000000..478e82871 --- /dev/null +++ b/scripts/bumpspecfile.py @@ -0,0 +1,71 @@ +#!/usr/bin/python +# +# Needs $GIT_COMMITTER_NAME and $GIT_COMMITTER_EMAIL set. +# +import re +import sys +import time +import os +import string + +class Specfile: + def __init__(self,filename): + file=open(filename,"r") + self.lines=file.readlines() + self.vr="" + + def getNextVR(self,aspec): + # Get VR for changelog entry. + (ver,rel) = os.popen("LC_ALL=C rpm --specfile -q --qf '%%{version} %%{release}\n' --define 'dist %%{nil}' %s | head -1" % aspec).read().strip().split(' ') + pos = 0 + # general released kernel case, bump 1st field + fedora_build = rel.split('.')[pos] + if fedora_build == "0": + # this is a devel kernel, bump 2nd field + pos = 1 + elif rel.split('.')[-1] != fedora_build: + # this is a branch, must bump 3rd field + pos = 2 + fedora_build = rel.split('.')[pos] + if pos == 1 and len(rel.split('.')) > 4: + # uh... what? devel kernel in a branch? private build? just do no VR in clog... + print "Warning: not adding any VR to changelog, couldn't tell for sure which field to bump" + pos = -1 + next_fedora_build = int(fedora_build) + 1 + if pos == 0: + nextrel = str(next_fedora_build) + elif pos == 1: + nextrel = "0." + str(next_fedora_build) + elif pos == 2: + nextrel = rel.split('.')[0] + "." + rel.split('.')[1] + "." + str(next_fedora_build) + if pos >= 0: + for s in rel.split('.')[pos + 1:]: + nextrel = nextrel + "." + s + self.vr = " "+ver+'-'+nextrel + + def addChangelogEntry(self,entry): + user = os.environ.get("GIT_COMMITTER_NAME","unknown") + email = os.environ.get("GIT_COMMITTER_EMAIL","unknown") + if (email == "unknown"): + email = os.environ.get("USER","unknown")+"@fedoraproject.org" + changematch=re.compile(r"^%changelog") + date=time.strftime("%a %b %d %Y", time.localtime(time.time())) + newchangelogentry="%changelog\n* "+date+" "+user+" <"+email+">"+self.vr+"\n"+entry+"\n\n" + for i in range(len(self.lines)): + if(changematch.match(self.lines[i])): + self.lines[i]=newchangelogentry + break + + def writeFile(self,filename): + file=open(filename,"w") + file.writelines(self.lines) + file.close() + +if __name__=="__main__": + aspec=(sys.argv[1]) + s=Specfile(aspec) + entry=(sys.argv[2]) + s.getNextVR(aspec) + s.addChangelogEntry(entry) + s.writeFile(aspec) + diff --git a/scripts/check-TODO.sh b/scripts/check-TODO.sh new file mode 100755 index 000000000..7067f0b44 --- /dev/null +++ b/scripts/check-TODO.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +for i in `grep ^* TODO | awk '{ print $2 }'` +do + if [ ! -f $i ]; then + echo "$i referenced in the TODO, but isn't in CVS!" + fi; +done + +# sometimes dead stuff lingers in cvs, even though it's not in the specfile. +for i in *.patch +do + for j in $(grep $i kernel.spec | grep Apply.*Patch | awk '{ print $2 }' | wc -l) + do + if [ "$j" = "0" ]; then + echo $i is in CVS, but not applied in spec file. + grep $i TODO | awk '{ print $2 " is also still in the TODO" }' + fi + done +done + +#for i in `grep ApplyPatch kernel.spec | awk '{ print $2 }'` +#do +# R=$(grep $i TODO) +# echo "$i is in CVS, but not mentioned in the TODO!" +#done + diff --git a/scripts/combine.sh b/scripts/combine.sh new file mode 100755 index 000000000..86a68d302 --- /dev/null +++ b/scripts/combine.sh @@ -0,0 +1,34 @@ +#! /bin/sh + +# combine a set of quilt patches + +# $1 : base dir (source tree) +# $2 : quilt dir (patches to apply) +# $3 : pre-patch to apply first (optional) + +# e.g.: +# combine.sh /home/user/fedora/trunk/kernel/F-11/kernel-2.6.30/vanilla-2.6.30 \ +# /home/user/git/stable-queue/queue-2.6.30 \ +# /home/user/fedora/trunk/kernel/F-11/patch-2.6.30.5.bz2 + +if [ $# -lt 2 ] ; then + exit 1 +fi + +TD="combine_temp.d" + +cd $1 || exit 1 +cd .. +[ -d $TD ] && rm -Rf $TD +mkdir $TD || exit 1 +cd $TD + +cp -al ../$(basename $1) work.d +cd work.d +[ "$3" ] && bzcat $3 | patch -p1 -s +ln -s $2 patches +[ -h patches ] || exit 1 +quilt snapshot +quilt upgrade +quilt push -a -q +quilt diff --snapshot >../combined.patch diff --git a/scripts/configcommon.pl b/scripts/configcommon.pl new file mode 100644 index 000000000..38bbe80dc --- /dev/null +++ b/scripts/configcommon.pl @@ -0,0 +1,82 @@ +#! /usr/bin/perl + +my @args=@ARGV; +my @configoptions; +my @configvalues; +my @common; +my $configcounter = 0; + +# first, read the 1st file + +open (FILE,"$args[0]") || die "Could not open $args[0]"; +while () { + my $str = $_; + if (/\# ([\w]+) is not set/) { + $configoptions[$configcounter] = $1; + $configvalues[$configcounter] = $str; + $common[$configcounter] = 1; + $configcounter ++; + } else { + if (/([\w]+)=/) { + $configoptions[$configcounter] = $1; + $configvalues[$configcounter] = $str; + $common[$configcounter] = 1; + $configcounter ++; + } else { + $configoptions[$configcounter] = "foobarbar"; + $configvalues[$configcounter] = $str; + $common[$configcounter] = 1; + $configcounter ++; + } + } +}; + +# now, read all configfiles and see of the options match the initial one. +# if not, mark it not common +my $cntr=1; + + +while ($cntr < @ARGV) { + open (FILE,$args[$cntr]) || die "Could not open $args[$cntr]"; + while () { + my $nooutput; + my $counter; + my $configname; + + if (/\# ([\w]+) is not set/) { + $configname = $1; + } else { + if (/([\w]+)=/) { + $configname = $1; + } + } + + $counter = 0; + $nooutput = 0; + while ($counter < $configcounter) { + if ("$configname" eq "$configoptions[$counter]") { + if ("$_" eq "$configvalues[$counter]") { + 1; + } else { + $common[$counter] = 0; + } + } + $counter++; + } + } + + $cntr++; +} + +# now print the common values +my $counter = 0; + +while ($counter < $configcounter) { + if ($common[$counter]!=0) { + print "$configvalues[$counter]"; + } + $counter++; +} + +1; + diff --git a/scripts/configdiff.pl b/scripts/configdiff.pl new file mode 100644 index 000000000..848d8df0f --- /dev/null +++ b/scripts/configdiff.pl @@ -0,0 +1,76 @@ +#! /usr/bin/perl + +my @args=@ARGV; +my @configoptions; +my @configvalues; +my @alreadyprinted; +my $configcounter = 0; + +# first, read the override file + +open (FILE,"$args[0]") || die "Could not open $args[0]"; +while () { + my $str = $_; + if (/\# ([\w]+) is not set/) { + $configoptions[$configcounter] = $1; + $configvalues[$configcounter] = $str; + $alreadprinted[$configcounter] = 0; + $configcounter ++; + } else { + if (/([\w]+)=/) { + $configoptions[$configcounter] = $1; + $configvalues[$configcounter] = $str; + $alreadprinted[$configcounter] = 0; + $configcounter ++; + } else { + $configoptions[$configcounter] = "$_"; + $configvalues[$configcounter] = $str; + $alreadprinted[$configcounter] = 0; + $configcounter ++; + } + } +}; + +# now, read and output the entire configfile, except for the overridden +# parts... for those the new value is printed. +# O(N^2) algorithm so if this is slow I need to look at it later + +open (FILE2,"$args[1]") || die "Could not open $args[1]"; +while () { + my $nooutput; + my $counter; + my $configname="$_"; + my $match; + + if (/\# ([\w]+) is not set/) { + $configname = $1; + } else { + if (/([\w]+)=/) { + $configname = $1; + } + } + + $counter = 0; + $nooutput = 0; + $match = 0; +# print "C : $configname"; + while ($counter < $configcounter) { + if ("$configname" eq "$configoptions[$counter]") { + if ( ("$_" eq "$configvalues[$counter]") || ("$configname" eq "") ) { + $match = 1; + } else { + $alreadyprinted[$configcounter] = 1; + print "$_"; + $match = 1; + } + } + $counter++; + } + if ($match == 0) { + print "$_"; + } + +} + + +1; diff --git a/scripts/cross-amd64.sh b/scripts/cross-amd64.sh new file mode 100644 index 000000000..b3119d8fc --- /dev/null +++ b/scripts/cross-amd64.sh @@ -0,0 +1,3 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=x86_64-linux- ARCH=x86_64 hammer + diff --git a/scripts/cross-i586.sh b/scripts/cross-i586.sh new file mode 100644 index 000000000..000f4ae7e --- /dev/null +++ b/scripts/cross-i586.sh @@ -0,0 +1,3 @@ +export PATH=$PATH:/opt/cross/bin +make ARCH=i386 i586 + diff --git a/scripts/cross-i686.sh b/scripts/cross-i686.sh new file mode 100644 index 000000000..5c0cfe137 --- /dev/null +++ b/scripts/cross-i686.sh @@ -0,0 +1,3 @@ +export PATH=$PATH:/opt/cross/bin +make ARCH=i386 i686 + diff --git a/scripts/cross-ia64.sh b/scripts/cross-ia64.sh new file mode 100644 index 000000000..5699a57c1 --- /dev/null +++ b/scripts/cross-ia64.sh @@ -0,0 +1,2 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=ia64-linux- ARCH=ia64 ia64 diff --git a/scripts/cross-iseries.sh b/scripts/cross-iseries.sh new file mode 100644 index 000000000..71bfd9156 --- /dev/null +++ b/scripts/cross-iseries.sh @@ -0,0 +1,3 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=ppc64-linux- ARCH=ppc64 ppc64iseries + diff --git a/scripts/cross-ppc.sh b/scripts/cross-ppc.sh new file mode 100644 index 000000000..c49b740f3 --- /dev/null +++ b/scripts/cross-ppc.sh @@ -0,0 +1,3 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=ppc-linux- ARCH=ppc ppc + diff --git a/scripts/cross-ppc64.sh b/scripts/cross-ppc64.sh new file mode 100644 index 000000000..fb29d4481 --- /dev/null +++ b/scripts/cross-ppc64.sh @@ -0,0 +1,3 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=ppc64-linux- ARCH=ppc64 ppc64 + diff --git a/scripts/cross-ppc8260.sh b/scripts/cross-ppc8260.sh new file mode 100644 index 000000000..10fbc3292 --- /dev/null +++ b/scripts/cross-ppc8260.sh @@ -0,0 +1,3 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=ppc-linux- ARCH=ppc ppc8260 + diff --git a/scripts/cross-ppc8560.sh b/scripts/cross-ppc8560.sh new file mode 100644 index 000000000..405f98a9c --- /dev/null +++ b/scripts/cross-ppc8560.sh @@ -0,0 +1,3 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=ppc-linux- ARCH=ppc ppc8560 + diff --git a/scripts/cross-pseries.sh b/scripts/cross-pseries.sh new file mode 100644 index 000000000..724a8e582 --- /dev/null +++ b/scripts/cross-pseries.sh @@ -0,0 +1,3 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=ppc64-linux- ARCH=ppc64 pseries64 + diff --git a/scripts/cross-s390.sh b/scripts/cross-s390.sh new file mode 100644 index 000000000..4a274397c --- /dev/null +++ b/scripts/cross-s390.sh @@ -0,0 +1,2 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=s390-linux- ARCH=s390 s390 diff --git a/scripts/cross-s390x.sh b/scripts/cross-s390x.sh new file mode 100644 index 000000000..1cdc98a73 --- /dev/null +++ b/scripts/cross-s390x.sh @@ -0,0 +1,2 @@ +export PATH=$PATH:/opt/cross/bin +make CROSS_COMPILE=s390x-linux- ARCH=s390 s390x diff --git a/scripts/get-snapshot.sh b/scripts/get-snapshot.sh new file mode 100755 index 000000000..79d2b095e --- /dev/null +++ b/scripts/get-snapshot.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +VER=$(tail -n1 upstream | sed s/bz2/id/) +rm -f $VER +wget -c http://www.kernel.org/pub/linux/kernel/v2.6/snapshots/$VER +SHA1=$(cat $VER) +rm -f patch-2.6.*-git*.id + +cd ~/src/git-trees/kernel/linux-2.6 +git pull + +DIF=$(git diff $SHA1.. | wc -l) +if [ "$DIF" = "0" ]; then + echo Nothing changed. + exit +fi +TOT=$(git log | head -n1) + +git diff $SHA1.. > ~/src/fedora/kernel/devel/git-linus-new.diff +cd ~/src/fedora/kernel/devel/ +DIF=$(cmp git-linus.diff git-linus-new.diff) +if [ "$?" = "0" ]; then + echo Nothing new in git + rm -f git-linus-new.diff + exit +fi +mv git-linus-new.diff git-linus.diff + +perl -p -i -e 's|^#ApplyPatch\ git-linus.diff|ApplyPatch\ git-linus.diff|' kernel.spec + +echo "- Merge Linux-2.6 up to" $TOT > ~/src/fedora/kernel/devel/clog.tmp +cd ~/src/fedora/kernel/devel/ +bumpspecfile.py kernel.spec "$(cat clog.tmp)" +rm -f clog.tmp +make clog diff --git a/scripts/grab-logs.sh b/scripts/grab-logs.sh new file mode 100755 index 000000000..8a445ec99 --- /dev/null +++ b/scripts/grab-logs.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +VER=$(make verrel) +ver=$(echo $VER | sed -e 's/-/ /g' | awk '{print $2}') +rev=$(echo $VER | sed -e 's/-/ /g' | awk '{print $3}') + +if [ -d logs ]; then + DIR=logs/ +else + DIR=./ +fi + +wget -O $DIR/build-$VER-i686.log http://kojipkgs.fedoraproject.org/packages/kernel/$ver/$rev/data/logs/i686/build.log +wget -O $DIR/build-$VER-x86-64.log http://kojipkgs.fedoraproject.org/packages/kernel/$ver/$rev/data/logs/x86_64/build.log +wget -O $DIR/build-$VER-noarch.log http://kojipkgs.fedoraproject.org/packages/kernel/$ver/$rev/data/logs/noarch/build.log + diff --git a/scripts/newpatch.sh b/scripts/newpatch.sh new file mode 100755 index 000000000..0dc2e837c --- /dev/null +++ b/scripts/newpatch.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# Easy application of new patches. +# Always adds to the very end. (Bumps last patch nr by 100) +# Parameters: +# $1 - patch filename +# $2 - description + +OLD=$(grep ^Patch kernel.spec | tail -n1 | awk '{ print $1 }' | sed s/Patch// | sed s/://) +NEW=$(($OLD/100*100+100)) + +sed -i "/^Patch$OLD:\ /a#\ $2\nPatch$NEW:\ $1" kernel.spec + +LAST=$(grep ^ApplyPatch kernel.spec | tail -n1 | awk '{ print $2 }') + +sed -i "/^ApplyPatch $LAST/aApplyPatch $1" kernel.spec + +cvs add $1 + +scripts/bumpspecfile.py kernel.spec "- $2" +make clog + diff --git a/scripts/pull-upstreams.sh b/scripts/pull-upstreams.sh new file mode 100755 index 000000000..e94fcfd48 --- /dev/null +++ b/scripts/pull-upstreams.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +utrace_base=2.6-current +utrace_base=2.6.34 + +url=http://people.redhat.com/roland/utrace/${1:-$utrace_base} + +wget -q -O /dev/stdout $url/series | grep 'patch$' | +while read i +do + rm -f linux-2.6-$i + wget -nv -O linux-2.6-$i $url/$i +done diff --git a/scripts/rebase.sh b/scripts/rebase.sh new file mode 100755 index 000000000..fc3157b24 --- /dev/null +++ b/scripts/rebase.sh @@ -0,0 +1,199 @@ +#!/bin/bash + +if [ ! -f /usr/bin/curl ]; then + echo yum install curl + exit 0 +fi + +# Current kernel bits +if [ `grep -c ^patch upstream` -ge 1 ]; then + export OLD=`grep ^patch upstream | tail -n1 | sed s/patch-// | sed s/\.bz2//` +else + export OLD=`grep linux-2.6 upstream | tail -n1 | sed s/linux-// | sed s/\.tar\.bz2//` +fi +export OLDBASE=`echo $OLD | sed s/-/\ /g | sed s/2\.6\.// | awk '{ print $1 }'` +if [ `echo $OLD | grep -c rc` -ge 1 ]; then + export OLDRC=`echo $OLD | sed s/-/\ /g | sed s/rc// | awk '{ print $2 }'` + if [ `echo $OLD | grep -c git` -ge 1 ]; then + export OLDGIT=`echo $OLD | sed s/-/\ /g | sed s/git// | awk '{ print $3 }'` + else + export OLDGIT=0 + fi +else + export OLDRC=0 + if [ `echo $OLD | grep -c git` -ge 1 ]; then + export OLDGIT=`echo $OLD | sed s/-/\ /g | sed s/git// | awk '{ print $2 }'` + else + export OLDGIT=0 + fi +fi + +# Is there a new snapshot or prepatch ? +NEW=`curl -s http://www.kernel.org/kdist/finger_banner | grep "latest snapshot 2.6 version"` +if [ -z "$NEW" ] ; then + NEW=`curl -s http://www.kernel.org/kdist/finger_banner | grep "latest mainline 2.6 version"` + if [ -z "$NEW" ] ; then + if [ "$OLDRC" -ne 0 ] ; then + NEW=`curl -s http://www.kernel.org/kdist/finger_banner | grep "latest stable 2.6." | head -n1` + else + echo "No new rc or git snapshot of stable branch". + exit 0 + fi + fi +fi +export N=`echo $NEW | awk '{ print $11 }'` +if [ -z "$N" ]; then + # "Stable version" + export NEW=`echo $NEW | awk '{ print $10 }'` +else + export NEW=`echo $NEW | awk '{ print $11 }'` +fi + +export NEWBASE=`echo $NEW | sed s/-/\ /g | sed s/2\.6\.// | awk '{ print $1 }'` +if [ `echo $NEW | grep -c rc` -ge 1 ]; then + export NEWRC=`echo $NEW | sed s/-/\ /g | sed s/rc// | awk '{ print $2 }'` + if [ `echo $NEW | grep -c git` -ge 1 ]; then + export NEWGIT=`echo $NEW | sed s/-/\ /g | sed s/git// | awk '{ print $3 }'` + else + export NEWGIT=0 + fi +else + export NEWRC=0 + if [ `echo $NEW | grep -c git` -ge 1 ]; then + export NEWGIT=`echo $NEW | sed s/-/\ /g | sed s/git// | awk '{ print $2 }'` + else + export NEWGIT=0 + fi +fi + +echo "OLD kernel was $OLD BASE=$OLDBASE RC=$OLDRC GIT=$OLDGIT" +echo "NEW kernel is $NEW BASE=$NEWBASE RC=$NEWRC GIT=$NEWGIT" + +if [ "$OLDRC" -eq 0 -a "$OLDGIT" -eq 0 -a "$OLDGIT" -ne "$NEWGIT" ]; then + echo "Rebasing from a stable release to a new git snapshot" + perl -p -i -e 's/^%define\ released_kernel\ 1/\%define\ released_kernel\ 0/' kernel.spec + perl -p -i -e 's/^%define\ rawhide_skip_docs\ 1/\%define\ rawhide_skip_docs\ 0/' kernel.spec + # force these to zero in this case, they may not have been when we rebased to stable + perl -p -i -e 's/^%define\ rcrev.*/\%define\ rcrev\ 0/' kernel.spec + perl -p -i -e 's/^%define\ gitrev.*/\%define\ gitrev\ 0/' kernel.spec +fi + +# make sure we build docs at least once per -rc kernel, shut it off otherwise +if [ "$OLDRC" -ne 0 -a "$NEWRC" -gt "$OLDRC" ]; then + perl -p -i -e 's/^%define\ rawhide_skip_docs\ 1/\%define\ rawhide_skip_docs\ 0/' kernel.spec +else + if [ "$NEWRC" -eq "$OLDRC" -a "$NEWGIT" -gt "$OLDGIT" ]; then + # common case, same -rc, new -git, make sure docs are off. + perl -p -i -e 's/^%define\ rawhide_skip_docs\ 0/\%define\ rawhide_skip_docs\ 1/' kernel.spec + fi +fi + +if [ "$NEWRC" -eq 0 -a "$NEWGIT" -eq 0 ]; then + echo "Rebasing from -rc to final release." + perl -p -i -e 's/^%define\ released_kernel\ 0/\%define\ released_kernel\ 1/' kernel.spec + perl -p -i -e 's/^%define\ rawhide_skip_docs\ 1/\%define\ rawhide_skip_docs\ 0/' kernel.spec + export OLD_TARBALL_BASE=$(($OLDBASE-1)) + perl -p -i -e 's/^%define\ base_sublevel\ $ENV{OLD_TARBALL_BASE}/%define\ base_sublevel\ $ENV{NEWBASE}/' kernel.spec + perl -p -i -e 's/^%define\ rcrev.*/\%define\ rcrev\ 0/' kernel.spec + perl -p -i -e 's/^%define\ gitrev.*/\%define\ gitrev\ 0/' kernel.spec + + grep -v kernel-2.6.$OLD_TARBALL_BASE .cvsignore >.cvsignore.tmp ; mv .cvsignore.tmp .cvsignore + echo kernel-2.6.$NEWBASE >> .cvsignore + + for i in upstream sources .cvsignore + do + grep -v linux-2.6.$OLD_TARBALL_BASE.tar.bz2 $i > .$i.tmp; mv .$i.tmp $i + grep -v patch-2.6.$OLDBASE-rc$OLDRC.bz2 $i > .$i.tmp; mv .$i.tmp $i + grep -v patch-2.6.$OLDBASE-rc$OLDRC-git$OLDGIT.bz2 $i > .$i.tmp; mv .$i.tmp $i + done + + echo linux-2.6.$NEWBASE.tar.bz2 >> upstream + + rm -f linux-2.6.$OLD_TARBALL_BASE.tar.bz2 + rm -f linux-2.6.$OLD_TARBALL_BASE.tar.bz2.sign + rm -f patch-2.6.$OLDBASE-rc$OLDRC.bz2 + rm -f patch-2.6.$OLDBASE-rc$OLDRC.bz2.sign + rm -f patch-2.6.$OLDBASE-rc$OLDRC-git$OLDGIT.bz2 + rm -f patch-2.6.$OLDBASE-rc$OLDRC-git$OLDGIT.bz2.sign + + cvs remove linux-2.6.$OLD_TARBALL_BASE.tar.bz2.sign + cvs remove patch-2.6.$OLDBASE-rc$OLDRC.bz2.sign + cvs remove patch-2.6.$OLDBASE-rc$OLDRC-git$OLDGIT.bz2.sign + + make download + make upload FILES=linux-$NEW.tar.bz2 + + cvs add linux-$NEW.tar.bz2.sign + + bumpspecfile.py kernel.spec "- $NEW" + make clog + echo FIXME! Fix up fedora_cvs_origin + make verrel + exit 1 +fi + +if [ "$OLDRC" != "$NEWRC" ]; then + echo "Different rc. Rebasing from $OLDRC to $NEWRC" + perl -p -i -e 's/^%define\ rcrev.*/\%define\ rcrev\ $ENV{"NEWRC"}/' kernel.spec + perl -p -i -e 's/$ENV{OLDBASE}-rc$ENV{OLDRC}.bz2/$ENV{NEWBASE}-rc$ENV{NEWRC}.bz2/' .cvsignore + perl -p -i -e 's/$ENV{OLDBASE}-rc$ENV{OLDRC}.bz2/$ENV{NEWBASE}-rc$ENV{NEWRC}.bz2/' upstream + grep -v patch-2.6.$OLDBASE-rc$OLDRC.bz2 sources > .sources.tmp; mv .sources.tmp sources + grep -v patch-2.6.$OLDBASE-rc$OLDRC-git$OLDGIT.bz2 .cvsignore >.cvsignore.tmp ; mv .cvsignore.tmp .cvsignore + if [ `grep -c patch-2.6.$NEWBASE-rc$NEWRC.bz2 upstream` -eq 0 ]; then + echo patch-2.6.$NEWBASE-rc$NEWRC.bz2 >> .cvsignore + echo patch-2.6.$NEWBASE-rc$NEWRC.bz2 >> upstream + fi + rm -f patch-2.6.$OLDBASE-rc$OLDRC.bz2 + rm -f patch-2.6.$OLDBASE-rc$OLDRC.bz2.sign + cvs remove patch-2.6.$OLDBASE-rc$OLDRC.bz2.sign + make download + make upload FILES=patch-2.6.$NEWBASE-rc$NEWRC.bz2 + cvs add patch-2.6.$NEWBASE-rc$NEWRC.bz2.sign + + # Another awkward (albeit unlikely) corner case. + # Moving from say 26-rc3-git1 to 26-rc4-git1 + # The above will grab the new -rc, but the below will + # think that the -git hasn't changed. + # Fudge around this, by pretending the old git was something crazy. + OLDGIT=99 +fi + +if [ "$OLDGIT" != "$NEWGIT" ]; then + if [ "$OLDRC" -eq 0 -a "$OLDGIT" -eq 0 ]; then + echo "Rebasing to pre-rc git$NEWGIT" + else + echo "Different git. Rebasing from git$OLDGIT to git$NEWGIT" + fi + perl -p -i -e 's/^%define\ gitrev.*/\%define\ gitrev\ $ENV{"NEWGIT"}/' kernel.spec + if [ "$OLDGIT" -ne 0 ]; then + if [ "$NEWGIT" -ne 0 ]; then + perl -p -i -e 's/$ENV{OLD}/$ENV{NEW}/' .cvsignore + perl -p -i -e 's/$ENV{OLD}/$ENV{NEW}/' upstream + fi + grep -v patch-$OLD.bz2 sources > .sources.tmp; mv .sources.tmp sources + grep -v patch-$OLD.bz2 upstream > .upstream.tmp; mv .upstream.tmp upstream + else + echo patch-$NEW.bz2 >> .cvsignore + echo patch-$NEW.bz2 >> upstream + fi + + make download + make upload FILES=patch-$NEW.bz2 + + cvs add patch-$NEW.bz2.sign + if [ "$OLDGIT" -ne 0 ]; then + rm -f patch-$OLD.bz2 + rm -f patch-$OLD.bz2.sign + cvs remove patch-$OLD.bz2.sign + fi +fi + +if [ "$OLDRC" != "$NEWRC" -o "$OLDGIT" != "$NEWGIT" ]; then + perl -p -i -e 's|^ApplyPatch\ git-linus.diff|#ApplyPatch\ git-linus.diff|' kernel.spec + > git-linus.diff + bumpspecfile.py kernel.spec "- $NEW" + make clog + exit 1 +else + exit 0 +fi diff --git a/scripts/reconfig.sh b/scripts/reconfig.sh new file mode 100755 index 000000000..d9e8fa7c3 --- /dev/null +++ b/scripts/reconfig.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +base_sublevel=$(grep "^%define base_sublevel" kernel.spec | head -n1 | awk '{ print $3 }') + +#if [ `grep -c "^%define released_kernel 1" kernel.spec` -ge 1 ]; then + V=$base_sublevel +#else +# let V=$base_sublevel+1 +#fi + +cd kernel-2.6.$base_sublevel/linux-2.6.$base_sublevel.noarch/ +rm -f kernel-*.config +cp ../../kernel-2.6.$V-*.config . + +for i in kernel-*.config +do + echo $i + rm -f .config + cp $i .config + Arch=`head -1 .config | cut -b 3-` + make ARCH=$Arch nonint_oldconfig > /dev/null || exit 1 + echo "# $Arch" > configs/$i + cat .config >> configs/$i + echo +done + diff --git a/scripts/rediffall.pl b/scripts/rediffall.pl new file mode 100644 index 000000000..29f12beb9 --- /dev/null +++ b/scripts/rediffall.pl @@ -0,0 +1,64 @@ +#!/usr/bin/perl -w +# +# Script to rediff all patches in the spec +# Usage: perl -w rediffall.pl < kernel-2.4.spec +# +# $workdir is where the new rediff'ed patches are created +# $origdir is where the original patches and tarball are located +# +# Note that both $workdir and $origdir must be absolute path names. +# Suggestion: create a /kernel symbolic link to the top of your CVS tree. + +my $workdir = "/dev/shm/redifftree"; +my $origdir = "/home/davej/devel"; +my $kernver = "linux-2.6.17"; +my $datestrip = "s/^\\(\\(+++\\|---\\) [^[:blank:]]\\+\\)[[:blank:]].*/\\1/"; +my $patchindex = 0; +my @patchlist; + +# phase 1: create a tree +print "Extracting pristine source..\n"; +system("mkdir -p $workdir"); +system("rm -rf $workdir/*"); +chdir("$workdir"); +system("tar -jxvf $origdir/$kernver.tar.bz2 > /dev/null"); +system("cp -al $kernver linux-$patchindex"); + +# phase 2: read the spec from stdin and store all patches +print "Reading specfile..\n"; + +while (<>) { + my $line = $_; + if ($line =~ /^Patch([0-9]+)\: ([a-zA-Z0-9\-\_\.\+]+\.patch)/) { + $patchlist[$1] = $2; + } else { + if ($line =~ /^Patch([0-9]+)\: ([a-zA-Z0-9\-\_\.]+\.bz2)/) { + $patchlist[$1] = $2; + } + } + + if ($line =~ /^%patch([0-9]+) -p1/) { + # copy the tree, apply the patch, diff and remove the old tree + my $oldindex = $patchindex; + $patchindex = $1; + + print "rediffing patch number $patchindex: $patchlist[$patchindex]\n"; + + system("cp -al linux-$oldindex linux-$patchindex"); + chdir("linux-$patchindex"); + if ($patchlist[$patchindex] =~ /bz2/) { + system("bzcat $origdir/$patchlist[$patchindex] | patch -p1 &>/dev/null"); + } else { + system("cat $origdir/$patchlist[$patchindex] | patch -p1 &>/dev/null"); + } + chdir("$workdir"); + system("rm -f `find -name \"*orig\"`"); + if ($patchlist[$patchindex] =~ /bz2/) { + } else { + system("diff -urNp --exclude-from=/home/davej/.exclude linux-$oldindex linux-$patchindex | sed '$datestrip' > $patchlist[$patchindex]"); + } + system("rm -rf linux-$oldindex"); + } +}; + +1; diff --git a/scripts/sort-config b/scripts/sort-config new file mode 100755 index 000000000..bc497ea0b --- /dev/null +++ b/scripts/sort-config @@ -0,0 +1,222 @@ +#!/bin/bash + +SRC=($(ls config-* 2>/dev/null)) +TGT=($(ls kernel-*/linux-*.noarch/configs/kernel-2.6.*-*.config \ + kernel-*/linux-*.noarch/configs/kernel-2.6.*-*-debug.config 2>/dev/null)) +TGT1=(${TGT[*]#kernel-*/linux-*.noarch/configs/kernel-2.6.*-}) + +ALL_OPTS="cdfimn" +if [ $# -lt 2 ] ; then + echo -e "Usage:\n $(basename $0) [-$ALL_OPTS] input target\n" + echo -e " Sort input config file into the same order as the target\n" + echo -e " -c: insert comments about non-matching/impossible items" + echo -e " -d: show raw unsorted output with extra debug text" + echo -e " -f: force output to match what is in the target config," + echo -e " and/or remove impossible config items" + echo -e " -i: find impossible config items" + echo -e " -m: find changed config items" + echo -e " -n: do not sort output\n" + echo -e " input: source config file" ' [' "${SRC[*]#config-}" ']\n' + echo -e " target: output arch name" ' [' "${TGT1[*]%.config}" ']\n' + exit 1 +fi + +while getopts "$ALL_OPTS" OPTION ; do +case $OPTION in +c) + ADDCOMMENT=1 ;; +d) + DEBUG=1 ;; +f) + FORCE=1 ;; +i) + FIND_IMPOSS=1 ;; +m) + FIND_CHANGED=1 ;; +n) + NOSORT=1 ;; +\?) + exit 2 ;; +esac +done + +if [ "$FORCE" -a "$ADDCOMMENT" ] ; then + echo "-f and -c options cannot be used together" + exit 2 +fi + +shift $((OPTIND-1)) + +TEMPFILES="xx00 xx01 xx98 xx99" +TEMPLEFT= +for FILE in $TEMPFILES ; do + [ -f "$FILE" ] && TEMPLEFT="Y" +done +if [ "$TEMPLEFT" ] ; then + echo "WARNING! Output files named xx?? already exist." >&2 + read -p "Press to erase files, or Ctrl-C to exit..." + echo >&2 +fi +rm -f $TEMPFILES + +SRCFILE=config-$1 +[ ! -f $SRCFILE ] && echo "Input file" $SRCFILE "missing" && exit 2 + +TGTFILE=kernel-*/linux-*.noarch/configs/kernel-2.6.*-$2.config +[ ! -f $TGTFILE ] && echo "No target file matching" $TGTFILE "exists" && exit 2 + +[ "$FIND_IMPOSS" ] && \ + find kernel-*/*.noarch -name Kconfig\* -type f \ + | xargs egrep -s -h '^[[:space:]]*(menu)?config[[:space:]]+' \ + | sed -r 's/^[[:space:]]*(menu)?config[[:space:]]+/CONFIG_/' \ + | sort | uniq >xx98 + +extract_optname() { + # extract the option name from $TEXT, setting $OPTNAME + OPTNAME= + if [ "${TEXT:0:7}" = "CONFIG_" ] ; then + OPTNAME=${TEXT%%=*} + elif [ "${TEXT:0:9}" = "# CONFIG_" ] ; then + OPTNAME=${TEXT%" is not set"} + OPTNAME=${OPTNAME#\# } + fi +} + +print_saved_comments() { + if [ $IX -gt 0 ] ; then + [ "$DEBUG" ] && echo " ->" $IX "comments were saved" + (( IX-- )) + for IX in $(seq 0 $IX) ; do + echo "$LINE":"${SAVECOMMENT[$IX]}" + done + unset SAVECOMMENT + IX=0 + fi +} + +assign_line_number() { + # use input line numbers if not sorting + [ "$NOSORT" ] && LINE=$IN + # make sure it has a line number + [ -z "$LINE" ] && LINE=999999 +} + +IX=0 +IN=0 +declare -a SAVECOMMENT + +cat ${SRCFILE} | { +while read TEXT ; do + + LINE= + COMMENT= + + # replace empty lines + [ -z "$TEXT" ] && TEXT='//' + + if [ "${TEXT:0:7}" = "CONFIG_" -o "${TEXT:0:9}" = "# CONFIG_" ] ; then + + LINE=$(grep -n "^$TEXT" $TGTFILE | head -1 | cut -f 1 -d ':') + if [ -z "$LINE" ] ; then + [ "$DEBUG" ] && echo "nofind ->" "$TEXT" + + extract_optname + if [ "$OPTNAME" ] ; then + + if [ "$FIND_CHANGED" ] ; then + for FINDTEXT in "^${OPTNAME}=" "^# ${OPTNAME} is not set" ; do + if [ -z "$LINE" ] ; then + [ "$DEBUG" ] && echo "looking for ->" "$FINDTEXT" + LINE=$(grep -n "$FINDTEXT" $TGTFILE | head -1 | cut -f 1 -d ':') + if [ "$LINE" ] ; then + CHANGED=$(grep "$FINDTEXT" $TGTFILE | head -1) + if [ "$FORCE" ] ; then + TEXT=$CHANGED + [ "$DEBUG" ] && echo 'forced ->' "$TEXT" + else + if [ "$ADDCOMMENT" ] ; then + if [ ${CHANGED:0:1} = '#' ] ; then + NEWOPT="not set" + else + NEWOPT=${CHANGED#$OPTNAME} + fi + COMMENT="# -- Next option changed to \"${NEWOPT}\" at target line $LINE --" + fi + fi + fi + fi + done + fi + + if [ "$FIND_IMPOSS" -a -z "$LINE" -a -z "$COMMENT" ] ; then + POSSIBLE=$(grep -n "^$OPTNAME" xx98) + if [ -z "$POSSIBLE" ] ; then + if [ "$ADDCOMMENT" ] ; then + COMMENT="# -- Next option is impossible --" + elif [ "$FORCE" ] ; then + [ "$DEBUG" ] && echo 'impossible ->' "$TEXT" + TEXT="" + fi + fi + fi + + fi + + fi + + else + # not a config variable + COMMENT="$TEXT" + TEXT= + fi + + [ "$DEBUG" -a "$COMMENT" ] && echo "comment ->" "$LINE" "$COMMENT" + [ "$DEBUG" -a "$TEXT" ] && echo "text ->" "$LINE" "$TEXT" + + if [ "$TEXT" ] ; then + + assign_line_number + + # print the saved comments first + print_saved_comments + # now print the latest comment and text + [ "$COMMENT" ] && echo "$LINE":"$COMMENT" + echo "$LINE":"$TEXT" + + elif [ "$COMMENT" ] ; then + + # no output yet, save the comment + SAVECOMMENT[$IX]="$COMMENT" + let IX++ + [ "$DEBUG" ] && echo 'savecomment (#'${IX}')' + + fi + + let IN++ + +done +# flush the buffers +assign_line_number +print_saved_comments +[ "$DEBUG" ] && echo "$IN lines read from input" +} >xx99 + +if [ "$DEBUG" ] ; then + # just show the raw output with debug info, then exit + cat xx99 +else + + # split output into two files, for matched and unmatched items + cat xx99 | sort -s -t ":" -k 1g | csplit -k -s - /^999999/ 2>/dev/null + + cat xx00 | cut -f 2- -d ':' | sed 's/^\/\/$//' + if [ -s xx01 ] ; then + echo + echo '# ------------ UNMATCHED OPTIONS ------------' + echo + cat xx01 | cut -f 2- -d ':' | sed 's/^\/\/$//' + fi + +fi + +rm -f $TEMPFILES diff --git a/sky2-optima-add-missing-write-bits.patch b/sky2-optima-add-missing-write-bits.patch new file mode 100644 index 000000000..98b924dad --- /dev/null +++ b/sky2-optima-add-missing-write-bits.patch @@ -0,0 +1,46 @@ +From: Takashi Iwai +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 +Signed-off-by: David S. Miller +--- + +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 000000000..78311ffed --- /dev/null +++ b/sky2-optima-add-register-definitions.patch @@ -0,0 +1,281 @@ +From: Stephen Hemminger +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 +Signed-off-by: David S. Miller +--- + +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 000000000..d0f78ed58 --- /dev/null +++ b/sky2-optima-fix-tcp-offload.patch @@ -0,0 +1,33 @@ +From: Takashi Iwai +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 +Signed-off-by: David S. Miller +--- + +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 000000000..0a2e148f8 --- /dev/null +++ b/sky2-optima-print-chip-name.patch @@ -0,0 +1,27 @@ +From: stephen hemminger +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 +Signed-off-by: David S. Miller +--- + +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 000000000..f56f9baeb --- /dev/null +++ b/sky2-optima-support.patch @@ -0,0 +1,157 @@ +From: Stephen Hemminger +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 +Signed-off-by: David S. Miller +--- + +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 e69de29bb..bca6be4cc 100644 --- a/sources +++ b/sources @@ -0,0 +1,2 @@ +260551284ac224c3a43c4adac7df4879 linux-2.6.32.tar.bz2 +98071c23205c84912386e10b86c561c8 patch-2.6.32.26.bz2 diff --git a/thinkpad-acpi-add-x100e.patch b/thinkpad-acpi-add-x100e.patch new file mode 100644 index 000000000..0ccd06223 --- /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 000000000..163902c9d --- /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/tty-icount-changeover-for-other-main-devices.patch b/tty-icount-changeover-for-other-main-devices.patch new file mode 100644 index 000000000..501aa87ce --- /dev/null +++ b/tty-icount-changeover-for-other-main-devices.patch @@ -0,0 +1,982 @@ +From f815dfecf23bfd19d4b5e9f1a660b1f5dbe70472 Mon Sep 17 00:00:00 2001 +From: Alan Cox +Date: Thu, 16 Sep 2010 18:21:52 +0100 +Subject: [PATCH 2/2] tty: icount changeover for other main devices + +Again basically cut and paste + +Convert the main driver set to use the hooks for GICOUNT + +Signed-off-by: Alan Cox +Signed-off-by: Greg Kroah-Hartman +--- + arch/ia64/hp/sim/simserial.c | 12 +------ + drivers/char/amiserial.c | 56 ++++++++++++++++------------- + drivers/char/cyclades.c | 49 +++++++++++++------------ + drivers/char/ip2/ip2main.c | 72 ++++++++++++++++++++++--------------- + drivers/char/mxser.c | 62 ++++++++++++++++++-------------- + drivers/char/nozomi.c | 37 +++++++++---------- + drivers/char/pcmcia/synclink_cs.c | 60 ++++++++++++++----------------- + drivers/char/synclink.c | 73 +++++++++++++++++-------------------- + drivers/char/synclink_gt.c | 56 +++++++++++++++-------------- + drivers/char/synclinkmp.c | 61 ++++++++++++++----------------- + drivers/serial/68360serial.c | 51 +++++++++++++------------- + net/bluetooth/rfcomm/tty.c | 4 -- + 12 files changed, 297 insertions(+), 296 deletions(-) + +diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c +index 2bef526..204f650 100644 +--- a/arch/ia64/hp/sim/simserial.c ++++ b/arch/ia64/hp/sim/simserial.c +@@ -395,7 +395,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, + { + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -433,16 +433,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, + case TIOCMIWAIT: + printk(KERN_INFO "rs_ioctl: TIOCMIWAIT: called\n"); + return 0; +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: +- printk(KERN_INFO "rs_ioctl: TIOCGICOUNT called\n"); +- return 0; +- + case TIOCSERGWILD: + case TIOCSERSWILD: + /* "setserial -W" is called in Debian boot */ +diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c +index 4f8d60c..2918d5d 100644 +--- a/drivers/char/amiserial.c ++++ b/drivers/char/amiserial.c +@@ -1263,6 +1263,36 @@ static int rs_break(struct tty_struct *tty, int break_state) + return 0; + } + ++/* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++static int rs_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ struct async_struct *info = tty->driver_data; ++ struct async_icount cnow; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ cnow = info->state->icount; ++ local_irq_restore(flags); ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ ++ return 0; ++} + + static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +@@ -1332,31 +1362,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, + } + /* NOTREACHED */ + +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: +- local_irq_save(flags); +- cnow = info->state->icount; +- local_irq_restore(flags); +- icount.cts = cnow.cts; +- icount.dsr = cnow.dsr; +- icount.rng = cnow.rng; +- icount.dcd = cnow.dcd; +- icount.rx = cnow.rx; +- icount.tx = cnow.tx; +- icount.frame = cnow.frame; +- icount.overrun = cnow.overrun; +- icount.parity = cnow.parity; +- icount.brk = cnow.brk; +- icount.buf_overrun = cnow.buf_overrun; +- +- if (copy_to_user(argp, &icount, sizeof(icount))) +- return -EFAULT; +- return 0; + case TIOCSERGWILD: + case TIOCSERSWILD: + /* "setserial -W" is called in Debian boot */ +@@ -1949,6 +1954,7 @@ static const struct tty_operations serial_ops = { + .wait_until_sent = rs_wait_until_sent, + .tiocmget = rs_tiocmget, + .tiocmset = rs_tiocmset, ++ .get_icount = rs_get_icount, + .proc_fops = &rs_proc_fops, + }; + +diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c +index 9824b41..2364df8 100644 +--- a/drivers/char/cyclades.c ++++ b/drivers/char/cyclades.c +@@ -2791,29 +2791,6 @@ cy_ioctl(struct tty_struct *tty, struct file *file, + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +- case TIOCGICOUNT: { +- struct serial_icounter_struct sic = { }; +- +- spin_lock_irqsave(&info->card->card_lock, flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->card->card_lock, flags); +- +- sic.cts = cnow.cts; +- sic.dsr = cnow.dsr; +- sic.rng = cnow.rng; +- sic.dcd = cnow.dcd; +- sic.rx = cnow.rx; +- sic.tx = cnow.tx; +- sic.frame = cnow.frame; +- sic.overrun = cnow.overrun; +- sic.parity = cnow.parity; +- sic.brk = cnow.brk; +- sic.buf_overrun = cnow.buf_overrun; +- +- if (copy_to_user(argp, &sic, sizeof(sic))) +- ret_val = -EFAULT; +- break; +- } + default: + ret_val = -ENOIOCTLCMD; + } +@@ -2825,6 +2802,31 @@ cy_ioctl(struct tty_struct *tty, struct file *file, + return ret_val; + } /* cy_ioctl */ + ++static int cy_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *sic) ++{ ++ struct cyclades_port *info = tty->driver_data; ++ struct cyclades_icount cnow; /* Used to snapshot */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->card->card_lock, flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->card->card_lock, flags); ++ ++ sic->cts = cnow.cts; ++ sic->dsr = cnow.dsr; ++ sic->rng = cnow.rng; ++ sic->dcd = cnow.dcd; ++ sic->rx = cnow.rx; ++ sic->tx = cnow.tx; ++ sic->frame = cnow.frame; ++ sic->overrun = cnow.overrun; ++ sic->parity = cnow.parity; ++ sic->brk = cnow.brk; ++ sic->buf_overrun = cnow.buf_overrun; ++ return 0; ++} ++ + /* + * This routine allows the tty driver to be notified when + * device's termios settings have changed. Note that a +@@ -4086,6 +4088,7 @@ static const struct tty_operations cy_ops = { + .wait_until_sent = cy_wait_until_sent, + .tiocmget = cy_tiocmget, + .tiocmset = cy_tiocmset, ++ .get_icount = cy_get_icount, + .proc_fops = &cyclades_proc_fops, + }; + +diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c +index 911e1da..c0f864c 100644 +--- a/drivers/char/ip2/ip2main.c ++++ b/drivers/char/ip2/ip2main.c +@@ -183,6 +183,8 @@ static void ip2_hangup(PTTY); + static int ip2_tiocmget(struct tty_struct *tty, struct file *file); + static int ip2_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); ++static int ip2_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount); + + static void set_irq(int, int); + static void ip2_interrupt_bh(struct work_struct *work); +@@ -454,6 +456,7 @@ static const struct tty_operations ip2_ops = { + .hangup = ip2_hangup, + .tiocmget = ip2_tiocmget, + .tiocmset = ip2_tiocmset, ++ .get_icount = ip2_get_icount, + .proc_fops = &ip2_proc_fops, + }; + +@@ -2124,7 +2127,6 @@ ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) + i2ChanStrPtr pCh = DevTable[tty->index]; + i2eBordStrPtr pB; + struct async_icount cprev, cnow; /* kernel counter temps */ +- struct serial_icounter_struct __user *p_cuser; + int rc = 0; + unsigned long flags; + void __user *argp = (void __user *)arg; +@@ -2293,34 +2295,6 @@ ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) + break; + + /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for RI where +- * only 0->1 is counted. The controller is quite capable of counting +- * both, but this done to preserve compatibility with the standard +- * serial driver. +- */ +- case TIOCGICOUNT: +- ip2trace (CHANN, ITRC_IOCTL, 11, 1, rc ); +- +- write_lock_irqsave(&pB->read_fifo_spinlock, flags); +- cnow = pCh->icount; +- write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); +- p_cuser = argp; +- rc = put_user(cnow.cts, &p_cuser->cts); +- rc = put_user(cnow.dsr, &p_cuser->dsr); +- rc = put_user(cnow.rng, &p_cuser->rng); +- rc = put_user(cnow.dcd, &p_cuser->dcd); +- rc = put_user(cnow.rx, &p_cuser->rx); +- rc = put_user(cnow.tx, &p_cuser->tx); +- rc = put_user(cnow.frame, &p_cuser->frame); +- rc = put_user(cnow.overrun, &p_cuser->overrun); +- rc = put_user(cnow.parity, &p_cuser->parity); +- rc = put_user(cnow.brk, &p_cuser->brk); +- rc = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); +- break; +- +- /* + * The rest are not supported by this driver. By returning -ENOIOCTLCMD they + * will be passed to the line discipline for it to handle. + */ +@@ -2344,6 +2318,46 @@ ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) + return rc; + } + ++static int ip2_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ i2ChanStrPtr pCh = DevTable[tty->index]; ++ i2eBordStrPtr pB; ++ struct async_icount cnow; /* kernel counter temp */ ++ unsigned long flags; ++ ++ if ( pCh == NULL ) ++ return -ENODEV; ++ ++ pB = pCh->pMyBord; ++ ++ /* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for RI where ++ * only 0->1 is counted. The controller is quite capable of counting ++ * both, but this done to preserve compatibility with the standard ++ * serial driver. ++ */ ++ ++ write_lock_irqsave(&pB->read_fifo_spinlock, flags); ++ cnow = pCh->icount; ++ write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ return 0; ++} ++ + /******************************************************************************/ + /* Function: GetSerialInfo() */ + /* Parameters: Pointer to channel structure */ +diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c +index d2692d4..65aeae8 100644 +--- a/drivers/char/mxser.c ++++ b/drivers/char/mxser.c +@@ -1700,7 +1700,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, + return 0; + } + +- if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && cmd != TIOCGICOUNT && ++ if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && + test_bit(TTY_IO_ERROR, &tty->flags)) + return -EIO; + +@@ -1730,32 +1730,6 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, + + return wait_event_interruptible(info->port.delta_msr_wait, + mxser_cflags_changed(info, arg, &cnow)); +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: { +- struct serial_icounter_struct icnt = { 0 }; +- spin_lock_irqsave(&info->slock, flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->slock, flags); +- +- icnt.frame = cnow.frame; +- icnt.brk = cnow.brk; +- icnt.overrun = cnow.overrun; +- icnt.buf_overrun = cnow.buf_overrun; +- icnt.parity = cnow.parity; +- icnt.rx = cnow.rx; +- icnt.tx = cnow.tx; +- icnt.cts = cnow.cts; +- icnt.dsr = cnow.dsr; +- icnt.rng = cnow.rng; +- icnt.dcd = cnow.dcd; +- +- return copy_to_user(argp, &icnt, sizeof(icnt)) ? -EFAULT : 0; +- } + case MOXA_HighSpeedOn: + return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp); + case MOXA_SDS_RSTICOUNTER: +@@ -1828,6 +1802,39 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, + return 0; + } + ++ /* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++ ++static int mxser_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++ ++{ ++ struct mxser_port *info = tty->driver_data; ++ struct async_icount cnow; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->slock, flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->slock, flags); ++ ++ icount->frame = cnow.frame; ++ icount->brk = cnow.brk; ++ icount->overrun = cnow.overrun; ++ icount->buf_overrun = cnow.buf_overrun; ++ icount->parity = cnow.parity; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ return 0; ++} ++ + static void mxser_stoprx(struct tty_struct *tty) + { + struct mxser_port *info = tty->driver_data; +@@ -2326,6 +2333,7 @@ static const struct tty_operations mxser_ops = { + .wait_until_sent = mxser_wait_until_sent, + .tiocmget = mxser_tiocmget, + .tiocmset = mxser_tiocmset, ++ .get_icount = mxser_get_icount, + }; + + struct tty_port_operations mxser_port_ops = { +diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c +index 824d67c..1dc493f 100644 +--- a/drivers/char/pcmcia/synclink_cs.c ++++ b/drivers/char/pcmcia/synclink_cs.c +@@ -2220,6 +2220,32 @@ static int mgslpc_break(struct tty_struct *tty, int break_state) + return 0; + } + ++static int mgslpc_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; ++ struct mgsl_icount cnow; /* kernel counter temps */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->lock,flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->lock,flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ ++ return 0; ++} ++ + /* Service an IOCTL request + * + * Arguments: +@@ -2235,11 +2261,7 @@ static int mgslpc_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) + { + MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; +- int error; +- struct mgsl_icount cnow; /* kernel counter temps */ +- struct serial_icounter_struct __user *p_cuser; /* user space */ + void __user *argp = (void __user *)arg; +- unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgslpc_ioctl %s cmd=%08X\n", __FILE__,__LINE__, +@@ -2249,7 +2271,7 @@ static int mgslpc_ioctl(struct tty_struct *tty, struct file * file, + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -2279,34 +2301,6 @@ static int mgslpc_ioctl(struct tty_struct *tty, struct file * file, + return wait_events(info, argp); + case TIOCMIWAIT: + return modem_input_wait(info,(int)arg); +- case TIOCGICOUNT: +- spin_lock_irqsave(&info->lock,flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->lock,flags); +- p_cuser = argp; +- PUT_USER(error,cnow.cts, &p_cuser->cts); +- if (error) return error; +- PUT_USER(error,cnow.dsr, &p_cuser->dsr); +- if (error) return error; +- PUT_USER(error,cnow.rng, &p_cuser->rng); +- if (error) return error; +- PUT_USER(error,cnow.dcd, &p_cuser->dcd); +- if (error) return error; +- PUT_USER(error,cnow.rx, &p_cuser->rx); +- if (error) return error; +- PUT_USER(error,cnow.tx, &p_cuser->tx); +- if (error) return error; +- PUT_USER(error,cnow.frame, &p_cuser->frame); +- if (error) return error; +- PUT_USER(error,cnow.overrun, &p_cuser->overrun); +- if (error) return error; +- PUT_USER(error,cnow.parity, &p_cuser->parity); +- if (error) return error; +- PUT_USER(error,cnow.brk, &p_cuser->brk); +- if (error) return error; +- PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun); +- if (error) return error; +- return 0; + default: + return -ENOIOCTLCMD; + } +diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c +index 0658fc5..9970aca 100644 +--- a/drivers/char/synclink.c ++++ b/drivers/char/synclink.c +@@ -2920,6 +2920,38 @@ static int mgsl_break(struct tty_struct *tty, int break_state) + + } /* end of mgsl_break() */ + ++/* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++static int msgl_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++ ++{ ++ struct mgsl_struct * info = tty->driver_data; ++ struct mgsl_icount cnow; /* kernel counter temps */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->irq_spinlock,flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->irq_spinlock,flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ return 0; ++} ++ + /* mgsl_ioctl() Service an IOCTL request + * + * Arguments: +@@ -2945,7 +2977,7 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file, + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -2958,11 +2990,7 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file, + + static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg) + { +- int error; +- struct mgsl_icount cnow; /* kernel counter temps */ + void __user *argp = (void __user *)arg; +- struct serial_icounter_struct __user *p_cuser; /* user space */ +- unsigned long flags; + + switch (cmd) { + case MGSL_IOCGPARAMS: +@@ -2991,40 +3019,6 @@ static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigne + case TIOCMIWAIT: + return modem_input_wait(info,(int)arg); + +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: +- spin_lock_irqsave(&info->irq_spinlock,flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->irq_spinlock,flags); +- p_cuser = argp; +- PUT_USER(error,cnow.cts, &p_cuser->cts); +- if (error) return error; +- PUT_USER(error,cnow.dsr, &p_cuser->dsr); +- if (error) return error; +- PUT_USER(error,cnow.rng, &p_cuser->rng); +- if (error) return error; +- PUT_USER(error,cnow.dcd, &p_cuser->dcd); +- if (error) return error; +- PUT_USER(error,cnow.rx, &p_cuser->rx); +- if (error) return error; +- PUT_USER(error,cnow.tx, &p_cuser->tx); +- if (error) return error; +- PUT_USER(error,cnow.frame, &p_cuser->frame); +- if (error) return error; +- PUT_USER(error,cnow.overrun, &p_cuser->overrun); +- if (error) return error; +- PUT_USER(error,cnow.parity, &p_cuser->parity); +- if (error) return error; +- PUT_USER(error,cnow.brk, &p_cuser->brk); +- if (error) return error; +- PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun); +- if (error) return error; +- return 0; + default: + return -ENOIOCTLCMD; + } +@@ -4325,6 +4319,7 @@ static const struct tty_operations mgsl_ops = { + .hangup = mgsl_hangup, + .tiocmget = tiocmget, + .tiocmset = tiocmset, ++ .get_icount = msgl_get_icount, + .proc_fops = &mgsl_proc_fops, + }; + +diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c +index 4561ce2..54fa0ee4 100644 +--- a/drivers/char/synclink_gt.c ++++ b/drivers/char/synclink_gt.c +@@ -1025,9 +1025,6 @@ static int ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) + { + struct slgt_info *info = tty->driver_data; +- struct mgsl_icount cnow; /* kernel counter temps */ +- struct serial_icounter_struct __user *p_cuser; /* user space */ +- unsigned long flags; + void __user *argp = (void __user *)arg; + int ret; + +@@ -1036,7 +1033,7 @@ static int ioctl(struct tty_struct *tty, struct file *file, + DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd)); + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -1089,25 +1086,6 @@ static int ioctl(struct tty_struct *tty, struct file *file, + case MGSL_IOCWAITGPIO: + ret = wait_gpio(info, argp); + break; +- case TIOCGICOUNT: +- spin_lock_irqsave(&info->lock,flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->lock,flags); +- p_cuser = argp; +- if (put_user(cnow.cts, &p_cuser->cts) || +- put_user(cnow.dsr, &p_cuser->dsr) || +- put_user(cnow.rng, &p_cuser->rng) || +- put_user(cnow.dcd, &p_cuser->dcd) || +- put_user(cnow.rx, &p_cuser->rx) || +- put_user(cnow.tx, &p_cuser->tx) || +- put_user(cnow.frame, &p_cuser->frame) || +- put_user(cnow.overrun, &p_cuser->overrun) || +- put_user(cnow.parity, &p_cuser->parity) || +- put_user(cnow.brk, &p_cuser->brk) || +- put_user(cnow.buf_overrun, &p_cuser->buf_overrun)) +- ret = -EFAULT; +- ret = 0; +- break; + default: + ret = -ENOIOCTLCMD; + } +@@ -1115,6 +1093,33 @@ static int ioctl(struct tty_struct *tty, struct file *file, + return ret; + } + ++static int get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++ ++{ ++ struct slgt_info *info = tty->driver_data; ++ struct mgsl_icount cnow; /* kernel counter temps */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->lock,flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->lock,flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ ++ return 0; ++} ++ + /* + * support for 32 bit ioctl calls on 64 bit systems + */ +@@ -1204,10 +1209,6 @@ static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, + case MGSL_IOCSGPIO: + case MGSL_IOCGGPIO: + case MGSL_IOCWAITGPIO: +- case TIOCGICOUNT: +- rc = ioctl(tty, file, cmd, (unsigned long)(compat_ptr(arg))); +- break; +- + case MGSL_IOCSTXIDLE: + case MGSL_IOCTXENABLE: + case MGSL_IOCRXENABLE: +@@ -3638,6 +3639,7 @@ static const struct tty_operations ops = { + .hangup = hangup, + .tiocmget = tiocmget, + .tiocmset = tiocmset, ++ .get_icount = get_icount, + .proc_fops = &synclink_gt_proc_fops, + }; + +diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c +index 2b18adc..beffc24 100644 +--- a/drivers/char/synclinkmp.c ++++ b/drivers/char/synclinkmp.c +@@ -1255,10 +1255,6 @@ static int do_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) + { + SLMP_INFO *info = tty->driver_data; +- int error; +- struct mgsl_icount cnow; /* kernel counter temps */ +- struct serial_icounter_struct __user *p_cuser; /* user space */ +- unsigned long flags; + void __user *argp = (void __user *)arg; + + if (debug_level >= DEBUG_LEVEL_INFO) +@@ -1269,7 +1265,7 @@ static int do_ioctl(struct tty_struct *tty, struct file *file, + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -1307,34 +1303,6 @@ static int do_ioctl(struct tty_struct *tty, struct file *file, + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +- case TIOCGICOUNT: +- spin_lock_irqsave(&info->lock,flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->lock,flags); +- p_cuser = argp; +- PUT_USER(error,cnow.cts, &p_cuser->cts); +- if (error) return error; +- PUT_USER(error,cnow.dsr, &p_cuser->dsr); +- if (error) return error; +- PUT_USER(error,cnow.rng, &p_cuser->rng); +- if (error) return error; +- PUT_USER(error,cnow.dcd, &p_cuser->dcd); +- if (error) return error; +- PUT_USER(error,cnow.rx, &p_cuser->rx); +- if (error) return error; +- PUT_USER(error,cnow.tx, &p_cuser->tx); +- if (error) return error; +- PUT_USER(error,cnow.frame, &p_cuser->frame); +- if (error) return error; +- PUT_USER(error,cnow.overrun, &p_cuser->overrun); +- if (error) return error; +- PUT_USER(error,cnow.parity, &p_cuser->parity); +- if (error) return error; +- PUT_USER(error,cnow.brk, &p_cuser->brk); +- if (error) return error; +- PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun); +- if (error) return error; +- return 0; + default: + return -ENOIOCTLCMD; + } +@@ -1351,6 +1319,32 @@ static int ioctl(struct tty_struct *tty, struct file *file, + return ret; + } + ++static int get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ SLMP_INFO *info = tty->driver_data; ++ struct mgsl_icount cnow; /* kernel counter temps */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->lock,flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->lock,flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ ++ return 0; ++} ++ + /* + * /proc fs routines.... + */ +@@ -3908,6 +3902,7 @@ static const struct tty_operations ops = { + .hangup = hangup, + .tiocmget = tiocmget, + .tiocmset = tiocmset, ++ .get_icount = get_icount, + .proc_fops = &synclinkmp_proc_fops, + }; + +diff --git a/drivers/serial/68360serial.c b/drivers/serial/68360serial.c +index 24661cd..1e4f831 100644 +--- a/drivers/serial/68360serial.c ++++ b/drivers/serial/68360serial.c +@@ -1381,6 +1381,30 @@ static void send_break(ser_info_t *info, unsigned int duration) + } + + ++/* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++static int rs_360_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ ser_info_t *info = (ser_info_t *)tty->driver_data; ++ struct async_icount cnow; ++ ++ local_irq_disable(); ++ cnow = info->state->icount; ++ local_irq_enable(); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ ++ return 0; ++} ++ + static int rs_360_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) + { +@@ -1394,7 +1418,7 @@ static int rs_360_ioctl(struct tty_struct *tty, struct file * file, + if (serial_paranoia_check(info, tty->name, "rs_ioctl")) + return -ENODEV; + +- if ((cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ if (cmd != TIOCMIWAIT) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -1477,31 +1501,6 @@ static int rs_360_ioctl(struct tty_struct *tty, struct file * file, + return 0; + #endif + +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: +- local_irq_disable(); +- cnow = info->state->icount; +- local_irq_enable(); +- p_cuser = (struct serial_icounter_struct *) arg; +-/* error = put_user(cnow.cts, &p_cuser->cts); */ +-/* if (error) return error; */ +-/* error = put_user(cnow.dsr, &p_cuser->dsr); */ +-/* if (error) return error; */ +-/* error = put_user(cnow.rng, &p_cuser->rng); */ +-/* if (error) return error; */ +-/* error = put_user(cnow.dcd, &p_cuser->dcd); */ +-/* if (error) return error; */ +- +- put_user(cnow.cts, &p_cuser->cts); +- put_user(cnow.dsr, &p_cuser->dsr); +- put_user(cnow.rng, &p_cuser->rng); +- put_user(cnow.dcd, &p_cuser->dcd); +- return 0; + + default: + return -ENOIOCTLCMD; +diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c +index 309b6c2..8996006 100644 +--- a/net/bluetooth/rfcomm/tty.c ++++ b/net/bluetooth/rfcomm/tty.c +@@ -844,10 +844,6 @@ static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned + BT_DBG("TIOCMIWAIT"); + break; + +- case TIOCGICOUNT: +- BT_DBG("TIOCGICOUNT"); +- break; +- + case TIOCGSERIAL: + BT_ERR("TIOCGSERIAL is not supported"); + return -ENOIOCTLCMD; +diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c +index dc52f75..19b3e1d 100644 +--- a/drivers/char/nozomi.c ++++ b/drivers/char/nozomi.c +@@ -1783,24 +1783,24 @@ static int ntty_cflags_changed(struct port *port, unsigned long flags, + return ret; + } + +-static int ntty_ioctl_tiocgicount(struct port *port, void __user *argp) ++static int ntty_tiocgicount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) + { ++ struct port *port = tty->driver_data; + const struct async_icount cnow = port->tty_icount; +- struct serial_icounter_struct icount; +- +- icount.cts = cnow.cts; +- icount.dsr = cnow.dsr; +- icount.rng = cnow.rng; +- icount.dcd = cnow.dcd; +- icount.rx = cnow.rx; +- icount.tx = cnow.tx; +- icount.frame = cnow.frame; +- icount.overrun = cnow.overrun; +- icount.parity = cnow.parity; +- icount.brk = cnow.brk; +- icount.buf_overrun = cnow.buf_overrun; +- +- return copy_to_user(argp, &icount, sizeof(icount)) ? -EFAULT : 0; ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ return 0; + } + + static int ntty_ioctl(struct tty_struct *tty, struct file *file, +@@ -1819,9 +1819,7 @@ static int ntty_ioctl(struct tty_struct *tty, struct file *file, + rval = wait_event_interruptible(port->tty_wait, + ntty_cflags_changed(port, arg, &cprev)); + break; +- } case TIOCGICOUNT: +- rval = ntty_ioctl_tiocgicount(port, argp); +- break; ++ } + default: + DBG1("ERR: 0x%08X, %d", cmd, cmd); + break; +@@ -1895,6 +1893,7 @@ static const struct tty_operations tty_ops = { + .chars_in_buffer = ntty_chars_in_buffer, + .tiocmget = ntty_tiocmget, + .tiocmset = ntty_tiocmset, ++ .get_icount = ntty_tiocgicount, + }; + + /* Module initialization */ + +-- +1.7.3.2 diff --git a/tty-make-tiocgicount-a-handler.patch b/tty-make-tiocgicount-a-handler.patch new file mode 100644 index 000000000..48d59b6c9 --- /dev/null +++ b/tty-make-tiocgicount-a-handler.patch @@ -0,0 +1,218 @@ +From 52583e6fde34587a38c72a5c094c17e0a3503791 Mon Sep 17 00:00:00 2001 +From: Alan Cox +Date: Thu, 16 Sep 2010 18:21:24 +0100 +Subject: [PATCH 1/2] tty: Make tiocgicount a handler + +Dan Rosenberg noted that various drivers return the struct with uncleared +fields. Instead of spending forever trying to stomp all the drivers that +get it wrong (and every new driver) do the job in one place. + +This first patch adds the needed operations and hooks them up, including +the needed USB midlayer and serial core plumbing. + +Signed-off-by: Alan Cox +Signed-off-by: Greg Kroah-Hartman +--- + drivers/char/tty_io.c | 21 +++++++++++++++++++++ + drivers/serial/serial_core.c | 37 +++++++++++++++++-------------------- + drivers/usb/serial/usb-serial.c | 13 +++++++++++++ + include/linux/tty_driver.h | 9 +++++++++ + include/linux/usb/serial.h | 2 ++ + 5 files changed, 62 insertions(+), 20 deletions(-) + +diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c +index 507441a..3a69c39 100644 +--- a/drivers/char/tty_io.c ++++ b/drivers/char/tty_io.c +@@ -96,6 +96,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -2456,6 +2457,20 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int + return tty->ops->tiocmset(tty, file, set, clear); + } + ++static int tty_tiocgicount(struct tty_struct *tty, void __user *arg) ++{ ++ int retval = -EINVAL; ++ struct serial_icounter_struct icount; ++ memset(&icount, 0, sizeof(icount)); ++ if (tty->ops->get_icount) ++ retval = tty->ops->get_icount(tty, &icount); ++ if (retval != 0) ++ return retval; ++ if (copy_to_user(arg, &icount, sizeof(icount))) ++ return -EFAULT; ++ return 0; ++} ++ + struct tty_struct *tty_pair_get_tty(struct tty_struct *tty) + { + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && +@@ -2576,6 +2591,12 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + case TIOCMBIC: + case TIOCMBIS: + return tty_tiocmset(tty, file, cmd, p); ++ case TIOCGICOUNT: ++ retval = tty_tiocgicount(tty, p); ++ /* For the moment allow fall through to the old method */ ++ if (retval != -EINVAL) ++ return retval; ++ break; + case TCFLSH: + switch (arg) { + case TCIFLUSH: +diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c +index 7f28307..232e2bb 100644 +--- a/drivers/serial/serial_core.c ++++ b/drivers/serial/serial_core.c +@@ -1074,10 +1074,10 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +-static int uart_get_count(struct uart_state *state, +- struct serial_icounter_struct __user *icnt) ++static int uart_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) + { +- struct serial_icounter_struct icount; ++ struct uart_state *state = tty->driver_data; + struct uart_icount cnow; + struct uart_port *uport = state->uart_port; + +@@ -1085,19 +1085,19 @@ static int uart_get_count(struct uart_state *state, + memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&uport->lock); + +- icount.cts = cnow.cts; +- icount.dsr = cnow.dsr; +- icount.rng = cnow.rng; +- icount.dcd = cnow.dcd; +- icount.rx = cnow.rx; +- icount.tx = cnow.tx; +- icount.frame = cnow.frame; +- icount.overrun = cnow.overrun; +- icount.parity = cnow.parity; +- icount.brk = cnow.brk; +- icount.buf_overrun = cnow.buf_overrun; ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; + +- return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; ++ return 0; + } + + /* +@@ -1150,10 +1150,6 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, + case TIOCMIWAIT: + ret = uart_wait_modem_status(state, arg); + break; +- +- case TIOCGICOUNT: +- ret = uart_get_count(state, uarg); +- break; + } + + if (ret != -ENOIOCTLCMD) +@@ -2305,6 +2301,7 @@ static const struct tty_operations uart_ops = { + #endif + .tiocmget = uart_tiocmget, + .tiocmset = uart_tiocmset, ++ .get_icount = uart_get_icount, + #ifdef CONFIG_CONSOLE_POLL + .poll_init = uart_poll_init, + .poll_get_char = uart_poll_get_char, +diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c +index 941c2d4..8aea96b 100644 +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -519,6 +519,18 @@ static int serial_tiocmset(struct tty_struct *tty, struct file *file, + return -EINVAL; + } + ++static int serial_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ struct usb_serial_port *port = tty->driver_data; ++ ++ dbg("%s - port %d", __func__, port->number); ++ ++ if (port->serial->type->get_icount) ++ return port->serial->type->get_icount(tty, icount); ++ return -EINVAL; ++} ++ + /* + * We would be calling tty_wakeup here, but unfortunately some line + * disciplines have an annoying habit of calling tty->write from +@@ -1208,6 +1220,7 @@ static const struct tty_operations serial_ops = { + .chars_in_buffer = serial_chars_in_buffer, + .tiocmget = serial_tiocmget, + .tiocmset = serial_tiocmset, ++ .get_icount = serial_get_icount, + .cleanup = serial_cleanup, + .install = serial_install, + .proc_fops = &serial_proc_fops, +diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h +index b086779..db2d227 100644 +--- a/include/linux/tty_driver.h ++++ b/include/linux/tty_driver.h +@@ -224,6 +224,12 @@ + * unless the tty also has a valid tty->termiox pointer. + * + * Optional: Called under the termios lock ++ * ++ * int (*get_icount)(struct tty_struct *tty, struct serial_icounter *icount); ++ * ++ * Called when the device receives a TIOCGICOUNT ioctl. Passed a kernel ++ * structure to complete. This method is optional and will only be called ++ * if provided (otherwise EINVAL will be returned). + */ + + #include +@@ -232,6 +238,7 @@ + + struct tty_struct; + struct tty_driver; ++struct serial_icounter_struct; + + struct tty_operations { + struct tty_struct * (*lookup)(struct tty_driver *driver, +@@ -268,6 +275,8 @@ struct tty_operations { + unsigned int set, unsigned int clear); + int (*resize)(struct tty_struct *tty, struct winsize *ws); + int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew); ++ int (*get_icount)(struct tty_struct *tty, ++ struct serial_icounter_struct *icount); + #ifdef CONFIG_CONSOLE_POLL + int (*poll_init)(struct tty_driver *driver, int line, char *options); + int (*poll_get_char)(struct tty_driver *driver, int line); +diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h +index 84a4c44..8288b57 100644 +--- a/include/linux/usb/serial.h ++++ b/include/linux/usb/serial.h +@@ -271,6 +271,8 @@ struct usb_serial_driver { + int (*tiocmget)(struct tty_struct *tty, struct file *file); + int (*tiocmset)(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); ++ int (*get_icount)(struct tty_struct *tty, ++ struct serial_icounter_struct *icount); + /* Called by the tty layer for port level work. There may or may not + be an attached tty at this point */ + void (*dtr_rts)(struct usb_serial_port *port, int on); +-- +1.7.3.2 + diff --git a/via-hwmon-temp-sensor.patch b/via-hwmon-temp-sensor.patch new file mode 100644 index 000000000..49ced3e15 --- /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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 000000000..359a0f5c3 --- /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 000000000..341003c09 --- /dev/null +++ b/wmi-check-find_guid-return-value-to-prevent-oops.patch @@ -0,0 +1,36 @@ +From: Paul Rolland +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 +Signed-off-by: Len Brown +Signed-off-by: Linus Torvalds +--- + +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 000000000..15c7be394 --- /dev/null +++ b/wmi-survive-bios-with-duplicate-guids.patch @@ -0,0 +1,76 @@ +From: Carlos Corbacho +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 +Reported-by: Larry Finger +Reported-by: Oldřich Jedlička +Signed-off-by: Len Brown +--- + +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;