Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 12 Oct 2014 01:19:00 +0000 (21:19 -0400)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 12 Oct 2014 01:19:00 +0000 (21:19 -0400)
Pull networking fixes from David Miller:
 "This set fixes a bunch of fallout from the changes that went in during
  this merge window, particularly:

   - Fix fsl_pq_mdio (Claudiu Manoil) and fm10k (Pranith Kumar) build
     failures.

   - Several networking drivers do atomic_set() on page counts where
     that's not exactly legal.  From Eric Dumazet.

   - Make __skb_flow_get_ports() work cleanly with unaligned data, from
     Alexander Duyck.

   - Fix some kernel-doc buglets in rfkill and netlabel, from Fabian
     Frederick.

   - Unbalanced enable_irq_wake usage in bcmgenet and systemport
     drivers, from Florian Fainelli.

   - pxa168_eth needs to depend on HAS_DMA, from Geert Uytterhoeven.

   - Multi-dequeue in the qdisc layer severely bypasses the fairness
     limits the previous code used to enforce, reintroduce in a way that
     at the same time doesn't compromise bulk dequeue opportunities.
     From Jesper Dangaard Brouer.

   - macvlan receive path unnecessarily hops through a softirq by using
     netif_rx() instead of netif_receive_skb().  From Jason Baron"

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: (51 commits)
  net: systemport: avoid unbalanced enable_irq_wake calls
  net: bcmgenet: avoid unbalanced enable_irq_wake calls
  net: bcmgenet: fix off-by-one in incrementing read pointer
  net: fix races in page->_count manipulation
  mlx4: fix race accessing page->_count
  ixgbe: fix race accessing page->_count
  igb: fix race accessing page->_count
  fm10k: fix race accessing page->_count
  net/phy: micrel: Add clock support for KSZ8021/KSZ8031
  flow-dissector: Fix alignment issue in __skb_flow_get_ports
  net: filter: fix the comments
  Documentation: replace __sk_run_filter with __bpf_prog_run
  macvlan: optimize the receive path
  macvlan: pass 'bool' type to macvlan_count_rx()
  drivers: net: xgene: Add 10GbE ethtool support
  drivers: net: xgene: Add 10GbE support
  drivers: net: xgene: Preparing for adding 10GbE support
  dtb: Add 10GbE node to APM X-Gene SoC device tree
  Documentation: dts: Update section header for APM X-Gene
  MAINTAINERS: Update APM X-Gene section
  ...

1857 files changed:
Documentation/ABI/stable/sysfs-devices-node
Documentation/ABI/testing/sysfs-block-zram
Documentation/ABI/testing/sysfs-bus-event_source-devices-hv_24x7
Documentation/ABI/testing/sysfs-bus-event_source-devices-hv_gpci
Documentation/ABI/testing/sysfs-bus-pci
Documentation/ABI/testing/sysfs-class-cxl [new file with mode: 0644]
Documentation/ABI/testing/sysfs-devices-memory
Documentation/DocBook/media/v4l/compat.xml
Documentation/DocBook/media/v4l/controls.xml
Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml
Documentation/DocBook/media/v4l/vidioc-dqevent.xml
Documentation/DocBook/media/v4l/vidioc-g-edid.xml
Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml
Documentation/DocBook/writing-an-alsa-driver.tmpl
Documentation/blockdev/zram.txt
Documentation/devicetree/bindings/arm/exynos/power_domain.txt
Documentation/devicetree/bindings/ata/qcom-sata.txt [new file with mode: 0644]
Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt [moved from Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt with 85% similarity]
Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-pca953x.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-xgene.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/mrvl-gpio.txt
Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt
Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt
Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt
Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt [new file with mode: 0644]
Documentation/devicetree/bindings/media/hix5hd2-ir.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/mmc.txt
Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
Documentation/devicetree/bindings/mmc/tmio_mmc.txt
Documentation/devicetree/bindings/pci/designware-pcie.txt
Documentation/devicetree/bindings/pci/fsl,pci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
Documentation/devicetree/bindings/pci/pci-keystone.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/xgene-pci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/xilinx-pcie.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/power_domain.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/rockchip-io-domain.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/adi,ssm2602.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/cs35l32.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/es8328.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl,esai.txt
Documentation/devicetree/bindings/sound/fsl,ssi.txt
Documentation/devicetree/bindings/sound/fsl-asoc-card.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl-sai.txt
Documentation/devicetree/bindings/sound/imx-audio-es8328.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt
Documentation/devicetree/bindings/sound/rt5677.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/simple-card.txt
Documentation/devicetree/bindings/sound/ssm4567.txt [new file with mode: 0644]
Documentation/devicetree/bindings/timer/amlogic,meson6-timer.txt [new file with mode: 0644]
Documentation/devicetree/bindings/timer/renesas,cmt.txt
Documentation/devicetree/bindings/timer/renesas,mtu2.txt
Documentation/devicetree/bindings/timer/renesas,tmu.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/devicetree/booting-without-of.txt
Documentation/devicetree/dynamic-resolution-notes.txt [new file with mode: 0644]
Documentation/devicetree/of_selftest.txt
Documentation/driver-model/devres.txt
Documentation/dvb/get_dvb_firmware
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
Documentation/gpio/driver.txt
Documentation/hwmon/k10temp
Documentation/hwmon/menf21bmc [new file with mode: 0644]
Documentation/ioctl/ioctl-number.txt
Documentation/kernel-parameters.txt
Documentation/memory-hotplug.txt
Documentation/power/suspend-and-interrupts.txt [new file with mode: 0644]
Documentation/powerpc/00-INDEX
Documentation/powerpc/cxl.txt [new file with mode: 0644]
Documentation/video4linux/vivid.txt [new file with mode: 0644]
MAINTAINERS
arch/alpha/include/asm/Kbuild
arch/alpha/include/asm/sections.h [deleted file]
arch/arc/include/asm/Kbuild
arch/arm/Kconfig
arch/arm/boot/dts/exynos5420-arndale-octa.dts
arch/arm/boot/dts/spear1310.dtsi
arch/arm/boot/dts/spear1340.dtsi
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
arch/arm/common/scoop.c
arch/arm/include/asm/arch_timer.h
arch/arm/include/asm/dma-mapping.h
arch/arm/include/asm/io.h
arch/arm/include/asm/irq_work.h [new file with mode: 0644]
arch/arm/include/asm/pgtable-2level.h
arch/arm/include/asm/pgtable-3level.h
arch/arm/include/asm/pgtable.h
arch/arm/include/asm/tlb.h
arch/arm/kernel/hibernate.c
arch/arm/kernel/irq.c
arch/arm/kernel/process.c
arch/arm/kernel/smp.c
arch/arm/mach-exynos/exynos.c
arch/arm/mach-exynos/pm_domains.c
arch/arm/mach-imx/avic.c
arch/arm/mach-imx/imx27-dt.c
arch/arm/mach-imx/mach-imx51.c
arch/arm/mach-imx/tzic.c
arch/arm/mach-integrator/pci_v3.c
arch/arm/mach-mvebu/pmsu.c
arch/arm/mach-omap2/pm.c
arch/arm/mach-s3c64xx/common.c
arch/arm/mach-s3c64xx/common.h
arch/arm/mach-s3c64xx/mach-anw6410.c
arch/arm/mach-s3c64xx/mach-crag6410.c
arch/arm/mach-s3c64xx/mach-hmt.c
arch/arm/mach-s3c64xx/mach-mini6410.c
arch/arm/mach-s3c64xx/mach-ncp.c
arch/arm/mach-s3c64xx/mach-real6410.c
arch/arm/mach-s3c64xx/mach-smartq5.c
arch/arm/mach-s3c64xx/mach-smartq7.c
arch/arm/mach-s3c64xx/mach-smdk6400.c
arch/arm/mach-s3c64xx/mach-smdk6410.c
arch/arm/mach-s3c64xx/pm.c
arch/arm/mach-shmobile/board-koelsch.c
arch/arm/mach-shmobile/board-lager.c
arch/arm/mach-shmobile/cpufreq.c
arch/arm/mach-shmobile/pm-r8a7779.c
arch/arm/mach-shmobile/pm-rmobile.c
arch/arm/mach-zynq/common.c
arch/arm/mm/dma-mapping.c
arch/arm/mm/flush.c
arch/arm/mm/init.c
arch/arm64/Kconfig
arch/arm64/boot/dts/apm-mustang.dts
arch/arm64/boot/dts/apm-storm.dtsi
arch/arm64/include/asm/Kbuild
arch/arm64/include/asm/arch_timer.h
arch/arm64/include/asm/hardirq.h
arch/arm64/include/asm/io.h
arch/arm64/include/asm/irq_work.h [new file with mode: 0644]
arch/arm64/include/asm/pci.h [new file with mode: 0644]
arch/arm64/include/asm/pgtable.h
arch/arm64/include/asm/smp.h
arch/arm64/include/asm/tlb.h
arch/arm64/kernel/Makefile
arch/arm64/kernel/irq.c
arch/arm64/kernel/pci.c [new file with mode: 0644]
arch/arm64/kernel/process.c
arch/arm64/kernel/smp.c
arch/arm64/mm/dma-mapping.c
arch/arm64/mm/flush.c
arch/avr32/include/asm/Kbuild
arch/blackfin/include/asm/Kbuild
arch/blackfin/mach-bf537/boards/cm_bf537e.c
arch/blackfin/mach-bf537/boards/cm_bf537u.c
arch/blackfin/mach-bf537/boards/stamp.c
arch/blackfin/mach-bf537/boards/tcm_bf537.c
arch/blackfin/mach-bf561/boards/cm_bf561.c
arch/c6x/include/asm/Kbuild
arch/cris/include/asm/Kbuild
arch/cris/include/asm/sections.h [deleted file]
arch/frv/include/asm/Kbuild
arch/frv/include/asm/processor.h
arch/frv/kernel/irq-mb93091.c
arch/frv/kernel/irq-mb93093.c
arch/frv/kernel/irq-mb93493.c
arch/frv/kernel/setup.c
arch/frv/kernel/time.c
arch/hexagon/include/asm/Kbuild
arch/ia64/include/asm/Kbuild
arch/ia64/kernel/msi_ia64.c
arch/ia64/sn/kernel/msi_sn.c
arch/m32r/include/asm/Kbuild
arch/m32r/include/asm/sections.h [deleted file]
arch/m32r/kernel/time.c
arch/m68k/include/asm/Kbuild
arch/m68k/kernel/sys_m68k.c
arch/metag/include/asm/Kbuild
arch/microblaze/include/asm/Kbuild
arch/mips/include/asm/Kbuild
arch/mips/include/asm/suspend.h [deleted file]
arch/mips/pci/msi-octeon.c
arch/mips/power/cpu.c
arch/mips/txx9/generic/setup.c
arch/mn10300/include/asm/Kbuild
arch/mn10300/include/asm/sections.h [deleted file]
arch/openrisc/Kconfig
arch/openrisc/include/asm/Kbuild
arch/openrisc/include/asm/irq.h
arch/openrisc/kernel/irq.c
arch/parisc/include/asm/Kbuild
arch/powerpc/Kconfig
arch/powerpc/Makefile
arch/powerpc/boot/Makefile
arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
arch/powerpc/boot/dts/fsl/t4240si-post.dtsi
arch/powerpc/boot/dts/t1040rdb.dts [new file with mode: 0644]
arch/powerpc/boot/dts/t1042rdb.dts [new file with mode: 0644]
arch/powerpc/boot/dts/t1042rdb_pi.dts [new file with mode: 0644]
arch/powerpc/boot/dts/t104xrdb.dtsi [new file with mode: 0644]
arch/powerpc/configs/cell_defconfig
arch/powerpc/configs/celleb_defconfig
arch/powerpc/configs/corenet32_smp_defconfig
arch/powerpc/configs/corenet64_smp_defconfig
arch/powerpc/configs/g5_defconfig
arch/powerpc/configs/maple_defconfig
arch/powerpc/configs/mpc85xx_defconfig
arch/powerpc/configs/mpc85xx_smp_defconfig
arch/powerpc/configs/mpc86xx_defconfig
arch/powerpc/configs/pasemi_defconfig
arch/powerpc/configs/ppc64_defconfig
arch/powerpc/include/asm/Kbuild
arch/powerpc/include/asm/bug.h
arch/powerpc/include/asm/copro.h [new file with mode: 0644]
arch/powerpc/include/asm/dma-mapping.h
arch/powerpc/include/asm/eeh.h
arch/powerpc/include/asm/hydra.h
arch/powerpc/include/asm/irq.h
arch/powerpc/include/asm/kexec.h
arch/powerpc/include/asm/machdep.h
arch/powerpc/include/asm/mmu-hash64.h
arch/powerpc/include/asm/opal.h
arch/powerpc/include/asm/page_64.h
arch/powerpc/include/asm/pgtable-ppc32.h
arch/powerpc/include/asm/pgtable-ppc64-4k.h
arch/powerpc/include/asm/pgtable-ppc64.h
arch/powerpc/include/asm/pgtable.h
arch/powerpc/include/asm/plpar_wrappers.h
arch/powerpc/include/asm/pnv-pci.h [new file with mode: 0644]
arch/powerpc/include/asm/prom.h
arch/powerpc/include/asm/pte-common.h
arch/powerpc/include/asm/reg.h
arch/powerpc/include/asm/rio.h
arch/powerpc/include/asm/spu.h
arch/powerpc/include/asm/sstep.h
arch/powerpc/include/asm/tsi108.h
arch/powerpc/include/asm/udbg.h
arch/powerpc/include/asm/word-at-a-time.h
arch/powerpc/include/asm/xics.h
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/crash_dump.c
arch/powerpc/kernel/dma-swiotlb.c
arch/powerpc/kernel/dma.c
arch/powerpc/kernel/eeh.c
arch/powerpc/kernel/eeh_driver.c
arch/powerpc/kernel/eeh_pe.c
arch/powerpc/kernel/eeh_sysfs.c
arch/powerpc/kernel/head_8xx.S
arch/powerpc/kernel/hw_breakpoint.c
arch/powerpc/kernel/ibmebus.c
arch/powerpc/kernel/idle_power7.S
arch/powerpc/kernel/irq.c
arch/powerpc/kernel/legacy_serial.c
arch/powerpc/kernel/module_32.c
arch/powerpc/kernel/module_64.c
arch/powerpc/kernel/msi.c
arch/powerpc/kernel/nvram_64.c
arch/powerpc/kernel/of_platform.c
arch/powerpc/kernel/pci-common.c
arch/powerpc/kernel/pci_of_scan.c
arch/powerpc/kernel/ppc_ksyms.c
arch/powerpc/kernel/ppc_ksyms_32.c [new file with mode: 0644]
arch/powerpc/kernel/process.c
arch/powerpc/kernel/prom.c
arch/powerpc/kernel/prom_init_check.sh
arch/powerpc/kernel/ptrace.c
arch/powerpc/kernel/rtasd.c
arch/powerpc/kernel/setup-common.c
arch/powerpc/kernel/setup_32.c
arch/powerpc/kernel/setup_64.c
arch/powerpc/kernel/smp.c
arch/powerpc/kernel/suspend.c
arch/powerpc/kernel/time.c
arch/powerpc/lib/Makefile
arch/powerpc/lib/feature-fixups.c
arch/powerpc/lib/ppc_ksyms.c [new file with mode: 0644]
arch/powerpc/lib/sstep.c
arch/powerpc/mm/Makefile
arch/powerpc/mm/copro_fault.c [moved from arch/powerpc/platforms/cell/spu_fault.c with 56% similarity]
arch/powerpc/mm/fault.c
arch/powerpc/mm/hash_native_64.c
arch/powerpc/mm/hash_utils_64.c
arch/powerpc/mm/init_32.c
arch/powerpc/mm/init_64.c
arch/powerpc/mm/mem.c
arch/powerpc/mm/numa.c
arch/powerpc/mm/pgtable.c
arch/powerpc/mm/slb.c
arch/powerpc/mm/slice.c
arch/powerpc/oprofile/backtrace.c
arch/powerpc/perf/core-book3s.c
arch/powerpc/perf/hv-24x7.c
arch/powerpc/platforms/40x/ep405.c
arch/powerpc/platforms/40x/ppc40x_simple.c
arch/powerpc/platforms/40x/virtex.c
arch/powerpc/platforms/40x/walnut.c
arch/powerpc/platforms/44x/Kconfig
arch/powerpc/platforms/44x/canyonlands.c
arch/powerpc/platforms/44x/ebony.c
arch/powerpc/platforms/44x/iss4xx.c
arch/powerpc/platforms/44x/ppc44x_simple.c
arch/powerpc/platforms/44x/ppc476.c
arch/powerpc/platforms/44x/sam440ep.c
arch/powerpc/platforms/44x/virtex.c
arch/powerpc/platforms/44x/warp.c
arch/powerpc/platforms/512x/mpc512x_shared.c
arch/powerpc/platforms/52xx/lite5200.c
arch/powerpc/platforms/52xx/media5200.c
arch/powerpc/platforms/52xx/mpc52xx_common.c
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
arch/powerpc/platforms/52xx/mpc52xx_pic.c
arch/powerpc/platforms/82xx/ep8248e.c
arch/powerpc/platforms/82xx/km82xx.c
arch/powerpc/platforms/82xx/mpc8272_ads.c
arch/powerpc/platforms/82xx/pq2fads.c
arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c
arch/powerpc/platforms/83xx/misc.c
arch/powerpc/platforms/83xx/mpc834x_itx.c
arch/powerpc/platforms/83xx/suspend.c
arch/powerpc/platforms/85xx/Kconfig
arch/powerpc/platforms/85xx/common.c
arch/powerpc/platforms/85xx/corenet_generic.c
arch/powerpc/platforms/85xx/ppa8548.c
arch/powerpc/platforms/85xx/qemu_e500.c
arch/powerpc/platforms/85xx/sgy_cts1000.c
arch/powerpc/platforms/86xx/gef_ppc9a.c
arch/powerpc/platforms/86xx/gef_sbc310.c
arch/powerpc/platforms/86xx/gef_sbc610.c
arch/powerpc/platforms/86xx/mpc8610_hpcd.c
arch/powerpc/platforms/86xx/mpc86xx_hpcn.c
arch/powerpc/platforms/86xx/sbc8641d.c
arch/powerpc/platforms/8xx/adder875.c
arch/powerpc/platforms/8xx/ep88xc.c
arch/powerpc/platforms/8xx/mpc86xads_setup.c
arch/powerpc/platforms/8xx/mpc885ads_setup.c
arch/powerpc/platforms/8xx/tqm8xx_setup.c
arch/powerpc/platforms/Kconfig.cputype
arch/powerpc/platforms/cell/Kconfig
arch/powerpc/platforms/cell/Makefile
arch/powerpc/platforms/cell/axon_msi.c
arch/powerpc/platforms/cell/celleb_pci.c
arch/powerpc/platforms/cell/celleb_setup.c
arch/powerpc/platforms/cell/spu_base.c
arch/powerpc/platforms/cell/spufs/fault.c
arch/powerpc/platforms/chrp/setup.c
arch/powerpc/platforms/embedded6xx/gamecube.c
arch/powerpc/platforms/embedded6xx/linkstation.c
arch/powerpc/platforms/embedded6xx/mvme5100.c
arch/powerpc/platforms/embedded6xx/storcenter.c
arch/powerpc/platforms/embedded6xx/wii.c
arch/powerpc/platforms/pasemi/gpio_mdio.c
arch/powerpc/platforms/pasemi/setup.c
arch/powerpc/platforms/powermac/setup.c
arch/powerpc/platforms/powernv/eeh-ioda.c
arch/powerpc/platforms/powernv/eeh-powernv.c
arch/powerpc/platforms/powernv/opal-dump.c
arch/powerpc/platforms/powernv/opal-elog.c
arch/powerpc/platforms/powernv/opal-lpc.c
arch/powerpc/platforms/powernv/opal-nvram.c
arch/powerpc/platforms/powernv/opal-rtc.c
arch/powerpc/platforms/powernv/opal-tracepoints.c
arch/powerpc/platforms/powernv/opal-wrappers.S
arch/powerpc/platforms/powernv/opal.c
arch/powerpc/platforms/powernv/pci-ioda.c
arch/powerpc/platforms/powernv/pci.c
arch/powerpc/platforms/powernv/pci.h
arch/powerpc/platforms/powernv/powernv.h
arch/powerpc/platforms/powernv/setup.c
arch/powerpc/platforms/powernv/smp.c
arch/powerpc/platforms/powernv/subcore.c
arch/powerpc/platforms/pseries/cmm.c
arch/powerpc/platforms/pseries/dlpar.c
arch/powerpc/platforms/pseries/eeh_pseries.c
arch/powerpc/platforms/pseries/hotplug-cpu.c
arch/powerpc/platforms/pseries/hotplug-memory.c
arch/powerpc/platforms/pseries/iommu.c
arch/powerpc/platforms/pseries/lpar.c
arch/powerpc/platforms/pseries/msi.c
arch/powerpc/platforms/pseries/nvram.c
arch/powerpc/platforms/pseries/pci.c
arch/powerpc/platforms/pseries/ras.c
arch/powerpc/platforms/pseries/setup.c
arch/powerpc/sysdev/axonram.c
arch/powerpc/sysdev/dcr.c
arch/powerpc/sysdev/fsl_85xx_l2ctlr.c
arch/powerpc/sysdev/fsl_msi.c
arch/powerpc/sysdev/fsl_msi.h
arch/powerpc/sysdev/fsl_pci.c
arch/powerpc/sysdev/mpic.c
arch/powerpc/sysdev/mpic_pasemi_msi.c
arch/powerpc/sysdev/mpic_u3msi.c
arch/powerpc/sysdev/msi_bitmap.c
arch/powerpc/sysdev/mv64x60_dev.c
arch/powerpc/sysdev/pmi.c
arch/powerpc/sysdev/ppc4xx_hsta_msi.c
arch/powerpc/sysdev/ppc4xx_msi.c
arch/powerpc/sysdev/xics/icp-native.c
arch/powerpc/sysdev/xilinx_intc.c
arch/powerpc/sysdev/xilinx_pci.c
arch/s390/Kconfig
arch/s390/include/asm/Kbuild
arch/s390/include/asm/dma-mapping.h
arch/s390/kernel/suspend.c
arch/score/include/asm/Kbuild
arch/score/include/asm/sections.h [deleted file]
arch/sh/boards/mach-x3proto/gpio.c
arch/sh/include/asm/Kbuild
arch/sh/include/asm/sections.h
arch/sparc/Kconfig
arch/sparc/include/asm/Kbuild
arch/sparc/include/asm/dma-mapping.h
arch/sparc/include/asm/hypervisor.h
arch/sparc/include/asm/irq_64.h
arch/sparc/include/asm/ldc.h
arch/sparc/include/asm/page_64.h
arch/sparc/include/asm/pgalloc_64.h
arch/sparc/include/asm/pgtable_64.h
arch/sparc/include/asm/spitfire.h
arch/sparc/include/asm/thread_info_64.h
arch/sparc/include/asm/tsb.h
arch/sparc/include/asm/vio.h
arch/sparc/kernel/cpu.c
arch/sparc/kernel/cpumap.c
arch/sparc/kernel/ds.c
arch/sparc/kernel/head_64.S
arch/sparc/kernel/hvapi.c
arch/sparc/kernel/hvcalls.S
arch/sparc/kernel/ioport.c
arch/sparc/kernel/irq_64.c
arch/sparc/kernel/ktlb.S
arch/sparc/kernel/ldc.c
arch/sparc/kernel/leon_kernel.c
arch/sparc/kernel/pcr.c
arch/sparc/kernel/perf_event.c
arch/sparc/kernel/setup_64.c
arch/sparc/kernel/smp_64.c
arch/sparc/kernel/sun4v_tlb_miss.S
arch/sparc/kernel/traps_64.c
arch/sparc/kernel/vio.c
arch/sparc/kernel/viohs.c
arch/sparc/kernel/vmlinux.lds.S
arch/sparc/lib/memset.S
arch/sparc/mm/fault_64.c
arch/sparc/mm/init_64.c
arch/sparc/mm/init_64.h
arch/sparc/power/hibernate.c
arch/sparc/power/hibernate_asm.S
arch/sparc/prom/bootstr_64.c
arch/sparc/prom/p1275.c
arch/tile/include/asm/Kbuild
arch/um/include/asm/Kbuild
arch/unicore32/include/asm/Kbuild
arch/unicore32/include/mach/pm.h
arch/unicore32/kernel/hibernate.c
arch/x86/Kconfig
arch/x86/include/asm/irq_work.h [new file with mode: 0644]
arch/x86/include/asm/pgtable_types.h
arch/x86/kernel/apic/io_apic.c
arch/x86/kernel/irq_work.c
arch/x86/kvm/mmu.c
arch/x86/mm/fault.c
arch/x86/mm/init_32.c
arch/x86/mm/init_64.c
arch/x86/pci/common.c
arch/x86/pci/i386.c
arch/x86/pci/mmconfig-shared.c
arch/x86/pci/pcbios.c
arch/x86/power/hibernate_32.c
arch/x86/power/hibernate_64.c
arch/x86/xen/efi.c
arch/x86/xen/enlighten.c
arch/x86/xen/mmu.c
arch/x86/xen/p2m.c
arch/x86/xen/p2m.h [new file with mode: 0644]
arch/x86/xen/setup.c
arch/x86/xen/smp.c
arch/x86/xen/smp.h
arch/x86/xen/xen-head.S
arch/xtensa/include/asm/Kbuild
block/blk-mq-sysfs.c
block/blk-mq.c
block/blk-sysfs.c
drivers/acpi/acpi_lpss.c
drivers/acpi/acpi_pnp.c
drivers/acpi/acpica/evxfgpe.c
drivers/acpi/acpica/hwgpe.c
drivers/acpi/acpica/utresrc.c
drivers/acpi/battery.c
drivers/acpi/blacklist.c
drivers/acpi/device_pm.c
drivers/acpi/fan.c
drivers/acpi/osl.c
drivers/acpi/pci_root.c
drivers/acpi/processor_core.c
drivers/acpi/sbs.c
drivers/acpi/sleep.c
drivers/acpi/utils.c
drivers/acpi/video.c
drivers/acpi/video_detect.c
drivers/amba/bus.c
drivers/ata/acard-ahci.c
drivers/ata/ahci.c
drivers/ata/ahci.h
drivers/ata/ahci_platform.c
drivers/ata/ahci_xgene.c
drivers/ata/libahci.c
drivers/ata/libahci_platform.c
drivers/ata/libata-core.c
drivers/ata/libata-sff.c
drivers/ata/pata_imx.c
drivers/ata/pata_of_platform.c
drivers/ata/pata_platform.c
drivers/ata/pata_serverworks.c
drivers/ata/sata_highbank.c
drivers/base/Kconfig
drivers/base/dma-mapping.c
drivers/base/memory.c
drivers/base/node.c
drivers/base/platform.c
drivers/base/power/clock_ops.c
drivers/base/power/common.c
drivers/base/power/domain.c
drivers/base/power/domain_governor.c
drivers/base/power/main.c
drivers/base/power/sysfs.c
drivers/base/power/wakeup.c
drivers/base/syscore.c
drivers/bcma/driver_gpio.c
drivers/block/sunvdc.c
drivers/block/xen-blkback/xenbus.c
drivers/block/xen-blkfront.c
drivers/block/zram/zram_drv.c
drivers/block/zram/zram_drv.h
drivers/char/tpm/xen-tpmfront.c
drivers/clocksource/Kconfig
drivers/clocksource/Makefile
drivers/clocksource/arm_arch_timer.c
drivers/clocksource/cadence_ttc_timer.c
drivers/clocksource/meson6_timer.c [new file with mode: 0644]
drivers/clocksource/timer-marco.c
drivers/clocksource/vf_pit_timer.c
drivers/cpufreq/Kconfig
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/Makefile
drivers/cpufreq/cpufreq-cpu0.c [deleted file]
drivers/cpufreq/cpufreq-dt.c [new file with mode: 0644]
drivers/cpufreq/cpufreq.c
drivers/cpufreq/exynos4210-cpufreq.c
drivers/cpufreq/exynos4x12-cpufreq.c
drivers/cpufreq/exynos5250-cpufreq.c
drivers/cpufreq/highbank-cpufreq.c
drivers/cpufreq/pmac32-cpufreq.c
drivers/cpufreq/powernv-cpufreq.c
drivers/cpufreq/ppc-corenet-cpufreq.c
drivers/cpufreq/s5pv210-cpufreq.c
drivers/cpuidle/Kconfig
drivers/cpuidle/Kconfig.arm
drivers/cpuidle/Kconfig.arm64 [new file with mode: 0644]
drivers/cpuidle/Makefile
drivers/cpuidle/cpuidle-arm64.c [new file with mode: 0644]
drivers/cpuidle/cpuidle-big_little.c
drivers/cpuidle/dt_idle_states.c [new file with mode: 0644]
drivers/cpuidle/dt_idle_states.h [new file with mode: 0644]
drivers/cpuidle/governor.c
drivers/devfreq/Kconfig
drivers/devfreq/devfreq.c
drivers/devfreq/exynos/exynos_ppmu.c
drivers/edac/sb_edac.c
drivers/firmware/memmap.c
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-adnp.c
drivers/gpio/gpio-bcm-kona.c
drivers/gpio/gpio-crystalcove.c
drivers/gpio/gpio-cs5535.c
drivers/gpio/gpio-dwapb.c
drivers/gpio/gpio-ks8695.c
drivers/gpio/gpio-mcp23s08.c
drivers/gpio/gpio-omap.c
drivers/gpio/gpio-pca953x.c
drivers/gpio/gpio-pch.c
drivers/gpio/gpio-samsung.c
drivers/gpio/gpio-stmpe.c
drivers/gpio/gpio-stp-xway.c
drivers/gpio/gpio-syscon.c
drivers/gpio/gpio-tc3589x.c
drivers/gpio/gpio-xgene.c [new file with mode: 0644]
drivers/gpio/gpio-xilinx.c
drivers/gpio/gpio-zynq.c
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib.c
drivers/gpu/drm/vmwgfx/svga_reg.h
drivers/hid/hid-cp2112.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/ab8500.c
drivers/hwmon/ads1015.c
drivers/hwmon/da9052-hwmon.c
drivers/hwmon/da9055-hwmon.c
drivers/hwmon/k10temp.c
drivers/hwmon/menf21bmc_hwmon.c [new file with mode: 0644]
drivers/hwmon/ntc_thermistor.c
drivers/hwmon/smsc47b397.c
drivers/i2c/i2c-core.c
drivers/input/keyboard/adp5588-keys.c
drivers/input/keyboard/adp5589-keys.c
drivers/input/misc/xen-kbdfront.c
drivers/input/touchscreen/ad7879.c
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-armada-370-xp.c
drivers/irqchip/irq-atmel-aic.c
drivers/irqchip/irq-atmel-aic5.c
drivers/irqchip/irq-bcm7120-l2.c [new file with mode: 0644]
drivers/irqchip/irq-clps711x.c
drivers/irqchip/irq-gic-common.c
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-gic.c
drivers/irqchip/irq-hip04.c [new file with mode: 0644]
drivers/irqchip/irq-keystone.c [new file with mode: 0644]
drivers/irqchip/irq-mmp.c
drivers/irqchip/irq-mxs.c
drivers/irqchip/irq-omap-intc.c
drivers/irqchip/irq-or1k-pic.c
drivers/irqchip/irq-orion.c
drivers/irqchip/irq-renesas-intc-irqpin.c
drivers/irqchip/irq-s3c24xx.c
drivers/irqchip/irq-sirfsoc.c
drivers/irqchip/irq-sun4i.c
drivers/irqchip/irq-versatile-fpga.c
drivers/irqchip/irq-vic.c
drivers/irqchip/irq-vt8500.c
drivers/irqchip/irq-zevio.c
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/leds-menf21bmc.c [new file with mode: 0644]
drivers/leds/leds-pca9532.c
drivers/leds/leds-tca6507.c
drivers/macintosh/adb.c
drivers/macintosh/via-cuda.c
drivers/media/common/b2c2/flexcop.h
drivers/media/common/saa7146/saa7146_fops.c
drivers/media/common/siano/sms-cards.c
drivers/media/common/siano/sms-cards.h
drivers/media/common/siano/smscoreapi.c
drivers/media/dvb-core/dmxdev.c
drivers/media/dvb-core/dvb-usb-ids.h
drivers/media/dvb-core/dvb_frontend.c
drivers/media/dvb-core/dvb_frontend.h
drivers/media/dvb-core/dvb_ringbuffer.c
drivers/media/dvb-core/dvb_ringbuffer.h
drivers/media/dvb-frontends/Kconfig
drivers/media/dvb-frontends/Makefile
drivers/media/dvb-frontends/af9013.c
drivers/media/dvb-frontends/af9033.c
drivers/media/dvb-frontends/af9033.h
drivers/media/dvb-frontends/af9033_priv.h
drivers/media/dvb-frontends/as102_fe.c [new file with mode: 0644]
drivers/media/dvb-frontends/as102_fe.h [new file with mode: 0644]
drivers/media/dvb-frontends/as102_fe_types.h [moved from drivers/staging/media/as102/as10x_types.h with 95% similarity]
drivers/media/dvb-frontends/bcm3510.c
drivers/media/dvb-frontends/cxd2820r_c.c
drivers/media/dvb-frontends/cxd2820r_core.c
drivers/media/dvb-frontends/cxd2820r_t.c
drivers/media/dvb-frontends/dib7000p.c
drivers/media/dvb-frontends/drx39xyj/drxj.c
drivers/media/dvb-frontends/drxd_hard.c
drivers/media/dvb-frontends/drxk_hard.c
drivers/media/dvb-frontends/m88ds3103.c
drivers/media/dvb-frontends/m88ds3103.h
drivers/media/dvb-frontends/mb86a16.c
drivers/media/dvb-frontends/mb86a20s.c
drivers/media/dvb-frontends/mt312.c
drivers/media/dvb-frontends/or51211.c
drivers/media/dvb-frontends/rtl2832.c
drivers/media/dvb-frontends/rtl2832_sdr.c
drivers/media/dvb-frontends/si2165.c
drivers/media/dvb-frontends/si2165_priv.h
drivers/media/dvb-frontends/si2168.c
drivers/media/dvb-frontends/si2168.h
drivers/media/dvb-frontends/si2168_priv.h
drivers/media/dvb-frontends/si21xx.c
drivers/media/dvb-frontends/sp2.c [new file with mode: 0644]
drivers/media/dvb-frontends/sp2.h [new file with mode: 0644]
drivers/media/dvb-frontends/sp2_priv.h [new file with mode: 0644]
drivers/media/dvb-frontends/sp8870.c
drivers/media/dvb-frontends/stv0367.c
drivers/media/dvb-frontends/stv0900_core.c
drivers/media/dvb-frontends/stv0900_sw.c
drivers/media/dvb-frontends/tc90522.c [new file with mode: 0644]
drivers/media/dvb-frontends/tc90522.h [new file with mode: 0644]
drivers/media/dvb-frontends/tda10071.c
drivers/media/dvb-frontends/zl10039.c
drivers/media/firewire/firedtv-avc.c
drivers/media/i2c/adv7343_regs.h
drivers/media/i2c/adv7604.c
drivers/media/i2c/adv7842.c
drivers/media/i2c/lm3560.c
drivers/media/i2c/ov7670.c
drivers/media/i2c/s5k5baf.c
drivers/media/i2c/saa6752hs.c
drivers/media/i2c/smiapp/smiapp-core.c
drivers/media/i2c/smiapp/smiapp.h
drivers/media/i2c/soc_camera/mt9t112.c
drivers/media/i2c/soc_camera/ov772x.c
drivers/media/i2c/soc_camera/ov9740.c
drivers/media/i2c/tda7432.c
drivers/media/i2c/tvp7002.c
drivers/media/i2c/vs6624.c
drivers/media/media-device.c
drivers/media/media-devnode.c
drivers/media/parport/pms.c
drivers/media/pci/Kconfig
drivers/media/pci/Makefile
drivers/media/pci/bt8xx/bttv-driver.c
drivers/media/pci/bt8xx/dst_ca.c
drivers/media/pci/cx18/cx18-alsa-pcm.c
drivers/media/pci/cx18/cx18-firmware.c
drivers/media/pci/cx18/cx18-queue.c
drivers/media/pci/cx23885/Kconfig
drivers/media/pci/cx23885/Makefile
drivers/media/pci/cx23885/altera-ci.c
drivers/media/pci/cx23885/altera-ci.h
drivers/media/pci/cx23885/cimax2.c
drivers/media/pci/cx23885/cimax2.h
drivers/media/pci/cx23885/cx23885-417.c
drivers/media/pci/cx23885/cx23885-alsa.c
drivers/media/pci/cx23885/cx23885-av.c
drivers/media/pci/cx23885/cx23885-av.h
drivers/media/pci/cx23885/cx23885-cards.c
drivers/media/pci/cx23885/cx23885-core.c
drivers/media/pci/cx23885/cx23885-dvb.c
drivers/media/pci/cx23885/cx23885-f300.c
drivers/media/pci/cx23885/cx23885-i2c.c
drivers/media/pci/cx23885/cx23885-input.c
drivers/media/pci/cx23885/cx23885-input.h
drivers/media/pci/cx23885/cx23885-ioctl.c
drivers/media/pci/cx23885/cx23885-ioctl.h
drivers/media/pci/cx23885/cx23885-ir.c
drivers/media/pci/cx23885/cx23885-ir.h
drivers/media/pci/cx23885/cx23885-reg.h
drivers/media/pci/cx23885/cx23885-vbi.c
drivers/media/pci/cx23885/cx23885-video.c
drivers/media/pci/cx23885/cx23885-video.h
drivers/media/pci/cx23885/cx23885.h
drivers/media/pci/cx23885/cx23888-ir.c
drivers/media/pci/cx23885/cx23888-ir.h
drivers/media/pci/cx23885/netup-eeprom.c
drivers/media/pci/cx23885/netup-eeprom.h
drivers/media/pci/cx23885/netup-init.c
drivers/media/pci/cx23885/netup-init.h
drivers/media/pci/cx25821/cx25821-video-upstream.c
drivers/media/pci/cx88/cx88-cards.c
drivers/media/pci/cx88/cx88-video.c
drivers/media/pci/ddbridge/ddbridge-core.c
drivers/media/pci/ddbridge/ddbridge.h
drivers/media/pci/dm1105/dm1105.c
drivers/media/pci/ivtv/ivtv-alsa-pcm.c
drivers/media/pci/ivtv/ivtv-firmware.c
drivers/media/pci/ivtv/ivtv-irq.c
drivers/media/pci/mantis/hopper_vp3028.c
drivers/media/pci/mantis/mantis_common.h
drivers/media/pci/mantis/mantis_vp1033.c
drivers/media/pci/mantis/mantis_vp1034.c
drivers/media/pci/mantis/mantis_vp1041.c
drivers/media/pci/mantis/mantis_vp2033.c
drivers/media/pci/mantis/mantis_vp2040.c
drivers/media/pci/mantis/mantis_vp3030.c
drivers/media/pci/ngene/ngene-cards.c
drivers/media/pci/ngene/ngene-core.c
drivers/media/pci/ngene/ngene-dvb.c
drivers/media/pci/ngene/ngene.h
drivers/media/pci/pt3/Kconfig [new file with mode: 0644]
drivers/media/pci/pt3/Makefile [new file with mode: 0644]
drivers/media/pci/pt3/pt3.c [new file with mode: 0644]
drivers/media/pci/pt3/pt3.h [new file with mode: 0644]
drivers/media/pci/pt3/pt3_dma.c [new file with mode: 0644]
drivers/media/pci/pt3/pt3_i2c.c [new file with mode: 0644]
drivers/media/pci/saa7134/Kconfig
drivers/media/pci/saa7134/Makefile
drivers/media/pci/saa7134/saa7134-cards.c
drivers/media/pci/saa7134/saa7134-core.c
drivers/media/pci/saa7134/saa7134-go7007.c [new file with mode: 0644]
drivers/media/pci/saa7134/saa7134-vbi.c
drivers/media/pci/saa7134/saa7134-video.c
drivers/media/pci/saa7134/saa7134.h
drivers/media/pci/saa7164/saa7164-api.c
drivers/media/pci/saa7164/saa7164-core.c
drivers/media/pci/solo6x10/Kconfig
drivers/media/pci/solo6x10/solo6x10-disp.c
drivers/media/pci/solo6x10/solo6x10-eeprom.c
drivers/media/pci/solo6x10/solo6x10.h
drivers/media/pci/sta2x11/Kconfig
drivers/media/pci/sta2x11/sta2x11_vip.c
drivers/media/pci/ttpci/Kconfig
drivers/media/pci/ttpci/Makefile
drivers/media/pci/ttpci/av7110.c
drivers/media/pci/tw68/Kconfig [new file with mode: 0644]
drivers/media/pci/tw68/Makefile [new file with mode: 0644]
drivers/media/pci/tw68/tw68-core.c [new file with mode: 0644]
drivers/media/pci/tw68/tw68-reg.h [new file with mode: 0644]
drivers/media/pci/tw68/tw68-risc.c [new file with mode: 0644]
drivers/media/pci/tw68/tw68-video.c [new file with mode: 0644]
drivers/media/pci/tw68/tw68.h [new file with mode: 0644]
drivers/media/pci/zoran/zoran_device.c
drivers/media/platform/Kconfig
drivers/media/platform/Makefile
drivers/media/platform/blackfin/Kconfig
drivers/media/platform/coda.c [deleted file]
drivers/media/platform/coda/Makefile [new file with mode: 0644]
drivers/media/platform/coda/coda-bit.c [new file with mode: 0644]
drivers/media/platform/coda/coda-common.c [new file with mode: 0644]
drivers/media/platform/coda/coda-h264.c [new file with mode: 0644]
drivers/media/platform/coda/coda.h [new file with mode: 0644]
drivers/media/platform/coda/coda_regs.h [moved from drivers/media/platform/coda.h with 100% similarity]
drivers/media/platform/davinci/Kconfig
drivers/media/platform/davinci/dm355_ccdc.c
drivers/media/platform/davinci/dm644x_ccdc.c
drivers/media/platform/davinci/vpfe_capture.c
drivers/media/platform/davinci/vpif.c
drivers/media/platform/davinci/vpif_capture.c
drivers/media/platform/davinci/vpif_display.c
drivers/media/platform/exynos-gsc/gsc-core.c
drivers/media/platform/exynos-gsc/gsc-m2m.c
drivers/media/platform/exynos-gsc/gsc-regs.c
drivers/media/platform/exynos4-is/Kconfig
drivers/media/platform/exynos4-is/fimc-is-errno.c
drivers/media/platform/exynos4-is/fimc-is-errno.h
drivers/media/platform/exynos4-is/fimc-is-param.c
drivers/media/platform/exynos4-is/fimc-is.c
drivers/media/platform/exynos4-is/fimc-isp-video.c
drivers/media/platform/exynos4-is/media-dev.c
drivers/media/platform/exynos4-is/mipi-csis.c
drivers/media/platform/marvell-ccic/Kconfig
drivers/media/platform/marvell-ccic/mcam-core.c
drivers/media/platform/mx2_emmaprp.c
drivers/media/platform/omap/Kconfig
drivers/media/platform/omap/omap_vout.c
drivers/media/platform/omap/omap_vout_vrfb.c
drivers/media/platform/omap/omap_vout_vrfb.h
drivers/media/platform/omap3isp/cfa_coef_table.h
drivers/media/platform/omap3isp/gamma_table.h
drivers/media/platform/omap3isp/isp.c
drivers/media/platform/omap3isp/isp.h
drivers/media/platform/omap3isp/ispccdc.c
drivers/media/platform/omap3isp/ispccdc.h
drivers/media/platform/omap3isp/ispccp2.c
drivers/media/platform/omap3isp/ispccp2.h
drivers/media/platform/omap3isp/ispcsi2.c
drivers/media/platform/omap3isp/ispcsi2.h
drivers/media/platform/omap3isp/ispcsiphy.c
drivers/media/platform/omap3isp/ispcsiphy.h
drivers/media/platform/omap3isp/isph3a.h
drivers/media/platform/omap3isp/isph3a_aewb.c
drivers/media/platform/omap3isp/isph3a_af.c
drivers/media/platform/omap3isp/isphist.c
drivers/media/platform/omap3isp/isphist.h
drivers/media/platform/omap3isp/isppreview.c
drivers/media/platform/omap3isp/isppreview.h
drivers/media/platform/omap3isp/ispreg.h
drivers/media/platform/omap3isp/ispresizer.c
drivers/media/platform/omap3isp/ispresizer.h
drivers/media/platform/omap3isp/ispstat.c
drivers/media/platform/omap3isp/ispstat.h
drivers/media/platform/omap3isp/ispvideo.c
drivers/media/platform/omap3isp/ispvideo.h
drivers/media/platform/omap3isp/luma_enhance_table.h
drivers/media/platform/omap3isp/noise_filter_table.h
drivers/media/platform/s3c-camif/camif-capture.c
drivers/media/platform/s3c-camif/camif-regs.c
drivers/media/platform/s5p-g2d/g2d.c
drivers/media/platform/s5p-jpeg/jpeg-core.c
drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c
drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
drivers/media/platform/s5p-mfc/s5p_mfc.c
drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
drivers/media/platform/s5p-mfc/s5p_mfc_common.h
drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
drivers/media/platform/s5p-tv/Kconfig
drivers/media/platform/s5p-tv/hdmi_drv.c
drivers/media/platform/s5p-tv/sdo_drv.c
drivers/media/platform/s5p-tv/sii9234_drv.c
drivers/media/platform/sh_veu.c
drivers/media/platform/soc_camera/Kconfig
drivers/media/platform/soc_camera/atmel-isi.c
drivers/media/platform/soc_camera/mx2_camera.c
drivers/media/platform/soc_camera/pxa_camera.c
drivers/media/platform/soc_camera/rcar_vin.c
drivers/media/platform/soc_camera/soc_camera.c
drivers/media/platform/ti-vpe/vpdma.c
drivers/media/platform/ti-vpe/vpe.c
drivers/media/platform/via-camera.c
drivers/media/platform/vivi.c [deleted file]
drivers/media/platform/vivid/Kconfig [new file with mode: 0644]
drivers/media/platform/vivid/Makefile [new file with mode: 0644]
drivers/media/platform/vivid/vivid-core.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-core.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-ctrls.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-ctrls.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-kthread-cap.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-kthread-cap.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-kthread-out.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-kthread-out.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-osd.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-osd.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-radio-common.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-radio-common.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-radio-rx.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-radio-rx.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-radio-tx.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-radio-tx.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-rds-gen.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-rds-gen.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-sdr-cap.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-sdr-cap.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-tpg-colors.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-tpg-colors.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-tpg.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-tpg.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vbi-cap.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vbi-cap.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vbi-gen.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vbi-gen.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vbi-out.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vbi-out.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vid-cap.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vid-cap.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vid-common.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vid-common.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vid-out.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-vid-out.h [new file with mode: 0644]
drivers/media/radio/radio-gemtek.c
drivers/media/radio/radio-sf16fmi.c
drivers/media/radio/radio-sf16fmr2.c
drivers/media/radio/radio-tea5764.c
drivers/media/radio/si470x/radio-si470x-common.c
drivers/media/radio/si470x/radio-si470x-usb.c
drivers/media/radio/wl128x/fmdrv_common.c
drivers/media/radio/wl128x/fmdrv_rx.c
drivers/media/radio/wl128x/fmdrv_tx.c
drivers/media/rc/Kconfig
drivers/media/rc/Makefile
drivers/media/rc/ene_ir.c
drivers/media/rc/fintek-cir.c
drivers/media/rc/img-ir/img-ir-hw.c
drivers/media/rc/img-ir/img-ir-hw.h
drivers/media/rc/imon.c
drivers/media/rc/ir-hix5hd2.c [new file with mode: 0644]
drivers/media/rc/ite-cir.c
drivers/media/rc/keymaps/Makefile
drivers/media/rc/keymaps/rc-dvbsky.c [new file with mode: 0644]
drivers/media/rc/lirc_dev.c
drivers/media/rc/mceusb.c
drivers/media/rc/nuvoton-cir.c
drivers/media/rc/st_rc.c
drivers/media/rc/streamzap.c
drivers/media/tuners/Kconfig
drivers/media/tuners/Makefile
drivers/media/tuners/e4000.c
drivers/media/tuners/it913x.c [new file with mode: 0644]
drivers/media/tuners/it913x.h [moved from drivers/media/tuners/tuner_it913x.h with 67% similarity]
drivers/media/tuners/m88ts2022.c
drivers/media/tuners/m88ts2022_priv.h
drivers/media/tuners/msi001.c
drivers/media/tuners/mt2060.c
drivers/media/tuners/mt2063.c
drivers/media/tuners/mxl301rf.c [new file with mode: 0644]
drivers/media/tuners/mxl301rf.h [new file with mode: 0644]
drivers/media/tuners/mxl5005s.c
drivers/media/tuners/qm1d1c0042.c [new file with mode: 0644]
drivers/media/tuners/qm1d1c0042.h [new file with mode: 0644]
drivers/media/tuners/si2157.c
drivers/media/tuners/si2157.h
drivers/media/tuners/si2157_priv.h
drivers/media/tuners/tda18212.c
drivers/media/tuners/tda18212.h
drivers/media/tuners/tda18271-common.c
drivers/media/tuners/tda18271-priv.h
drivers/media/tuners/tuner-xc2028.c
drivers/media/tuners/tuner_it913x.c [deleted file]
drivers/media/tuners/tuner_it913x_priv.h [deleted file]
drivers/media/tuners/xc4000.c
drivers/media/tuners/xc5000.c
drivers/media/usb/Kconfig
drivers/media/usb/Makefile
drivers/media/usb/airspy/airspy.c
drivers/media/usb/as102/Kconfig [moved from drivers/staging/media/as102/Kconfig with 100% similarity]
drivers/media/usb/as102/Makefile [moved from drivers/staging/media/as102/Makefile with 65% similarity]
drivers/media/usb/as102/as102_drv.c [moved from drivers/staging/media/as102/as102_drv.c with 66% similarity]
drivers/media/usb/as102/as102_drv.h [moved from drivers/staging/media/as102/as102_drv.h with 75% similarity]
drivers/media/usb/as102/as102_fw.c [moved from drivers/staging/media/as102/as102_fw.c with 96% similarity]
drivers/media/usb/as102/as102_fw.h [moved from drivers/staging/media/as102/as102_fw.h with 83% similarity]
drivers/media/usb/as102/as102_usb_drv.c [moved from drivers/staging/media/as102/as102_usb_drv.c with 90% similarity]
drivers/media/usb/as102/as102_usb_drv.h [moved from drivers/staging/media/as102/as102_usb_drv.h with 90% similarity]
drivers/media/usb/as102/as10x_cmd.c [moved from drivers/staging/media/as102/as10x_cmd.c with 92% similarity]
drivers/media/usb/as102/as10x_cmd.h [moved from drivers/staging/media/as102/as10x_cmd.h with 89% similarity]
drivers/media/usb/as102/as10x_cmd_cfg.c [moved from drivers/staging/media/as102/as10x_cmd_cfg.c with 93% similarity]
drivers/media/usb/as102/as10x_cmd_stream.c [moved from drivers/staging/media/as102/as10x_cmd_stream.c with 96% similarity]
drivers/media/usb/as102/as10x_handle.h [moved from drivers/staging/media/as102/as10x_handle.h with 88% similarity]
drivers/media/usb/au0828/au0828-cards.c
drivers/media/usb/au0828/au0828-core.c
drivers/media/usb/au0828/au0828-dvb.c
drivers/media/usb/au0828/au0828-i2c.c
drivers/media/usb/au0828/au0828-input.c
drivers/media/usb/au0828/au0828-vbi.c
drivers/media/usb/au0828/au0828-video.c
drivers/media/usb/au0828/au0828.h
drivers/media/usb/cx231xx/cx231xx-avcore.c
drivers/media/usb/cx231xx/cx231xx-cards.c
drivers/media/usb/cx231xx/cx231xx-core.c
drivers/media/usb/cx231xx/cx231xx-dvb.c
drivers/media/usb/dvb-usb-v2/Kconfig
drivers/media/usb/dvb-usb-v2/Makefile
drivers/media/usb/dvb-usb-v2/af9015.c
drivers/media/usb/dvb-usb-v2/af9035.c
drivers/media/usb/dvb-usb-v2/af9035.h
drivers/media/usb/dvb-usb-v2/anysee.c
drivers/media/usb/dvb-usb-v2/anysee.h
drivers/media/usb/dvb-usb-v2/dvb_usb.h
drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c
drivers/media/usb/dvb-usb-v2/dvbsky.c [new file with mode: 0644]
drivers/media/usb/dvb-usb-v2/lmedm04.c
drivers/media/usb/dvb-usb-v2/mxl111sf.c
drivers/media/usb/dvb-usb/Kconfig
drivers/media/usb/dvb-usb/af9005.c
drivers/media/usb/dvb-usb/cxusb.c
drivers/media/usb/dvb-usb/cxusb.h
drivers/media/usb/dvb-usb/dib0700_devices.c
drivers/media/usb/dvb-usb/dibusb-common.c
drivers/media/usb/dvb-usb/dw2102.c
drivers/media/usb/dvb-usb/opera1.c
drivers/media/usb/dvb-usb/pctv452e.c
drivers/media/usb/em28xx/em28xx-audio.c
drivers/media/usb/em28xx/em28xx-cards.c
drivers/media/usb/em28xx/em28xx-core.c
drivers/media/usb/em28xx/em28xx-dvb.c
drivers/media/usb/em28xx/em28xx-input.c
drivers/media/usb/em28xx/em28xx-vbi.c
drivers/media/usb/em28xx/em28xx-video.c
drivers/media/usb/em28xx/em28xx.h
drivers/media/usb/go7007/go7007-usb.c
drivers/media/usb/gspca/gspca.c
drivers/media/usb/gspca/gspca.h
drivers/media/usb/gspca/kinect.c
drivers/media/usb/gspca/sn9c20x.c
drivers/media/usb/hackrf/Kconfig [new file with mode: 0644]
drivers/media/usb/hackrf/Makefile [new file with mode: 0644]
drivers/media/usb/hackrf/hackrf.c [new file with mode: 0644]
drivers/media/usb/hdpvr/hdpvr-control.c
drivers/media/usb/hdpvr/hdpvr-core.c
drivers/media/usb/msi2500/msi2500.c
drivers/media/usb/pwc/pwc-v4l.c
drivers/media/usb/s2255/s2255drv.c
drivers/media/usb/siano/smsusb.c
drivers/media/usb/ttusb-dec/ttusbdecfe.c
drivers/media/usb/usbtv/Kconfig
drivers/media/usb/usbtv/Makefile
drivers/media/usb/usbtv/usbtv-audio.c [new file with mode: 0644]
drivers/media/usb/usbtv/usbtv-core.c
drivers/media/usb/usbtv/usbtv-video.c
drivers/media/usb/usbtv/usbtv.h
drivers/media/usb/uvc/uvc_ctrl.c
drivers/media/usb/uvc/uvc_driver.c
drivers/media/usb/uvc/uvc_v4l2.c
drivers/media/usb/uvc/uvc_video.c
drivers/media/usb/uvc/uvcvideo.h
drivers/media/v4l2-core/tuner-core.c
drivers/media/v4l2-core/v4l2-common.c
drivers/media/v4l2-core/v4l2-compat-ioctl32.c
drivers/media/v4l2-core/v4l2-ctrls.c
drivers/media/v4l2-core/v4l2-dv-timings.c
drivers/media/v4l2-core/v4l2-ioctl.c
drivers/media/v4l2-core/v4l2-subdev.c
drivers/media/v4l2-core/videobuf-core.c
drivers/media/v4l2-core/videobuf-dma-sg.c
drivers/media/v4l2-core/videobuf2-core.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/asic3.c
drivers/mfd/htc-i2cpld.c
drivers/mfd/menf21bmc.c [new file with mode: 0644]
drivers/mfd/sm501.c
drivers/mfd/tc6393xb.c
drivers/mfd/ucb1x00-core.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/cxl/Kconfig [new file with mode: 0644]
drivers/misc/cxl/Makefile [new file with mode: 0644]
drivers/misc/cxl/base.c [new file with mode: 0644]
drivers/misc/cxl/context.c [new file with mode: 0644]
drivers/misc/cxl/cxl.h [new file with mode: 0644]
drivers/misc/cxl/debugfs.c [new file with mode: 0644]
drivers/misc/cxl/fault.c [new file with mode: 0644]
drivers/misc/cxl/file.c [new file with mode: 0644]
drivers/misc/cxl/irq.c [new file with mode: 0644]
drivers/misc/cxl/main.c [new file with mode: 0644]
drivers/misc/cxl/native.c [new file with mode: 0644]
drivers/misc/cxl/pci.c [new file with mode: 0644]
drivers/misc/cxl/sysfs.c [new file with mode: 0644]
drivers/misc/vmw_vmci/vmci_guest.c
drivers/mmc/card/block.c
drivers/mmc/card/queue.c
drivers/mmc/card/sdio_uart.c
drivers/mmc/core/core.c
drivers/mmc/core/host.c
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/mmc_ops.h
drivers/mmc/core/sd.c
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_bus.c
drivers/mmc/core/sdio_irq.c
drivers/mmc/core/slot-gpio.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/au1xmmc.c
drivers/mmc/host/dw_mmc-pci.c
drivers/mmc/host/dw_mmc-pltfm.c
drivers/mmc/host/dw_mmc-rockchip.c [new file with mode: 0644]
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/dw_mmc.h
drivers/mmc/host/jz4740_mmc.c
drivers/mmc/host/mmc_spi.c
drivers/mmc/host/mmci.c
drivers/mmc/host/mmci_qcom_dml.c [new file with mode: 0644]
drivers/mmc/host/mmci_qcom_dml.h [new file with mode: 0644]
drivers/mmc/host/moxart-mmc.c
drivers/mmc/host/mxcmmc.c
drivers/mmc/host/mxs-mmc.c
drivers/mmc/host/omap.c
drivers/mmc/host/omap_hsmmc.c
drivers/mmc/host/pxamci.c
drivers/mmc/host/rtsx_pci_sdmmc.c
drivers/mmc/host/rtsx_usb_sdmmc.c
drivers/mmc/host/s3cmci.c
drivers/mmc/host/sdhci-acpi.c
drivers/mmc/host/sdhci-bcm-kona.c
drivers/mmc/host/sdhci-bcm2835.c
drivers/mmc/host/sdhci-cns3xxx.c
drivers/mmc/host/sdhci-dove.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-msm.c
drivers/mmc/host/sdhci-of-arasan.c
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mmc/host/sdhci-of-hlwd.c
drivers/mmc/host/sdhci-pci.c
drivers/mmc/host/sdhci-pci.h
drivers/mmc/host/sdhci-pltfm.c
drivers/mmc/host/sdhci-pxav2.c
drivers/mmc/host/sdhci-pxav3.c
drivers/mmc/host/sdhci-s3c.c
drivers/mmc/host/sdhci-sirf.c
drivers/mmc/host/sdhci-spear.c
drivers/mmc/host/sdhci-tegra.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mmc/host/sh_mmcif.c
drivers/mmc/host/sh_mobile_sdhi.c
drivers/mmc/host/sunxi-mmc.c
drivers/mmc/host/tifm_sd.c
drivers/mmc/host/tmio_mmc.c
drivers/mmc/host/tmio_mmc.h
drivers/mmc/host/tmio_mmc_dma.c
drivers/mmc/host/tmio_mmc_pio.c
drivers/mmc/host/wbsd.c
drivers/net/ethernet/sun/sunvnet.c
drivers/net/tun.c
drivers/net/vmxnet3/vmxnet3_int.h
drivers/net/xen-netback/xenbus.c
drivers/net/xen-netfront.c
drivers/of/Kconfig
drivers/of/Makefile
drivers/of/address.c
drivers/of/base.c
drivers/of/of_pci.c
drivers/of/resolver.c [new file with mode: 0644]
drivers/of/selftest.c
drivers/of/testcase-data/testcases.dts
drivers/pci/host/Kconfig
drivers/pci/host/Makefile
drivers/pci/host/pci-imx6.c
drivers/pci/host/pci-keystone-dw.c [new file with mode: 0644]
drivers/pci/host/pci-keystone.c [new file with mode: 0644]
drivers/pci/host/pci-keystone.h [new file with mode: 0644]
drivers/pci/host/pci-mvebu.c
drivers/pci/host/pci-tegra.c
drivers/pci/host/pci-xgene.c [new file with mode: 0644]
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-designware.h
drivers/pci/host/pcie-rcar.c
drivers/pci/host/pcie-spear13xx.c
drivers/pci/host/pcie-xilinx.c [new file with mode: 0644]
drivers/pci/hotplug/Makefile
drivers/pci/hotplug/acpi_pcihp.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/hotplug/acpiphp_ibm.c
drivers/pci/hotplug/cpci_hotplug_core.c
drivers/pci/hotplug/cpcihp_generic.c
drivers/pci/hotplug/cpcihp_zt5550.c
drivers/pci/hotplug/cpqphp.h
drivers/pci/hotplug/cpqphp_core.c
drivers/pci/hotplug/cpqphp_ctrl.c
drivers/pci/hotplug/cpqphp_nvram.c
drivers/pci/hotplug/ibmphp_core.c
drivers/pci/hotplug/ibmphp_ebda.c
drivers/pci/hotplug/ibmphp_hpc.c
drivers/pci/hotplug/ibmphp_pci.c
drivers/pci/hotplug/ibmphp_res.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/hotplug/pciehp_pci.c
drivers/pci/hotplug/pcihp_slot.c [deleted file]
drivers/pci/hotplug/shpchp_ctrl.c
drivers/pci/hotplug/shpchp_hpc.c
drivers/pci/hotplug/shpchp_pci.c
drivers/pci/iov.c
drivers/pci/msi.c
drivers/pci/pci-acpi.c
drivers/pci/pci-driver.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.c
drivers/pci/pcie/aer/aerdrv_errprint.c
drivers/pci/pcie/pme.c
drivers/pci/pcie/portdrv_pci.c
drivers/pci/probe.c
drivers/pci/quirks.c
drivers/pci/search.c
drivers/pci/setup-bus.c
drivers/pci/xen-pcifront.c
drivers/pinctrl/Kconfig
drivers/pinctrl/nomadik/pinctrl-abx500.c
drivers/pinctrl/nomadik/pinctrl-nomadik.c
drivers/pinctrl/qcom/pinctrl-msm.c
drivers/pinctrl/samsung/pinctrl-exynos5440.c
drivers/pinctrl/samsung/pinctrl-samsung.c
drivers/pinctrl/sirf/pinctrl-sirf.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/intel_pmic_gpio.c
drivers/power/avs/Kconfig
drivers/power/avs/Makefile
drivers/power/avs/rockchip-io-domain.c [new file with mode: 0644]
drivers/power/reset/restart-poweroff.c
drivers/scsi/Kconfig
drivers/scsi/Makefile
drivers/scsi/vmw_pvscsi.h
drivers/scsi/xen-scsifront.c [new file with mode: 0644]
drivers/sh/pm_runtime.c
drivers/spi/spi.c
drivers/ssb/driver_gpio.c
drivers/staging/media/Kconfig
drivers/staging/media/Makefile
drivers/staging/media/as102/as102_fe.c [deleted file]
drivers/staging/media/davinci_vpfe/Kconfig
drivers/staging/media/dt3155v4l/Kconfig
drivers/staging/media/lirc/lirc_imon.c
drivers/staging/media/lirc/lirc_sasem.c
drivers/staging/media/omap4iss/Kconfig
drivers/staging/vme/devices/vme_pio2_gpio.c
drivers/target/target_core_tpg.c
drivers/tty/hvc/hvc_vio.c
drivers/tty/hvc/hvc_xen.c
drivers/tty/serial/max310x.c
drivers/tty/serial/sc16is7xx.c
drivers/tty/tty_io.c
drivers/vfio/pci/vfio_pci.c
drivers/vfio/pci/vfio_pci_config.c
drivers/vfio/pci/vfio_pci_intrs.c
drivers/vfio/vfio_iommu_type1.c
drivers/vfio/vfio_spapr_eeh.c
drivers/video/backlight/88pm860x_bl.c
drivers/video/backlight/aat2870_bl.c
drivers/video/backlight/adp5520_bl.c
drivers/video/backlight/adp8860_bl.c
drivers/video/backlight/adp8870_bl.c
drivers/video/backlight/ams369fg06.c
drivers/video/backlight/as3711_bl.c
drivers/video/backlight/corgi_lcd.c
drivers/video/backlight/cr_bllcd.c
drivers/video/backlight/da903x_bl.c
drivers/video/backlight/da9052_bl.c
drivers/video/backlight/ep93xx_bl.c
drivers/video/backlight/generic_bl.c
drivers/video/backlight/gpio_backlight.c
drivers/video/backlight/ili922x.c
drivers/video/backlight/jornada720_bl.c
drivers/video/backlight/jornada720_lcd.c
drivers/video/backlight/ld9040.c
drivers/video/backlight/lm3533_bl.c
drivers/video/backlight/lm3639_bl.c
drivers/video/backlight/lms501kf03.c
drivers/video/backlight/lp855x_bl.c
drivers/video/backlight/lp8788_bl.c
drivers/video/backlight/max8925_bl.c
drivers/video/backlight/omap1_bl.c
drivers/video/backlight/ot200_bl.c
drivers/video/backlight/pandora_bl.c
drivers/video/backlight/pcf50633-backlight.c
drivers/video/backlight/platform_lcd.c
drivers/video/backlight/pwm_bl.c
drivers/video/backlight/s6e63m0.c
drivers/video/backlight/tdo24m.c
drivers/video/backlight/tps65217_bl.c
drivers/video/backlight/wm831x_bl.c
drivers/video/fbdev/via/via-gpio.c
drivers/video/fbdev/xen-fbfront.c
drivers/virtio/Kconfig
drivers/virtio/virtio_balloon.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/alim7101_wdt.c
drivers/watchdog/menf21bmc_wdt.c [new file with mode: 0644]
drivers/watchdog/moxart_wdt.c
drivers/watchdog/sunxi_wdt.c
drivers/xen/Kconfig
drivers/xen/Makefile
drivers/xen/efi.c
drivers/xen/events/events_base.c
drivers/xen/grant-table.c
drivers/xen/xen-pciback/pci_stub.c
drivers/xen/xen-pciback/xenbus.c
drivers/xen/xen-scsiback.c [new file with mode: 0644]
drivers/xen/xenbus/xenbus_client.c
drivers/xen/xenbus/xenbus_probe.c
drivers/xen/xenbus/xenbus_probe.h
drivers/xen/xenbus/xenbus_probe_backend.c
drivers/xen/xenbus/xenbus_probe_frontend.c
fs/aio.c
fs/block_dev.c
fs/btrfs/async-thread.c
fs/btrfs/async-thread.h
fs/btrfs/backref.c
fs/btrfs/backref.h
fs/btrfs/btrfs_inode.h
fs/btrfs/check-integrity.c
fs/btrfs/compression.c
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/delayed-inode.c
fs/btrfs/dev-replace.c
fs/btrfs/dir-item.c
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/export.c
fs/btrfs/extent-tree.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/file-item.c
fs/btrfs/file.c
fs/btrfs/free-space-cache.c
fs/btrfs/hash.c
fs/btrfs/inode-item.c
fs/btrfs/inode-map.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/lzo.c
fs/btrfs/orphan.c
fs/btrfs/print-tree.c
fs/btrfs/qgroup.c
fs/btrfs/raid56.c
fs/btrfs/reada.c
fs/btrfs/relocation.c
fs/btrfs/scrub.c
fs/btrfs/send.c
fs/btrfs/super.c
fs/btrfs/sysfs.c
fs/btrfs/sysfs.h
fs/btrfs/tests/free-space-tests.c
fs/btrfs/transaction.c
fs/btrfs/transaction.h
fs/btrfs/tree-log.c
fs/btrfs/tree-log.h
fs/btrfs/uuid-tree.c
fs/btrfs/volumes.c
fs/btrfs/volumes.h
fs/btrfs/xattr.c
fs/btrfs/zlib.c
fs/buffer.c
fs/cifs/cifsfs.c
fs/dlm/plock.c
fs/ecryptfs/file.c
fs/ecryptfs/inode.c
fs/ecryptfs/keystore.c
fs/ecryptfs/messaging.c
fs/ext2/super.c
fs/ext3/ext3.h
fs/ext3/super.c
fs/ext4/super.c
fs/fcntl.c
fs/file_table.c
fs/gfs2/dir.c
fs/gfs2/dir.h
fs/gfs2/file.c
fs/gfs2/glock.c
fs/gfs2/glops.c
fs/gfs2/inode.c
fs/gfs2/rgrp.c
fs/gfs2/rgrp.h
fs/gfs2/trans.c
fs/internal.h
fs/libfs.c
fs/lockd/svclock.c
fs/locks.c
fs/mpage.c
fs/nfs/file.c
fs/nfs/internal.h
fs/nfs/nfs4file.c
fs/nfsd/nfs4state.c
fs/nfsd/state.h
fs/notify/dnotify/dnotify.c
fs/notify/fanotify/fanotify_user.c
fs/notify/fsnotify.h
fs/notify/group.c
fs/notify/inotify/inotify_fsnotify.c
fs/ntfs/debug.c
fs/ntfs/file.c
fs/ntfs/super.c
fs/ocfs2/aops.c
fs/ocfs2/cluster/heartbeat.c
fs/ocfs2/cluster/heartbeat.h
fs/ocfs2/cluster/netdebug.c
fs/ocfs2/cluster/tcp.c
fs/ocfs2/dlm/dlmdebug.c
fs/ocfs2/dlm/dlmdomain.c
fs/ocfs2/dlm/dlmmaster.c
fs/ocfs2/dlm/dlmrecovery.c
fs/ocfs2/dlmglue.c
fs/ocfs2/file.c
fs/ocfs2/inode.h
fs/ocfs2/move_extents.c
fs/ocfs2/quota.h
fs/ocfs2/quota_global.c
fs/ocfs2/quota_local.c
fs/ocfs2/stack_user.c
fs/ocfs2/super.c
fs/proc/base.c
fs/proc/internal.h
fs/proc/kcore.c
fs/proc/page.c
fs/proc/task_mmu.c
fs/proc/task_nommu.c
fs/quota/dquot.c
fs/reiserfs/reiserfs.h
fs/reiserfs/super.c
fs/super.c
fs/timerfd.c
fs/udf/file.c
fs/udf/inode.c
fs/udf/super.c
fs/udf/udfdecl.h
fs/udf/udftime.c
include/acpi/acnames.h
include/acpi/acpixf.h
include/acpi/actbl1.h
include/acpi/actbl3.h
include/asm-generic/dma-mapping-common.h
include/asm-generic/gpio.h
include/asm-generic/io.h
include/asm-generic/irq_work.h [new file with mode: 0644]
include/asm-generic/pgtable.h
include/asm-generic/sections.h
include/dt-bindings/sound/cs35l32.h [new file with mode: 0644]
include/linux/acpi.h
include/linux/aer.h
include/linux/ahci_platform.h
include/linux/ata_platform.h
include/linux/atmel-mci.h
include/linux/balloon_compaction.h
include/linux/blk-mq.h
include/linux/blkdev.h
include/linux/cgroup.h
include/linux/compaction.h
include/linux/cpufreq.h
include/linux/cpuset.h
include/linux/dma-mapping.h
include/linux/flex_proportions.h
include/linux/fs.h
include/linux/fsl_ifc.h
include/linux/genalloc.h
include/linux/gfp.h
include/linux/gpio/driver.h
include/linux/huge_mm.h
include/linux/interrupt.h
include/linux/iommu.h
include/linux/ioport.h
include/linux/irq.h
include/linux/irq_work.h
include/linux/irqchip/arm-gic.h
include/linux/irqdesc.h
include/linux/kernel.h
include/linux/libata.h
include/linux/lockd/lockd.h
include/linux/memcontrol.h
include/linux/memory_hotplug.h
include/linux/mempolicy.h
include/linux/mfd/tmio.h
include/linux/migrate.h
include/linux/mm.h
include/linux/mmc/card.h
include/linux/mmc/dw_mmc.h
include/linux/mmc/host.h
include/linux/mmc/mmc.h
include/linux/mmc/sdhci.h
include/linux/mmc/slot-gpio.h
include/linux/mmdebug.h
include/linux/mmzone.h
include/linux/msi.h
include/linux/of.h
include/linux/of_address.h
include/linux/of_pci.h
include/linux/pagemap.h
include/linux/pci.h
include/linux/pci_hotplug.h
include/linux/pci_ids.h
include/linux/percpu-refcount.h
include/linux/percpu.h
include/linux/percpu_counter.h
include/linux/platform_data/gpio-dwapb.h [new file with mode: 0644]
include/linux/pm.h
include/linux/pm_domain.h
include/linux/proportions.h
include/linux/reboot.h
include/linux/rmap.h
include/linux/sched.h
include/linux/screen_info.h
include/linux/security.h
include/linux/slab.h
include/linux/slab_def.h
include/linux/spi/mcp23s08.h
include/linux/suspend.h
include/linux/swap.h
include/linux/tick.h
include/linux/topology.h
include/linux/vm_event_item.h
include/linux/zsmalloc.h
include/media/davinci/dm644x_ccdc.h
include/media/omap3isp.h
include/media/rc-map.h
include/media/videobuf2-core.h
include/misc/cxl.h [new file with mode: 0644]
include/net/dst_ops.h
include/net/inet_frag.h
include/ras/ras_event.h
include/sound/pcm.h
include/sound/rt5645.h
include/sound/rt5677.h
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/vx_core.h
include/trace/events/asoc.h
include/trace/events/btrfs.h
include/trace/events/filelock.h
include/uapi/Kbuild
include/uapi/linux/Kbuild
include/uapi/linux/kernel-page-flags.h
include/uapi/linux/pci_regs.h
include/uapi/linux/prctl.h
include/uapi/linux/smiapp.h [new file with mode: 0644]
include/uapi/linux/v4l2-controls.h
include/uapi/linux/v4l2-dv-timings.h
include/uapi/linux/vfio.h
include/uapi/linux/videodev2.h
include/uapi/misc/Kbuild [new file with mode: 0644]
include/uapi/misc/cxl.h [new file with mode: 0644]
include/uapi/sound/asound.h
include/xen/events.h
include/xen/interface/elfnote.h
include/xen/interface/io/vscsiif.h [new file with mode: 0644]
include/xen/interface/xen.h
include/xen/xenbus.h
init/Kconfig
init/main.c
kernel/acct.c
kernel/async.c
kernel/cgroup.c
kernel/cpuset.c
kernel/events/core.c
kernel/fork.c
kernel/irq/Kconfig
kernel/irq/chip.c
kernel/irq/internals.h
kernel/irq/irqdesc.c
kernel/irq/manage.c
kernel/irq/pm.c
kernel/irq_work.c
kernel/kthread.c
kernel/power/Kconfig
kernel/power/process.c
kernel/power/snapshot.c
kernel/power/suspend.c
kernel/power/suspend_test.c
kernel/reboot.c
kernel/resource.c
kernel/sched/fair.c
kernel/sys.c
kernel/sysctl.c
kernel/time/tick-common.c
kernel/time/tick-internal.h
kernel/time/tick-sched.c
kernel/time/timer.c
kernel/watchdog.c
lib/flex_proportions.c
lib/genalloc.c
lib/percpu-refcount.c
lib/percpu_counter.c
lib/proportions.c
mm/Kconfig
mm/Makefile
mm/backing-dev.c
mm/balloon_compaction.c
mm/bootmem.c
mm/cma.c
mm/compaction.c
mm/debug.c [new file with mode: 0644]
mm/dmapool.c
mm/filemap.c
mm/gup.c
mm/huge_memory.c
mm/hugetlb.c
mm/internal.h
mm/interval_tree.c
mm/kmemcheck.c
mm/ksm.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory_hotplug.c
mm/mempolicy.c
mm/migrate.c
mm/mlock.c
mm/mmap.c
mm/mremap.c
mm/nommu.c
mm/oom_kill.c
mm/page-writeback.c
mm/page_alloc.c
mm/pagewalk.c
mm/percpu-km.c
mm/percpu-vm.c
mm/percpu.c
mm/rmap.c
mm/shmem.c
mm/slab.c
mm/slab.h
mm/slab_common.c
mm/slob.c
mm/slub.c
mm/swap.c
mm/swap_state.c
mm/util.c
mm/vmalloc.c
mm/vmscan.c
mm/vmstat.c
mm/zbud.c
mm/zsmalloc.c
net/dccp/proto.c
net/ipv4/tcp.c
net/ipv4/tcp_memcontrol.c
net/sctp/protocol.c
net/socket.c
security/capability.c
security/security.c
security/selinux/hooks.c
security/smack/smack_lsm.c
sound/core/misc.c
sound/core/pcm.c
sound/core/pcm_lib.c
sound/core/pcm_misc.c
sound/core/pcm_native.c
sound/drivers/vx/vx_core.c
sound/drivers/vx/vx_mixer.c
sound/drivers/vx/vx_pcm.c
sound/drivers/vx/vx_uer.c
sound/pci/au88x0/au88x0.c
sound/pci/au88x0/au88x0_a3d.c
sound/pci/au88x0/au88x0_core.c
sound/pci/au88x0/au88x0_eq.c
sound/pci/au88x0/au88x0_game.c
sound/pci/au88x0/au88x0_mpu401.c
sound/pci/au88x0/au88x0_pcm.c
sound/pci/au88x0/au88x0_synth.c
sound/pci/ctxfi/ctamixer.c
sound/pci/ctxfi/ctamixer.h
sound/pci/ctxfi/ctatc.c
sound/pci/ctxfi/ctatc.h
sound/pci/ctxfi/ctdaio.c
sound/pci/ctxfi/ctdaio.h
sound/pci/ctxfi/cthardware.h
sound/pci/ctxfi/cthw20k1.c
sound/pci/ctxfi/cthw20k2.c
sound/pci/ctxfi/ctmixer.c
sound/pci/ctxfi/ctpcm.c
sound/pci/ctxfi/ctresource.c
sound/pci/ctxfi/ctresource.h
sound/pci/ctxfi/ctsrc.c
sound/pci/ctxfi/ctsrc.h
sound/pci/ctxfi/ctvmem.c
sound/pci/ctxfi/xfi.c
sound/pci/hda/hda_auto_parser.c
sound/pci/hda/hda_auto_parser.h
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_generic.h
sound/pci/hda/hda_jack.c
sound/pci/hda/hda_jack.h
sound/pci/hda/hda_local.h
sound/pci/hda/hda_sysfs.c
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/ice1712/ice1712.c
sound/pci/lx6464es/lx6464es.c
sound/pci/lx6464es/lx6464es.h
sound/pci/lx6464es/lx_core.c
sound/pci/lx6464es/lx_core.h
sound/pci/mixart/mixart.c
sound/pci/mixart/mixart.h
sound/pci/mixart/mixart_core.c
sound/pci/mixart/mixart_core.h
sound/pci/oxygen/oxygen_pcm.c
sound/pci/oxygen/virtuoso.c
sound/pci/oxygen/xonar_pcm179x.c
sound/pci/pcxhr/pcxhr.c
sound/pci/pcxhr/pcxhr.h
sound/pci/pcxhr/pcxhr_core.c
sound/pci/pcxhr/pcxhr_core.h
sound/pci/vx222/vx222.c
sound/pcmcia/pdaudiocf/pdaudiocf.c
sound/pcmcia/pdaudiocf/pdaudiocf.h
sound/pcmcia/pdaudiocf/pdaudiocf_core.c
sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
sound/pcmcia/vx/vxp_ops.c
sound/pcmcia/vx/vxpocket.c
sound/soc/codecs/88pm860x-codec.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ab8500-codec.c
sound/soc/codecs/ac97.c
sound/soc/codecs/adau1373.c
sound/soc/codecs/adau1761.c
sound/soc/codecs/adau1781.c
sound/soc/codecs/adau17x1.c
sound/soc/codecs/adau17x1.h
sound/soc/codecs/adav80x.c
sound/soc/codecs/cs35l32.c [new file with mode: 0644]
sound/soc/codecs/cs35l32.h [new file with mode: 0644]
sound/soc/codecs/cs4265.c
sound/soc/codecs/cs42l52.c
sound/soc/codecs/cs42l56.c
sound/soc/codecs/cs42l73.c
sound/soc/codecs/da732x.c
sound/soc/codecs/es8328-i2c.c [new file with mode: 0644]
sound/soc/codecs/es8328-spi.c [new file with mode: 0644]
sound/soc/codecs/es8328.c [new file with mode: 0644]
sound/soc/codecs/es8328.h [new file with mode: 0644]
sound/soc/codecs/jz4740.c
sound/soc/codecs/lm49453.c
sound/soc/codecs/max98090.c
sound/soc/codecs/max98090.h
sound/soc/codecs/mc13783.c
sound/soc/codecs/ml26124.c
sound/soc/codecs/rt286.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5640.h
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5645.h
sound/soc/codecs/rt5677.c
sound/soc/codecs/rt5677.h
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/ssm2518.c
sound/soc/codecs/ssm2602-i2c.c
sound/soc/codecs/ssm2602-spi.c
sound/soc/codecs/ssm2602.c
sound/soc/codecs/ssm4567.c [new file with mode: 0644]
sound/soc/codecs/tas2552.c
sound/soc/codecs/tlv320aic31xx.c
sound/soc/codecs/tlv320aic31xx.h
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8741.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8804.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8971.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8995.c
sound/soc/codecs/wm8996.c
sound/soc/davinci/Kconfig
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/edma-pcm.c
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/fsl-asoc-card.c [new file with mode: 0644]
sound/soc/fsl/fsl_asrc.c
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_esai.h
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_sai.h
sound/soc/fsl/fsl_spdif.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-es8328.c [new file with mode: 0644]
sound/soc/generic/simple-card.c
sound/soc/intel/Makefile
sound/soc/intel/byt-max98090.c
sound/soc/intel/byt-rt5640.c
sound/soc/intel/sst-atom-controls.c [new file with mode: 0644]
sound/soc/intel/sst-atom-controls.h
sound/soc/intel/sst-haswell-pcm.c
sound/soc/intel/sst-mfld-platform-compress.c
sound/soc/intel/sst-mfld-platform-pcm.c
sound/soc/intel/sst-mfld-platform.h
sound/soc/omap/rx51.c
sound/soc/rockchip/Kconfig
sound/soc/rockchip/Makefile
sound/soc/rockchip/rockchip_i2s.c
sound/soc/samsung/idma.c
sound/soc/samsung/odroidx2_max98090.c
sound/soc/samsung/speyside.c
sound/soc/sh/fsi.c
sound/soc/sh/rcar/core.c
sound/soc/sh/siu_pcm.c
sound/soc/sirf/sirf-usp.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-generic-dmaengine-pcm.c
sound/soc/soc-io.c
sound/soc/soc-pcm.c
sound/soc/tegra/tegra_max98090.c
sound/soc/txx9/txx9aclc.c
sound/usb/caiaq/audio.c
sound/usb/midi.c
sound/usb/quirks.c
tools/testing/selftests/powerpc/Makefile
tools/testing/selftests/powerpc/primitives/Makefile [new file with mode: 0644]
tools/testing/selftests/powerpc/primitives/asm/asm-compat.h [new symlink]
tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h [new file with mode: 0644]
tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c [new file with mode: 0644]
tools/testing/selftests/powerpc/primitives/word-at-a-time.h [new symlink]
tools/testing/selftests/vm/Makefile
tools/testing/selftests/vm/transhuge-stress.c [new file with mode: 0644]
tools/vm/page-types.c
virt/kvm/assigned-dev.c
virt/kvm/iommu.c

index ce259c13c36a4e716ef6cac9e8436b8c22645088..5b2d0f08867cd899df072f89a059995944fb8eec 100644 (file)
@@ -85,14 +85,6 @@ Description:
                will be compacted. When it completes, memory will be freed
                into blocks which have as many contiguous pages as possible
 
-What:          /sys/devices/system/node/nodeX/scan_unevictable_pages
-Date:          October 2008
-Contact:       Lee Schermerhorn <lee.schermerhorn@hp.com>
-Description:
-               When set, it triggers scanning the node's unevictable lists
-               and move any pages that have become evictable onto the respective
-               zone's inactive list. See mm/vmscan.c
-
 What:          /sys/devices/system/node/nodeX/hugepages/hugepages-<size>/
 Date:          December 2009
 Contact:       Lee Schermerhorn <lee.schermerhorn@hp.com>
index 70ec992514d03e91e7f5b1644929c5e195ff8d13..a6148eaf91e5f32f43b8448a7ec0ee16677c83eb 100644 (file)
@@ -77,11 +77,14 @@ What:               /sys/block/zram<id>/notify_free
 Date:          August 2010
 Contact:       Nitin Gupta <ngupta@vflare.org>
 Description:
-               The notify_free file is read-only and specifies the number of
-               swap slot free notifications received by this device. These
-               notifications are sent to a swap block device when a swap slot
-               is freed. This statistic is applicable only when this disk is
-               being used as a swap disk.
+               The notify_free file is read-only. Depending on device usage
+               scenario it may account a) the number of pages freed because
+               of swap slot free notifications or b) the number of pages freed
+               because of REQ_DISCARD requests sent by bio. The former ones
+               are sent to a swap block device when a swap slot is freed, which
+               implies that this disk is being used as a swap disk. The latter
+               ones are sent by filesystem mounted with discard option,
+               whenever some data blocks are getting discarded.
 
 What:          /sys/block/zram<id>/zero_pages
 Date:          August 2010
@@ -119,3 +122,22 @@ Description:
                efficiency can be calculated using compr_data_size and this
                statistic.
                Unit: bytes
+
+What:          /sys/block/zram<id>/mem_used_max
+Date:          August 2014
+Contact:       Minchan Kim <minchan@kernel.org>
+Description:
+               The mem_used_max file is read/write and specifies the amount
+               of maximum memory zram have consumed to store compressed data.
+               For resetting the value, you should write "0". Otherwise,
+               you could see -EINVAL.
+               Unit: bytes
+
+What:          /sys/block/zram<id>/mem_limit
+Date:          August 2014
+Contact:       Minchan Kim <minchan@kernel.org>
+Description:
+               The mem_limit file is read/write and specifies the maximum
+               amount of memory ZRAM can use to store the compressed data.  The
+               limit could be changed in run time and "0" means disable the
+               limit.  No limit is the initial state.  Unit: bytes
index e78ee798d7bd2c1fb7e0fe7aff56683370ecaf05..32f3f5f8bba21100d0d59a70da25501bfe62c619 100644 (file)
@@ -1,6 +1,6 @@
 What:          /sys/bus/event_source/devices/hv_24x7/interface/catalog
 Date:          February 2014
-Contact:       Cody P Schafer <cody@linux.vnet.ibm.com>
+Contact:       Linux on PowerPC Developer List <linuxppc-dev@lists.ozlabs.org>
 Description:
                Provides access to the binary "24x7 catalog" provided by the
                hypervisor on POWER7 and 8 systems. This catalog lists events
@@ -10,14 +10,14 @@ Description:
 
 What:          /sys/bus/event_source/devices/hv_24x7/interface/catalog_length
 Date:          February 2014
-Contact:       Cody P Schafer <cody@linux.vnet.ibm.com>
+Contact:       Linux on PowerPC Developer List <linuxppc-dev@lists.ozlabs.org>
 Description:
                A number equal to the length in bytes of the catalog. This is
                also extractable from the provided binary "catalog" sysfs entry.
 
 What:          /sys/bus/event_source/devices/hv_24x7/interface/catalog_version
 Date:          February 2014
-Contact:       Cody P Schafer <cody@linux.vnet.ibm.com>
+Contact:       Linux on PowerPC Developer List <linuxppc-dev@lists.ozlabs.org>
 Description:
                Exposes the "version" field of the 24x7 catalog. This is also
                extractable from the provided binary "catalog" sysfs entry.
index 3fa58c23f13b2083ad84f47c0452a1717fef7f5f..3ca4e554d2f92dc43f4d7c2cd57c24ba22ffb93a 100644 (file)
@@ -1,6 +1,6 @@
 What:          /sys/bus/event_source/devices/hv_gpci/interface/collect_privileged
 Date:          February 2014
-Contact:       Cody P Schafer <cody@linux.vnet.ibm.com>
+Contact:       Linux on PowerPC Developer List <linuxppc-dev@lists.ozlabs.org>
 Description:
                '0' if the hypervisor is configured to forbid access to event
                counters being accumulated by other guests and to physical
@@ -9,35 +9,35 @@ Description:
 
 What:          /sys/bus/event_source/devices/hv_gpci/interface/ga
 Date:          February 2014
-Contact:       Cody P Schafer <cody@linux.vnet.ibm.com>
+Contact:       Linux on PowerPC Developer List <linuxppc-dev@lists.ozlabs.org>
 Description:
                0 or 1. Indicates whether we have access to "GA" events (listed
                in arch/powerpc/perf/hv-gpci.h).
 
 What:          /sys/bus/event_source/devices/hv_gpci/interface/expanded
 Date:          February 2014
-Contact:       Cody P Schafer <cody@linux.vnet.ibm.com>
+Contact:       Linux on PowerPC Developer List <linuxppc-dev@lists.ozlabs.org>
 Description:
                0 or 1. Indicates whether we have access to "EXPANDED" events (listed
                in arch/powerpc/perf/hv-gpci.h).
 
 What:          /sys/bus/event_source/devices/hv_gpci/interface/lab
 Date:          February 2014
-Contact:       Cody P Schafer <cody@linux.vnet.ibm.com>
+Contact:       Linux on PowerPC Developer List <linuxppc-dev@lists.ozlabs.org>
 Description:
                0 or 1. Indicates whether we have access to "LAB" events (listed
                in arch/powerpc/perf/hv-gpci.h).
 
 What:          /sys/bus/event_source/devices/hv_gpci/interface/version
 Date:          February 2014
-Contact:       Cody P Schafer <cody@linux.vnet.ibm.com>
+Contact:       Linux on PowerPC Developer List <linuxppc-dev@lists.ozlabs.org>
 Description:
                A number indicating the version of the gpci interface that the
                hypervisor reports supporting.
 
 What:          /sys/bus/event_source/devices/hv_gpci/interface/kernel_version
 Date:          February 2014
-Contact:       Cody P Schafer <cody@linux.vnet.ibm.com>
+Contact:       Linux on PowerPC Developer List <linuxppc-dev@lists.ozlabs.org>
 Description:
                A number indicating the latest version of the gpci interface
                that the kernel is aware of.
index 6615fda0abfb15d2c198f17fdeb31e1df8b84888..ee6c040364927253020a4272e24efaa132c1bff2 100644 (file)
@@ -65,6 +65,16 @@ Description:
                force a rescan of all PCI buses in the system, and
                re-discover previously removed devices.
 
+What:          /sys/bus/pci/devices/.../msi_bus
+Date:          September 2014
+Contact:       Linux PCI developers <linux-pci@vger.kernel.org>
+Description:
+               Writing a zero value to this attribute disallows MSI and
+               MSI-X for any future drivers of the device.  If the device
+               is a bridge, MSI and MSI-X will be disallowed for future
+               drivers of all child devices under the bridge.  Drivers
+               must be reloaded for the new setting to take effect.
+
 What:          /sys/bus/pci/devices/.../msi_irqs/
 Date:          September, 2011
 Contact:       Neil Horman <nhorman@tuxdriver.com>
diff --git a/Documentation/ABI/testing/sysfs-class-cxl b/Documentation/ABI/testing/sysfs-class-cxl
new file mode 100644 (file)
index 0000000..554405e
--- /dev/null
@@ -0,0 +1,129 @@
+Slave contexts (eg. /sys/class/cxl/afu0.0s):
+
+What:           /sys/class/cxl/<afu>/irqs_max
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read/write
+                Decimal value of maximum number of interrupts that can be
+                requested by userspace.  The default on probe is the maximum
+                that hardware can support (eg. 2037). Write values will limit
+                userspace applications to that many userspace interrupts. Must
+                be >= irqs_min.
+
+What:           /sys/class/cxl/<afu>/irqs_min
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Decimal value of the minimum number of interrupts that
+                userspace must request on a CXL_START_WORK ioctl. Userspace may
+                omit the num_interrupts field in the START_WORK IOCTL to get
+                this minimum automatically.
+
+What:           /sys/class/cxl/<afu>/mmio_size
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Decimal value of the size of the MMIO space that may be mmaped
+                by userspace.
+
+What:           /sys/class/cxl/<afu>/modes_supported
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                List of the modes this AFU supports. One per line.
+                Valid entries are: "dedicated_process" and "afu_directed"
+
+What:           /sys/class/cxl/<afu>/mode
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read/write
+                The current mode the AFU is using. Will be one of the modes
+                given in modes_supported. Writing will change the mode
+                provided that no user contexts are attached.
+
+
+What:           /sys/class/cxl/<afu>/prefault_mode
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read/write
+                Set the mode for prefaulting in segments into the segment table
+                when performing the START_WORK ioctl. Possible values:
+                        none: No prefaulting (default)
+                        work_element_descriptor: Treat the work element
+                                 descriptor as an effective address and
+                                 prefault what it points to.
+                        all: all segments process calling START_WORK maps.
+
+What:           /sys/class/cxl/<afu>/reset
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    write only
+                Writing 1 here will reset the AFU provided there are not
+                contexts active on the AFU.
+
+What:           /sys/class/cxl/<afu>/api_version
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Decimal value of the current version of the kernel/user API.
+
+What:           /sys/class/cxl/<afu>/api_version_com
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Decimal value of the the lowest version of the userspace API
+                this this kernel supports.
+
+
+
+Master contexts (eg. /sys/class/cxl/afu0.0m)
+
+What:           /sys/class/cxl/<afu>m/mmio_size
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Decimal value of the size of the MMIO space that may be mmaped
+                by userspace. This includes all slave contexts space also.
+
+What:           /sys/class/cxl/<afu>m/pp_mmio_len
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Decimal value of the Per Process MMIO space length.
+
+What:           /sys/class/cxl/<afu>m/pp_mmio_off
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Decimal value of the Per Process MMIO space offset.
+
+
+Card info (eg. /sys/class/cxl/card0)
+
+What:           /sys/class/cxl/<card>/caia_version
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Identifies the CAIA Version the card implements.
+
+What:           /sys/class/cxl/<card>/psl_version
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Identifies the revision level of the PSL.
+
+What:           /sys/class/cxl/<card>/base_image
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Identifies the revision level of the base image for devices
+                that support loadable PSLs. For FPGAs this field identifies
+                the image contained in the on-adapter flash which is loaded
+                during the initial program load.
+
+What:           /sys/class/cxl/<card>/image_loaded
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Will return "user" or "factory" depending on the image loaded
+                onto the card.
index 7405de26ee60fef64e7b30db1be5dea7ea17d03c..deef3b5723cf2685925a791e20bc97027142b5df 100644 (file)
@@ -61,6 +61,14 @@ Users:               hotplug memory remove tools
                http://www.ibm.com/developerworks/wikis/display/LinuxP/powerpc-utils
 
 
+What:           /sys/devices/system/memory/memoryX/valid_zones
+Date:           July 2014
+Contact:       Zhang Zhen <zhenzhang.zhang@huawei.com>
+Description:
+               The file /sys/devices/system/memory/memoryX/valid_zones is
+               read-only and is designed to show which zone this memory
+               block can be onlined to.
+
 What:          /sys/devices/system/memoryX/nodeY
 Date:          October 2009
 Contact:       Linux Memory Management list <linux-mm@kvack.org>
index 3a626d1b8f2e4d8022831f0961fbb83bc359b487..07ffc76553ba098490cbba874f6f513f8981222e 100644 (file)
@@ -2566,6 +2566,12 @@ fields changed from _s32 to _u32.
          <para>Added compound control types and &VIDIOC-QUERY-EXT-CTRL;.
          </para>
         </listitem>
+      <title>V4L2 in Linux 3.18</title>
+      <orderedlist>
+       <listitem>
+         <para>Added <constant>V4L2_CID_PAN_SPEED</constant> and
+ <constant>V4L2_CID_TILT_SPEED</constant> camera controls.</para>
+       </listitem>
       </orderedlist>
     </section>
 
index 9f5ffd85560b58b3032867b57ec095153e7bfd07..e013e4bf244c509fef7477cd8ae182bcded29693 100644 (file)
@@ -3965,6 +3965,27 @@ by exposure, white balance or focus controls.</entry>
          </row>
          <row><entry></entry></row>
 
+         <row>
+           <entry spanname="id"><constant>V4L2_CID_PAN_SPEED</constant>&nbsp;</entry>
+           <entry>integer</entry>
+         </row><row><entry spanname="descr">This control turns the
+camera horizontally at the specific speed. The unit is undefined. A
+positive value moves the camera to the right (clockwise when viewed
+from above), a negative value to the left. A value of zero stops the motion
+if one is in progress and has no effect otherwise.</entry>
+         </row>
+         <row><entry></entry></row>
+
+         <row>
+           <entry spanname="id"><constant>V4L2_CID_TILT_SPEED</constant>&nbsp;</entry>
+           <entry>integer</entry>
+         </row><row><entry spanname="descr">This control turns the
+camera vertically at the specified speed. The unit is undefined. A
+positive value moves the camera up, a negative value down. A value of zero
+stops the motion if one is in progress and has no effect otherwise.</entry>
+         </row>
+         <row><entry></entry></row>
+
        </tbody>
       </tgroup>
     </table>
@@ -4790,6 +4811,40 @@ interface and may change in the future.</para>
            conversion.
            </entry>
          </row>
+         <row>
+           <entry spanname="id"><constant>V4L2_CID_TEST_PATTERN_RED</constant></entry>
+           <entry>integer</entry>
+         </row>
+         <row>
+           <entry spanname="descr">Test pattern red colour component.
+           </entry>
+         </row>
+         <row>
+           <entry spanname="id"><constant>V4L2_CID_TEST_PATTERN_GREENR</constant></entry>
+           <entry>integer</entry>
+         </row>
+         <row>
+           <entry spanname="descr">Test pattern green (next to red)
+           colour component.
+           </entry>
+         </row>
+         <row>
+           <entry spanname="id"><constant>V4L2_CID_TEST_PATTERN_BLUE</constant></entry>
+           <entry>integer</entry>
+         </row>
+         <row>
+           <entry spanname="descr">Test pattern blue colour component.
+           </entry>
+         </row>
+         <row>
+           <entry spanname="id"><constant>V4L2_CID_TEST_PATTERN_GREENB</constant></entry>
+           <entry>integer</entry>
+         </row>
+         <row>
+           <entry spanname="descr">Test pattern green (next to blue)
+           colour component.
+           </entry>
+         </row>
          <row><entry></entry></row>
        </tbody>
       </tgroup>
index 2aae8e9452a42dd41656f9692e78122343957d21..6ab4f0f3db64abdc9de76ff3ccf458881f9b49e2 100644 (file)
@@ -237,9 +237,9 @@ for a pixel lie next to each other in memory.</para>
            <entry>g<subscript>4</subscript></entry>
            <entry>g<subscript>3</subscript></entry>
          </row>
-         <row id="V4L2-PIX-FMT-RGB555X">
-           <entry><constant>V4L2_PIX_FMT_RGB555X</constant></entry>
-           <entry>'RGBQ'</entry>
+         <row id="V4L2-PIX-FMT-ARGB555X">
+           <entry><constant>V4L2_PIX_FMT_ARGB555X</constant></entry>
+           <entry>'AR15' | (1 &lt;&lt; 31)</entry>
            <entry></entry>
            <entry>a</entry>
            <entry>r<subscript>4</subscript></entry>
@@ -259,6 +259,28 @@ for a pixel lie next to each other in memory.</para>
            <entry>b<subscript>1</subscript></entry>
            <entry>b<subscript>0</subscript></entry>
          </row>
+         <row id="V4L2-PIX-FMT-XRGB555X">
+           <entry><constant>V4L2_PIX_FMT_XRGB555X</constant></entry>
+           <entry>'XR15' | (1 &lt;&lt; 31)</entry>
+           <entry></entry>
+           <entry>-</entry>
+           <entry>r<subscript>4</subscript></entry>
+           <entry>r<subscript>3</subscript></entry>
+           <entry>r<subscript>2</subscript></entry>
+           <entry>r<subscript>1</subscript></entry>
+           <entry>r<subscript>0</subscript></entry>
+           <entry>g<subscript>4</subscript></entry>
+           <entry>g<subscript>3</subscript></entry>
+           <entry></entry>
+           <entry>g<subscript>2</subscript></entry>
+           <entry>g<subscript>1</subscript></entry>
+           <entry>g<subscript>0</subscript></entry>
+           <entry>b<subscript>4</subscript></entry>
+           <entry>b<subscript>3</subscript></entry>
+           <entry>b<subscript>2</subscript></entry>
+           <entry>b<subscript>1</subscript></entry>
+           <entry>b<subscript>0</subscript></entry>
+         </row>
          <row id="V4L2-PIX-FMT-RGB565X">
            <entry><constant>V4L2_PIX_FMT_RGB565X</constant></entry>
            <entry>'RGBR'</entry>
@@ -464,7 +486,7 @@ for a pixel lie next to each other in memory.</para>
          </row>
          <row id="V4L2-PIX-FMT-ARGB32">
            <entry><constant>V4L2_PIX_FMT_ARGB32</constant></entry>
-           <entry>'AX24'</entry>
+           <entry>'BA24'</entry>
            <entry></entry>
            <entry>a<subscript>7</subscript></entry>
            <entry>a<subscript>6</subscript></entry>
@@ -800,6 +822,28 @@ image</title>
            <entry>g<subscript>4</subscript></entry>
            <entry>g<subscript>3</subscript></entry>
          </row>
+         <row id="V4L2-PIX-FMT-RGB555X">
+           <entry><constant>V4L2_PIX_FMT_RGB555X</constant></entry>
+           <entry>'RGBQ'</entry>
+           <entry></entry>
+           <entry>a</entry>
+           <entry>r<subscript>4</subscript></entry>
+           <entry>r<subscript>3</subscript></entry>
+           <entry>r<subscript>2</subscript></entry>
+           <entry>r<subscript>1</subscript></entry>
+           <entry>r<subscript>0</subscript></entry>
+           <entry>g<subscript>4</subscript></entry>
+           <entry>g<subscript>3</subscript></entry>
+           <entry></entry>
+           <entry>g<subscript>2</subscript></entry>
+           <entry>g<subscript>1</subscript></entry>
+           <entry>g<subscript>0</subscript></entry>
+           <entry>b<subscript>4</subscript></entry>
+           <entry>b<subscript>3</subscript></entry>
+           <entry>b<subscript>2</subscript></entry>
+           <entry>b<subscript>1</subscript></entry>
+           <entry>b<subscript>0</subscript></entry>
+         </row>
          <row id="V4L2-PIX-FMT-BGR32">
            <entry><constant>V4L2_PIX_FMT_BGR32</constant></entry>
            <entry>'BGR4'</entry>
index cb7732582f0365861ccf14f25a3a83ecfdb5d05e..b036f8963353a2a2f88c336855da9282e7d68fb6 100644 (file)
            <entry></entry>
            <entry>&v4l2-event-vsync;</entry>
             <entry><structfield>vsync</structfield></entry>
-           <entry>Event data for event V4L2_EVENT_VSYNC.
+           <entry>Event data for event <constant>V4L2_EVENT_VSYNC</constant>.
             </entry>
          </row>
          <row>
            <entry></entry>
            <entry>&v4l2-event-ctrl;</entry>
             <entry><structfield>ctrl</structfield></entry>
-           <entry>Event data for event V4L2_EVENT_CTRL.
+           <entry>Event data for event <constant>V4L2_EVENT_CTRL</constant>.
             </entry>
          </row>
          <row>
            <entry></entry>
            <entry>&v4l2-event-frame-sync;</entry>
             <entry><structfield>frame_sync</structfield></entry>
-           <entry>Event data for event V4L2_EVENT_FRAME_SYNC.</entry>
+           <entry>Event data for event
+           <constant>V4L2_EVENT_FRAME_SYNC</constant>.</entry>
          </row>
          <row>
            <entry></entry>
index ce4563b87131c5ad384a22cf63fadcd95d85206d..6df40db4c8ba9f7674dda280e914a365ed3abe7d 100644 (file)
@@ -24,7 +24,7 @@
        <funcdef>int <function>ioctl</function></funcdef>
        <paramdef>int <parameter>fd</parameter></paramdef>
        <paramdef>int <parameter>request</parameter></paramdef>
-       <paramdef>const struct v4l2_edid *<parameter>argp</parameter></paramdef>
+       <paramdef>struct v4l2_edid *<parameter>argp</parameter></paramdef>
       </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
            maximum number of blocks as defined by the standard). When you set the EDID and
            <structfield>blocks</structfield> is 0, then the EDID is disabled or erased.</entry>
          </row>
-         <row>
-           <entry>__u8&nbsp;*</entry>
-           <entry><structfield>edid</structfield></entry>
-           <entry>Pointer to memory that contains the EDID. The minimum size is
-           <structfield>blocks</structfield>&nbsp;*&nbsp;128.</entry>
-         </row>
          <row>
            <entry>__u32</entry>
            <entry><structfield>reserved</structfield>[5]</entry>
            <entry>Reserved for future extensions. Applications and drivers must
            set the array to zero.</entry>
          </row>
+         <row>
+           <entry>__u8&nbsp;*</entry>
+           <entry><structfield>edid</structfield></entry>
+           <entry>Pointer to memory that contains the EDID. The minimum size is
+           <structfield>blocks</structfield>&nbsp;*&nbsp;128.</entry>
+         </row>
        </tbody>
       </tgroup>
     </table>
index 9f6095608837b29085aa49171f544474801d3c6e..d7c9365ecdbe245e05aee3875ee09ab5b8fa0199 100644 (file)
          </row>
          <row>
            <entry><constant>V4L2_EVENT_MOTION_DET</constant></entry>
-           <entry>5</entry>
+           <entry>6</entry>
            <entry>
              <para>Triggered whenever the motion detection state for one or more of the regions
              changes. This event has a &v4l2-event-motion-det; associated with it.</para>
index 6f639d9530b51b69394a83c1d29e9ee2a42bce32..784793df81ed12933ebbb76682d8ec39597603d8 100644 (file)
@@ -2742,7 +2742,9 @@ struct _snd_pcm_runtime {
 
         <para>
           Another note is that this callback is non-atomic
-        (schedulable). This is important, because the
+        (schedulable) as default, i.e. when no
+       <structfield>nonatomic</structfield> flag set.
+       This is important, because the
         <structfield>trigger</structfield> callback 
         is atomic (non-schedulable). That is, mutexes or any
         schedule-related functions are not available in
@@ -2900,8 +2902,9 @@ struct _snd_pcm_runtime {
         </para>
 
         <para>
-          As mentioned, this callback is atomic.  You cannot call
-         functions which may sleep.
+          As mentioned, this callback is atomic as default unless
+         <structfield>nonatomic</structfield> flag set, and
+         you cannot call functions which may sleep.
          The trigger callback should be as minimal as possible,
          just really triggering the DMA.  The other stuff should be
          initialized hw_params and prepare callbacks properly
@@ -2936,7 +2939,7 @@ struct _snd_pcm_runtime {
         </para>
 
         <para>
-          This callback is also atomic.
+          This callback is also atomic as default.
         </para>
       </section>
 
@@ -2972,7 +2975,7 @@ struct _snd_pcm_runtime {
        is useful only for such a purpose.
        </para>
        <para>
-         This callback is atomic.
+         This callback is atomic as default.
        </para>
       </section>
 
@@ -3175,6 +3178,21 @@ struct _snd_pcm_runtime {
       called with local interrupts disabled.
       </para>
 
+      <para>
+      The recent changes in PCM core code, however, allow all PCM
+      operations to be non-atomic.  This assumes that the all caller
+      sides are in non-atomic contexts.  For example, the function
+      <function>snd_pcm_period_elapsed()</function> is called
+      typically from the interrupt handler.  But, if you set up the
+      driver to use a threaded interrupt handler, this call can be in
+      non-atomic context, too.  In such a case, you can set
+      <structfield>nonatomic</structfield> filed of
+      <structname>snd_pcm</structname> object after creating it.
+      When this flag is set, mutex and rwsem are used internally in
+      the PCM core instead of spin and rwlocks, so that you can call
+      all PCM functions safely in a non-atomic context.
+      </para>
+
     </section>
     <section id="pcm-interface-constraints">
       <title>Constraints</title>
index 0595c3f56ccfaadaa8f36d76021d8c6ce1a66140..7fcf9c6592ecc45d0765b7f1ae018228e8f758f7 100644 (file)
@@ -74,14 +74,30 @@ There is little point creating a zram of greater than twice the size of memory
 since we expect a 2:1 compression ratio. Note that zram uses about 0.1% of the
 size of the disk when not in use so a huge zram is wasteful.
 
-5) Activate:
+5) Set memory limit: Optional
+       Set memory limit by writing the value to sysfs node 'mem_limit'.
+       The value can be either in bytes or you can use mem suffixes.
+       In addition, you could change the value in runtime.
+       Examples:
+           # limit /dev/zram0 with 50MB memory
+           echo $((50*1024*1024)) > /sys/block/zram0/mem_limit
+
+           # Using mem suffixes
+           echo 256K > /sys/block/zram0/mem_limit
+           echo 512M > /sys/block/zram0/mem_limit
+           echo 1G > /sys/block/zram0/mem_limit
+
+           # To disable memory limit
+           echo 0 > /sys/block/zram0/mem_limit
+
+6) Activate:
        mkswap /dev/zram0
        swapon /dev/zram0
 
        mkfs.ext4 /dev/zram1
        mount /dev/zram1 /tmp
 
-6) Stats:
+7) Stats:
        Per-device statistics are exported as various nodes under
        /sys/block/zram<id>/
                disksize
@@ -95,12 +111,13 @@ size of the disk when not in use so a huge zram is wasteful.
                orig_data_size
                compr_data_size
                mem_used_total
+               mem_used_max
 
-7) Deactivate:
+8) Deactivate:
        swapoff /dev/zram0
        umount /dev/zram1
 
-8) Reset:
+9) Reset:
        Write any positive value to 'reset' sysfs node
        echo 1 > /sys/block/zram0/reset
        echo 1 > /sys/block/zram1/reset
index 8b4f7b7fe88b9fc181b41e4a6eb68406dd6094d3..abde1ea8a1198a6744a2bfd3d84e8054a7275f85 100644 (file)
@@ -8,6 +8,8 @@ Required Properties:
     * samsung,exynos4210-pd - for exynos4210 type power domain.
 - reg: physical base address of the controller and length of memory mapped
     region.
+- #power-domain-cells: number of cells in power domain specifier;
+    must be 0.
 
 Optional Properties:
 - clocks: List of clock handles. The parent clocks of the input clocks to the
@@ -29,6 +31,7 @@ Example:
        lcd0: power-domain-lcd0 {
                compatible = "samsung,exynos4210-pd";
                reg = <0x10023C00 0x10>;
+               #power-domain-cells = <0>;
        };
 
        mfc_pd: power-domain@10044060 {
@@ -37,12 +40,8 @@ Example:
                clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MOUT_SW_ACLK333>,
                        <&clock CLK_MOUT_USER_ACLK333>;
                clock-names = "oscclk", "pclk0", "clk0";
+               #power-domain-cells = <0>;
        };
 
-Example of the node using power domain:
-
-       node {
-               /* ... */
-               samsung,power-domain = <&lcd0>;
-               /* ... */
-       };
+See Documentation/devicetree/bindings/power/power_domain.txt for description
+of consumer-side bindings.
diff --git a/Documentation/devicetree/bindings/ata/qcom-sata.txt b/Documentation/devicetree/bindings/ata/qcom-sata.txt
new file mode 100644 (file)
index 0000000..094de91
--- /dev/null
@@ -0,0 +1,48 @@
+* Qualcomm AHCI SATA Controller
+
+SATA nodes are defined to describe on-chip Serial ATA controllers.
+Each SATA controller should have its own node.
+
+Required properties:
+- compatible           : compatible list, must contain "generic-ahci"
+- interrupts           : <interrupt mapping for SATA IRQ>
+- reg                  : <registers mapping>
+- phys                 : Must contain exactly one entry as specified
+                         in phy-bindings.txt
+- phy-names            : Must be "sata-phy"
+
+Required properties for "qcom,ipq806x-ahci" compatible:
+- clocks               : Must contain an entry for each entry in clock-names.
+- clock-names          : Shall be:
+                               "slave_iface" - Fabric port AHB clock for SATA
+                               "iface" - AHB clock
+                               "core" - core clock
+                               "rxoob" - RX out-of-band clock
+                               "pmalive" - Power Module Alive clock
+- assigned-clocks      : Shall be:
+                               SATA_RXOOB_CLK
+                               SATA_PMALIVE_CLK
+- assigned-clock-rates : Shall be:
+                               100Mhz (100000000) for SATA_RXOOB_CLK
+                               100Mhz (100000000) for SATA_PMALIVE_CLK
+
+Example:
+       sata@29000000 {
+               compatible = "qcom,ipq806x-ahci", "generic-ahci";
+               reg = <0x29000000 0x180>;
+
+               interrupts = <0 209 0x0>;
+
+               clocks = <&gcc SFAB_SATA_S_H_CLK>,
+                        <&gcc SATA_H_CLK>,
+                        <&gcc SATA_A_CLK>,
+                        <&gcc SATA_RXOOB_CLK>,
+                        <&gcc SATA_PMALIVE_CLK>;
+               clock-names = "slave_iface", "iface", "core",
+                               "rxoob", "pmalive";
+               assigned-clocks = <&gcc SATA_RXOOB_CLK>, <&gcc SATA_PMALIVE_CLK>;
+               assigned-clock-rates = <100000000>, <100000000>;
+
+               phys = <&sata_phy>;
+               phy-names = "sata-phy";
+       };
similarity index 85%
rename from Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt
rename to Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt
index 366690cb86a3065768b74dbf8136a9c560f94155..e41c98ffbccb2a53e408e83b9886ec6e56713557 100644 (file)
@@ -1,8 +1,8 @@
-Generic CPU0 cpufreq driver
+Generic cpufreq driver
 
-It is a generic cpufreq driver for CPU0 frequency management.  It
-supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
-systems which share clock and voltage across all CPUs.
+It is a generic DT based cpufreq driver for frequency management.  It supports
+both uniprocessor (UP) and symmetric multiprocessor (SMP) systems which share
+clock and voltage across all CPUs.
 
 Both required and optional properties listed below must be defined
 under node /cpus/cpu@0.
diff --git a/Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt b/Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt
new file mode 100644 (file)
index 0000000..6c7e6c7
--- /dev/null
@@ -0,0 +1,39 @@
+Keystone 2 DSP GPIO controller bindings
+
+HOST OS userland running on ARM can send interrupts to DSP cores using
+the DSP GPIO controller IP. It provides 28 IRQ signals per each DSP core.
+This is one of the component used by the IPC mechanism used on Keystone SOCs.
+
+For example TCI6638K2K SoC has 8 DSP GPIO controllers:
+ - 8 for C66x CorePacx CPUs 0-7
+
+Keystone 2 DSP GPIO controller has specific features:
+- each GPIO can be configured only as output pin;
+- setting GPIO value to 1 causes IRQ generation on target DSP core;
+- reading pin value returns 0 - if IRQ was handled or 1 - IRQ is still
+  pending.
+
+Required Properties:
+- compatible: should be "ti,keystone-dsp-gpio"
+- ti,syscon-dev: phandle/offset pair. The phandle to syscon used to
+  access device state control registers and the offset of device's specific
+  registers within device state control registers range.
+- gpio-controller: Marks the device node as a gpio controller.
+- #gpio-cells: Should be 2.
+
+Please refer to gpio.txt in this directory for details of the common GPIO
+bindings used by client devices.
+
+Example:
+       dspgpio0: keystone_dsp_gpio@02620240 {
+               compatible = "ti,keystone-dsp-gpio";
+               ti,syscon-dev = <&devctrl 0x240>;
+               gpio-controller;
+               #gpio-cells = <2>;
+       };
+
+       dsp0: dsp0 {
+               compatible = "linux,rproc-user";
+               ...
+               kick-gpio = <&dspgpio0 27>;
+       };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
new file mode 100644 (file)
index 0000000..b9a42f2
--- /dev/null
@@ -0,0 +1,39 @@
+* NXP PCA953x I2C GPIO multiplexer
+
+Required properties:
+ - compatible: Has to contain one of the following:
+       nxp,pca9505
+       nxp,pca9534
+       nxp,pca9535
+       nxp,pca9536
+       nxp,pca9537
+       nxp,pca9538
+       nxp,pca9539
+       nxp,pca9554
+       nxp,pca9555
+       nxp,pca9556
+       nxp,pca9557
+       nxp,pca9574
+       nxp,pca9575
+       nxp,pca9698
+       maxim,max7310
+       maxim,max7312
+       maxim,max7313
+       maxim,max7315
+       ti,pca6107
+       ti,tca6408
+       ti,tca6416
+       ti,tca6424
+       exar,xra1202
+
+Example:
+
+
+       gpio@20 {
+               compatible = "nxp,pca9505";
+               reg = <0x20>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_pca9505>;
+               interrupt-parent = <&gpio3>;
+               interrupts = <23 IRQ_TYPE_LEVEL_LOW>;
+       };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-xgene.txt b/Documentation/devicetree/bindings/gpio/gpio-xgene.txt
new file mode 100644 (file)
index 0000000..86dbb05
--- /dev/null
@@ -0,0 +1,22 @@
+APM X-Gene SoC GPIO controller bindings
+
+This is a gpio controller that is part of the flash controller.
+This gpio controller controls a total of 48 gpios.
+
+Required properties:
+- compatible: "apm,xgene-gpio" for X-Gene GPIO controller
+- reg: Physical base address and size of the controller's registers
+- #gpio-cells: Should be two.
+       - first cell is the pin number
+       - second cell is used to specify the gpio polarity:
+               0 = active high
+               1 = active low
+- gpio-controller: Marks the device node as a GPIO controller.
+
+Example:
+       gpio0: gpio0@1701c000 {
+               compatible = "apm,xgene-gpio";
+               reg = <0x0 0x1701c000 0x0 0x40>;
+               gpio-controller;
+               #gpio-cells = <2>;
+       };
index 66416261e14df5a0b621a52ead96e4ef52482472..b2afdb27adeb3d5786a7f5e31d22bdb80e10affa 100644 (file)
@@ -19,7 +19,7 @@ Required properties:
 - gpio-controller : Marks the device node as a gpio controller.
 - #gpio-cells : Should be one.  It is the pin number.
 
-Example:
+Example for a MMP platform:
 
        gpio: gpio@d4019000 {
                compatible = "marvell,mmp-gpio";
@@ -32,6 +32,19 @@ Example:
                #interrupt-cells = <1>;
       };
 
+Example for a PXA3xx platform:
+
+       gpio: gpio@40e00000 {
+               compatible = "intel,pxa3xx-gpio";
+               reg = <0x40e00000 0x10000>;
+               interrupt-names = "gpio0", "gpio1", "gpio_mux";
+               interrupts = <8 9 10>;
+               gpio-controller;
+               #gpio-cells = <0x2>;
+               interrupt-controller;
+               #interrupt-cells = <0x2>;
+       };
+
 * Marvell Orion GPIO Controller
 
 Required properties:
index 2391e5c41999afd69df9feafc1f70b15accfefe0..fcca8e744f41f9c4692b3b8915d120dcbd0f0768 100644 (file)
@@ -25,6 +25,9 @@ Requires node properties:
 - "io-channels"        Channel node of ADC to be used for
                conversion.
 
+Optional node properties:
+- "#thermal-sensor-cells" Used to expose itself to thermal fw.
+
 Read more about iio bindings at
        Documentation/devicetree/bindings/iio/iio-bindings.txt
 
index 2742e9cfd6b1166c18fff7cabe63f972aea92b49..f292917fa00d8bc3b6dce45b26d5cb8370a98289 100644 (file)
@@ -2,7 +2,7 @@
 
 Required properties:
 - compatible: Should be "atmel,<chip>-aic"
-  <chip> can be "at91rm9200" or "sama5d3"
+  <chip> can be "at91rm9200", "sama5d3" or "sama5d4"
 - interrupt-controller: Identifies the node as an interrupt controller.
 - interrupt-parent: For single AIC system, it is an empty property.
 - #interrupt-cells: The number of cells to define the interrupts. It should be 3.
diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt
new file mode 100644 (file)
index 0000000..ff812a8
--- /dev/null
@@ -0,0 +1,86 @@
+Broadcom BCM7120-style Level 2 interrupt controller
+
+This interrupt controller hardware is a second level interrupt controller that
+is hooked to a parent interrupt controller: e.g: ARM GIC for ARM-based
+platforms. It can be found on BCM7xxx products starting with BCM7120.
+
+Such an interrupt controller has the following hardware design:
+
+- outputs multiple interrupts signals towards its interrupt controller parent
+
+- controls how some of the interrupts will be flowing, whether they will
+  directly output an interrupt signal towards the interrupt controller parent,
+  or if they will output an interrupt signal at this 2nd level interrupt
+  controller, in particular for UARTs
+
+- not all 32-bits within the interrupt controller actually map to an interrupt
+
+The typical hardware layout for this controller is represented below:
+
+2nd level interrupt line               Outputs for the parent controller (e.g: ARM GIC)
+
+0 -----[ MUX ] ------------|==========> GIC interrupt 75
+          \-----------\
+                       |
+1 -----[ MUX ] --------)---|==========> GIC interrupt 76
+          \------------|
+                       |
+2 -----[ MUX ] --------)---|==========> GIC interrupt 77
+          \------------|
+                       |
+3 ---------------------|
+4 ---------------------|
+5 ---------------------|
+7 ---------------------|---|===========> GIC interrupt 66
+9 ---------------------|
+10 --------------------|
+11 --------------------/
+
+6 ------------------------\
+                           |===========> GIC interrupt 64
+8 ------------------------/
+
+12 ........................ X
+13 ........................ X          (not connected)
+..
+31 ........................ X
+
+Required properties:
+
+- compatible: should be "brcm,bcm7120-l2-intc"
+- reg: specifies the base physical address and size of the registers
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: specifies the number of cells needed to encode an interrupt
+  source, should be 1.
+- interrupt-parent: specifies the phandle to the parent interrupt controller
+  this one is cascaded from
+- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
+  node, valid values depend on the type of parent interrupt controller
+- brcm,int-map-mask: 32-bits bit mask describing how many and which interrupts
+  are wired to this 2nd level interrupt controller, and how they match their
+  respective interrupt parents. Should match exactly the number of interrupts
+  specified in the 'interrupts' property.
+
+Optional properties:
+
+- brcm,irq-can-wake: if present, this means the L2 controller can be used as a
+  wakeup source for system suspend/resume.
+
+- brcm,int-fwd-mask: if present, a 32-bits bit mask to configure for the
+  interrupts which have a mux gate, typically UARTs. Setting these bits will
+  make their respective interrupts outputs bypass this 2nd level interrupt
+  controller completely, it completely transparent for the interrupt controller
+  parent
+
+Example:
+
+irq0_intc: interrupt-controller@f0406800 {
+       compatible = "brcm,bcm7120-l2-intc";
+       interrupt-parent = <&intc>;
+       #interrupt-cells = <1>;
+       reg = <0xf0406800 0x8>;
+       interrupt-controller;
+       interrupts = <0x0 0x42 0x0>, <0x0 0x40 0x0>;
+       brcm,int-map-mask = <0xeb8>, <0x140>;
+       brcm,int-fwd-mask = <0x7>;
+};
index 1f8b0c507c26cb4c0a7f3341aa1000a7df3c4dea..c73acd060093acba9958e497251d5871545eaab5 100644 (file)
@@ -2,7 +2,13 @@ DT bindings for the R-/SH-Mobile irqpin controller
 
 Required properties:
 
-- compatible: has to be "renesas,intc-irqpin"
+- compatible: has to be "renesas,intc-irqpin-<soctype>", "renesas,intc-irqpin"
+  as fallback.
+  Examples with soctypes are:
+    - "renesas,intc-irqpin-r8a7740" (R-Mobile A1)
+    - "renesas,intc-irqpin-r8a7778" (R-Car M1A)
+    - "renesas,intc-irqpin-r8a7779" (R-Car H1)
+    - "renesas,intc-irqpin-sh73a0" (SH-Mobile AG5)
 - #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
   interrupts.txt in this directory
 
diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt
new file mode 100644 (file)
index 0000000..1a88e62
--- /dev/null
@@ -0,0 +1,32 @@
+DT bindings for the R-Mobile/R-Car interrupt controller
+
+Required properties:
+
+- compatible: has to be "renesas,irqc-<soctype>", "renesas,irqc" as fallback.
+  Examples with soctypes are:
+    - "renesas,irqc-r8a73a4" (R-Mobile AP6)
+    - "renesas,irqc-r8a7790" (R-Car H2)
+    - "renesas,irqc-r8a7791" (R-Car M2-W)
+    - "renesas,irqc-r8a7792" (R-Car V2H)
+    - "renesas,irqc-r8a7793" (R-Car M2-N)
+    - "renesas,irqc-r8a7794" (R-Car E2)
+- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
+  interrupts.txt in this directory
+
+Optional properties:
+
+- any properties, listed in interrupts.txt, and any standard resource allocation
+  properties
+
+Example:
+
+       irqc0: interrupt-controller@e61c0000 {
+               compatible = "renesas,irqc-r8a7790", "renesas,irqc";
+               #interrupt-cells = <2>;
+               interrupt-controller;
+               reg = <0 0xe61c0000 0 0x200>;
+               interrupts = <0 0 IRQ_TYPE_LEVEL_HIGH>,
+                            <0 1 IRQ_TYPE_LEVEL_HIGH>,
+                            <0 2 IRQ_TYPE_LEVEL_HIGH>,
+                            <0 3 IRQ_TYPE_LEVEL_HIGH>;
+       };
diff --git a/Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt b/Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt
new file mode 100644 (file)
index 0000000..d9bb106
--- /dev/null
@@ -0,0 +1,36 @@
+Keystone 2 IRQ controller IP
+
+On Keystone SOCs, DSP cores can send interrupts to ARM
+host using the IRQ controller IP. It provides 28 IRQ signals to ARM.
+The IRQ handler running on HOST OS can identify DSP signal source by
+analyzing SRCCx bits in IPCARx registers. This is one of the component
+used by the IPC mechanism used on Keystone SOCs.
+
+Required Properties:
+- compatible: should be "ti,keystone-irq"
+- ti,syscon-dev : phandle and offset pair. The phandle to syscon used to
+                       access device control registers and the offset inside
+                       device control registers range.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode interrupt
+                                        source should be 1.
+- interrupts: interrupt reference to primary interrupt controller
+
+Please refer to interrupts.txt in this directory for details of the common
+Interrupt Controllers bindings used by client devices.
+
+Example:
+       kirq0: keystone_irq0@026202a0 {
+               compatible = "ti,keystone-irq";
+               ti,syscon-dev = <&devctrl 0x2a0>;
+               interrupts = <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>;
+               interrupt-controller;
+               #interrupt-cells = <1>;
+       };
+
+       dsp0: dsp0 {
+               compatible = "linux,rproc-user";
+               ...
+               interrupt-parent = <&kirq0>;
+               interrupts = <10 2>;
+       };
diff --git a/Documentation/devicetree/bindings/media/hix5hd2-ir.txt b/Documentation/devicetree/bindings/media/hix5hd2-ir.txt
new file mode 100644 (file)
index 0000000..fb5e760
--- /dev/null
@@ -0,0 +1,25 @@
+Device-Tree bindings for hix5hd2 ir IP
+
+Required properties:
+       - compatible: Should contain "hisilicon,hix5hd2-ir".
+       - reg: Base physical address of the controller and length of memory
+         mapped region.
+       - interrupts: interrupt-specifier for the sole interrupt generated by
+         the device. The interrupt specifier format depends on the interrupt
+         controller parent.
+       - clocks: clock phandle and specifier pair.
+       - hisilicon,power-syscon: phandle of syscon used to control power.
+
+Optional properties:
+       - linux,rc-map-name : Remote control map name.
+
+Example node:
+
+       ir: ir@f8001000 {
+               compatible = "hisilicon,hix5hd2-ir";
+               reg = <0xf8001000 0x1000>;
+               interrupts = <0 47 4>;
+               clocks = <&clock HIX5HD2_FIXED_24M>;
+               hisilicon,power-syscon = <&sysctrl>;
+               linux,rc-map-name = "rc-tivo";
+       };
index 431716e37a3964638245b2905badff16128b9495..b52628b18a537af7dac6be18150efe7a95adf78b 100644 (file)
@@ -40,6 +40,8 @@ Optional properties:
 - mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
 - mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported
 - mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported
+- dsr: Value the card's (optional) Driver Stage Register (DSR) should be
+  programmed with. Valid range: [0 .. 0xffff].
 
 *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
 polarity properties, we have to fix the meaning of the "normal" and "inverted"
index c559f3f36309e57f1eccda86e926771047960cbb..c327c2d6f23d7f043799fd9bd4e1e9d94a7b4322 100644 (file)
@@ -10,12 +10,14 @@ extensions to the Synopsys Designware Mobile Storage Host Controller.
 Required Properties:
 
 * compatible: should be
-       - "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following
+       - "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following,
+                                                       before RK3288
+       - "rockchip,rk3288-dw-mshc": for Rockchip RK3288
 
 Example:
 
        rkdwmmc0@12200000 {
-               compatible = "rockchip,rk2928-dw-mshc";
+               compatible = "rockchip,rk3288-dw-mshc";
                reg = <0x12200000 0x1000>;
                interrupts = <0 75 0>;
                #address-cells = <1>;
index fa0f327cde01417339f8e3618f075e6ef51db12e..400b640fabc768642f3c4b7f8ac9314067af5836 100644 (file)
@@ -19,6 +19,9 @@ Required properties:
                "renesas,sdhi-r8a7779" - SDHI IP on R8A7779 SoC
                "renesas,sdhi-r8a7790" - SDHI IP on R8A7790 SoC
                "renesas,sdhi-r8a7791" - SDHI IP on R8A7791 SoC
+               "renesas,sdhi-r8a7792" - SDHI IP on R8A7792 SoC
+               "renesas,sdhi-r8a7793" - SDHI IP on R8A7793 SoC
+               "renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
 
 Optional properties:
 - toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
index ed0d9b9fff2be5f5be5902554d0da70ed81591df..9f4faa8e8d005ee1810f0dc4272d63f4af3e17e6 100644 (file)
@@ -23,3 +23,6 @@ Required properties:
 
 Optional properties:
 - reset-gpio: gpio pin number of power good signal
+- bus-range: PCI bus numbers covered (it is recommended for new devicetrees to
+  specify this property, to keep backwards compatibility a range of 0x00-0xff
+  is assumed if not present)
diff --git a/Documentation/devicetree/bindings/pci/fsl,pci.txt b/Documentation/devicetree/bindings/pci/fsl,pci.txt
new file mode 100644 (file)
index 0000000..d8ac4a7
--- /dev/null
@@ -0,0 +1,27 @@
+* Bus Enumeration by Freescale PCI-X Agent
+
+Typically any Freescale PCI-X bridge hardware strapped into Agent mode
+is prevented from enumerating the bus. The PrPMC form-factor requires
+all mezzanines to be PCI-X Agents, but one per system may still
+enumerate the bus.
+
+The property defined below will allow a PCI-X bridge to be used for bus
+enumeration despite being strapped into Agent mode.
+
+Required properties:
+- fsl,pci-agent-force-enum : There is no value associated with this
+  property. The property itself is treated as a boolean.
+
+Example:
+
+       /* PCI-X bridge known to be PrPMC Monarch */
+       pci0: pci@ef008000 {
+               fsl,pci-agent-force-enum;
+               #interrupt-cells = <1>;
+               #size-cells = <2>;
+               #address-cells = <3>;
+               compatible = "fsl,mpc8540-pcix", "fsl,mpc8540-pci";
+               device_type = "pci";
+               ...
+               ...
+       };
index 0823362548dc04c03416a83e001ef19677dbba62..d763e047c6aeb1b6a31a7f06a1e07bda605504b1 100644 (file)
@@ -1,7 +1,10 @@
 NVIDIA Tegra PCIe controller
 
 Required properties:
-- compatible: "nvidia,tegra20-pcie" or "nvidia,tegra30-pcie"
+- compatible: Must be one of:
+  - "nvidia,tegra20-pcie"
+  - "nvidia,tegra30-pcie"
+  - "nvidia,tegra124-pcie"
 - device_type: Must be "pci"
 - reg: A list of physical base address and length for each set of controller
   registers. Must contain an entry for each entry in the reg-names property.
@@ -57,6 +60,11 @@ Required properties:
   - afi
   - pcie_x
 
+Required properties on Tegra124 and later:
+- phys: Must contain an entry for each entry in phy-names.
+- phy-names: Must include the following entries:
+  - pcie
+
 Power supplies for Tegra20:
 - avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
 - vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
@@ -84,6 +92,21 @@ Power supplies for Tegra30:
     - avdd-pexb-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
     - vdd-pexb-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
 
+Power supplies for Tegra124:
+- Required:
+  - avddio-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
+  - dvddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
+  - avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
+    supply 1.05 V.
+  - hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
+    Must supply 3.3 V.
+  - hvdd-pex-pll-e-supply: High-voltage supply for PLLE (shared with USB3).
+    Must supply 3.3 V.
+  - vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
+    supply 2.8-3.3 V.
+  - avdd-pll-erefe-supply: Power supply for PLLE (shared with USB3). Must
+    supply 1.05 V.
+
 Root ports are defined as subnodes of the PCIe controller node.
 
 Required properties:
diff --git a/Documentation/devicetree/bindings/pci/pci-keystone.txt b/Documentation/devicetree/bindings/pci/pci-keystone.txt
new file mode 100644 (file)
index 0000000..54eae29
--- /dev/null
@@ -0,0 +1,63 @@
+TI Keystone PCIe interface
+
+Keystone PCI host Controller is based on Designware PCI h/w version 3.65.
+It shares common functions with PCIe Designware core driver and inherit
+common properties defined in
+Documentation/devicetree/bindings/pci/designware-pci.txt
+
+Please refer to Documentation/devicetree/bindings/pci/designware-pci.txt
+for the details of Designware DT bindings.  Additional properties are
+described here as well as properties that are not applicable.
+
+Required Properties:-
+
+compatibility: "ti,keystone-pcie"
+reg:   index 1 is the base address and length of DW application registers.
+       index 2 is the base address and length of PCI device ID register.
+
+pcie_msi_intc : Interrupt controller device node for MSI IRQ chip
+       interrupt-cells: should be set to 1
+       interrupt-parent: Parent interrupt controller phandle
+       interrupts: GIC interrupt lines connected to PCI MSI interrupt lines
+
+ Example:
+       pcie_msi_intc: msi-interrupt-controller {
+                       interrupt-controller;
+                       #interrupt-cells = <1>;
+                       interrupt-parent = <&gic>;
+                       interrupts = <GIC_SPI 30 IRQ_TYPE_EDGE_RISING>,
+                                       <GIC_SPI 31 IRQ_TYPE_EDGE_RISING>,
+                                       <GIC_SPI 32 IRQ_TYPE_EDGE_RISING>,
+                                       <GIC_SPI 33 IRQ_TYPE_EDGE_RISING>,
+                                       <GIC_SPI 34 IRQ_TYPE_EDGE_RISING>,
+                                       <GIC_SPI 35 IRQ_TYPE_EDGE_RISING>,
+                                       <GIC_SPI 36 IRQ_TYPE_EDGE_RISING>,
+                                       <GIC_SPI 37 IRQ_TYPE_EDGE_RISING>;
+       };
+
+pcie_intc: Interrupt controller device node for Legacy IRQ chip
+       interrupt-cells: should be set to 1
+       interrupt-parent: Parent interrupt controller phandle
+       interrupts: GIC interrupt lines connected to PCI Legacy interrupt lines
+
+ Example:
+       pcie_intc: legacy-interrupt-controller {
+               interrupt-controller;
+               #interrupt-cells = <1>;
+               interrupt-parent = <&gic>;
+               interrupts = <GIC_SPI 26 IRQ_TYPE_EDGE_RISING>,
+                       <GIC_SPI 27 IRQ_TYPE_EDGE_RISING>,
+                       <GIC_SPI 28 IRQ_TYPE_EDGE_RISING>,
+                       <GIC_SPI 29 IRQ_TYPE_EDGE_RISING>;
+       };
+
+Optional properties:-
+       phys: phandle to Generic Keystone SerDes phy for PCI
+       phy-names: name of the Generic Keystine SerDes phy for PCI
+         - If boot loader already does PCI link establishment, then phys and
+           phy-names shouldn't be present.
+
+Designware DT Properties not applicable for Keystone PCI
+
+1. pcie_bus clock-names not used.  Instead, a phandle to phys is used.
+
diff --git a/Documentation/devicetree/bindings/pci/xgene-pci.txt b/Documentation/devicetree/bindings/pci/xgene-pci.txt
new file mode 100644 (file)
index 0000000..1070b06
--- /dev/null
@@ -0,0 +1,57 @@
+* AppliedMicro X-Gene PCIe interface
+
+Required properties:
+- device_type: set to "pci"
+- compatible: should contain "apm,xgene-pcie" to identify the core.
+- reg: A list of physical base address and length for each set of controller
+       registers. Must contain an entry for each entry in the reg-names
+       property.
+- reg-names: Must include the following entries:
+  "csr": controller configuration registers.
+  "cfg": pcie configuration space registers.
+- #address-cells: set to <3>
+- #size-cells: set to <2>
+- ranges: ranges for the outbound memory, I/O regions.
+- dma-ranges: ranges for the inbound memory regions.
+- #interrupt-cells: set to <1>
+- interrupt-map-mask and interrupt-map: standard PCI properties
+       to define the mapping of the PCIe interface to interrupt
+       numbers.
+- clocks: from common clock binding: handle to pci clock.
+
+Optional properties:
+- status: Either "ok" or "disabled".
+- dma-coherent: Present if dma operations are coherent
+
+Example:
+
+SoC specific DT Entry:
+
+       pcie0: pcie@1f2b0000 {
+               status = "disabled";
+               device_type = "pci";
+               compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+               #interrupt-cells = <1>;
+               #size-cells = <2>;
+               #address-cells = <3>;
+               reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+                       0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+               reg-names = "csr", "cfg";
+               ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+                         0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+               dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+                             0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+               interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+               interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+                                0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+                                0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+                                0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+               dma-coherent;
+               clocks = <&pcie0clk 0>;
+       };
+
+
+Board specific DT Entry:
+       &pcie0 {
+               status = "ok";
+       };
diff --git a/Documentation/devicetree/bindings/pci/xilinx-pcie.txt b/Documentation/devicetree/bindings/pci/xilinx-pcie.txt
new file mode 100644 (file)
index 0000000..3e2c88d
--- /dev/null
@@ -0,0 +1,62 @@
+* Xilinx AXI PCIe Root Port Bridge DT description
+
+Required properties:
+- #address-cells: Address representation for root ports, set to <3>
+- #size-cells: Size representation for root ports, set to <2>
+- #interrupt-cells: specifies the number of cells needed to encode an
+       interrupt source. The value must be 1.
+- compatible: Should contain "xlnx,axi-pcie-host-1.00.a"
+- reg: Should contain AXI PCIe registers location and length
+- device_type: must be "pci"
+- interrupts: Should contain AXI PCIe interrupt
+- interrupt-map-mask,
+  interrupt-map: standard PCI properties to define the mapping of the
+       PCI interface to interrupt numbers.
+- ranges: ranges for the PCI memory regions (I/O space region is not
+       supported by hardware)
+       Please refer to the standard PCI bus binding document for a more
+       detailed explanation
+
+Optional properties:
+- bus-range: PCI bus numbers covered
+
+Interrupt controller child node
++++++++++++++++++++++++++++++++
+Required properties:
+- interrupt-controller: identifies the node as an interrupt controller
+- #address-cells: specifies the number of cells needed to encode an
+       address. The value must be 0.
+- #interrupt-cells: specifies the number of cells needed to encode an
+       interrupt source. The value must be 1.
+
+NOTE:
+The core provides a single interrupt for both INTx/MSI messages. So,
+created a interrupt controller node to support 'interrupt-map' DT
+functionality.  The driver will create an IRQ domain for this map, decode
+the four INTx interrupts in ISR and route them to this domain.
+
+
+Example:
+++++++++
+
+       pci_express: axi-pcie@50000000 {
+               #address-cells = <3>;
+               #size-cells = <2>;
+               #interrupt-cells = <1>;
+               compatible = "xlnx,axi-pcie-host-1.00.a";
+               reg = < 0x50000000 0x10000000 >;
+               device_type = "pci";
+               interrupts = < 0 52 4 >;
+               interrupt-map-mask = <0 0 0 7>;
+               interrupt-map = <0 0 0 1 &pcie_intc 1>,
+                               <0 0 0 2 &pcie_intc 2>,
+                               <0 0 0 3 &pcie_intc 3>,
+                               <0 0 0 4 &pcie_intc 4>;
+               ranges = < 0x02000000 0 0x60000000 0x60000000 0 0x10000000 >;
+
+               pcie_intc: interrupt-controller {
+                       interrupt-controller;
+                       #address-cells = <0>;
+                       #interrupt-cells = <1>;
+               }
+       };
diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
new file mode 100644 (file)
index 0000000..98c1667
--- /dev/null
@@ -0,0 +1,49 @@
+* Generic PM domains
+
+System on chip designs are often divided into multiple PM domains that can be
+used for power gating of selected IP blocks for power saving by reduced leakage
+current.
+
+This device tree binding can be used to bind PM domain consumer devices with
+their PM domains provided by PM domain providers. A PM domain provider can be
+represented by any node in the device tree and can provide one or more PM
+domains. A consumer node can refer to the provider by a phandle and a set of
+phandle arguments (so called PM domain specifiers) of length specified by the
+#power-domain-cells property in the PM domain provider node.
+
+==PM domain providers==
+
+Required properties:
+ - #power-domain-cells : Number of cells in a PM domain specifier;
+   Typically 0 for nodes representing a single PM domain and 1 for nodes
+   providing multiple PM domains (e.g. power controllers), but can be any value
+   as specified by device tree binding documentation of particular provider.
+
+Example:
+
+       power: power-controller@12340000 {
+               compatible = "foo,power-controller";
+               reg = <0x12340000 0x1000>;
+               #power-domain-cells = <1>;
+       };
+
+The node above defines a power controller that is a PM domain provider and
+expects one cell as its phandle argument.
+
+==PM domain consumers==
+
+Required properties:
+ - power-domains : A phandle and PM domain specifier as defined by bindings of
+                   the power controller specified by phandle.
+
+Example:
+
+       leaky-device@12350000 {
+               compatible = "foo,i-leak-current";
+               reg = <0x12350000 0x1000>;
+               power-domains = <&power 0>;
+       };
+
+The node above defines a typical PM domain consumer device, which is located
+inside a PM domain with index 0 of a power controller represented by a node
+with the label "power".
diff --git a/Documentation/devicetree/bindings/power/rockchip-io-domain.txt b/Documentation/devicetree/bindings/power/rockchip-io-domain.txt
new file mode 100644 (file)
index 0000000..6fbf6e7
--- /dev/null
@@ -0,0 +1,83 @@
+Rockchip SRAM for IO Voltage Domains:
+-------------------------------------
+
+IO domain voltages on some Rockchip SoCs are variable but need to be
+kept in sync between the regulators and the SoC using a special
+register.
+
+A specific example using rk3288:
+- If the regulator hooked up to a pin like SDMMC0_VDD is 3.3V then
+  bit 7 of GRF_IO_VSEL needs to be 0.  If the regulator hooked up to
+  that same pin is 1.8V then bit 7 of GRF_IO_VSEL needs to be 1.
+
+Said another way, this driver simply handles keeping bits in the SoC's
+general register file (GRF) in sync with the actual value of a voltage
+hooked up to the pins.
+
+Note that this driver specifically doesn't include:
+- any logic for deciding what voltage we should set regulators to
+- any logic for deciding whether regulators (or internal SoC blocks)
+  should have power or not have power
+
+If there were some other software that had the smarts of making
+decisions about regulators, it would work in conjunction with this
+driver.  When that other software adjusted a regulator's voltage then
+this driver would handle telling the SoC about it.  A good example is
+vqmmc for SD.  In that case the dw_mmc driver simply is told about a
+regulator.  It changes the regulator between 3.3V and 1.8V at the
+right time.  This driver notices the change and makes sure that the
+SoC is on the same page.
+
+
+Required properties:
+- compatible: should be one of:
+  - "rockchip,rk3188-io-voltage-domain" for rk3188
+  - "rockchip,rk3288-io-voltage-domain" for rk3288
+- rockchip,grf: phandle to the syscon managing the "general register files"
+
+
+You specify supplies using the standard regulator bindings by including
+a phandle the the relevant regulator.  All specified supplies must be able
+to report their voltage.  The IO Voltage Domain for any non-specified
+supplies will be not be touched.
+
+Possible supplies for rk3188:
+- ap0-supply:    The supply connected to AP0_VCC.
+- ap1-supply:    The supply connected to AP1_VCC.
+- cif-supply:    The supply connected to CIF_VCC.
+- flash-supply:  The supply connected to FLASH_VCC.
+- lcdc0-supply:  The supply connected to LCD0_VCC.
+- lcdc1-supply:  The supply connected to LCD1_VCC.
+- vccio0-supply: The supply connected to VCCIO0.
+- vccio1-supply: The supply connected to VCCIO1.
+                 Sometimes also labeled VCCIO1 and VCCIO2.
+
+Possible supplies for rk3288:
+- audio-supply:  The supply connected to APIO4_VDD.
+- bb-supply:     The supply connected to APIO5_VDD.
+- dvp-supply:    The supply connected to DVPIO_VDD.
+- flash0-supply: The supply connected to FLASH0_VDD.  Typically for eMMC
+- flash1-supply: The supply connected to FLASH1_VDD.  Also known as SDIO1.
+- gpio30-supply: The supply connected to APIO1_VDD.
+- gpio1830       The supply connected to APIO2_VDD.
+- lcdc-supply:   The supply connected to LCDC_VDD.
+- sdcard-supply: The supply connected to SDMMC0_VDD.
+- wifi-supply:   The supply connected to APIO3_VDD.  Also known as SDIO0.
+
+
+Example:
+
+       io-domains {
+               compatible = "rockchip,rk3288-io-voltage-domain";
+               rockchip,grf = <&grf>;
+
+               audio-supply = <&vcc18_codec>;
+               bb-supply = <&vcc33_io>;
+               dvp-supply = <&vcc_18>;
+               flash0-supply = <&vcc18_flashio>;
+               gpio1830-supply = <&vcc33_io>;
+               gpio30-supply = <&vcc33_pmuio>;
+               lcdc-supply = <&vcc33_lcd>;
+               sdcard-supply = <&vccio_sd>;
+               wifi-supply = <&vcc18_wl>;
+       };
diff --git a/Documentation/devicetree/bindings/sound/adi,ssm2602.txt b/Documentation/devicetree/bindings/sound/adi,ssm2602.txt
new file mode 100644 (file)
index 0000000..3b3302f
--- /dev/null
@@ -0,0 +1,19 @@
+Analog Devices SSM2602, SSM2603 and SSM2604 I2S audio CODEC devices
+
+SSM2602 support both I2C and SPI as the configuration interface,
+the selection is made by the MODE strap-in pin.
+SSM2603 and SSM2604 only support I2C as the configuration interface.
+
+Required properties:
+
+  - compatible : One of "adi,ssm2602", "adi,ssm2603" or "adi,ssm2604"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+ Example:
+
+       ssm2602: ssm2602@1a {
+               compatible = "adi,ssm2602";
+               reg = <0x1a>;
+       };
diff --git a/Documentation/devicetree/bindings/sound/cs35l32.txt b/Documentation/devicetree/bindings/sound/cs35l32.txt
new file mode 100644 (file)
index 0000000..1417d3f
--- /dev/null
@@ -0,0 +1,62 @@
+CS35L32 audio CODEC
+
+Required properties:
+
+  - compatible : "cirrus,cs35l32"
+
+  - reg : the I2C address of the device for I2C. Address is determined by the level
+  of the AD0 pin. Level 0 is 0x40 while Level 1 is 0x41.
+
+  - VA-supply, VP-supply : power supplies for the device,
+  as covered in Documentation/devicetree/bindings/regulator/regulator.txt.
+
+Optional properties:
+
+  - reset-gpios : a GPIO spec for the reset pin. If specified, it will be
+  deasserted before communication to the codec starts.
+
+  - cirrus,boost-manager : Boost voltage control.
+  0 = Automatically managed.  Boost-converter output voltage is the higher
+  of the two: Class G or adaptive LED voltage.
+  1 = Automatically managed irrespective of audio, adapting for low-power
+  dissipation when LEDs are ON, and operating in Fixed-Boost Bypass Mode
+  if LEDs are OFF (VBST = VP).
+  2 = (Default) Boost voltage fixed in Bypass Mode (VBST = VP).
+  3 = Boost voltage fixed at 5 V.
+
+  - cirrus,sdout-datacfg : Data configuration for dual CS35L32 applications only.
+  Determines the data packed in a two-CS35L32 configuration.
+  0 = Left/right channels VMON[11:0], IMON[11:0], VPMON[7:0].
+  1 = Left/right channels VMON[11:0], IMON[11:0], STATUS.
+  2 = (Default) left/right channels VMON[15:0], IMON [15:0].
+  3 = Left/right channels VPMON[7:0], STATUS.
+
+  - cirrus,sdout-share : SDOUT sharing. Determines whether one or two CS35L32
+  devices are on board sharing SDOUT.
+  0 = (Default) One IC.
+  1 = Two IC's.
+
+  - cirrus,battery-recovery : Low battery nominal recovery threshold, rising VP.
+  0 = 3.1V
+  1 = 3.2V
+  2 = 3.3V (Default)
+  3 = 3.4V
+
+  - cirrus,battery-threshold : Low battery nominal threshold, falling VP.
+  0 = 3.1V
+  1 = 3.2V
+  2 = 3.3V
+  3 = 3.4V (Default)
+  4 = 3.5V
+  5 = 3.6V
+
+Example:
+
+codec: codec@40 {
+       compatible = "cirrus,cs35l32";
+       reg = <0x40>;
+       reset-gpios = <&gpio 10 0>;
+       cirrus,boost-manager = <0x03>;
+       cirrus,sdout-datacfg = <0x02>;
+       VA-supply = <&reg_audio>;
+};
diff --git a/Documentation/devicetree/bindings/sound/es8328.txt b/Documentation/devicetree/bindings/sound/es8328.txt
new file mode 100644 (file)
index 0000000..30ea8a3
--- /dev/null
@@ -0,0 +1,38 @@
+Everest ES8328 audio CODEC
+
+This device supports both I2C and SPI.
+
+Required properties:
+
+  - compatible : "everest,es8328"
+  - DVDD-supply : Regulator providing digital core supply voltage 1.8 - 3.6V
+  - AVDD-supply : Regulator providing analog supply voltage 3.3V
+  - PVDD-supply : Regulator providing digital IO supply voltage 1.8 - 3.6V
+  - IPVDD-supply : Regulator providing analog output voltage 3.3V
+  - clocks : A 22.5792 or 11.2896 MHz clock
+  - reg : the I2C address of the device for I2C, the chip select number for SPI
+
+Pins on the device (for linking into audio routes):
+
+  * LOUT1
+  * LOUT2
+  * ROUT1
+  * ROUT2
+  * LINPUT1
+  * RINPUT1
+  * LINPUT2
+  * RINPUT2
+  * Mic Bias
+
+
+Example:
+
+codec: es8328@11 {
+       compatible = "everest,es8328";
+       DVDD-supply = <&reg_3p3v>;
+       AVDD-supply = <&reg_3p3v>;
+       PVDD-supply = <&reg_3p3v>;
+       HPVDD-supply = <&reg_3p3v>;
+       clocks = <&clks 169>;
+       reg = <0x11>;
+};
index aeb8c4a0b88d4de37d17e97238d6beb16c6fd20e..52f5b6bf3e8ed7323e7c5da7998731a0ae1c495c 100644 (file)
@@ -7,7 +7,8 @@ other DSPs. It has up to six transmitters and four receivers.
 
 Required properties:
 
-  - compatible : Compatible list, must contain "fsl,imx35-esai".
+  - compatible : Compatible list, must contain "fsl,imx35-esai" or
+                "fsl,vf610-esai"
 
   - reg : Offset and length of the register set for the device.
 
index 3aa4a8f528f486b1b647e3720adbfef6068bbf6c..5b76be45d18bfecce403bf4b1f09730bb74a3c0c 100644 (file)
@@ -58,13 +58,7 @@ Optional properties:
                    Documentation/devicetree/bindings/dma/dma.txt.
 - dma-names:       Two dmas have to be defined, "tx" and "rx", if fsl,imx-fiq
                    is not defined.
-- fsl,mode:         The operating mode for the SSI interface.
-                    "i2s-slave" - I2S mode, SSI is clock slave
-                    "i2s-master" - I2S mode, SSI is clock master
-                    "lj-slave" - left-justified mode, SSI is clock slave
-                    "lj-master" - l.j. mode, SSI is clock master
-                    "rj-slave" - right-justified mode, SSI is clock slave
-                    "rj-master" - r.j., SSI is clock master
+- fsl,mode:         The operating mode for the AC97 interface only.
                     "ac97-slave" - AC97 mode, SSI is clock slave
                     "ac97-master" - AC97 mode, SSI is clock master
 
diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
new file mode 100644 (file)
index 0000000..a96774c
--- /dev/null
@@ -0,0 +1,82 @@
+Freescale Generic ASoC Sound Card with ASRC support
+
+The Freescale Generic ASoC Sound Card can be used, ideally, for all Freescale
+SoCs connecting with external CODECs.
+
+The idea of this generic sound card is a bit like ASoC Simple Card. However,
+for Freescale SoCs (especially those released in recent years), most of them
+have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And
+this is a specific feature that might be painstakingly controlled and merged
+into the Simple Card.
+
+So having this generic sound card allows all Freescale SoC users to benefit
+from the simplification of a new card support and the capability of the wide
+sample rates support through ASRC.
+
+Note: The card is initially designed for those sound cards who use I2S and
+      PCM DAI formats. However, it'll be also possible to support those non
+      I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long
+      as the driver has been properly upgraded.
+
+
+The compatible list for this generic sound card currently:
+ "fsl,imx-audio-cs42888"
+
+ "fsl,imx-audio-wm8962"
+ (compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt)
+
+ "fsl,imx-audio-sgtl5000"
+ (compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt)
+
+Required properties:
+
+  - compatible         : Contains one of entries in the compatible list.
+
+  - model              : The user-visible name of this sound complex
+
+  - audio-cpu          : The phandle of an CPU DAI controller
+
+  - audio-codec                : The phandle of an audio codec
+
+  - audio-routing      : A list of the connections between audio components.
+                         Each entry is a pair of strings, the first being the
+                         connection's sink, the second being the connection's
+                         source. There're a few pre-designed board connectors:
+                          * Line Out Jack
+                          * Line In Jack
+                          * Headphone Jack
+                          * Mic Jack
+                          * Ext Spk
+                          * AMIC (stands for Analog Microphone Jack)
+                          * DMIC (stands for Digital Microphone Jack)
+
+                         Note: The "Mic Jack" and "AMIC" are redundant while
+                               coexsiting in order to support the old bindings
+                               of wm8962 and sgtl5000.
+
+Optional properties:
+
+  - audio-asrc         : The phandle of ASRC. It can be absent if there's no
+                         need to add ASRC support via DPCM.
+
+Example:
+sound-cs42888 {
+       compatible = "fsl,imx-audio-cs42888";
+       model = "cs42888-audio";
+       audio-cpu = <&esai>;
+       audio-asrc = <&asrc>;
+       audio-codec = <&cs42888>;
+       audio-routing =
+               "Line Out Jack", "AOUT1L",
+               "Line Out Jack", "AOUT1R",
+               "Line Out Jack", "AOUT2L",
+               "Line Out Jack", "AOUT2R",
+               "Line Out Jack", "AOUT3L",
+               "Line Out Jack", "AOUT3R",
+               "Line Out Jack", "AOUT4L",
+               "Line Out Jack", "AOUT4R",
+               "AIN1L", "Line In Jack",
+               "AIN1R", "Line In Jack",
+               "AIN2L", "Line In Jack",
+               "AIN2R", "Line In Jack";
+};
index 0f4e23828190f7bda8d9b829ab7410276b2ead5f..4956b14d4b06175a1d9ba5df9cdb9f72b0813ec5 100644 (file)
@@ -18,12 +18,26 @@ Required properties:
 - pinctrl-names: Must contain a "default" entry.
 - pinctrl-NNN: One property must exist for each entry in pinctrl-names.
   See ../pinctrl/pinctrl-bindings.txt for details of the property values.
-- big-endian-regs: If this property is absent, the little endian mode will
-  be in use as default, or the big endian mode will be in use for all the
-  device registers.
-- big-endian-data: If this property is absent, the little endian mode will
-  be in use as default, or the big endian mode will be in use for all the
-  fifo data.
+- big-endian: Boolean property, required if all the FTM_PWM registers
+  are big-endian rather than little-endian.
+- lsb-first: Configures whether the LSB or the MSB is transmitted first for
+  the fifo data. If this property is absent, the MSB is transmitted first as
+  default, or the LSB is transmitted first.
+- fsl,sai-synchronous-rx: This is a boolean property. If present, indicating
+  that SAI will work in the synchronous mode (sync Tx with Rx) which means
+  both the transimitter and receiver will send and receive data by following
+  receiver's bit clocks and frame sync clocks.
+- fsl,sai-asynchronous: This is a boolean property. If present, indicating
+  that SAI will work in the asynchronous mode, which means both transimitter
+  and receiver will send and receive data by following their own bit clocks
+  and frame sync clocks separately.
+
+Note:
+- If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
+  default synchronous mode (sync Rx with Tx) will be used, which means both
+  transimitter and receiver will send and receive data by following clocks
+  of transimitter.
+- fsl,sai-asynchronous and fsl,sai-synchronous-rx are exclusive.
 
 Example:
 sai2: sai@40031000 {
@@ -38,6 +52,6 @@ sai2: sai@40031000 {
              dma-names = "tx", "rx";
              dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>,
                   <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>;
-             big-endian-regs;
-             big-endian-data;
+             big-endian;
+             lsb-first;
 };
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-es8328.txt b/Documentation/devicetree/bindings/sound/imx-audio-es8328.txt
new file mode 100644 (file)
index 0000000..07b68ab
--- /dev/null
@@ -0,0 +1,60 @@
+Freescale i.MX audio complex with ES8328 codec
+
+Required properties:
+- compatible       : "fsl,imx-audio-es8328"
+- model            : The user-visible name of this sound complex
+- ssi-controller   : The phandle of the i.MX SSI controller
+- jack-gpio        : Optional GPIO for headphone jack
+- audio-amp-supply : Power regulator for speaker amps
+- audio-codec      : The phandle of the ES8328 audio codec
+- audio-routing    : A list of the connections between audio components.
+                     Each entry is a pair of strings, the first being the
+                    connection's sink, the second being the connection's
+                    source. Valid names could be power supplies, ES8328
+                    pins, and the jacks on the board:
+
+                       Power supplies:
+                          * audio-amp
+
+                       ES8328 pins:
+                          * LOUT1
+                          * LOUT2
+                          * ROUT1
+                          * ROUT2
+                          * LINPUT1
+                          * LINPUT2
+                          * RINPUT1
+                          * RINPUT2
+                          * Mic PGA
+
+                       Board connectors:
+                          * Headphone
+                          * Speaker
+                          * Mic Jack
+- mux-int-port     : The internal port of the i.MX audio muxer (AUDMUX)
+- mux-ext-port     : The external port of the i.MX audio muxer (AUDMIX)
+
+Note: The AUDMUX port numbering should start at 1, which is consistent with
+hardware manual.
+
+Example:
+
+sound {
+       compatible = "fsl,imx-audio-es8328";
+       model = "imx-audio-es8328";
+       ssi-controller = <&ssi1>;
+       audio-codec = <&codec>;
+       jack-gpio = <&gpio5 15 0>;
+       audio-amp-supply = <&reg_audio_amp>;
+       audio-routing =
+               "Speaker", "LOUT2",
+               "Speaker", "ROUT2",
+               "Speaker", "audio-amp",
+               "Headphone", "ROUT1",
+               "Headphone", "LOUT1",
+               "LINPUT1", "Mic Jack",
+               "RINPUT1", "Mic Jack",
+               "Mic Jack", "Mic Bias";
+       mux-int-port = <1>;
+       mux-ext-port = <3>;
+};
index 9c7c55c7137046f0a539d5df15a8febe56e32a56..c949abc2992f5fd1d8c2e1fcb2eb5a75521037a6 100644 (file)
@@ -25,6 +25,7 @@ Required properties:
 
 Optional properties:
 - nvidia,hp-det-gpios : The GPIO that detect headphones are plugged in
+- nvidia,mic-det-gpios : The GPIO that detect microphones are plugged in
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/sound/rt5677.txt b/Documentation/devicetree/bindings/sound/rt5677.txt
new file mode 100644 (file)
index 0000000..0701b83
--- /dev/null
@@ -0,0 +1,59 @@
+RT5677 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+- compatible : "realtek,rt5677".
+
+- reg : The I2C address of the device.
+
+- interrupts : The CODEC's interrupt output.
+
+- gpio-controller : Indicates this device is a GPIO controller.
+
+- #gpio-cells : Should be two. The first cell is the pin number and the
+  second cell is used to specify optional parameters (currently unused).
+
+Optional properties:
+
+- realtek,pow-ldo2-gpio : The GPIO that controls the CODEC's POW_LDO2 pin.
+
+- realtek,in1-differential
+- realtek,in2-differential
+- realtek,lout1-differential
+- realtek,lout2-differential
+- realtek,lout3-differential
+  Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential,
+  rather than single-ended.
+
+Pins on the device (for linking into audio routes):
+
+  * IN1P
+  * IN1N
+  * IN2P
+  * IN2N
+  * MICBIAS1
+  * DMIC1
+  * DMIC2
+  * DMIC3
+  * DMIC4
+  * LOUT1
+  * LOUT2
+  * LOUT3
+
+Example:
+
+rt5677 {
+       compatible = "realtek,rt5677";
+       reg = <0x2c>;
+       interrupt-parent = <&gpio>;
+       interrupts = <TEGRA_GPIO(W, 3) GPIO_ACTIVE_HIGH>;
+
+       gpio-controller;
+       #gpio-cells = <2>;
+
+       realtek,pow-ldo2-gpio =
+               <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
+       realtek,in1-differential = "true";
+};
index c2e9841dfce4e2c258bc85a0f37d4934aa7cf14f..c3cba600bf112b70b4667e2001fe6c02ccb8da93 100644 (file)
@@ -17,6 +17,10 @@ Optional properties:
                                          source.
 - simple-audio-card,mclk-fs             : Multiplication factor between stream rate and codec
                                          mclk.
+- simple-audio-card,hp-det-gpio                : Reference to GPIO that signals when
+                                         headphones are attached.
+- simple-audio-card,mic-det-gpio       : Reference to GPIO that signals when
+                                         a microphone is attached.
 
 Optional subnodes:
 
diff --git a/Documentation/devicetree/bindings/sound/ssm4567.txt b/Documentation/devicetree/bindings/sound/ssm4567.txt
new file mode 100644 (file)
index 0000000..ec3d9e7
--- /dev/null
@@ -0,0 +1,15 @@
+Analog Devices SSM4567 audio amplifier
+
+This device supports I2C only.
+
+Required properties:
+  - compatible : Must be "adi,ssm4567"
+  - reg : the I2C address of the device. This will either be 0x34 (LR_SEL/ADDR connected to AGND),
+       0x35 (LR_SEL/ADDR connected to IOVDD) or 0x36 (LR_SEL/ADDR open).
+
+Example:
+
+       ssm4567: ssm4567@34 {
+               compatible = "adi,ssm4567";
+               reg = <0x34>;
+       };
diff --git a/Documentation/devicetree/bindings/timer/amlogic,meson6-timer.txt b/Documentation/devicetree/bindings/timer/amlogic,meson6-timer.txt
new file mode 100644 (file)
index 0000000..a092053
--- /dev/null
@@ -0,0 +1,15 @@
+Amlogic Meson6 SoCs Timer Controller
+
+Required properties:
+
+- compatible : should be "amlogic,meson6-timer"
+- reg : Specifies base physical address and size of the registers.
+- interrupts : The interrupt of the first timer
+
+Example:
+
+timer@c1109940 {
+       compatible = "amlogic,meson6-timer";
+       reg = <0xc1109940 0x14>;
+       interrupts = <0 10 1>;
+};
index a17418b0ece324b657355158afdd8ff4b1cc573c..1a05c1b243c1d04834b56e591deb763ffdc52b58 100644 (file)
@@ -11,15 +11,47 @@ datasheets.
 
 Required Properties:
 
-  - compatible: must contain one of the following.
-    - "renesas,cmt-32" for the 32-bit CMT
+  - compatible: must contain one or more of the following:
+    - "renesas,cmt-32-r8a7740" for the r8a7740 32-bit CMT
+               (CMT0)
+    - "renesas,cmt-32-sh7372" for the sh7372 32-bit CMT
+               (CMT0)
+    - "renesas,cmt-32-sh73a0" for the sh73a0 32-bit CMT
+               (CMT0)
+    - "renesas,cmt-32" for all 32-bit CMT without fast clock support
                (CMT0 on sh7372, sh73a0 and r8a7740)
-    - "renesas,cmt-32-fast" for the 32-bit CMT with fast clock support
+               This is a fallback for the above renesas,cmt-32-* entries.
+
+    - "renesas,cmt-32-fast-r8a7740" for the r8a7740 32-bit CMT with fast
+               clock support (CMT[234])
+    - "renesas,cmt-32-fast-sh7372" for the sh7372 32-bit CMT with fast
+               clock support (CMT[234])
+    - "renesas,cmt-32-fast-sh73a0" for the sh73A0 32-bit CMT with fast
+               clock support (CMT[234])
+    - "renesas,cmt-32-fast" for all 32-bit CMT with fast clock support
                (CMT[234] on sh7372, sh73a0 and r8a7740)
-    - "renesas,cmt-48" for the 48-bit CMT
+               This is a fallback for the above renesas,cmt-32-fast-* entries.
+
+    - "renesas,cmt-48-sh7372" for the sh7372 48-bit CMT
+               (CMT1)
+    - "renesas,cmt-48-sh73a0" for the sh73A0 48-bit CMT
+               (CMT1)
+    - "renesas,cmt-48-r8a7740" for the r8a7740 48-bit CMT
+               (CMT1)
+    - "renesas,cmt-48" for all non-second generation 48-bit CMT
                (CMT1 on sh7372, sh73a0 and r8a7740)
-    - "renesas,cmt-48-gen2" for the second generation 48-bit CMT
+               This is a fallback for the above renesas,cmt-48-* entries.
+
+    - "renesas,cmt-48-r8a73a4" for the r8a73a4 48-bit CMT
+               (CMT[01])
+    - "renesas,cmt-48-r8a7790" for the r8a7790 48-bit CMT
+               (CMT[01])
+    - "renesas,cmt-48-r8a7791" for the r8a7791 48-bit CMT
+               (CMT[01])
+    - "renesas,cmt-48-gen2" for all second generation 48-bit CMT
                (CMT[01] on r8a73a4, r8a7790 and r8a7791)
+               This is a fallback for the renesas,cmt-48-r8a73a4,
+               renesas,cmt-48-r8a7790 and renesas,cmt-48-r8a7791 entries.
 
   - reg: base address and length of the registers block for the timer module.
   - interrupts: interrupt-specifier for the timer, one per channel.
@@ -36,7 +68,7 @@ Example: R8A7790 (R-Car H2) CMT0 node
        them channels 0 and 1 in the documentation.
 
        cmt0: timer@ffca0000 {
-               compatible = "renesas,cmt-48-gen2";
+               compatible = "renesas,cmt-48-r8a7790", "renesas,cmt-48-gen2";
                reg = <0 0xffca0000 0 0x1004>;
                interrupts = <0 142 IRQ_TYPE_LEVEL_HIGH>,
                             <0 142 IRQ_TYPE_LEVEL_HIGH>;
index 917453f826bc42e6a3a8a9995c88b58dc77ef254..d9a8d5af1a21270ff2e79810c86079bac0757df2 100644 (file)
@@ -8,7 +8,10 @@ are independent. The MTU2 hardware supports five channels indexed from 0 to 4.
 
 Required Properties:
 
-  - compatible: must contain "renesas,mtu2"
+  - compatible: must be one or more of the following:
+    - "renesas,mtu2-r7s72100" for the r7s72100 MTU2
+    - "renesas,mtu2" for any MTU2
+      This is a fallback for the above renesas,mtu2-* entries
 
   - reg: base address and length of the registers block for the timer module.
 
@@ -26,7 +29,7 @@ Required Properties:
 Example: R7S72100 (RZ/A1H) MTU2 node
 
        mtu2: timer@fcff0000 {
-               compatible = "renesas,mtu2";
+               compatible = "renesas,mtu2-r7s72100", "renesas,mtu2";
                reg = <0xfcff0000 0x400>;
                interrupts = <0 139 IRQ_TYPE_LEVEL_HIGH>,
                             <0 146 IRQ_TYPE_LEVEL_HIGH>,
index 425d0c5f4aee4bee45b9f8c7063c8751120ac495..7db89fb2544411b5418caf94cb74fc16a648c188 100644 (file)
@@ -8,7 +8,10 @@ are independent. The TMU hardware supports up to three channels.
 
 Required Properties:
 
-  - compatible: must contain "renesas,tmu"
+  - compatible: must contain one or more of the following:
+    - "renesas,tmu-r8a7779" for the r8a7779 TMU
+    - "renesas,tmu" for any TMU.
+      This is a fallback for the above renesas,tmu-* entries
 
   - reg: base address and length of the registers block for the timer module.
 
@@ -27,7 +30,7 @@ Optional Properties:
 Example: R8A7779 (R-Car H1) TMU0 node
 
        tmu0: timer@ffd80000 {
-               compatible = "renesas,tmu";
+               compatible = "renesas,tmu-r8a7779", "renesas,tmu";
                reg = <0xffd80000 0x30>;
                interrupts = <0 32 IRQ_TYPE_LEVEL_HIGH>,
                             <0 33 IRQ_TYPE_LEVEL_HIGH>,
index 653beaa392dc0495e9db612f775d386ab5014dbc..f67e3f84e8bc45077c51f68766eceba16215857f 100644 (file)
@@ -46,11 +46,13 @@ dmo Data Modul AG
 ebv    EBV Elektronik
 edt    Emerging Display Technologies
 emmicro        EM Microelectronic
+energymicro    Silicon Laboratories (formerly Energy Micro AS)
 epcos  EPCOS AG
 epfl   Ecole Polytechnique Fédérale de Lausanne
 epson  Seiko Epson Corp.
 est    ESTeem Wireless Modems
 eukrea  Eukréa Electromatique
+everest        Everest Semiconductor Co. Ltd.
 excito Excito
 fcs    Fairchild Semiconductor
 fsl    Freescale Semiconductor
@@ -61,6 +63,7 @@ globalscale   Globalscale Technologies, Inc.
 gmt    Global Mixed-mode Technology, Inc.
 google Google, Inc.
 gumstix        Gumstix, Inc.
+gw     Gateworks Corporation
 haoyu  Haoyu Microelectronic Co. Ltd.
 hisilicon      Hisilicon Limited.
 honeywell      Honeywell
@@ -70,6 +73,7 @@ ibm   International Business Machines (IBM)
 idt    Integrated Device Technologies, Inc.
 iom    Iomega Corporation
 img    Imagination Technologies Ltd.
+innolux        Innolux Corporation
 intel  Intel Corporation
 intercontrol   Inter Control Group
 isee   ISEE 2007 S.L.
@@ -131,6 +135,7 @@ simtek
 sii    Seiko Instruments, Inc.
 silergy        Silergy Corp.
 sirf   SiRF Technology, Inc.
+sitronix       Sitronix Technology Corporation
 smsc   Standard Microsystems Corporation
 snps   Synopsys, Inc.
 solidrun       SolidRun
index 1f013bd0d32026f92e72351469dff05903d233b0..77685185cf3b77d465a63dd667b794cb57eea7a9 100644 (file)
@@ -51,6 +51,8 @@ Table of Contents
 
   VIII - Specifying device power management information (sleep property)
 
+  IX - Specifying dma bus information
+
   Appendix A - Sample SOC node for MPC8540
 
 
@@ -1332,6 +1334,57 @@ reasonably grouped in this manner, then create a virtual sleep controller
 (similar to an interrupt nexus, except that defining a standardized
 sleep-map should wait until its necessity is demonstrated).
 
+IX - Specifying dma bus information
+
+Some devices may have DMA memory range shifted relatively to the beginning of
+RAM, or even placed outside of kernel RAM. For example, the Keystone 2 SoC
+worked in LPAE mode with 4G memory has:
+- RAM range: [0x8 0000 0000, 0x8 FFFF FFFF]
+- DMA range: [  0x8000 0000,   0xFFFF FFFF]
+and DMA range is aliased into first 2G of RAM in HW.
+
+In such cases, DMA addresses translation should be performed between CPU phys
+and DMA addresses. The "dma-ranges" property is intended to be used
+for describing the configuration of such system in DT.
+
+In addition, each DMA master device on the DMA bus may or may not support
+coherent DMA operations. The "dma-coherent" property is intended to be used
+for identifying devices supported coherent DMA operations in DT.
+
+* DMA Bus master
+Optional property:
+- dma-ranges: <prop-encoded-array> encoded as arbitrary number of triplets of
+       (child-bus-address, parent-bus-address, length). Each triplet specified
+       describes a contiguous DMA address range.
+       The dma-ranges property is used to describe the direct memory access (DMA)
+       structure of a memory-mapped bus whose device tree parent can be accessed
+       from DMA operations originating from the bus. It provides a means of
+       defining a mapping or translation between the physical address space of
+       the bus and the physical address space of the parent of the bus.
+       (for more information see ePAPR specification)
+
+* DMA Bus child
+Optional property:
+- dma-ranges: <empty> value. if present - It means that DMA addresses
+       translation has to be enabled for this device.
+- dma-coherent: Present if dma operations are coherent
+
+Example:
+soc {
+               compatible = "ti,keystone","simple-bus";
+               ranges = <0x0 0x0 0x0 0xc0000000>;
+               dma-ranges = <0x80000000 0x8 0x00000000 0x80000000>;
+
+               [...]
+
+               usb: usb@2680000 {
+                       compatible = "ti,keystone-dwc3";
+
+                       [...]
+                       dma-coherent;
+               };
+};
+
 Appendix A - Sample SOC node for MPC8540
 ========================================
 
diff --git a/Documentation/devicetree/dynamic-resolution-notes.txt b/Documentation/devicetree/dynamic-resolution-notes.txt
new file mode 100644 (file)
index 0000000..083d232
--- /dev/null
@@ -0,0 +1,25 @@
+Device Tree Dynamic Resolver Notes
+----------------------------------
+
+This document describes the implementation of the in-kernel
+Device Tree resolver, residing in drivers/of/resolver.c and is a
+companion document to Documentation/devicetree/dt-object-internal.txt[1]
+
+How the resolver works
+----------------------
+
+The resolver is given as an input an arbitrary tree compiled with the
+proper dtc option and having a /plugin/ tag. This generates the
+appropriate __fixups__ & __local_fixups__ nodes as described in [1].
+
+In sequence the resolver works by the following steps:
+
+1. Get the maximum device tree phandle value from the live tree + 1.
+2. Adjust all the local phandles of the tree to resolve by that amount.
+3. Using the __local__fixups__ node information adjust all local references
+   by the same amount.
+4. For each property in the __fixups__ node locate the node it references
+   in the live tree. This is the label used to tag the node.
+5. Retrieve the phandle of the target of the fixup.
+6. For each fixup in the property locate the node:property:offset location
+   and replace it with the phandle value.
index 3a2f54d07fc52c7903d02a650e07cca161542582..1e3d5c92b5e3b5b8a90ee49612f0417ed335336c 100644 (file)
@@ -67,14 +67,14 @@ struct device_node {
     ...
  };
 
-Figure 1, describes a generic structure of machine’s un-flattened device tree
+Figure 1, describes a generic structure of machine's un-flattened device tree
 considering only child and sibling pointers. There exists another pointer,
 *parent, that is used to traverse the tree in the reverse direction. So, at
 a particular level the child node and all the sibling nodes will have a parent
-pointer pointing to a common node (e.g. child1, sibling2, sibling3, sibling4’s
+pointer pointing to a common node (e.g. child1, sibling2, sibling3, sibling4's
 parent points to root node)
 
-root (‘/’)
+root ('/')
    |
 child1 -> sibling2 -> sibling3 -> sibling4 -> null
    |         |           |           |
@@ -113,8 +113,8 @@ via the following kernel symbols:
 __dtb_testcases_begin - address marking the start of test data blob
 __dtb_testcases_end   - address marking the end of test data blob
 
-Secondly, it calls of_fdt_unflatten_device_tree() to unflatten the flattened
-blob. And finally, if the machine’s device tree (i.e live tree) is present,
+Secondly, it calls of_fdt_unflatten_tree() to unflatten the flattened
+blob. And finally, if the machine's device tree (i.e live tree) is present,
 then it attaches the unflattened test data tree to the live tree, else it
 attaches itself as a live device tree.
 
@@ -122,7 +122,7 @@ attach_node_and_children() uses of_attach_node() to attach the nodes into the
 live tree as explained below. To explain the same, the test data tree described
  in Figure 2 is attached to the live tree described in Figure 1.
 
-root (‘/’)
+root ('/')
     |
  testcase-data
     |
@@ -138,8 +138,8 @@ root->testcase-data->test-child0->test-child01->test-sibling1->test-sibling2
 
 Figure 2: Example test data tree to be attached to live tree.
 
-According to the scenario above, the live tree is already present so it isn’t
-required to attach the root(‘/’) node. All other nodes are attached by calling
+According to the scenario above, the live tree is already present so it isn't
+required to attach the root('/') node. All other nodes are attached by calling
 of_attach_node() on each node.
 
 In the function of_attach_node(), the new node is attached as the child of the
@@ -148,7 +148,7 @@ replaces the current child and turns it into its sibling. So, when the testcase
 data node is attached to the live tree above (Figure 1), the final structure is
  as shown in Figure 3.
 
-root (‘/’)
+root ('/')
    |
 testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
    |               |          |           |           |
@@ -170,7 +170,7 @@ testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
                                           null
 -----------------------------------------------------------------------
 
-root (‘/’)
+root ('/')
    |
 testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
    |               |          |           |           |
@@ -191,8 +191,8 @@ test-child0 the test-sibling1 is attached that pushes the child node
  as mentioned above.
 
 If a duplicate node is found (i.e. if a node with same full_name property is
-already present in the live tree), then the node isn’t attached rather its
-properties are updated to the live tree’s node by calling the function
+already present in the live tree), then the node isn't attached rather its
+properties are updated to the live tree's node by calling the function
 update_node_properties().
 
 
@@ -205,7 +205,7 @@ whole tree). selftest_data_remove() calls detach_node_and_children() that uses
 of_detach_node() to detach the nodes from the live device tree.
 
 To detach a node, of_detach_node() first updates all_next linked list, by
-attaching the previous node’s allnext to current node’s allnext pointer. And
-then, it either updates the child pointer of given node’s parent to its
-sibling or attaches the previous sibling to the given node’s sibling, as
+attaching the previous node's allnext to current node's allnext pointer. And
+then, it either updates the child pointer of given node's parent to its
+sibling or attaches the previous sibling to the given node's sibling, as
 appropriate. That is it :)
index 40677443c0c5a3ab1b21f440082b2b5ab51664a2..b5ab416cd53a694f0b97ab564a05113c477433cd 100644 (file)
@@ -264,8 +264,10 @@ IIO
 IO region
   devm_release_mem_region()
   devm_release_region()
+  devm_release_resource()
   devm_request_mem_region()
   devm_request_region()
+  devm_request_resource()
 
 IOMAP
   devm_ioport_map()
index 26c623dd3aa34699561a5cbbeeab4592ff161e97..91b43d2738c7935d8a919a6655b52fc95add8541 100755 (executable)
@@ -708,23 +708,25 @@ sub drxk_terratec_htc_stick {
 }
 
 sub it9135 {
-       my $sourcefile = "dvb-usb-it9135.zip";
-       my $url = "http://www.ite.com.tw/uploads/firmware/v3.6.0.0/$sourcefile";
-       my $hash = "1e55f6c8833f1d0ae067c2bb2953e6a9";
-       my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 0);
-       my $outfile = "dvb-usb-it9135.fw";
+       my $url = "http://www.ite.com.tw/uploads/firmware/v3.25.0.0/";
+       my $file1 = "dvb-usb-it9135-01.zip";
        my $fwfile1 = "dvb-usb-it9135-01.fw";
+       my $hash1 = "02fcf11174eda84745dae7e61c5ff9ba";
+       my $file2 = "dvb-usb-it9135-02.zip";
        my $fwfile2 = "dvb-usb-it9135-02.fw";
+       my $hash2 = "d5e1437dc24358578e07999475d4cac9";
 
        checkstandard();
 
-       wgetfile($sourcefile, $url);
-       unzip($sourcefile, $tmpdir);
-       verify("$tmpdir/$outfile", $hash);
-       extract("$tmpdir/$outfile", 64, 8128, "$fwfile1");
-       extract("$tmpdir/$outfile", 12866, 5817, "$fwfile2");
+       wgetfile($file1, $url . $file1);
+       unzip($file1, "");
+       verify("$fwfile1", $hash1);
+
+       wgetfile($file2, $url . $file2);
+       unzip($file2, "");
+       verify("$fwfile2", $hash2);
 
-       "$fwfile1 $fwfile2"
+       "$file1 $file2"
 }
 
 sub tda10071 {
index f1997e9da61f1cb2fbb122b610c648c537520185..94d93b1f8b530d44ab3855cdbbdc220fa8a8c60f 100644 (file)
@@ -464,15 +464,12 @@ prototypes:
                        size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *,
                        size_t, unsigned int);
-       int (*setlease)(struct file *, long, struct file_lock **);
+       int (*setlease)(struct file *, long, struct file_lock **, void **);
        long (*fallocate)(struct file *, int, loff_t, loff_t);
 };
 
 locking rules:
-       All may block except for ->setlease.
-       No VFS locks held on entry except for ->setlease.
-
-->setlease has the file_list_lock held and must not sleep.
+       All may block.
 
 ->llseek() locking has moved from llseek to the individual llseek
 implementations.  If your fs is not using generic_file_llseek, you
@@ -496,6 +493,10 @@ components. And there are other reasons why the current interface is a mess...
 ->read on directories probably must go away - we should just enforce -EISDIR
 in sys_read() and friends.
 
+->setlease operations should call generic_setlease() before or after setting
+the lease within the individual filesystem to record the result of the
+operation
+
 --------------------------- dquot_operations -------------------------------
 prototypes:
        int (*write_dquot) (struct dquot *);
index 61d65cc65c54a333bef994fc49f881ece37032c5..8be1ea3bdd5ae83f190bd19c99c05cef7cfe0f82 100644 (file)
@@ -826,7 +826,7 @@ struct file_operations {
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, struct pipe_inode_info *, size_t, unsigned int);
-       int (*setlease)(struct file *, long arg, struct file_lock **);
+       int (*setlease)(struct file *, long arg, struct file_lock **, void **);
        long (*fallocate)(struct file *, int mode, loff_t offset, loff_t len);
        int (*show_fdinfo)(struct seq_file *m, struct file *f);
 };
@@ -895,8 +895,9 @@ otherwise noted.
   splice_read: called by the VFS to splice data from file to a pipe. This
               method is used by the splice(2) system call
 
-  setlease: called by the VFS to set or release a file lock lease.
-           setlease has the file_lock_lock held and must not sleep.
+  setlease: called by the VFS to set or release a file lock lease. setlease
+           implementations should call generic_setlease to record or remove
+           the lease in the inode after setting it.
 
   fallocate: called by the VFS to preallocate blocks or punch a hole.
 
index 18790c237977b5a585953ffef3e45a0eab64e468..31e0b5db55d89a1e1d9e8da5e9f3fef2a4694450 100644 (file)
@@ -124,7 +124,8 @@ symbol:
 * gpiochip_set_chained_irqchip(): sets up a chained irq handler for a
   gpio_chip from a parent IRQ and passes the struct gpio_chip* as handler
   data. (Notice handler data, since the irqchip data is likely used by the
-  parent irqchip!) This is for the chained type of chip.
+  parent irqchip!) This is for the chained type of chip. This is also used
+  to set up a nested irqchip if NULL is passed as handler.
 
 To use the helpers please keep the following in mind:
 
@@ -178,7 +179,8 @@ does not help since it pins the module to the kernel forever (it calls
 try_module_get()). A GPIO driver can use the following functions instead
 to request and free descriptors without being pinned to the kernel forever.
 
-       int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label)
+       struct gpio_desc *gpiochip_request_own_desc(struct gpio_desc *desc,
+                                                   const char *label)
 
        void gpiochip_free_own_desc(struct gpio_desc *desc)
 
index ee6d30ec152217a20d6e5fea30d790467eae8720..254d2f55345a2786773eacbc60fb100c96400cd5 100644 (file)
@@ -11,7 +11,7 @@ Supported chips:
   Socket S1G2: Athlon (X2), Sempron (X2), Turion X2 (Ultra)
 * AMD Family 12h processors: "Llano" (E2/A4/A6/A8-Series)
 * AMD Family 14h processors: "Brazos" (C/E/G/Z-Series)
-* AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity", "Kaveri"
+* AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity", "Kaveri", "Carrizo"
 * AMD Family 16h processors: "Kabini", "Mullins"
 
   Prefix: 'k10temp'
diff --git a/Documentation/hwmon/menf21bmc b/Documentation/hwmon/menf21bmc
new file mode 100644 (file)
index 0000000..2a273a0
--- /dev/null
@@ -0,0 +1,50 @@
+Kernel driver menf21bmc_hwmon
+=============================
+
+Supported chips:
+       * MEN 14F021P00
+         Prefix: 'menf21bmc_hwmon'
+         Adresses scanned: -
+
+Author: Andreas Werner <andreas.werner@men.de>
+
+Description
+-----------
+
+The menf21bmc is a Board Management Controller (BMC) which provides an I2C
+interface to the host to access the features implemented in the BMC.
+
+This driver gives access to the voltage monitoring feature of the main
+voltages of the board.
+The voltage sensors are connected to the ADC inputs of the BMC which is
+a PIC16F917 Mikrocontroller.
+
+Usage Notes
+-----------
+
+This driver is part of the MFD driver named "menf21bmc" and does
+not auto-detect devices.
+You will have to instantiate the MFD driver explicitly.
+Please see Documentation/i2c/instantiating-devices for
+details.
+
+Sysfs entries
+-------------
+
+The following attributes are supported. All attributes are read only
+The Limits are read once by the driver.
+
+in0_input      +3.3V input voltage
+in1_input      +5.0V input voltage
+in2_input      +12.0V input voltage
+in3_input      +5V Standby input voltage
+in4_input      VBAT (on board battery)
+
+in[0-4]_min    Minimum voltage limit
+in[0-4]_max    Maximum voltage limit
+
+in0_label      "MON_3_3V"
+in1_label      "MON_5V"
+in2_label      "MON_12V"
+in3_label      "5V_STANDBY"
+in4_label      "VBAT"
index 7e240a7c9ab1bff49a57bbb97904f64cf9c63b9e..8136e1fd30fdede7141d70bc00f5c6b8097ed981 100644 (file)
@@ -313,6 +313,7 @@ Code  Seq#(hex)     Include File            Comments
 0xB1   00-1F   PPPoX                   <mailto:mostrows@styx.uwaterloo.ca>
 0xB3   00      linux/mmc/ioctl.h
 0xC0   00-0F   linux/usb/iowarrior.h
+0xCA   00-0F   uapi/misc/cxl.h
 0xCB   00-1F   CBM serial IEC bus      in development:
                                        <mailto:michael.klein@puffin.lb.shuttle.de>
 0xCD   01      linux/reiserfs_fs.h
index d9a452e8fb9b3bb1f34d89ddaa87cb8dd90b04bd..41f7ec1fcf6137af6c07ff71a22ac98cbc52beaa 100644 (file)
@@ -656,7 +656,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        Sets the size of kernel global memory area for
                        contiguous memory allocations and optionally the
                        placement constraint by the physical address range of
-                       memory allocations. For more information, see
+                       memory allocations. A value of 0 disables CMA
+                       altogether. For more information, see
                        include/linux/dma-contiguous.h
 
        cmo_free_hint=  [PPC] Format: { yes | no }
@@ -3158,6 +3159,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
        slram=          [HW,MTD]
 
+       slab_nomerge    [MM]
+                       Disable merging of slabs with similar size. May be
+                       necessary if there is some reason to distinguish
+                       allocs to different slabs. Debug options disable
+                       merging on their own.
+                       For more information see Documentation/vm/slub.txt.
+
        slab_max_order= [MM, SLAB]
                        Determines the maximum allowed order for slabs.
                        A high setting may cause OOMs due to memory
@@ -3193,11 +3201,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        For more information see Documentation/vm/slub.txt.
 
        slub_nomerge    [MM, SLUB]
-                       Disable merging of slabs with similar size. May be
-                       necessary if there is some reason to distinguish
-                       allocs to different slabs. Debug options disable
-                       merging on their own.
-                       For more information see Documentation/vm/slub.txt.
+                       Same with slab_nomerge. This is supported for legacy.
+                       See slab_nomerge for more information.
 
        smart2=         [HW]
                        Format: <io1>[,<io2>[,...,<io8>]]
@@ -3321,11 +3326,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
        tdfx=           [HW,DRM]
 
-       test_suspend=   [SUSPEND]
+       test_suspend=   [SUSPEND][,N]
                        Specify "mem" (for Suspend-to-RAM) or "standby" (for
-                       standby suspend) as the system sleep state to briefly
-                       enter during system startup.  The system is woken from
-                       this state using a wakeup-capable RTC alarm.
+                       standby suspend) or "freeze" (for suspend type freeze)
+                       as the system sleep state during system startup with
+                       the optional capability to repeat N number of times.
+                       The system is woken from this state using a
+                       wakeup-capable RTC alarm.
 
        thash_entries=  [KNL,NET]
                        Set number of hash buckets for TCP connection
index 45134dc2385424fc3ce0ecd01ab20a221f7d8140..ea03abfc97e95dc660b8028559d940357bc1c8f7 100644 (file)
@@ -155,6 +155,7 @@ Under each memory block, you can see 4 files:
 /sys/devices/system/memory/memoryXXX/phys_device
 /sys/devices/system/memory/memoryXXX/state
 /sys/devices/system/memory/memoryXXX/removable
+/sys/devices/system/memory/memoryXXX/valid_zones
 
 'phys_index'      : read-only and contains memory block id, same as XXX.
 'state'           : read-write
@@ -170,6 +171,15 @@ Under each memory block, you can see 4 files:
                     block is removable and a value of 0 indicates that
                     it is not removable. A memory block is removable only if
                     every section in the block is removable.
+'valid_zones'     : read-only: designed to show which zones this memory block
+                   can be onlined to.
+                   The first column shows it's default zone.
+                   "memory6/valid_zones: Normal Movable" shows this memoryblock
+                   can be onlined to ZONE_NORMAL by default and to ZONE_MOVABLE
+                   by online_movable.
+                   "memory7/valid_zones: Movable Normal" shows this memoryblock
+                   can be onlined to ZONE_MOVABLE by default and to ZONE_NORMAL
+                   by online_kernel.
 
 NOTE:
   These directories/files appear after physical memory hotplug phase.
@@ -408,7 +418,6 @@ node if necessary.
   - allowing memory hot-add to ZONE_MOVABLE. maybe we need some switch like
     sysctl or new control file.
   - showing memory block and physical device relationship.
-  - showing memory block is under ZONE_MOVABLE or not
   - test and make it better memory offlining.
   - support HugeTLB page migration and offlining.
   - memmap removing at memory offline.
diff --git a/Documentation/power/suspend-and-interrupts.txt b/Documentation/power/suspend-and-interrupts.txt
new file mode 100644 (file)
index 0000000..6966364
--- /dev/null
@@ -0,0 +1,123 @@
+System Suspend and Device Interrupts
+
+Copyright (C) 2014 Intel Corp.
+Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+
+
+Suspending and Resuming Device IRQs
+-----------------------------------
+
+Device interrupt request lines (IRQs) are generally disabled during system
+suspend after the "late" phase of suspending devices (that is, after all of the
+->prepare, ->suspend and ->suspend_late callbacks have been executed for all
+devices).  That is done by suspend_device_irqs().
+
+The rationale for doing so is that after the "late" phase of device suspend
+there is no legitimate reason why any interrupts from suspended devices should
+trigger and if any devices have not been suspended properly yet, it is better to
+block interrupts from them anyway.  Also, in the past we had problems with
+interrupt handlers for shared IRQs that device drivers implementing them were
+not prepared for interrupts triggering after their devices had been suspended.
+In some cases they would attempt to access, for example, memory address spaces
+of suspended devices and cause unpredictable behavior to ensue as a result.
+Unfortunately, such problems are very difficult to debug and the introduction
+of suspend_device_irqs(), along with the "noirq" phase of device suspend and
+resume, was the only practical way to mitigate them.
+
+Device IRQs are re-enabled during system resume, right before the "early" phase
+of resuming devices (that is, before starting to execute ->resume_early
+callbacks for devices).  The function doing that is resume_device_irqs().
+
+
+The IRQF_NO_SUSPEND Flag
+------------------------
+
+There are interrupts that can legitimately trigger during the entire system
+suspend-resume cycle, including the "noirq" phases of suspending and resuming
+devices as well as during the time when nonboot CPUs are taken offline and
+brought back online.  That applies to timer interrupts in the first place,
+but also to IPIs and to some other special-purpose interrupts.
+
+The IRQF_NO_SUSPEND flag is used to indicate that to the IRQ subsystem when
+requesting a special-purpose interrupt.  It causes suspend_device_irqs() to
+leave the corresponding IRQ enabled so as to allow the interrupt to work all
+the time as expected.
+
+Note that the IRQF_NO_SUSPEND flag affects the entire IRQ and not just one
+user of it.  Thus, if the IRQ is shared, all of the interrupt handlers installed
+for it will be executed as usual after suspend_device_irqs(), even if the
+IRQF_NO_SUSPEND flag was not passed to request_irq() (or equivalent) by some of
+the IRQ's users.  For this reason, using IRQF_NO_SUSPEND and IRQF_SHARED at the
+same time should be avoided.
+
+
+System Wakeup Interrupts, enable_irq_wake() and disable_irq_wake()
+------------------------------------------------------------------
+
+System wakeup interrupts generally need to be configured to wake up the system
+from sleep states, especially if they are used for different purposes (e.g. as
+I/O interrupts) in the working state.
+
+That may involve turning on a special signal handling logic within the platform
+(such as an SoC) so that signals from a given line are routed in a different way
+during system sleep so as to trigger a system wakeup when needed.  For example,
+the platform may include a dedicated interrupt controller used specifically for
+handling system wakeup events.  Then, if a given interrupt line is supposed to
+wake up the system from sleep sates, the corresponding input of that interrupt
+controller needs to be enabled to receive signals from the line in question.
+After wakeup, it generally is better to disable that input to prevent the
+dedicated controller from triggering interrupts unnecessarily.
+
+The IRQ subsystem provides two helper functions to be used by device drivers for
+those purposes.  Namely, enable_irq_wake() turns on the platform's logic for
+handling the given IRQ as a system wakeup interrupt line and disable_irq_wake()
+turns that logic off.
+
+Calling enable_irq_wake() causes suspend_device_irqs() to treat the given IRQ
+in a special way.  Namely, the IRQ remains enabled, by on the first interrupt
+it will be disabled, marked as pending and "suspended" so that it will be
+re-enabled by resume_device_irqs() during the subsequent system resume.  Also
+the PM core is notified about the event which casues the system suspend in
+progress to be aborted (that doesn't have to happen immediately, but at one
+of the points where the suspend thread looks for pending wakeup events).
+
+This way every interrupt from a wakeup interrupt source will either cause the
+system suspend currently in progress to be aborted or wake up the system if
+already suspended.  However, after suspend_device_irqs() interrupt handlers are
+not executed for system wakeup IRQs.  They are only executed for IRQF_NO_SUSPEND
+IRQs at that time, but those IRQs should not be configured for system wakeup
+using enable_irq_wake().
+
+
+Interrupts and Suspend-to-Idle
+------------------------------
+
+Suspend-to-idle (also known as the "freeze" sleep state) is a relatively new
+system sleep state that works by idling all of the processors and waiting for
+interrupts right after the "noirq" phase of suspending devices.
+
+Of course, this means that all of the interrupts with the IRQF_NO_SUSPEND flag
+set will bring CPUs out of idle while in that state, but they will not cause the
+IRQ subsystem to trigger a system wakeup.
+
+System wakeup interrupts, in turn, will trigger wakeup from suspend-to-idle in
+analogy with what they do in the full system suspend case.  The only difference
+is that the wakeup from suspend-to-idle is signaled using the usual working
+state interrupt delivery mechanisms and doesn't require the platform to use
+any special interrupt handling logic for it to work.
+
+
+IRQF_NO_SUSPEND and enable_irq_wake()
+-------------------------------------
+
+There are no valid reasons to use both enable_irq_wake() and the IRQF_NO_SUSPEND
+flag on the same IRQ.
+
+First of all, if the IRQ is not shared, the rules for handling IRQF_NO_SUSPEND
+interrupts (interrupt handlers are invoked after suspend_device_irqs()) are
+directly at odds with the rules for handling system wakeup interrupts (interrupt
+handlers are not invoked after suspend_device_irqs()).
+
+Second, both enable_irq_wake() and IRQF_NO_SUSPEND apply to entire IRQs and not
+to individual interrupt handlers, so sharing an IRQ between a system wakeup
+interrupt source and an IRQF_NO_SUSPEND interrupt source does not make sense.
index a68784d0a1ee1a99c432d1dd984c1f9bf9ae7406..6fd0e8bb814097233280897b9916190a429eda22 100644 (file)
@@ -11,6 +11,8 @@ bootwrapper.txt
 cpu_features.txt
        - info on how we support a variety of CPUs with minimal compile-time
        options.
+cxl.txt
+       - Overview of the CXL driver.
 eeh-pci-error-recovery.txt
        - info on PCI Bus EEH Error Recovery
 firmware-assisted-dump.txt
diff --git a/Documentation/powerpc/cxl.txt b/Documentation/powerpc/cxl.txt
new file mode 100644 (file)
index 0000000..2c71ecc
--- /dev/null
@@ -0,0 +1,379 @@
+Coherent Accelerator Interface (CXL)
+====================================
+
+Introduction
+============
+
+    The coherent accelerator interface is designed to allow the
+    coherent connection of accelerators (FPGAs and other devices) to a
+    POWER system. These devices need to adhere to the Coherent
+    Accelerator Interface Architecture (CAIA).
+
+    IBM refers to this as the Coherent Accelerator Processor Interface
+    or CAPI. In the kernel it's referred to by the name CXL to avoid
+    confusion with the ISDN CAPI subsystem.
+
+    Coherent in this context means that the accelerator and CPUs can
+    both access system memory directly and with the same effective
+    addresses.
+
+
+Hardware overview
+=================
+
+          POWER8               FPGA
+       +----------+        +---------+
+       |          |        |         |
+       |   CPU    |        |   AFU   |
+       |          |        |         |
+       |          |        |         |
+       |          |        |         |
+       +----------+        +---------+
+       |   PHB    |        |         |
+       |   +------+        |   PSL   |
+       |   | CAPP |<------>|         |
+       +---+------+  PCIE  +---------+
+
+    The POWER8 chip has a Coherently Attached Processor Proxy (CAPP)
+    unit which is part of the PCIe Host Bridge (PHB). This is managed
+    by Linux by calls into OPAL. Linux doesn't directly program the
+    CAPP.
+
+    The FPGA (or coherently attached device) consists of two parts.
+    The POWER Service Layer (PSL) and the Accelerator Function Unit
+    (AFU). The AFU is used to implement specific functionality behind
+    the PSL. The PSL, among other things, provides memory address
+    translation services to allow each AFU direct access to userspace
+    memory.
+
+    The AFU is the core part of the accelerator (eg. the compression,
+    crypto etc function). The kernel has no knowledge of the function
+    of the AFU. Only userspace interacts directly with the AFU.
+
+    The PSL provides the translation and interrupt services that the
+    AFU needs. This is what the kernel interacts with. For example, if
+    the AFU needs to read a particular effective address, it sends
+    that address to the PSL, the PSL then translates it, fetches the
+    data from memory and returns it to the AFU. If the PSL has a
+    translation miss, it interrupts the kernel and the kernel services
+    the fault. The context to which this fault is serviced is based on
+    who owns that acceleration function.
+
+
+AFU Modes
+=========
+
+    There are two programming modes supported by the AFU. Dedicated
+    and AFU directed. AFU may support one or both modes.
+
+    When using dedicated mode only one MMU context is supported. In
+    this mode, only one userspace process can use the accelerator at
+    time.
+
+    When using AFU directed mode, up to 16K simultaneous contexts can
+    be supported. This means up to 16K simultaneous userspace
+    applications may use the accelerator (although specific AFUs may
+    support fewer). In this mode, the AFU sends a 16 bit context ID
+    with each of its requests. This tells the PSL which context is
+    associated with each operation. If the PSL can't translate an
+    operation, the ID can also be accessed by the kernel so it can
+    determine the userspace context associated with an operation.
+
+
+MMIO space
+==========
+
+    A portion of the accelerator MMIO space can be directly mapped
+    from the AFU to userspace. Either the whole space can be mapped or
+    just a per context portion. The hardware is self describing, hence
+    the kernel can determine the offset and size of the per context
+    portion.
+
+
+Interrupts
+==========
+
+    AFUs may generate interrupts that are destined for userspace. These
+    are received by the kernel as hardware interrupts and passed onto
+    userspace by a read syscall documented below.
+
+    Data storage faults and error interrupts are handled by the kernel
+    driver.
+
+
+Work Element Descriptor (WED)
+=============================
+
+    The WED is a 64-bit parameter passed to the AFU when a context is
+    started. Its format is up to the AFU hence the kernel has no
+    knowledge of what it represents. Typically it will be the
+    effective address of a work queue or status block where the AFU
+    and userspace can share control and status information.
+
+
+
+
+User API
+========
+
+    For AFUs operating in AFU directed mode, two character device
+    files will be created. /dev/cxl/afu0.0m will correspond to a
+    master context and /dev/cxl/afu0.0s will correspond to a slave
+    context. Master contexts have access to the full MMIO space an
+    AFU provides. Slave contexts have access to only the per process
+    MMIO space an AFU provides.
+
+    For AFUs operating in dedicated process mode, the driver will
+    only create a single character device per AFU called
+    /dev/cxl/afu0.0d. This will have access to the entire MMIO space
+    that the AFU provides (like master contexts in AFU directed).
+
+    The types described below are defined in include/uapi/misc/cxl.h
+
+    The following file operations are supported on both slave and
+    master devices.
+
+
+open
+----
+
+    Opens the device and allocates a file descriptor to be used with
+    the rest of the API.
+
+    A dedicated mode AFU only has one context and only allows the
+    device to be opened once.
+
+    An AFU directed mode AFU can have many contexts, the device can be
+    opened once for each context that is available.
+
+    When all available contexts are allocated the open call will fail
+    and return -ENOSPC.
+
+    Note: IRQs need to be allocated for each context, which may limit
+          the number of contexts that can be created, and therefore
+          how many times the device can be opened. The POWER8 CAPP
+          supports 2040 IRQs and 3 are used by the kernel, so 2037 are
+          left. If 1 IRQ is needed per context, then only 2037
+          contexts can be allocated. If 4 IRQs are needed per context,
+          then only 2037/4 = 509 contexts can be allocated.
+
+
+ioctl
+-----
+
+    CXL_IOCTL_START_WORK:
+        Starts the AFU context and associates it with the current
+        process. Once this ioctl is successfully executed, all memory
+        mapped into this process is accessible to this AFU context
+        using the same effective addresses. No additional calls are
+        required to map/unmap memory. The AFU memory context will be
+        updated as userspace allocates and frees memory. This ioctl
+        returns once the AFU context is started.
+
+        Takes a pointer to a struct cxl_ioctl_start_work:
+
+                struct cxl_ioctl_start_work {
+                        __u64 flags;
+                        __u64 work_element_descriptor;
+                        __u64 amr;
+                        __s16 num_interrupts;
+                        __s16 reserved1;
+                        __s32 reserved2;
+                        __u64 reserved3;
+                        __u64 reserved4;
+                        __u64 reserved5;
+                        __u64 reserved6;
+                };
+
+            flags:
+                Indicates which optional fields in the structure are
+                valid.
+
+            work_element_descriptor:
+                The Work Element Descriptor (WED) is a 64-bit argument
+                defined by the AFU. Typically this is an effective
+                address pointing to an AFU specific structure
+                describing what work to perform.
+
+            amr:
+                Authority Mask Register (AMR), same as the powerpc
+                AMR. This field is only used by the kernel when the
+                corresponding CXL_START_WORK_AMR value is specified in
+                flags. If not specified the kernel will use a default
+                value of 0.
+
+            num_interrupts:
+                Number of userspace interrupts to request. This field
+                is only used by the kernel when the corresponding
+                CXL_START_WORK_NUM_IRQS value is specified in flags.
+                If not specified the minimum number required by the
+                AFU will be allocated. The min and max number can be
+                obtained from sysfs.
+
+            reserved fields:
+                For ABI padding and future extensions
+
+    CXL_IOCTL_GET_PROCESS_ELEMENT:
+        Get the current context id, also known as the process element.
+        The value is returned from the kernel as a __u32.
+
+
+mmap
+----
+
+    An AFU may have an MMIO space to facilitate communication with the
+    AFU. If it does, the MMIO space can be accessed via mmap. The size
+    and contents of this area are specific to the particular AFU. The
+    size can be discovered via sysfs.
+
+    In AFU directed mode, master contexts are allowed to map all of
+    the MMIO space and slave contexts are allowed to only map the per
+    process MMIO space associated with the context. In dedicated
+    process mode the entire MMIO space can always be mapped.
+
+    This mmap call must be done after the START_WORK ioctl.
+
+    Care should be taken when accessing MMIO space. Only 32 and 64-bit
+    accesses are supported by POWER8. Also, the AFU will be designed
+    with a specific endianness, so all MMIO accesses should consider
+    endianness (recommend endian(3) variants like: le64toh(),
+    be64toh() etc). These endian issues equally apply to shared memory
+    queues the WED may describe.
+
+
+read
+----
+
+    Reads events from the AFU. Blocks if no events are pending
+    (unless O_NONBLOCK is supplied). Returns -EIO in the case of an
+    unrecoverable error or if the card is removed.
+
+    read() will always return an integral number of events.
+
+    The buffer passed to read() must be at least 4K bytes.
+
+    The result of the read will be a buffer of one or more events,
+    each event is of type struct cxl_event, of varying size.
+
+            struct cxl_event {
+                    struct cxl_event_header header;
+                    union {
+                            struct cxl_event_afu_interrupt irq;
+                            struct cxl_event_data_storage fault;
+                            struct cxl_event_afu_error afu_error;
+                    };
+            };
+
+    The struct cxl_event_header is defined as:
+
+            struct cxl_event_header {
+                    __u16 type;
+                    __u16 size;
+                    __u16 process_element;
+                    __u16 reserved1;
+            };
+
+        type:
+            This defines the type of event. The type determines how
+            the rest of the event is structured. These types are
+            described below and defined by enum cxl_event_type.
+
+        size:
+            This is the size of the event in bytes including the
+            struct cxl_event_header. The start of the next event can
+            be found at this offset from the start of the current
+            event.
+
+        process_element:
+            Context ID of the event.
+
+        reserved field:
+            For future extensions and padding.
+
+    If the event type is CXL_EVENT_AFU_INTERRUPT then the event
+    structure is defined as:
+
+            struct cxl_event_afu_interrupt {
+                    __u16 flags;
+                    __u16 irq; /* Raised AFU interrupt number */
+                    __u32 reserved1;
+            };
+
+        flags:
+            These flags indicate which optional fields are present
+            in this struct. Currently all fields are mandatory.
+
+        irq:
+            The IRQ number sent by the AFU.
+
+        reserved field:
+            For future extensions and padding.
+
+    If the event type is CXL_EVENT_DATA_STORAGE then the event
+    structure is defined as:
+
+            struct cxl_event_data_storage {
+                    __u16 flags;
+                    __u16 reserved1;
+                    __u32 reserved2;
+                    __u64 addr;
+                    __u64 dsisr;
+                    __u64 reserved3;
+            };
+
+        flags:
+            These flags indicate which optional fields are present in
+            this struct. Currently all fields are mandatory.
+
+        address:
+            The address that the AFU unsuccessfully attempted to
+            access. Valid accesses will be handled transparently by the
+            kernel but invalid accesses will generate this event.
+
+        dsisr:
+            This field gives information on the type of fault. It is a
+            copy of the DSISR from the PSL hardware when the address
+            fault occurred. The form of the DSISR is as defined in the
+            CAIA.
+
+        reserved fields:
+            For future extensions
+
+    If the event type is CXL_EVENT_AFU_ERROR then the event structure
+    is defined as:
+
+            struct cxl_event_afu_error {
+                    __u16 flags;
+                    __u16 reserved1;
+                    __u32 reserved2;
+                    __u64 error;
+            };
+
+        flags:
+            These flags indicate which optional fields are present in
+            this struct. Currently all fields are Mandatory.
+
+        error:
+            Error status from the AFU. Defined by the AFU.
+
+        reserved fields:
+            For future extensions and padding
+
+Sysfs Class
+===========
+
+    A cxl sysfs class is added under /sys/class/cxl to facilitate
+    enumeration and tuning of the accelerators. Its layout is
+    described in Documentation/ABI/testing/sysfs-class-cxl
+
+Udev rules
+==========
+
+    The following udev rules could be used to create a symlink to the
+    most logical chardev to use in any programming mode (afuX.Yd for
+    dedicated, afuX.Ys for afu directed), since the API is virtually
+    identical for each:
+
+       SUBSYSTEM=="cxl", ATTRS{mode}=="dedicated_process", SYMLINK="cxl/%b"
+       SUBSYSTEM=="cxl", ATTRS{mode}=="afu_directed", \
+                         KERNEL=="afu[0-9]*.[0-9]*s", SYMLINK="cxl/%b"
diff --git a/Documentation/video4linux/vivid.txt b/Documentation/video4linux/vivid.txt
new file mode 100644 (file)
index 0000000..eeb11a2
--- /dev/null
@@ -0,0 +1,1111 @@
+vivid: Virtual Video Test Driver
+================================
+
+This driver emulates video4linux hardware of various types: video capture, video
+output, vbi capture and output, radio receivers and transmitters and a software
+defined radio receiver. In addition a simple framebuffer device is available for
+testing capture and output overlays.
+
+Up to 64 vivid instances can be created, each with up to 16 inputs and 16 outputs.
+
+Each input can be a webcam, TV capture device, S-Video capture device or an HDMI
+capture device. Each output can be an S-Video output device or an HDMI output
+device.
+
+These inputs and outputs act exactly as a real hardware device would behave. This
+allows you to use this driver as a test input for application development, since
+you can test the various features without requiring special hardware.
+
+This document describes the features implemented by this driver:
+
+- Support for read()/write(), MMAP, USERPTR and DMABUF streaming I/O.
+- A large list of test patterns and variations thereof
+- Working brightness, contrast, saturation and hue controls
+- Support for the alpha color component
+- Full colorspace support, including limited/full RGB range
+- All possible control types are present
+- Support for various pixel aspect ratios and video aspect ratios
+- Error injection to test what happens if errors occur
+- Supports crop/compose/scale in any combination for both input and output
+- Can emulate up to 4K resolutions
+- All Field settings are supported for testing interlaced capturing
+- Supports all standard YUV and RGB formats, including two multiplanar YUV formats
+- Raw and Sliced VBI capture and output support
+- Radio receiver and transmitter support, including RDS support
+- Software defined radio (SDR) support
+- Capture and output overlay support
+
+These features will be described in more detail below.
+
+
+Table of Contents
+-----------------
+
+Section 1: Configuring the driver
+Section 2: Video Capture
+Section 2.1: Webcam Input
+Section 2.2: TV and S-Video Inputs
+Section 2.3: HDMI Input
+Section 3: Video Output
+Section 3.1: S-Video Output
+Section 3.2: HDMI Output
+Section 4: VBI Capture
+Section 5: VBI Output
+Section 6: Radio Receiver
+Section 7: Radio Transmitter
+Section 8: Software Defined Radio Receiver
+Section 9: Controls
+Section 9.1: User Controls - Test Controls
+Section 9.2: User Controls - Video Capture
+Section 9.3: User Controls - Audio
+Section 9.4: Vivid Controls
+Section 9.4.1: Test Pattern Controls
+Section 9.4.2: Capture Feature Selection Controls
+Section 9.4.3: Output Feature Selection Controls
+Section 9.4.4: Error Injection Controls
+Section 9.4.5: VBI Raw Capture Controls
+Section 9.5: Digital Video Controls
+Section 9.6: FM Radio Receiver Controls
+Section 9.7: FM Radio Modulator
+Section 10: Video, VBI and RDS Looping
+Section 10.1: Video and Sliced VBI looping
+Section 10.2: Radio & RDS Looping
+Section 11: Cropping, Composing, Scaling
+Section 12: Formats
+Section 13: Capture Overlay
+Section 14: Output Overlay
+Section 15: Some Future Improvements
+
+
+Section 1: Configuring the driver
+---------------------------------
+
+By default the driver will create a single instance that has a video capture
+device with webcam, TV, S-Video and HDMI inputs, a video output device with
+S-Video and HDMI outputs, one vbi capture device, one vbi output device, one
+radio receiver device, one radio transmitter device and one SDR device.
+
+The number of instances, devices, video inputs and outputs and their types are
+all configurable using the following module options:
+
+n_devs: number of driver instances to create. By default set to 1. Up to 64
+       instances can be created.
+
+node_types: which devices should each driver instance create. An array of
+       hexadecimal values, one for each instance. The default is 0x1d3d.
+       Each value is a bitmask with the following meaning:
+               bit 0: Video Capture node
+               bit 2-3: VBI Capture node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both
+               bit 4: Radio Receiver node
+               bit 5: Software Defined Radio Receiver node
+               bit 8: Video Output node
+               bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both
+               bit 12: Radio Transmitter node
+               bit 16: Framebuffer for testing overlays
+
+       So to create four instances, the first two with just one video capture
+       device, the second two with just one video output device you would pass
+       these module options to vivid:
+
+               n_devs=4 node_types=0x1,0x1,0x100,0x100
+
+num_inputs: the number of inputs, one for each instance. By default 4 inputs
+       are created for each video capture device. At most 16 inputs can be created,
+       and there must be at least one.
+
+input_types: the input types for each instance, the default is 0xe4. This defines
+       what the type of each input is when the inputs are created for each driver
+       instance. This is a hexadecimal value with up to 16 pairs of bits, each
+       pair gives the type and bits 0-1 map to input 0, bits 2-3 map to input 1,
+       30-31 map to input 15. Each pair of bits has the following meaning:
+
+               00: this is a webcam input
+               01: this is a TV tuner input
+               10: this is an S-Video input
+               11: this is an HDMI input
+
+       So to create a video capture device with 8 inputs where input 0 is a TV
+       tuner, inputs 1-3 are S-Video inputs and inputs 4-7 are HDMI inputs you
+       would use the following module options:
+
+               num_inputs=8 input_types=0xffa9
+
+num_outputs: the number of outputs, one for each instance. By default 2 outputs
+       are created for each video output device. At most 16 outputs can be
+       created, and there must be at least one.
+
+output_types: the output types for each instance, the default is 0x02. This defines
+       what the type of each output is when the outputs are created for each
+       driver instance. This is a hexadecimal value with up to 16 bits, each bit
+       gives the type and bit 0 maps to output 0, bit 1 maps to output 1, bit
+       15 maps to output 15. The meaning of each bit is as follows:
+
+               0: this is an S-Video output
+               1: this is an HDMI output
+
+       So to create a video output device with 8 outputs where outputs 0-3 are
+       S-Video outputs and outputs 4-7 are HDMI outputs you would use the
+       following module options:
+
+               num_outputs=8 output_types=0xf0
+
+vid_cap_nr: give the desired videoX start number for each video capture device.
+       The default is -1 which will just take the first free number. This allows
+       you to map capture video nodes to specific videoX device nodes. Example:
+
+               n_devs=4 vid_cap_nr=2,4,6,8
+
+       This will attempt to assign /dev/video2 for the video capture device of
+       the first vivid instance, video4 for the next up to video8 for the last
+       instance. If it can't succeed, then it will just take the next free
+       number.
+
+vid_out_nr: give the desired videoX start number for each video output device.
+        The default is -1 which will just take the first free number.
+
+vbi_cap_nr: give the desired vbiX start number for each vbi capture device.
+        The default is -1 which will just take the first free number.
+
+vbi_out_nr: give the desired vbiX start number for each vbi output device.
+        The default is -1 which will just take the first free number.
+
+radio_rx_nr: give the desired radioX start number for each radio receiver device.
+        The default is -1 which will just take the first free number.
+
+radio_tx_nr: give the desired radioX start number for each radio transmitter
+       device. The default is -1 which will just take the first free number.
+
+sdr_cap_nr: give the desired swradioX start number for each SDR capture device.
+        The default is -1 which will just take the first free number.
+
+ccs_cap_mode: specify the allowed video capture crop/compose/scaling combination
+       for each driver instance. Video capture devices can have any combination
+       of cropping, composing and scaling capabilities and this will tell the
+       vivid driver which of those is should emulate. By default the user can
+       select this through controls.
+
+       The value is either -1 (controlled by the user) or a set of three bits,
+       each enabling (1) or disabling (0) one of the features:
+
+               bit 0: Enable crop support. Cropping will take only part of the
+                      incoming picture.
+               bit 1: Enable compose support. Composing will copy the incoming
+                      picture into a larger buffer.
+               bit 2: Enable scaling support. Scaling can scale the incoming
+                      picture. The scaler of the vivid driver can enlarge up
+                      or down to four times the original size. The scaler is
+                      very simple and low-quality. Simplicity and speed were
+                      key, not quality.
+
+       Note that this value is ignored by webcam inputs: those enumerate
+       discrete framesizes and that is incompatible with cropping, composing
+       or scaling.
+
+ccs_out_mode: specify the allowed video output crop/compose/scaling combination
+       for each driver instance. Video output devices can have any combination
+       of cropping, composing and scaling capabilities and this will tell the
+       vivid driver which of those is should emulate. By default the user can
+       select this through controls.
+
+       The value is either -1 (controlled by the user) or a set of three bits,
+       each enabling (1) or disabling (0) one of the features:
+
+               bit 0: Enable crop support. Cropping will take only part of the
+                      outgoing buffer.
+               bit 1: Enable compose support. Composing will copy the incoming
+                      buffer into a larger picture frame.
+               bit 2: Enable scaling support. Scaling can scale the incoming
+                      buffer. The scaler of the vivid driver can enlarge up
+                      or down to four times the original size. The scaler is
+                      very simple and low-quality. Simplicity and speed were
+                      key, not quality.
+
+multiplanar: select whether each device instance supports multi-planar formats,
+       and thus the V4L2 multi-planar API. By default the first device instance
+       is single-planar, the second multi-planar, and it keeps alternating.
+
+       This module option can override that for each instance. Values are:
+
+               0: use alternating single and multi-planar devices.
+               1: this is a single-planar instance.
+               2: this is a multi-planar instance.
+
+vivid_debug: enable driver debugging info
+
+no_error_inj: if set disable the error injecting controls. This option is
+       needed in order to run a tool like v4l2-compliance. Tools like that
+       exercise all controls including a control like 'Disconnect' which
+       emulates a USB disconnect, making the device inaccessible and so
+       all tests that v4l2-compliance is doing will fail afterwards.
+
+       There may be other situations as well where you want to disable the
+       error injection support of vivid. When this option is set, then the
+       controls that select crop, compose and scale behavior are also
+       removed. Unless overridden by ccs_cap_mode and/or ccs_out_mode the
+       will default to enabling crop, compose and scaling.
+
+Taken together, all these module options allow you to precisely customize
+the driver behavior and test your application with all sorts of permutations.
+It is also very suitable to emulate hardware that is not yet available, e.g.
+when developing software for a new upcoming device.
+
+
+Section 2: Video Capture
+------------------------
+
+This is probably the most frequently used feature. The video capture device
+can be configured by using the module options num_inputs, input_types and
+ccs_cap_mode (see section 1 for more detailed information), but by default
+four inputs are configured: a webcam, a TV tuner, an S-Video and an HDMI
+input, one input for each input type. Those are described in more detail
+below.
+
+Special attention has been given to the rate at which new frames become
+available. The jitter will be around 1 jiffie (that depends on the HZ
+configuration of your kernel, so usually 1/100, 1/250 or 1/1000 of a second),
+but the long-term behavior is exactly following the framerate. So a
+framerate of 59.94 Hz is really different from 60 Hz. If the framerate
+exceeds your kernel's HZ value, then you will get dropped frames, but the
+frame/field sequence counting will keep track of that so the sequence
+count will skip whenever frames are dropped.
+
+
+Section 2.1: Webcam Input
+-------------------------
+
+The webcam input supports three framesizes: 320x180, 640x360 and 1280x720. It
+supports frames per second settings of 10, 15, 25, 30, 50 and 60 fps. Which ones
+are available depends on the chosen framesize: the larger the framesize, the
+lower the maximum frames per second.
+
+The initially selected colorspace when you switch to the webcam input will be
+sRGB.
+
+
+Section 2.2: TV and S-Video Inputs
+----------------------------------
+
+The only difference between the TV and S-Video input is that the TV has a
+tuner. Otherwise they behave identically.
+
+These inputs support audio inputs as well: one TV and one Line-In. They
+both support all TV standards. If the standard is queried, then the Vivid
+controls 'Standard Signal Mode' and 'Standard' determine what
+the result will be.
+
+These inputs support all combinations of the field setting. Special care has
+been taken to faithfully reproduce how fields are handled for the different
+TV standards. This is particularly noticable when generating a horizontally
+moving image so the temporal effect of using interlaced formats becomes clearly
+visible. For 50 Hz standards the top field is the oldest and the bottom field
+is the newest in time. For 60 Hz standards that is reversed: the bottom field
+is the oldest and the top field is the newest in time.
+
+When you start capturing in V4L2_FIELD_ALTERNATE mode the first buffer will
+contain the top field for 50 Hz standards and the bottom field for 60 Hz
+standards. This is what capture hardware does as well.
+
+Finally, for PAL/SECAM standards the first half of the top line contains noise.
+This simulates the Wide Screen Signal that is commonly placed there.
+
+The initially selected colorspace when you switch to the TV or S-Video input
+will be SMPTE-170M.
+
+The pixel aspect ratio will depend on the TV standard. The video aspect ratio
+can be selected through the 'Standard Aspect Ratio' Vivid control.
+Choices are '4x3', '16x9' which will give letterboxed widescreen video and
+'16x9 Anomorphic' which will give full screen squashed anamorphic widescreen
+video that will need to be scaled accordingly.
+
+The TV 'tuner' supports a frequency range of 44-958 MHz. Channels are available
+every 6 MHz, starting from 49.25 MHz. For each channel the generated image
+will be in color for the +/- 0.25 MHz around it, and in grayscale for
++/- 1 MHz around the channel. Beyond that it is just noise. The VIDIOC_G_TUNER
+ioctl will return 100% signal strength for +/- 0.25 MHz and 50% for +/- 1 MHz.
+It will also return correct afc values to show whether the frequency is too
+low or too high.
+
+The audio subchannels that are returned are MONO for the +/- 1 MHz range around
+a valid channel frequency. When the frequency is within +/- 0.25 MHz of the
+channel it will return either MONO, STEREO, either MONO | SAP (for NTSC) or
+LANG1 | LANG2 (for others), or STEREO | SAP.
+
+Which one is returned depends on the chosen channel, each next valid channel
+will cycle through the possible audio subchannel combinations. This allows
+you to test the various combinations by just switching channels..
+
+Finally, for these inputs the v4l2_timecode struct is filled in in the
+dequeued v4l2_buffer struct.
+
+
+Section 2.3: HDMI Input
+-----------------------
+
+The HDMI inputs supports all CEA-861 and DMT timings, both progressive and
+interlaced, for pixelclock frequencies between 25 and 600 MHz. The field
+mode for interlaced formats is always V4L2_FIELD_ALTERNATE. For HDMI the
+field order is always top field first, and when you start capturing an
+interlaced format you will receive the top field first.
+
+The initially selected colorspace when you switch to the HDMI input or
+select an HDMI timing is based on the format resolution: for resolutions
+less than or equal to 720x576 the colorspace is set to SMPTE-170M, for
+others it is set to REC-709 (CEA-861 timings) or sRGB (VESA DMT timings).
+
+The pixel aspect ratio will depend on the HDMI timing: for 720x480 is it
+set as for the NTSC TV standard, for 720x576 it is set as for the PAL TV
+standard, and for all others a 1:1 pixel aspect ratio is returned.
+
+The video aspect ratio can be selected through the 'DV Timings Aspect Ratio'
+Vivid control. Choices are 'Source Width x Height' (just use the
+same ratio as the chosen format), '4x3' or '16x9', either of which can
+result in pillarboxed or letterboxed video.
+
+For HDMI inputs it is possible to set the EDID. By default a simple EDID
+is provided. You can only set the EDID for HDMI inputs. Internally, however,
+the EDID is shared between all HDMI inputs.
+
+No interpretation is done of the EDID data.
+
+
+Section 3: Video Output
+-----------------------
+
+The video output device can be configured by using the module options
+num_outputs, output_types and ccs_out_mode (see section 1 for more detailed
+information), but by default two outputs are configured: an S-Video and an
+HDMI input, one output for each output type. Those are described in more detail
+below.
+
+Like with video capture the framerate is also exact in the long term.
+
+
+Section 3.1: S-Video Output
+---------------------------
+
+This output supports audio outputs as well: "Line-Out 1" and "Line-Out 2".
+The S-Video output supports all TV standards.
+
+This output supports all combinations of the field setting.
+
+The initially selected colorspace when you switch to the TV or S-Video input
+will be SMPTE-170M.
+
+
+Section 3.2: HDMI Output
+------------------------
+
+The HDMI output supports all CEA-861 and DMT timings, both progressive and
+interlaced, for pixelclock frequencies between 25 and 600 MHz. The field
+mode for interlaced formats is always V4L2_FIELD_ALTERNATE.
+
+The initially selected colorspace when you switch to the HDMI output or
+select an HDMI timing is based on the format resolution: for resolutions
+less than or equal to 720x576 the colorspace is set to SMPTE-170M, for
+others it is set to REC-709 (CEA-861 timings) or sRGB (VESA DMT timings).
+
+The pixel aspect ratio will depend on the HDMI timing: for 720x480 is it
+set as for the NTSC TV standard, for 720x576 it is set as for the PAL TV
+standard, and for all others a 1:1 pixel aspect ratio is returned.
+
+An HDMI output has a valid EDID which can be obtained through VIDIOC_G_EDID.
+
+
+Section 4: VBI Capture
+----------------------
+
+There are three types of VBI capture devices: those that only support raw
+(undecoded) VBI, those that only support sliced (decoded) VBI and those that
+support both. This is determined by the node_types module option. In all
+cases the driver will generate valid VBI data: for 60 Hz standards it will
+generate Closed Caption and XDS data. The closed caption stream will
+alternate between "Hello world!" and "Closed captions test" every second.
+The XDS stream will give the current time once a minute. For 50 Hz standards
+it will generate the Wide Screen Signal which is based on the actual Video
+Aspect Ratio control setting and teletext pages 100-159, one page per frame.
+
+The VBI device will only work for the S-Video and TV inputs, it will give
+back an error if the current input is a webcam or HDMI.
+
+
+Section 5: VBI Output
+---------------------
+
+There are three types of VBI output devices: those that only support raw
+(undecoded) VBI, those that only support sliced (decoded) VBI and those that
+support both. This is determined by the node_types module option.
+
+The sliced VBI output supports the Wide Screen Signal and the teletext signal
+for 50 Hz standards and Closed Captioning + XDS for 60 Hz standards.
+
+The VBI device will only work for the S-Video output, it will give
+back an error if the current output is HDMI.
+
+
+Section 6: Radio Receiver
+-------------------------
+
+The radio receiver emulates an FM/AM/SW receiver. The FM band also supports RDS.
+The frequency ranges are:
+
+       FM: 64 MHz - 108 MHz
+       AM: 520 kHz - 1710 kHz
+       SW: 2300 kHz - 26.1 MHz
+
+Valid channels are emulated every 1 MHz for FM and every 100 kHz for AM and SW.
+The signal strength decreases the further the frequency is from the valid
+frequency until it becomes 0% at +/- 50 kHz (FM) or 5 kHz (AM/SW) from the
+ideal frequency. The initial frequency when the driver is loaded is set to
+95 MHz.
+
+The FM receiver supports RDS as well, both using 'Block I/O' and 'Controls'
+modes. In the 'Controls' mode the RDS information is stored in read-only
+controls. These controls are updated every time the frequency is changed,
+or when the tuner status is requested. The Block I/O method uses the read()
+interface to pass the RDS blocks on to the application for decoding.
+
+The RDS signal is 'detected' for +/- 12.5 kHz around the channel frequency,
+and the further the frequency is away from the valid frequency the more RDS
+errors are randomly introduced into the block I/O stream, up to 50% of all
+blocks if you are +/- 12.5 kHz from the channel frequency. All four errors
+can occur in equal proportions: blocks marked 'CORRECTED', blocks marked
+'ERROR', blocks marked 'INVALID' and dropped blocks.
+
+The generated RDS stream contains all the standard fields contained in a
+0B group, and also radio text and the current time.
+
+The receiver supports HW frequency seek, either in Bounded mode, Wrap Around
+mode or both, which is configurable with the "Radio HW Seek Mode" control.
+
+
+Section 7: Radio Transmitter
+----------------------------
+
+The radio transmitter emulates an FM/AM/SW transmitter. The FM band also supports RDS.
+The frequency ranges are:
+
+       FM: 64 MHz - 108 MHz
+       AM: 520 kHz - 1710 kHz
+       SW: 2300 kHz - 26.1 MHz
+
+The initial frequency when the driver is loaded is 95.5 MHz.
+
+The FM transmitter supports RDS as well, both using 'Block I/O' and 'Controls'
+modes. In the 'Controls' mode the transmitted RDS information is configured
+using controls, and in 'Block I/O' mode the blocks are passed to the driver
+using write().
+
+
+Section 8: Software Defined Radio Receiver
+------------------------------------------
+
+The SDR receiver has three frequency bands for the ADC tuner:
+
+       - 300 kHz
+       - 900 kHz - 2800 kHz
+       - 3200 kHz
+
+The RF tuner supports 50 MHz - 2000 MHz.
+
+The generated data contains the In-phase and Quadrature components of a
+1 kHz tone that has an amplitude of sqrt(2).
+
+
+Section 9: Controls
+-------------------
+
+Different devices support different controls. The sections below will describe
+each control and which devices support them.
+
+
+Section 9.1: User Controls - Test Controls
+------------------------------------------
+
+The Button, Boolean, Integer 32 Bits, Integer 64 Bits, Menu, String, Bitmask and
+Integer Menu are controls that represent all possible control types. The Menu
+control and the Integer Menu control both have 'holes' in their menu list,
+meaning that one or more menu items return EINVAL when VIDIOC_QUERYMENU is called.
+Both menu controls also have a non-zero minimum control value.  These features
+allow you to check if your application can handle such things correctly.
+These controls are supported for every device type.
+
+
+Section 9.2: User Controls - Video Capture
+------------------------------------------
+
+The following controls are specific to video capture.
+
+The Brightness, Contrast, Saturation and Hue controls actually work and are
+standard. There is one special feature with the Brightness control: each
+video input has its own brightness value, so changing input will restore
+the brightness for that input. In addition, each video input uses a different
+brightness range (minimum and maximum control values). Switching inputs will
+cause a control event to be sent with the V4L2_EVENT_CTRL_CH_RANGE flag set.
+This allows you to test controls that can change their range.
+
+The 'Gain, Automatic' and Gain controls can be used to test volatile controls:
+if 'Gain, Automatic' is set, then the Gain control is volatile and changes
+constantly. If 'Gain, Automatic' is cleared, then the Gain control is a normal
+control.
+
+The 'Horizontal Flip' and 'Vertical Flip' controls can be used to flip the
+image. These combine with the 'Sensor Flipped Horizontally/Vertically' Vivid
+controls.
+
+The 'Alpha Component' control can be used to set the alpha component for
+formats containing an alpha channel.
+
+
+Section 9.3: User Controls - Audio
+----------------------------------
+
+The following controls are specific to video capture and output and radio
+receivers and transmitters.
+
+The 'Volume' and 'Mute' audio controls are typical for such devices to
+control the volume and mute the audio. They don't actually do anything in
+the vivid driver.
+
+
+Section 9.4: Vivid Controls
+---------------------------
+
+These vivid custom controls control the image generation, error injection, etc.
+
+
+Section 9.4.1: Test Pattern Controls
+------------------------------------
+
+The Test Pattern Controls are all specific to video capture.
+
+Test Pattern: selects which test pattern to use. Use the CSC Colorbar for
+       testing colorspace conversions: the colors used in that test pattern
+       map to valid colors in all colorspaces. The colorspace conversion
+       is disabled for the other test patterns.
+
+OSD Text Mode: selects whether the text superimposed on the
+       test pattern should be shown, and if so, whether only counters should
+       be displayed or the full text.
+
+Horizontal Movement: selects whether the test pattern should
+       move to the left or right and at what speed.
+
+Vertical Movement: does the same for the vertical direction.
+
+Show Border: show a two-pixel wide border at the edge of the actual image,
+       excluding letter or pillarboxing.
+
+Show Square: show a square in the middle of the image. If the image is
+       displayed with the correct pixel and image aspect ratio corrections,
+       then the width and height of the square on the monitor should be
+       the same.
+
+Insert SAV Code in Image: adds a SAV (Start of Active Video) code to the image.
+       This can be used to check if such codes in the image are inadvertently
+       interpreted instead of being ignored.
+
+Insert EAV Code in Image: does the same for the EAV (End of Active Video) code.
+
+
+Section 9.4.2: Capture Feature Selection Controls
+-------------------------------------------------
+
+These controls are all specific to video capture.
+
+Sensor Flipped Horizontally: the image is flipped horizontally and the
+       V4L2_IN_ST_HFLIP input status flag is set. This emulates the case where
+       a sensor is for example mounted upside down.
+
+Sensor Flipped Vertically: the image is flipped vertically and the
+       V4L2_IN_ST_VFLIP input status flag is set. This emulates the case where
+        a sensor is for example mounted upside down.
+
+Standard Aspect Ratio: selects if the image aspect ratio as used for the TV or
+       S-Video input should be 4x3, 16x9 or anamorphic widescreen. This may
+       introduce letterboxing.
+
+DV Timings Aspect Ratio: selects if the image aspect ratio as used for the HDMI
+       input should be the same as the source width and height ratio, or if
+       it should be 4x3 or 16x9. This may introduce letter or pillarboxing.
+
+Timestamp Source: selects when the timestamp for each buffer is taken.
+
+Colorspace: selects which colorspace should be used when generating the image.
+       This only applies if the CSC Colorbar test pattern is selected,
+       otherwise the test pattern will go through unconverted (except for
+       the so-called 'Transfer Function' corrections and the R'G'B' to Y'CbCr
+       conversion). This behavior is also what you want, since a 75% Colorbar
+       should really have 75% signal intensity and should not be affected
+       by colorspace conversions.
+
+       Changing the colorspace will result in the V4L2_EVENT_SOURCE_CHANGE
+       to be sent since it emulates a detected colorspace change.
+
+Limited RGB Range (16-235): selects if the RGB range of the HDMI source should
+       be limited or full range. This combines with the Digital Video 'Rx RGB
+       Quantization Range' control and can be used to test what happens if
+       a source provides you with the wrong quantization range information.
+       See the description of that control for more details.
+
+Apply Alpha To Red Only: apply the alpha channel as set by the 'Alpha Component'
+       user control to the red color of the test pattern only.
+
+Enable Capture Cropping: enables crop support. This control is only present if
+       the ccs_cap_mode module option is set to the default value of -1 and if
+       the no_error_inj module option is set to 0 (the default).
+
+Enable Capture Composing: enables composing support. This control is only
+       present if the ccs_cap_mode module option is set to the default value of
+       -1 and if the no_error_inj module option is set to 0 (the default).
+
+Enable Capture Scaler: enables support for a scaler (maximum 4 times upscaling
+       and downscaling). This control is only present if the ccs_cap_mode
+       module option is set to the default value of -1 and if the no_error_inj
+       module option is set to 0 (the default).
+
+Maximum EDID Blocks: determines how many EDID blocks the driver supports.
+       Note that the vivid driver does not actually interpret new EDID
+       data, it just stores it. It allows for up to 256 EDID blocks
+       which is the maximum supported by the standard.
+
+Fill Percentage of Frame: can be used to draw only the top X percent
+       of the image. Since each frame has to be drawn by the driver, this
+       demands a lot of the CPU. For large resolutions this becomes
+       problematic. By drawing only part of the image this CPU load can
+       be reduced.
+
+
+Section 9.4.3: Output Feature Selection Controls
+------------------------------------------------
+
+These controls are all specific to video output.
+
+Enable Output Cropping: enables crop support. This control is only present if
+       the ccs_out_mode module option is set to the default value of -1 and if
+       the no_error_inj module option is set to 0 (the default).
+
+Enable Output Composing: enables composing support. This control is only
+       present if the ccs_out_mode module option is set to the default value of
+       -1 and if the no_error_inj module option is set to 0 (the default).
+
+Enable Output Scaler: enables support for a scaler (maximum 4 times upscaling
+       and downscaling). This control is only present if the ccs_out_mode
+       module option is set to the default value of -1 and if the no_error_inj
+       module option is set to 0 (the default).
+
+
+Section 9.4.4: Error Injection Controls
+---------------------------------------
+
+The following two controls are only valid for video and vbi capture.
+
+Standard Signal Mode: selects the behavior of VIDIOC_QUERYSTD: what should
+       it return?
+
+       Changing this control will result in the V4L2_EVENT_SOURCE_CHANGE
+       to be sent since it emulates a changed input condition (e.g. a cable
+       was plugged in or out).
+
+Standard: selects the standard that VIDIOC_QUERYSTD should return if the
+       previous control is set to "Selected Standard".
+
+       Changing this control will result in the V4L2_EVENT_SOURCE_CHANGE
+       to be sent since it emulates a changed input standard.
+
+
+The following two controls are only valid for video capture.
+
+DV Timings Signal Mode: selects the behavior of VIDIOC_QUERY_DV_TIMINGS: what
+       should it return?
+
+       Changing this control will result in the V4L2_EVENT_SOURCE_CHANGE
+       to be sent since it emulates a changed input condition (e.g. a cable
+       was plugged in or out).
+
+DV Timings: selects the timings the VIDIOC_QUERY_DV_TIMINGS should return
+       if the previous control is set to "Selected DV Timings".
+
+       Changing this control will result in the V4L2_EVENT_SOURCE_CHANGE
+       to be sent since it emulates changed input timings.
+
+
+The following controls are only present if the no_error_inj module option
+is set to 0 (the default). These controls are valid for video and vbi
+capture and output streams and for the SDR capture device except for the
+Disconnect control which is valid for all devices.
+
+Wrap Sequence Number: test what happens when you wrap the sequence number in
+       struct v4l2_buffer around.
+
+Wrap Timestamp: test what happens when you wrap the timestamp in struct
+       v4l2_buffer around.
+
+Percentage of Dropped Buffers: sets the percentage of buffers that
+       are never returned by the driver (i.e., they are dropped).
+
+Disconnect: emulates a USB disconnect. The device will act as if it has
+       been disconnected. Only after all open filehandles to the device
+       node have been closed will the device become 'connected' again.
+
+Inject V4L2_BUF_FLAG_ERROR: when pressed, the next frame returned by
+       the driver will have the error flag set (i.e. the frame is marked
+       corrupt).
+
+Inject VIDIOC_REQBUFS Error: when pressed, the next REQBUFS or CREATE_BUFS
+       ioctl call will fail with an error. To be precise: the videobuf2
+       queue_setup() op will return -EINVAL.
+
+Inject VIDIOC_QBUF Error: when pressed, the next VIDIOC_QBUF or
+       VIDIOC_PREPARE_BUFFER ioctl call will fail with an error. To be
+       precise: the videobuf2 buf_prepare() op will return -EINVAL.
+
+Inject VIDIOC_STREAMON Error: when pressed, the next VIDIOC_STREAMON ioctl
+       call will fail with an error. To be precise: the videobuf2
+       start_streaming() op will return -EINVAL.
+
+Inject Fatal Streaming Error: when pressed, the streaming core will be
+       marked as having suffered a fatal error, the only way to recover
+       from that is to stop streaming. To be precise: the videobuf2
+       vb2_queue_error() function is called.
+
+
+Section 9.4.5: VBI Raw Capture Controls
+---------------------------------------
+
+Interlaced VBI Format: if set, then the raw VBI data will be interlaced instead
+       of providing it grouped by field.
+
+
+Section 9.5: Digital Video Controls
+-----------------------------------
+
+Rx RGB Quantization Range: sets the RGB quantization detection of the HDMI
+       input. This combines with the Vivid 'Limited RGB Range (16-235)'
+       control and can be used to test what happens if a source provides
+       you with the wrong quantization range information. This can be tested
+       by selecting an HDMI input, setting this control to Full or Limited
+       range and selecting the opposite in the 'Limited RGB Range (16-235)'
+       control. The effect is easy to see if the 'Gray Ramp' test pattern
+       is selected.
+
+Tx RGB Quantization Range: sets the RGB quantization detection of the HDMI
+       output. It is currently not used for anything in vivid, but most HDMI
+       transmitters would typically have this control.
+
+Transmit Mode: sets the transmit mode of the HDMI output to HDMI or DVI-D. This
+       affects the reported colorspace since DVI_D outputs will always use
+       sRGB.
+
+
+Section 9.6: FM Radio Receiver Controls
+---------------------------------------
+
+RDS Reception: set if the RDS receiver should be enabled.
+
+RDS Program Type:
+RDS PS Name:
+RDS Radio Text:
+RDS Traffic Announcement:
+RDS Traffic Program:
+RDS Music: these are all read-only controls. If RDS Rx I/O Mode is set to
+       "Block I/O", then they are inactive as well. If RDS Rx I/O Mode is set
+       to "Controls", then these controls report the received RDS data. Note
+       that the vivid implementation of this is pretty basic: they are only
+       updated when you set a new frequency or when you get the tuner status
+       (VIDIOC_G_TUNER).
+
+Radio HW Seek Mode: can be one of "Bounded", "Wrap Around" or "Both". This
+       determines if VIDIOC_S_HW_FREQ_SEEK will be bounded by the frequency
+       range or wrap-around or if it is selectable by the user.
+
+Radio Programmable HW Seek: if set, then the user can provide the lower and
+       upper bound of the HW Seek. Otherwise the frequency range boundaries
+       will be used.
+
+Generate RBDS Instead of RDS: if set, then generate RBDS (the US variant of
+       RDS) data instead of RDS (European-style RDS). This affects only the
+       PICODE and PTY codes.
+
+RDS Rx I/O Mode: this can be "Block I/O" where the RDS blocks have to be read()
+       by the application, or "Controls" where the RDS data is provided by
+       the RDS controls mentioned above.
+
+
+Section 9.7: FM Radio Modulator Controls
+----------------------------------------
+
+RDS Program ID:
+RDS Program Type:
+RDS PS Name:
+RDS Radio Text:
+RDS Stereo:
+RDS Artificial Head:
+RDS Compressed:
+RDS Dymanic PTY:
+RDS Traffic Announcement:
+RDS Traffic Program:
+RDS Music: these are all controls that set the RDS data that is transmitted by
+       the FM modulator.
+
+RDS Tx I/O Mode: this can be "Block I/O" where the application has to use write()
+       to pass the RDS blocks to the driver, or "Controls" where the RDS data is
+       provided by the RDS controls mentioned above.
+
+
+Section 10: Video, VBI and RDS Looping
+--------------------------------------
+
+The vivid driver supports looping of video output to video input, VBI output
+to VBI input and RDS output to RDS input. For video/VBI looping this emulates
+as if a cable was hooked up between the output and input connector. So video
+and VBI looping is only supported between S-Video and HDMI inputs and outputs.
+VBI is only valid for S-Video as it makes no sense for HDMI.
+
+Since radio is wireless this looping always happens if the radio receiver
+frequency is close to the radio transmitter frequency. In that case the radio
+transmitter will 'override' the emulated radio stations.
+
+Looping is currently supported only between devices created by the same
+vivid driver instance.
+
+
+Section 10.1: Video and Sliced VBI looping
+------------------------------------------
+
+The way to enable video/VBI looping is currently fairly crude. A 'Loop Video'
+control is available in the "Vivid" control class of the video
+output and VBI output devices. When checked the video looping will be enabled.
+Once enabled any video S-Video or HDMI input will show a static test pattern
+until the video output has started. At that time the video output will be
+looped to the video input provided that:
+
+- the input type matches the output type. So the HDMI input cannot receive
+  video from the S-Video output.
+
+- the video resolution of the video input must match that of the video output.
+  So it is not possible to loop a 50 Hz (720x576) S-Video output to a 60 Hz
+  (720x480) S-Video input, or a 720p60 HDMI output to a 1080p30 input.
+
+- the pixel formats must be identical on both sides. Otherwise the driver would
+  have to do pixel format conversion as well, and that's taking things too far.
+
+- the field settings must be identical on both sides. Same reason as above:
+  requiring the driver to convert from one field format to another complicated
+  matters too much. This also prohibits capturing with 'Field Top' or 'Field
+  Bottom' when the output video is set to 'Field Alternate'. This combination,
+  while legal, became too complicated to support. Both sides have to be 'Field
+  Alternate' for this to work. Also note that for this specific case the
+  sequence and field counting in struct v4l2_buffer on the capture side may not
+  be 100% accurate.
+
+- on the input side the "Standard Signal Mode" for the S-Video input or the
+  "DV Timings Signal Mode" for the HDMI input should be configured so that a
+  valid signal is passed to the video input.
+
+The framerates do not have to match, although this might change in the future.
+
+By default you will see the OSD text superimposed on top of the looped video.
+This can be turned off by changing the "OSD Text Mode" control of the video
+capture device.
+
+For VBI looping to work all of the above must be valid and in addition the vbi
+output must be configured for sliced VBI. The VBI capture side can be configured
+for either raw or sliced VBI. Note that at the moment only CC/XDS (60 Hz formats)
+and WSS (50 Hz formats) VBI data is looped. Teletext VBI data is not looped.
+
+
+Section 10.2: Radio & RDS Looping
+---------------------------------
+
+As mentioned in section 6 the radio receiver emulates stations are regular
+frequency intervals. Depending on the frequency of the radio receiver a
+signal strength value is calculated (this is returned by VIDIOC_G_TUNER).
+However, it will also look at the frequency set by the radio transmitter and
+if that results in a higher signal strength than the settings of the radio
+transmitter will be used as if it was a valid station. This also includes
+the RDS data (if any) that the transmitter 'transmits'. This is received
+faithfully on the receiver side. Note that when the driver is loaded the
+frequencies of the radio receiver and transmitter are not identical, so
+initially no looping takes place.
+
+
+Section 11: Cropping, Composing, Scaling
+----------------------------------------
+
+This driver supports cropping, composing and scaling in any combination. Normally
+which features are supported can be selected through the Vivid controls,
+but it is also possible to hardcode it when the module is loaded through the
+ccs_cap_mode and ccs_out_mode module options. See section 1 on the details of
+these module options.
+
+This allows you to test your application for all these variations.
+
+Note that the webcam input never supports cropping, composing or scaling. That
+only applies to the TV/S-Video/HDMI inputs and outputs. The reason is that
+webcams, including this virtual implementation, normally use
+VIDIOC_ENUM_FRAMESIZES to list a set of discrete framesizes that it supports.
+And that does not combine with cropping, composing or scaling. This is
+primarily a limitation of the V4L2 API which is carefully reproduced here.
+
+The minimum and maximum resolutions that the scaler can achieve are 16x16 and
+(4096 * 4) x (2160 x 4), but it can only scale up or down by a factor of 4 or
+less. So for a source resolution of 1280x720 the minimum the scaler can do is
+320x180 and the maximum is 5120x2880. You can play around with this using the
+qv4l2 test tool and you will see these dependencies.
+
+This driver also supports larger 'bytesperline' settings, something that
+VIDIOC_S_FMT allows but that few drivers implement.
+
+The scaler is a simple scaler that uses the Coarse Bresenham algorithm. It's
+designed for speed and simplicity, not quality.
+
+If the combination of crop, compose and scaling allows it, then it is possible
+to change crop and compose rectangles on the fly.
+
+
+Section 12: Formats
+-------------------
+
+The driver supports all the regular packed YUYV formats, 16, 24 and 32 RGB
+packed formats and two multiplanar formats (one luma and one chroma plane).
+
+The alpha component can be set through the 'Alpha Component' User control
+for those formats that support it. If the 'Apply Alpha To Red Only' control
+is set, then the alpha component is only used for the color red and set to
+0 otherwise.
+
+The driver has to be configured to support the multiplanar formats. By default
+the first driver instance is single-planar, the second is multi-planar, and it
+keeps alternating. This can be changed by setting the multiplanar module option,
+see section 1 for more details on that option.
+
+If the driver instance is using the multiplanar formats/API, then the first
+single planar format (YUYV) and the multiplanar NV16M and NV61M formats the
+will have a plane that has a non-zero data_offset of 128 bytes. It is rare for
+data_offset to be non-zero, so this is a useful feature for testing applications.
+
+Video output will also honor any data_offset that the application set.
+
+
+Section 13: Capture Overlay
+---------------------------
+
+Note: capture overlay support is implemented primarily to test the existing
+V4L2 capture overlay API. In practice few if any GPUs support such overlays
+anymore, and neither are they generally needed anymore since modern hardware
+is so much more capable. By setting flag 0x10000 in the node_types module
+option the vivid driver will create a simple framebuffer device that can be
+used for testing this API. Whether this API should be used for new drivers is
+questionable.
+
+This driver has support for a destructive capture overlay with bitmap clipping
+and list clipping (up to 16 rectangles) capabilities. Overlays are not
+supported for multiplanar formats. It also honors the struct v4l2_window field
+setting: if it is set to FIELD_TOP or FIELD_BOTTOM and the capture setting is
+FIELD_ALTERNATE, then only the top or bottom fields will be copied to the overlay.
+
+The overlay only works if you are also capturing at that same time. This is a
+vivid limitation since it copies from a buffer to the overlay instead of
+filling the overlay directly. And if you are not capturing, then no buffers
+are available to fill.
+
+In addition, the pixelformat of the capture format and that of the framebuffer
+must be the same for the overlay to work. Otherwise VIDIOC_OVERLAY will return
+an error.
+
+In order to really see what it going on you will need to create two vivid
+instances: the first with a framebuffer enabled. You configure the capture
+overlay of the second instance to use the framebuffer of the first, then
+you start capturing in the second instance. For the first instance you setup
+the output overlay for the video output, turn on video looping and capture
+to see the blended framebuffer overlay that's being written to by the second
+instance. This setup would require the following commands:
+
+       $ sudo modprobe vivid n_devs=2 node_types=0x10101,0x1 multiplanar=1,1
+       $ v4l2-ctl -d1 --find-fb
+       /dev/fb1 is the framebuffer associated with base address 0x12800000
+       $ sudo v4l2-ctl -d2 --set-fbuf fb=1
+       $ v4l2-ctl -d1 --set-fbuf fb=1
+       $ v4l2-ctl -d0 --set-fmt-video=pixelformat='AR15'
+       $ v4l2-ctl -d1 --set-fmt-video-out=pixelformat='AR15'
+       $ v4l2-ctl -d2 --set-fmt-video=pixelformat='AR15'
+       $ v4l2-ctl -d0 -i2
+       $ v4l2-ctl -d2 -i2
+       $ v4l2-ctl -d2 -c horizontal_movement=4
+       $ v4l2-ctl -d1 --overlay=1
+       $ v4l2-ctl -d1 -c loop_video=1
+       $ v4l2-ctl -d2 --stream-mmap --overlay=1
+
+And from another console:
+
+       $ v4l2-ctl -d1 --stream-out-mmap
+
+And yet another console:
+
+       $ qv4l2
+
+and start streaming.
+
+As you can see, this is not for the faint of heart...
+
+
+Section 14: Output Overlay
+--------------------------
+
+Note: output overlays are primarily implemented in order to test the existing
+V4L2 output overlay API. Whether this API should be used for new drivers is
+questionable.
+
+This driver has support for an output overlay and is capable of:
+
+       - bitmap clipping,
+       - list clipping (up to 16 rectangles)
+       - chromakey
+       - source chromakey
+       - global alpha
+       - local alpha
+       - local inverse alpha
+
+Output overlays are not supported for multiplanar formats. In addition, the
+pixelformat of the capture format and that of the framebuffer must be the
+same for the overlay to work. Otherwise VIDIOC_OVERLAY will return an error.
+
+Output overlays only work if the driver has been configured to create a
+framebuffer by setting flag 0x10000 in the node_types module option. The
+created framebuffer has a size of 720x576 and supports ARGB 1:5:5:5 and
+RGB 5:6:5.
+
+In order to see the effects of the various clipping, chromakeying or alpha
+processing capabilities you need to turn on video looping and see the results
+on the capture side. The use of the clipping, chromakeying or alpha processing
+capabilities will slow down the video loop considerably as a lot of checks have
+to be done per pixel.
+
+
+Section 15: Some Future Improvements
+------------------------------------
+
+Just as a reminder and in no particular order:
+
+- Add a virtual alsa driver to test audio
+- Add virtual sub-devices and media controller support
+- Some support for testing compressed video
+- Add support to loop raw VBI output to raw VBI input
+- Add support to loop teletext sliced VBI output to VBI input
+- Fix sequence/field numbering when looping of video with alternate fields
+- Add support for V4L2_CID_BG_COLOR for video outputs
+- Add ARGB888 overlay support: better testing of the alpha channel
+- Add custom DV timings support
+- Add support for V4L2_DV_FL_REDUCED_FPS
+- Improve pixel aspect support in the tpg code by passing a real v4l2_fract
+- Use per-queue locks and/or per-device locks to improve throughput
+- Add support to loop from a specific output to a specific input across
+  vivid instances
+- Add support for VIDIOC_EXPBUF once support for that has been added to vb2
+- The SDR radio should use the same 'frequencies' for stations as the normal
+  radio receiver, and give back noise if the frequency doesn't match up with
+  a station frequency
+- Improve the sine generation of the SDR radio.
+- Make a thread for the RDS generation, that would help in particular for the
+  "Controls" RDS Rx I/O Mode as the read-only RDS controls could be updated
+  in real-time.
index 1a85af84332623edfc4a302dd3588f9d4a47f193..d6964389f028f3adf502cc500bbfd7cebc51f4d2 100644 (file)
@@ -2760,6 +2760,18 @@ W:       http://www.chelsio.com
 S:     Supported
 F:     drivers/net/ethernet/chelsio/cxgb4vf/
 
+CXL (IBM Coherent Accelerator Processor Interface CAPI) DRIVER
+M:     Ian Munsie <imunsie@au1.ibm.com>
+M:     Michael Neuling <mikey@neuling.org>
+L:     linuxppc-dev@lists.ozlabs.org
+S:     Supported
+F:     drivers/misc/cxl/
+F:     include/misc/cxl.h
+F:     include/uapi/misc/cxl.h
+F:     Documentation/powerpc/cxl.txt
+F:     Documentation/powerpc/cxl.txt
+F:     Documentation/ABI/testing/sysfs-class-cxl
+
 STMMAC ETHERNET DRIVER
 M:     Giuseppe Cavallaro <peppe.cavallaro@st.com>
 L:     netdev@vger.kernel.org
@@ -4232,6 +4244,16 @@ L:       linuxppc-dev@lists.ozlabs.org
 S:     Odd Fixes
 F:     drivers/tty/hvc/
 
+HACKRF MEDIA DRIVER
+M:     Antti Palosaari <crope@iki.fi>
+L:     linux-media@vger.kernel.org
+W:     http://linuxtv.org/
+W:     http://palosaari.fi/linux/
+Q:     http://patchwork.linuxtv.org/project/linux-media/list/
+T:     git git://linuxtv.org/anttip/media_tree.git
+S:     Maintained
+F:     drivers/media/usb/hackrf/
+
 HARDWARE MONITORING
 M:     Jean Delvare <jdelvare@suse.de>
 M:     Guenter Roeck <linux@roeck-us.net>
@@ -5044,6 +5066,7 @@ L:        linux-kernel@vger.kernel.org
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
 T:     git git://git.infradead.org/users/jcooper/linux.git irqchip/core
+F:     Documentation/devicetree/bindings/interrupt-controller/
 F:     drivers/irqchip/
 
 IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
@@ -5129,7 +5152,7 @@ W:        http://palosaari.fi/linux/
 Q:     http://patchwork.linuxtv.org/project/linux-media/list/
 T:     git git://linuxtv.org/anttip/media_tree.git
 S:     Maintained
-F:     drivers/media/tuners/tuner_it913x*
+F:     drivers/media/tuners/it913x*
 
 IVTV VIDEO4LINUX DRIVER
 M:     Andy Walls <awalls@md.metrocast.net>
@@ -6604,10 +6627,9 @@ S:       Maintained
 F:     drivers/mmc/host/omap.c
 
 OMAP HS MMC SUPPORT
-M:     Balaji T K <balajitk@ti.com>
 L:     linux-mmc@vger.kernel.org
 L:     linux-omap@vger.kernel.org
-S:     Maintained
+S:     Orphan
 F:     drivers/mmc/host/omap_hsmmc.c
 
 OMAP RANDOM NUMBER GENERATOR SUPPORT
@@ -6937,6 +6959,14 @@ F:       include/linux/pci*
 F:     arch/x86/pci/
 F:     arch/x86/kernel/quirks.c
 
+PCI DRIVER FOR APPLIEDMICRO XGENE
+M:     Tanmay Inamdar <tinamdar@apm.com>
+L:     linux-pci@vger.kernel.org
+L:     linux-arm-kernel@lists.infradead.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/pci/xgene-pci.txt
+F:     drivers/pci/host/pci-xgene.c
+
 PCI DRIVER FOR IMX6
 M:     Richard Zhu <r65037@freescale.com>
 M:     Lucas Stach <l.stach@pengutronix.de>
@@ -6945,6 +6975,13 @@ L:       linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     drivers/pci/host/*imx6*
 
+PCI DRIVER FOR TI KEYSTONE
+M:     Murali Karicheri <m-karicheri2@ti.com>
+L:     linux-pci@vger.kernel.org
+L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:     Maintained
+F:     drivers/pci/host/*keystone*
+
 PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
 M:     Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
 M:     Jason Cooper <jason@lakedaemon.net>
@@ -7347,6 +7384,14 @@ T:       git git://linuxtv.org/media_tree.git
 S:     Maintained
 F:     drivers/media/usb/pwc/*
 
+PWM FAN DRIVER
+M:     Kamil Debski <k.debski@samsung.com>
+L:     lm-sensors@lm-sensors.org
+S:     Supported
+F:     Documentation/devicetree/bindings/hwmon/pwm-fan.txt
+F:     Documentation/hwmon/pwm-fan
+F:     drivers/hwmon/pwm-fan.c
+
 PWM SUBSYSTEM
 M:     Thierry Reding <thierry.reding@gmail.com>
 L:     linux-pwm@vger.kernel.org
@@ -8473,11 +8518,11 @@ S:      Maintained
 F:     Documentation/security/Smack.txt
 F:     security/smack/
 
-SMARTREFLEX DRIVERS FOR ADAPTIVE VOLTAGE SCALING (AVS)
+DRIVERS FOR ADAPTIVE VOLTAGE SCALING (AVS)
 M:     Kevin Hilman <khilman@kernel.org>
 M:     Nishanth Menon <nm@ti.com>
 S:     Maintained
-F:     drivers/power/avs/smartreflex.c
+F:     drivers/power/avs/
 F:     include/linux/power/smartreflex.h
 L:     linux-pm@vger.kernel.org
 
@@ -8647,6 +8692,14 @@ F:       include/sound/dmaengine_pcm.h
 F:     sound/core/pcm_dmaengine.c
 F:     sound/soc/soc-generic-dmaengine-pcm.c
 
+SP2 MEDIA DRIVER
+M:     Olli Salonen <olli.salonen@iki.fi>
+L:     linux-media@vger.kernel.org
+W:     http://linuxtv.org/
+Q:     http://patchwork.linuxtv.org/project/linux-media/list/
+S:     Maintained
+F:     drivers/media/dvb-frontends/sp2*
+
 SPARC + UltraSPARC (sparc/sparc64)
 M:     "David S. Miller" <davem@davemloft.net>
 L:     sparclinux@vger.kernel.org
@@ -9350,6 +9403,14 @@ T:       git git://linuxtv.org/media_tree.git
 S:     Odd fixes
 F:     drivers/media/usb/tm6000/
 
+TW68 VIDEO4LINUX DRIVER
+M:     Hans Verkuil <hverkuil@xs4all.nl>
+L:     linux-media@vger.kernel.org
+T:     git git://linuxtv.org/media_tree.git
+W:     http://linuxtv.org
+S:     Odd Fixes
+F:     drivers/media/pci/tw68/
+
 TPM DEVICE DRIVER
 M:     Peter Huewe <peterhuewe@gmx.de>
 M:     Ashley Lai <ashley@ashleylai.com>
@@ -10218,6 +10279,15 @@ S:     Supported
 F:     drivers/block/xen-blkback/*
 F:     drivers/block/xen*
 
+XEN PVSCSI DRIVERS
+M:     Juergen Gross <jgross@suse.com>
+L:     xen-devel@lists.xenproject.org (moderated for non-subscribers)
+L:     linux-scsi@vger.kernel.org
+S:     Supported
+F:     drivers/scsi/xen-scsifront.c
+F:     drivers/xen/xen-scsiback.c
+F:     include/xen/interface/io/vscsiif.h
+
 XEN SWIOTLB SUBSYSTEM
 M:     Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
 L:     xen-devel@lists.xenproject.org (moderated for non-subscribers)
index e858aa0ad8af3cc4008b464914c264acd39e3980..25b49725df07d7ecad8e222060486e9d746925b9 100644 (file)
@@ -4,7 +4,9 @@ generic-y += clkdev.h
 generic-y += cputime.h
 generic-y += exec.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += preempt.h
 generic-y += scatterlist.h
+generic-y += sections.h
 generic-y += trace_clock.h
diff --git a/arch/alpha/include/asm/sections.h b/arch/alpha/include/asm/sections.h
deleted file mode 100644 (file)
index 43b40ed..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _ALPHA_SECTIONS_H
-#define _ALPHA_SECTIONS_H
-
-/* nothing to see, move along */
-#include <asm-generic/sections.h>
-
-#endif
index e76fd79f32b0126ee2e83dfaa5ba05d3d4c98452..b8fffc1a2ac237b8feca4a42cc0fd036a9bc8ba2 100644 (file)
@@ -18,6 +18,7 @@ generic-y += ioctl.h
 generic-y += ioctls.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kmap_types.h
 generic-y += kvm_para.h
 generic-y += local.h
index 82dfdeac3595e100a07a001c9e98356420938fe6..89c4b5ccc68df8200aeff85bf89adaf87ce24e12 100644 (file)
@@ -14,6 +14,7 @@ config ARM
        select CLONE_BACKWARDS
        select CPU_PM if (SUSPEND || CPU_IDLE)
        select DCACHE_WORD_ACCESS if HAVE_EFFICIENT_UNALIGNED_ACCESS
+       select GENERIC_ALLOCATOR
        select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI)
        select GENERIC_CLOCKEVENTS_BROADCAST if SMP
        select GENERIC_IDLE_POLL_SETUP
@@ -24,6 +25,7 @@ config ARM
        select GENERIC_SMP_IDLE_THREAD
        select GENERIC_STRNCPY_FROM_USER
        select GENERIC_STRNLEN_USER
+       select HANDLE_DOMAIN_IRQ
        select HARDIRQS_SW_RESEND
        select HAVE_ARCH_AUDITSYSCALL if (AEABI && !OABI_COMPAT)
        select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
@@ -60,6 +62,7 @@ config ARM
        select HAVE_PERF_EVENTS
        select HAVE_PERF_REGS
        select HAVE_PERF_USER_STACK_DUMP
+       select HAVE_RCU_TABLE_FREE if (SMP && ARM_LPAE)
        select HAVE_REGS_AND_STACK_ACCESS_API
        select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_UID16
@@ -1658,6 +1661,10 @@ config ARCH_SELECT_MEMORY_MODEL
 config HAVE_ARCH_PFN_VALID
        def_bool ARCH_HAS_HOLES_MEMORYMODEL || !SPARSEMEM
 
+config HAVE_GENERIC_RCU_GUP
+       def_bool y
+       depends on ARM_LPAE
+
 config HIGHMEM
        bool "High Memory Support"
        depends on MMU
@@ -1772,7 +1779,7 @@ config XEN_DOM0
        depends on XEN
 
 config XEN
-       bool "Xen guest support on ARM (EXPERIMENTAL)"
+       bool "Xen guest support on ARM"
        depends on ARM && AEABI && OF
        depends on CPU_V7 && !CPU_V6
        depends on !GENERIC_ATOMIC64
index 70a559cf1a3d11b0d191fedcbe15779011560fc8..4f2df61c1cfc6fde03a39968d6c8d5666ba5e834 100644 (file)
@@ -69,7 +69,8 @@
                samsung,dw-mshc-ddr-timing = <1 2>;
                pinctrl-names = "default";
                pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
-               vmmc-supply = <&ldo10_reg>;
+               vmmc-supply = <&ldo19_reg>;
+               vqmmc-supply = <&ldo13_reg>;
                bus-width = <4>;
                cap-sd-highspeed;
        };
index fa5f2bb5f106fd29aafddb40be9e821feab4ce86..9d342920695a5f4e465b04332cebddc9ff73c347 100644 (file)
@@ -85,7 +85,8 @@
 
                pcie0: pcie@b1000000 {
                        compatible = "st,spear1340-pcie", "snps,dw-pcie";
-                       reg = <0xb1000000 0x4000>;
+                       reg = <0xb1000000 0x4000>, <0x80000000 0x20000>;
+                       reg-names = "dbi", "config";
                        interrupts = <0 68 0x4>;
                        interrupt-map-mask = <0 0 0 0>;
                        interrupt-map = <0x0 0 &gic 0 68 0x4>;
                        #address-cells = <3>;
                        #size-cells = <2>;
                        device_type = "pci";
-                       ranges = <0x00000800 0 0x80000000 0x80000000 0 0x00020000   /* configuration space */
-                               0x81000000 0 0   0x80020000 0 0x00010000   /* downstream I/O */
+                       ranges = <0x81000000 0 0         0x80020000 0 0x00010000   /* downstream I/O */
                                0x82000000 0 0x80030000 0xc0030000 0 0x0ffd0000>; /* non-prefetchable memory */
                        status = "disabled";
                };
 
                pcie1: pcie@b1800000 {
                        compatible = "st,spear1340-pcie", "snps,dw-pcie";
-                       reg = <0xb1800000 0x4000>;
+                       reg = <0xb1800000 0x4000>, <0x90000000 0x20000>;
+                       reg-names = "dbi", "config";
                        interrupts = <0 69 0x4>;
                        interrupt-map-mask = <0 0 0 0>;
                        interrupt-map = <0x0 0 &gic 0 69 0x4>;
                        #address-cells = <3>;
                        #size-cells = <2>;
                        device_type = "pci";
-                       ranges = <0x00000800 0 0x90000000 0x90000000 0 0x00020000   /* configuration space */
-                               0x81000000 0 0  0x90020000 0 0x00010000   /* downstream I/O */
+                       ranges = <0x81000000 0 0  0x90020000 0 0x00010000   /* downstream I/O */
                                0x82000000 0 0x90030000 0x90030000 0 0x0ffd0000>; /* non-prefetchable memory */
                        status = "disabled";
                };
 
                pcie2: pcie@b4000000 {
                        compatible = "st,spear1340-pcie", "snps,dw-pcie";
-                       reg = <0xb4000000 0x4000>;
+                       reg = <0xb4000000 0x4000>, <0xc0000000 0x20000>;
+                       reg-names = "dbi", "config";
                        interrupts = <0 70 0x4>;
                        interrupt-map-mask = <0 0 0 0>;
                        interrupt-map = <0x0 0 &gic 0 70 0x4>;
                        #address-cells = <3>;
                        #size-cells = <2>;
                        device_type = "pci";
-                       ranges = <0x00000800 0 0xc0000000 0xc0000000 0 0x00020000   /* configuration space */
-                               0x81000000 0 0   0xc0020000 0 0x00010000   /* downstream I/O */
+                       ranges = <0x81000000 0 0         0xc0020000 0 0x00010000   /* downstream I/O */
                                0x82000000 0 0xc0030000 0xc0030000 0 0x0ffd0000>; /* non-prefetchable memory */
                        status = "disabled";
                };
index e71df0f2cb52d579bcfef4d114cba5d6e5acddfb..13e1aa33daa2e379bde1b3cbc2882b1816ba3a39 100644 (file)
@@ -50,7 +50,8 @@
 
                pcie0: pcie@b1000000 {
                        compatible = "st,spear1340-pcie", "snps,dw-pcie";
-                       reg = <0xb1000000 0x4000>;
+                       reg = <0xb1000000 0x4000>, <0x80000000 0x20000>;
+                       reg-names = "dbi", "config";
                        interrupts = <0 68 0x4>;
                        interrupt-map-mask = <0 0 0 0>;
                        interrupt-map = <0x0 0 &gic 0 68 0x4>;
@@ -60,8 +61,7 @@
                        #address-cells = <3>;
                        #size-cells = <2>;
                        device_type = "pci";
-                       ranges = <0x00000800 0 0x80000000 0x80000000 0 0x00020000   /* configuration space */
-                               0x81000000 0 0   0x80020000 0 0x00010000   /* downstream I/O */
+                       ranges = <0x81000000 0 0         0x80020000 0 0x00010000   /* downstream I/O */
                                0x82000000 0 0x80030000 0xc0030000 0 0x0ffd0000>; /* non-prefetchable memory */
                        status = "disabled";
                };
index a25c262326dcdcc2e681f6f2e900de8ea59891a2..322fd1519b09fe049a731b662de2408ff37074ff 100644 (file)
@@ -38,6 +38,7 @@
                        compatible = "arm,cortex-a15";
                        reg = <0>;
                        cci-control-port = <&cci_control1>;
+                       cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
                };
 
                cpu1: cpu@1 {
@@ -45,6 +46,7 @@
                        compatible = "arm,cortex-a15";
                        reg = <1>;
                        cci-control-port = <&cci_control1>;
+                       cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
                };
 
                cpu2: cpu@2 {
@@ -52,6 +54,7 @@
                        compatible = "arm,cortex-a7";
                        reg = <0x100>;
                        cci-control-port = <&cci_control2>;
+                       cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
                };
 
                cpu3: cpu@3 {
@@ -59,6 +62,7 @@
                        compatible = "arm,cortex-a7";
                        reg = <0x101>;
                        cci-control-port = <&cci_control2>;
+                       cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
                };
 
                cpu4: cpu@4 {
                        compatible = "arm,cortex-a7";
                        reg = <0x102>;
                        cci-control-port = <&cci_control2>;
+                       cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
+               };
+
+               idle-states {
+                       CLUSTER_SLEEP_BIG: cluster-sleep-big {
+                               compatible = "arm,idle-state";
+                               local-timer-stop;
+                               entry-latency-us = <1000>;
+                               exit-latency-us = <700>;
+                               min-residency-us = <2000>;
+                       };
+
+                       CLUSTER_SLEEP_LITTLE: cluster-sleep-little {
+                               compatible = "arm,idle-state";
+                               local-timer-stop;
+                               entry-latency-us = <1000>;
+                               exit-latency-us = <500>;
+                               min-residency-us = <2500>;
+                       };
                };
        };
 
index a20fa80776d3d873c6ef8ef849a8ac2b214b2776..45f4c21e393c1992bfdcc980e859fe081968dffd 100644 (file)
@@ -243,18 +243,12 @@ err_ioremap:
 static int scoop_remove(struct platform_device *pdev)
 {
        struct scoop_dev *sdev = platform_get_drvdata(pdev);
-       int ret;
 
        if (!sdev)
                return -EINVAL;
 
-       if (sdev->gpio.base != -1) {
-               ret = gpiochip_remove(&sdev->gpio);
-               if (ret) {
-                       dev_err(&pdev->dev, "Can't remove gpio chip: %d\n", ret);
-                       return ret;
-               }
-       }
+       if (sdev->gpio.base != -1)
+               gpiochip_remove(&sdev->gpio);
 
        platform_set_drvdata(pdev, NULL);
        iounmap(sdev->base);
index 0704e0cf557146da78c086079daeec8c0112d326..92793ba69c4020bc3ae1aac0560f96b7be0347a5 100644 (file)
@@ -99,31 +99,6 @@ static inline void arch_timer_set_cntkctl(u32 cntkctl)
        asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
 }
 
-static inline void arch_counter_set_user_access(void)
-{
-       u32 cntkctl = arch_timer_get_cntkctl();
-
-       /* Disable user access to both physical/virtual counters/timers */
-       /* Also disable virtual event stream */
-       cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
-                       | ARCH_TIMER_USR_VT_ACCESS_EN
-                       | ARCH_TIMER_VIRT_EVT_EN
-                       | ARCH_TIMER_USR_VCT_ACCESS_EN
-                       | ARCH_TIMER_USR_PCT_ACCESS_EN);
-       arch_timer_set_cntkctl(cntkctl);
-}
-
-static inline void arch_timer_evtstrm_enable(int divider)
-{
-       u32 cntkctl = arch_timer_get_cntkctl();
-       cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK;
-       /* Set the divider and enable virtual event stream */
-       cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT)
-                       | ARCH_TIMER_VIRT_EVT_EN;
-       arch_timer_set_cntkctl(cntkctl);
-       elf_hwcap |= HWCAP_EVTSTRM;
-}
-
 #endif
 
 #endif
index c45b61a4b4a5250e66908038be02203f9880ebec..85738b200023a4d09243175eb023040cd50a0527 100644 (file)
@@ -265,22 +265,6 @@ extern int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
                        void *cpu_addr, dma_addr_t dma_addr, size_t size,
                        struct dma_attrs *attrs);
 
-static inline void *dma_alloc_writecombine(struct device *dev, size_t size,
-                                      dma_addr_t *dma_handle, gfp_t flag)
-{
-       DEFINE_DMA_ATTRS(attrs);
-       dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
-       return dma_alloc_attrs(dev, size, dma_handle, flag, &attrs);
-}
-
-static inline void dma_free_writecombine(struct device *dev, size_t size,
-                                    void *cpu_addr, dma_addr_t dma_handle)
-{
-       DEFINE_DMA_ATTRS(attrs);
-       dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
-       return dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs);
-}
-
 /*
  * This can be called during early boot to increase the size of the atomic
  * coherent DMA pool above the default value of 256KiB. It must be called
index 3d23418cbdddca28036d0e126c915b85d2aeddc3..180567408ee8a0310eed2185fbafa2d06ae991d5 100644 (file)
@@ -178,6 +178,7 @@ static inline void __iomem *__typesafe_io(unsigned long addr)
 
 /* PCI fixed i/o mapping */
 #define PCI_IO_VIRT_BASE       0xfee00000
+#define PCI_IOBASE             ((void __iomem *)PCI_IO_VIRT_BASE)
 
 #if defined(CONFIG_PCI)
 void pci_ioremap_set_mem_type(int mem_type);
diff --git a/arch/arm/include/asm/irq_work.h b/arch/arm/include/asm/irq_work.h
new file mode 100644 (file)
index 0000000..712d03e
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __ASM_ARM_IRQ_WORK_H
+#define __ASM_ARM_IRQ_WORK_H
+
+#include <asm/smp_plat.h>
+
+static inline bool arch_irq_work_has_interrupt(void)
+{
+       return is_smp();
+}
+
+#endif /* _ASM_ARM_IRQ_WORK_H */
index 219ac88a954243b3e3acaebcebd9871ea49825e4..f0279411847d72e2f73ffd430a9a5d53d9968514 100644 (file)
@@ -182,6 +182,8 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
 #define pmd_addr_end(addr,end) (end)
 
 #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
+#define pte_special(pte)       (0)
+static inline pte_t pte_mkspecial(pte_t pte) { return pte; }
 
 /*
  * We don't have huge page support for short descriptors, for the moment
index 06e0bc0f8b00b2c7f9fe9f6dac97c81b00b803bb..a31ecdad4b5966dbf15953054bcc0c9d260cd302 100644 (file)
@@ -213,10 +213,19 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
 #define pmd_isclear(pmd, val)  (!(pmd_val(pmd) & (val)))
 
 #define pmd_young(pmd)         (pmd_isset((pmd), PMD_SECT_AF))
+#define pte_special(pte)       (pte_isset((pte), L_PTE_SPECIAL))
+static inline pte_t pte_mkspecial(pte_t pte)
+{
+       pte_val(pte) |= L_PTE_SPECIAL;
+       return pte;
+}
+#define        __HAVE_ARCH_PTE_SPECIAL
 
 #define __HAVE_ARCH_PMD_WRITE
 #define pmd_write(pmd)         (pmd_isclear((pmd), L_PMD_SECT_RDONLY))
 #define pmd_dirty(pmd)         (pmd_isset((pmd), L_PMD_SECT_DIRTY))
+#define pud_page(pud)          pmd_page(__pmd(pud_val(pud)))
+#define pud_write(pud)         pmd_write(__pmd(pud_val(pud)))
 
 #define pmd_hugewillfault(pmd) (!pmd_young(pmd) || !pmd_write(pmd))
 #define pmd_thp_or_huge(pmd)   (pmd_huge(pmd) || pmd_trans_huge(pmd))
@@ -224,6 +233,12 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 #define pmd_trans_huge(pmd)    (pmd_val(pmd) && !pmd_table(pmd))
 #define pmd_trans_splitting(pmd) (pmd_isset((pmd), L_PMD_SECT_SPLITTING))
+
+#ifdef CONFIG_HAVE_RCU_TABLE_FREE
+#define __HAVE_ARCH_PMDP_SPLITTING_FLUSH
+void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address,
+                         pmd_t *pmdp);
+#endif
 #endif
 
 #define PMD_BIT_FUNC(fn,op) \
index 01baef07cd0ca64a16f8b84c52c56a88c7ef6989..90aa4583b3086812e7bbea226f9092c0cc422cd7 100644 (file)
@@ -226,7 +226,6 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
 #define pte_dirty(pte)         (pte_isset((pte), L_PTE_DIRTY))
 #define pte_young(pte)         (pte_isset((pte), L_PTE_YOUNG))
 #define pte_exec(pte)          (pte_isclear((pte), L_PTE_XN))
-#define pte_special(pte)       (0)
 
 #define pte_valid_user(pte)    \
        (pte_valid(pte) && pte_isset((pte), L_PTE_USER) && pte_young(pte))
@@ -245,7 +244,8 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
        unsigned long ext = 0;
 
        if (addr < TASK_SIZE && pte_valid_user(pteval)) {
-               __sync_icache_dcache(pteval);
+               if (!pte_special(pteval))
+                       __sync_icache_dcache(pteval);
                ext |= PTE_EXT_NG;
        }
 
@@ -264,8 +264,6 @@ PTE_BIT_FUNC(mkyoung,   |= L_PTE_YOUNG);
 PTE_BIT_FUNC(mkexec,   &= ~L_PTE_XN);
 PTE_BIT_FUNC(mknexec,   |= L_PTE_XN);
 
-static inline pte_t pte_mkspecial(pte_t pte) { return pte; }
-
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
        const pteval_t mask = L_PTE_XN | L_PTE_RDONLY | L_PTE_USER |
index f1a0dace3efee423e7727e143550aae06f081fd5..3cadb726ec88715ee854b5084454e5cb71c4aef3 100644 (file)
 
 #define MMU_GATHER_BUNDLE      8
 
+#ifdef CONFIG_HAVE_RCU_TABLE_FREE
+static inline void __tlb_remove_table(void *_table)
+{
+       free_page_and_swap_cache((struct page *)_table);
+}
+
+struct mmu_table_batch {
+       struct rcu_head         rcu;
+       unsigned int            nr;
+       void                    *tables[0];
+};
+
+#define MAX_TABLE_BATCH                \
+       ((PAGE_SIZE - sizeof(struct mmu_table_batch)) / sizeof(void *))
+
+extern void tlb_table_flush(struct mmu_gather *tlb);
+extern void tlb_remove_table(struct mmu_gather *tlb, void *table);
+
+#define tlb_remove_entry(tlb, entry)   tlb_remove_table(tlb, entry)
+#else
+#define tlb_remove_entry(tlb, entry)   tlb_remove_page(tlb, entry)
+#endif /* CONFIG_HAVE_RCU_TABLE_FREE */
+
 /*
  * TLB handling.  This allows us to remove pages from the page
  * tables, and efficiently handle the TLB issues.
  */
 struct mmu_gather {
        struct mm_struct        *mm;
+#ifdef CONFIG_HAVE_RCU_TABLE_FREE
+       struct mmu_table_batch  *batch;
+       unsigned int            need_flush;
+#endif
        unsigned int            fullmm;
        struct vm_area_struct   *vma;
        unsigned long           start, end;
@@ -101,6 +128,9 @@ static inline void __tlb_alloc_page(struct mmu_gather *tlb)
 static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb)
 {
        tlb_flush(tlb);
+#ifdef CONFIG_HAVE_RCU_TABLE_FREE
+       tlb_table_flush(tlb);
+#endif
 }
 
 static inline void tlb_flush_mmu_free(struct mmu_gather *tlb)
@@ -129,6 +159,10 @@ tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start
        tlb->pages = tlb->local;
        tlb->nr = 0;
        __tlb_alloc_page(tlb);
+
+#ifdef CONFIG_HAVE_RCU_TABLE_FREE
+       tlb->batch = NULL;
+#endif
 }
 
 static inline void
@@ -205,7 +239,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
        tlb_add_flush(tlb, addr + SZ_1M);
 #endif
 
-       tlb_remove_page(tlb, pte);
+       tlb_remove_entry(tlb, pte);
 }
 
 static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
@@ -213,7 +247,7 @@ static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
 {
 #ifdef CONFIG_ARM_LPAE
        tlb_add_flush(tlb, addr);
-       tlb_remove_page(tlb, virt_to_page(pmdp));
+       tlb_remove_entry(tlb, virt_to_page(pmdp));
 #endif
 }
 
index bb8b796486430d7fa118881758ad67badb10fd7e..c4cc50e58c13c3e032031339852c012ca7b344ca 100644 (file)
@@ -21,8 +21,7 @@
 #include <asm/idmap.h>
 #include <asm/suspend.h>
 #include <asm/memory.h>
-
-extern const void __nosave_begin, __nosave_end;
+#include <asm/sections.h>
 
 int pfn_is_nosave(unsigned long pfn)
 {
index 88de943eebd627786a72b6debf58e1d96d7545eb..7c81ec428b9b5397b05526d9c45c6c8d39508b8c 100644 (file)
@@ -65,24 +65,7 @@ int arch_show_interrupts(struct seq_file *p, int prec)
  */
 void handle_IRQ(unsigned int irq, struct pt_regs *regs)
 {
-       struct pt_regs *old_regs = set_irq_regs(regs);
-
-       irq_enter();
-
-       /*
-        * Some hardware gives randomly wrong interrupts.  Rather
-        * than crashing, do something sensible.
-        */
-       if (unlikely(irq >= nr_irqs)) {
-               if (printk_ratelimit())
-                       printk(KERN_WARNING "Bad IRQ%u\n", irq);
-               ack_bad_irq(irq);
-       } else {
-               generic_handle_irq(irq);
-       }
-
-       irq_exit();
-       set_irq_regs(old_regs);
+       __handle_domain_irq(NULL, irq, false, regs);
 }
 
 /*
index a0a691d1cbeee0e675e3d18674b15fc1f1224342..fe972a2f3df398f9eddbf02b25d4edff987b442d 100644 (file)
@@ -114,18 +114,13 @@ void soft_restart(unsigned long addr)
        BUG();
 }
 
-static void null_restart(enum reboot_mode reboot_mode, const char *cmd)
-{
-}
-
 /*
  * Function pointers to optional machine specific functions
  */
 void (*pm_power_off)(void);
 EXPORT_SYMBOL(pm_power_off);
 
-void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd) = null_restart;
-EXPORT_SYMBOL_GPL(arm_pm_restart);
+void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
 
 /*
  * This is our default idle handler.
@@ -230,7 +225,10 @@ void machine_restart(char *cmd)
        local_irq_disable();
        smp_send_stop();
 
-       arm_pm_restart(reboot_mode, cmd);
+       if (arm_pm_restart)
+               arm_pm_restart(reboot_mode, cmd);
+       else
+               do_kernel_restart(cmd);
 
        /* Give a grace period for failure to restart of 1s */
        mdelay(1000);
index 39c74a2c3df9c7370fe2ca9cf2b67ea6dd8985fd..13396d3d600ee26f3578e62edd24e8b7b09315f9 100644 (file)
@@ -499,7 +499,7 @@ void arch_send_call_function_single_ipi(int cpu)
 #ifdef CONFIG_IRQ_WORK
 void arch_irq_work_raise(void)
 {
-       if (is_smp())
+       if (arch_irq_work_has_interrupt())
                smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK);
 }
 #endif
index 6a24e111d6e1819f8b259adc4d1a41306a627651..b89e5f35db841e50b64a5790af79e0c11c1e783f 100644 (file)
@@ -193,7 +193,6 @@ static void __init exynos_init_late(void)
                /* to be supported later */
                return;
 
-       pm_genpd_poweroff_unused();
        exynos_pm_init();
 }
 
index fd76e1b5a471a4f3ffcfd3a2c4539a16b2d26f40..20f267121b3e7876e4ab806ab6c2f655e9467499 100644 (file)
@@ -105,78 +105,6 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain)
        return exynos_pd_power(domain, false);
 }
 
-static void exynos_add_device_to_domain(struct exynos_pm_domain *pd,
-                                        struct device *dev)
-{
-       int ret;
-
-       dev_dbg(dev, "adding to power domain %s\n", pd->pd.name);
-
-       while (1) {
-               ret = pm_genpd_add_device(&pd->pd, dev);
-               if (ret != -EAGAIN)
-                       break;
-               cond_resched();
-       }
-
-       pm_genpd_dev_need_restore(dev, true);
-}
-
-static void exynos_remove_device_from_domain(struct device *dev)
-{
-       struct generic_pm_domain *genpd = dev_to_genpd(dev);
-       int ret;
-
-       dev_dbg(dev, "removing from power domain %s\n", genpd->name);
-
-       while (1) {
-               ret = pm_genpd_remove_device(genpd, dev);
-               if (ret != -EAGAIN)
-                       break;
-               cond_resched();
-       }
-}
-
-static void exynos_read_domain_from_dt(struct device *dev)
-{
-       struct platform_device *pd_pdev;
-       struct exynos_pm_domain *pd;
-       struct device_node *node;
-
-       node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0);
-       if (!node)
-               return;
-       pd_pdev = of_find_device_by_node(node);
-       if (!pd_pdev)
-               return;
-       pd = platform_get_drvdata(pd_pdev);
-       exynos_add_device_to_domain(pd, dev);
-}
-
-static int exynos_pm_notifier_call(struct notifier_block *nb,
-                                   unsigned long event, void *data)
-{
-       struct device *dev = data;
-
-       switch (event) {
-       case BUS_NOTIFY_BIND_DRIVER:
-               if (dev->of_node)
-                       exynos_read_domain_from_dt(dev);
-
-               break;
-
-       case BUS_NOTIFY_UNBOUND_DRIVER:
-               exynos_remove_device_from_domain(dev);
-
-               break;
-       }
-       return NOTIFY_DONE;
-}
-
-static struct notifier_block platform_nb = {
-       .notifier_call = exynos_pm_notifier_call,
-};
-
 static __init int exynos4_pm_init_power_domain(void)
 {
        struct platform_device *pdev;
@@ -202,7 +130,6 @@ static __init int exynos4_pm_init_power_domain(void)
                pd->base = of_iomap(np, 0);
                pd->pd.power_off = exynos_pd_power_off;
                pd->pd.power_on = exynos_pd_power_on;
-               pd->pd.of_node = np;
 
                pd->oscclk = clk_get(dev, "oscclk");
                if (IS_ERR(pd->oscclk))
@@ -228,15 +155,12 @@ static __init int exynos4_pm_init_power_domain(void)
                        clk_put(pd->oscclk);
 
 no_clk:
-               platform_set_drvdata(pdev, pd);
-
                on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN;
 
                pm_genpd_init(&pd->pd, NULL, !on);
+               of_genpd_add_provider_simple(np, &pd->pd);
        }
 
-       bus_register_notifier(&platform_bus_type, &platform_nb);
-
        return 0;
 }
 arch_initcall(exynos4_pm_init_power_domain);
index 24b103c67f82cae548785b66bf1cd1de7277e0d9..1a8932335b2136b9ff941adc3d2bc35abdebb325 100644 (file)
@@ -144,7 +144,7 @@ static void __exception_irq_entry avic_handle_irq(struct pt_regs *regs)
                if (nivector == 0xffff)
                        break;
 
-               handle_IRQ(irq_find_mapping(domain, nivector), regs);
+               handle_domain_irq(domain, nivector, regs);
        } while (1);
 }
 
index 080e66c6a1d02722022f12a7991cf0b0b566f72e..dc8f1a6f45f20a9551045105400a30587d00b1a1 100644 (file)
@@ -20,7 +20,7 @@
 
 static void __init imx27_dt_init(void)
 {
-       struct platform_device_info devinfo = { .name = "cpufreq-cpu0", };
+       struct platform_device_info devinfo = { .name = "cpufreq-dt", };
 
        mxc_arch_reset_init_dt();
 
index c77deb3f08939f50304797ea5ac1c1b17983fef7..2c5fcaf8675b96bfdd4ee391871c1602eba5ae08 100644 (file)
@@ -51,7 +51,7 @@ static void __init imx51_ipu_mipi_setup(void)
 
 static void __init imx51_dt_init(void)
 {
-       struct platform_device_info devinfo = { .name = "cpufreq-cpu0", };
+       struct platform_device_info devinfo = { .name = "cpufreq-dt", };
 
        mxc_arch_reset_init_dt();
        imx51_ipu_mipi_setup();
index 1d4f384ca773f78e72de41248a8f242db20c5ec1..4de65eeda1eb15666e9c82557f50477e37628ec8 100644 (file)
@@ -141,8 +141,7 @@ static void __exception_irq_entry tzic_handle_irq(struct pt_regs *regs)
                        while (stat) {
                                handled = 1;
                                irqofs = fls(stat) - 1;
-                               handle_IRQ(irq_find_mapping(domain,
-                                               irqofs + i * 32), regs);
+                               handle_domain_irq(domain, irqofs + i * 32, regs);
                                stat &= ~(1 << irqofs);
                        }
                }
index 05e1f73a1e8dbd022d1d5be3d72be182d3086c9d..c186a17c2cffec838205e78fc4fa8e78aed56d3a 100644 (file)
@@ -660,6 +660,7 @@ static void __init pci_v3_preinit(void)
 {
        unsigned long flags;
        unsigned int temp;
+       phys_addr_t io_address = pci_pio_to_address(io_mem.start);
 
        pcibios_min_mem = 0x00100000;
 
@@ -701,7 +702,7 @@ static void __init pci_v3_preinit(void)
        /*
         * Setup window 2 - PCI IO
         */
-       v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_mem.start) |
+       v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_address) |
                        V3_LB_BASE_ENABLE);
        v3_writew(V3_LB_MAP2, v3_addr_to_lb_map2(0));
 
@@ -742,6 +743,7 @@ static void __init pci_v3_preinit(void)
 static void __init pci_v3_postinit(void)
 {
        unsigned int pci_cmd;
+       phys_addr_t io_address = pci_pio_to_address(io_mem.start);
 
        pci_cmd = PCI_COMMAND_MEMORY |
                  PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
@@ -758,7 +760,7 @@ static void __init pci_v3_postinit(void)
                       "interrupt: %d\n", ret);
 #endif
 
-       register_isa_ports(non_mem.start, io_mem.start, 0);
+       register_isa_ports(non_mem.start, io_address, 0);
 }
 
 /*
@@ -867,33 +869,32 @@ static int __init pci_v3_probe(struct platform_device *pdev)
 
        for_each_of_pci_range(&parser, &range) {
                if (!range.flags) {
-                       of_pci_range_to_resource(&range, np, &conf_mem);
+                       ret = of_pci_range_to_resource(&range, np, &conf_mem);
                        conf_mem.name = "PCIv3 config";
                }
                if (range.flags & IORESOURCE_IO) {
-                       of_pci_range_to_resource(&range, np, &io_mem);
+                       ret = of_pci_range_to_resource(&range, np, &io_mem);
                        io_mem.name = "PCIv3 I/O";
                }
                if ((range.flags & IORESOURCE_MEM) &&
                        !(range.flags & IORESOURCE_PREFETCH)) {
                        non_mem_pci = range.pci_addr;
                        non_mem_pci_sz = range.size;
-                       of_pci_range_to_resource(&range, np, &non_mem);
+                       ret = of_pci_range_to_resource(&range, np, &non_mem);
                        non_mem.name = "PCIv3 non-prefetched mem";
                }
                if ((range.flags & IORESOURCE_MEM) &&
                        (range.flags & IORESOURCE_PREFETCH)) {
                        pre_mem_pci = range.pci_addr;
                        pre_mem_pci_sz = range.size;
-                       of_pci_range_to_resource(&range, np, &pre_mem);
+                       ret = of_pci_range_to_resource(&range, np, &pre_mem);
                        pre_mem.name = "PCIv3 prefetched mem";
                }
-       }
 
-       if (!conf_mem.start || !io_mem.start ||
-           !non_mem.start || !pre_mem.start) {
-               dev_err(&pdev->dev, "missing ranges in device node\n");
-               return -EINVAL;
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "missing ranges in device node\n");
+                       return ret;
+               }
        }
 
        pci_v3.map_irq = of_irq_parse_and_map_pci;
index 8a70a51533fd4507e02f10cb576667500cfbf575..bbd8664d1bacb2732ec58072d630e70d963904de 100644 (file)
@@ -644,7 +644,7 @@ static int __init armada_xp_pmsu_cpufreq_init(void)
                }
        }
 
-       platform_device_register_simple("cpufreq-generic", -1, NULL, 0);
+       platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
        return 0;
 }
 
index 828aee9ea6a8b4146cff3b90ae1c1e909e0bc217..58920bc8807bce963536296bc7914db421c2b6aa 100644 (file)
@@ -282,7 +282,7 @@ static inline void omap_init_cpufreq(void)
        if (!of_have_populated_dt())
                devinfo.name = "omap-cpufreq";
        else
-               devinfo.name = "cpufreq-cpu0";
+               devinfo.name = "cpufreq-dt";
        platform_device_register_full(&devinfo);
 }
 
index 5c45aae675b63e86f21c44aea48e41bfccddb444..16547f2641a32d343bb8ea44adfdf106ad781f49 100644 (file)
@@ -440,8 +440,3 @@ void s3c64xx_restart(enum reboot_mode mode, const char *cmd)
        /* if all else fails, or mode was for soft, jump to 0 */
        soft_restart(0);
 }
-
-void __init s3c64xx_init_late(void)
-{
-       s3c64xx_pm_late_initcall();
-}
index 7043e7a3a67ed868f9e4465d7afae197619dcdc9..9eb8644129116e9354274283c0cfd4d66e032f9e 100644 (file)
@@ -23,7 +23,6 @@ void s3c64xx_init_irq(u32 vic0, u32 vic1);
 void s3c64xx_init_io(struct map_desc *mach_desc, int size);
 
 void s3c64xx_restart(enum reboot_mode mode, const char *cmd);
-void s3c64xx_init_late(void);
 
 void s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
        unsigned long xusbxti_f, bool is_s3c6400, void __iomem *reg_base);
@@ -52,12 +51,6 @@ extern void s3c6410_map_io(void);
 #define s3c6410_init NULL
 #endif
 
-#ifdef CONFIG_PM
-int __init s3c64xx_pm_late_initcall(void);
-#else
-static inline int s3c64xx_pm_late_initcall(void) { return 0; }
-#endif
-
 #ifdef CONFIG_S3C64XX_PL080
 extern struct pl08x_platform_data s3c64xx_dma0_plat_data;
 extern struct pl08x_platform_data s3c64xx_dma1_plat_data;
index 60576dfbea8d42cf152cdd0aced552f10f888d27..6224c67f5061251568995a80d0d278b060c26840 100644 (file)
@@ -233,7 +233,6 @@ MACHINE_START(ANW6410, "A&W6410")
        .init_irq       = s3c6410_init_irq,
        .map_io         = anw6410_map_io,
        .init_machine   = anw6410_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index fe116334afda929958a2671ba224bb06d8fe88be..10b913baab2883562acb916ecef103d96b35b761 100644 (file)
@@ -857,7 +857,6 @@ MACHINE_START(WLF_CRAGG_6410, "Wolfson Cragganmore 6410")
        .init_irq       = s3c6410_init_irq,
        .map_io         = crag6410_map_io,
        .init_machine   = crag6410_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index 19e8feb908fdba504cda3f150202f053dbd21255..e4b087c58ee61ae14fbe69f8204c85d806909666 100644 (file)
@@ -277,7 +277,6 @@ MACHINE_START(HMT, "Airgoo-HMT")
        .init_irq       = s3c6410_init_irq,
        .map_io         = hmt_map_io,
        .init_machine   = hmt_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index 9cbc07602ef388cb78a97e0196899c041e7ce927..ab61af50bfb9c485e2488ca67579777a11ebafda 100644 (file)
@@ -366,7 +366,6 @@ MACHINE_START(MINI6410, "MINI6410")
        .init_irq       = s3c6410_init_irq,
        .map_io         = mini6410_map_io,
        .init_machine   = mini6410_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index 4bae7dc49eeabe9a9602f8efda7bb06e89eb931b..80cb1446f69f8241d20bd3e7f69d72b7a29b63a4 100644 (file)
@@ -103,7 +103,6 @@ MACHINE_START(NCP, "NCP")
        .init_irq       = s3c6410_init_irq,
        .map_io         = ncp_map_io,
        .init_machine   = ncp_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index fbad2af1ef1604a46ffa1a06dc0f387ef40006c0..85fa9598b9801f21833b75dc10d46f2017d12ac6 100644 (file)
@@ -335,7 +335,6 @@ MACHINE_START(REAL6410, "REAL6410")
        .init_irq       = s3c6410_init_irq,
        .map_io         = real6410_map_io,
        .init_machine   = real6410_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index dec4c08e834f4d5026284342568102d2a8e6a1a4..33224ab36fac8c8692ab84a7455c63f520184ada 100644 (file)
@@ -156,7 +156,6 @@ MACHINE_START(SMARTQ5, "SmartQ 5")
        .init_irq       = s3c6410_init_irq,
        .map_io         = smartq_map_io,
        .init_machine   = smartq5_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index 27b322069c7dd4420495eb26b370ffd01c8e84c0..fc7fece22fb0d8e5a12e83aac2c880154ae50d7c 100644 (file)
@@ -172,7 +172,6 @@ MACHINE_START(SMARTQ7, "SmartQ 7")
        .init_irq       = s3c6410_init_irq,
        .map_io         = smartq_map_io,
        .init_machine   = smartq7_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index 91074976834042646cb8a42e6d4ffe44eb1fd59c..6f425126a735eb05e66732097a9b050e76b2234b 100644 (file)
@@ -92,7 +92,6 @@ MACHINE_START(SMDK6400, "SMDK6400")
        .init_irq       = s3c6400_init_irq,
        .map_io         = smdk6400_map_io,
        .init_machine   = smdk6400_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index 1dc86d76b530032a75cdd644a34219909cf8f7c9..661eb662d05159861fb9fffd9b95705b8dbac238 100644 (file)
@@ -705,7 +705,6 @@ MACHINE_START(SMDK6410, "SMDK6410")
        .init_irq       = s3c6410_init_irq,
        .map_io         = smdk6410_map_io,
        .init_machine   = smdk6410_machine_init,
-       .init_late      = s3c64xx_init_late,
        .init_time      = samsung_timer_init,
        .restart        = s3c64xx_restart,
 MACHINE_END
index 6b37694fa3351fc55a66f0a5b0b4377e23ab734a..aaf7bea4032f4824a611f92710335aa717f70a33 100644 (file)
@@ -347,10 +347,3 @@ static __init int s3c64xx_pm_initcall(void)
        return 0;
 }
 arch_initcall(s3c64xx_pm_initcall);
-
-int __init s3c64xx_pm_late_initcall(void)
-{
-       pm_genpd_poweroff_unused();
-
-       return 0;
-}
index b7d5bc7659cda13537707ecbb4e39f43f6e76c61..126a8b4ec491fa0b6c449eb0628ad02e33ad7c1d 100644 (file)
@@ -331,7 +331,6 @@ SDHI_REGULATOR(2, RCAR_GP_PIN(7, 19), RCAR_GP_PIN(2, 26));
 static struct sh_mobile_sdhi_info sdhi0_info __initdata = {
        .tmio_caps      = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
                          MMC_CAP_POWER_OFF_CARD,
-       .tmio_caps2     = MMC_CAP2_NO_MULTI_READ,
        .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT,
 };
 
@@ -344,7 +343,6 @@ static struct resource sdhi0_resources[] __initdata = {
 static struct sh_mobile_sdhi_info sdhi1_info __initdata = {
        .tmio_caps      = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
                          MMC_CAP_POWER_OFF_CARD,
-       .tmio_caps2     = MMC_CAP2_NO_MULTI_READ,
        .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT,
 };
 
@@ -357,7 +355,6 @@ static struct resource sdhi1_resources[] __initdata = {
 static struct sh_mobile_sdhi_info sdhi2_info __initdata = {
        .tmio_caps      = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
                          MMC_CAP_POWER_OFF_CARD,
-       .tmio_caps2     = MMC_CAP2_NO_MULTI_READ,
        .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT |
                          TMIO_MMC_WRPROTECT_DISABLE,
 };
index e1d8215da0b050cb47512bbcdb24818ec8750674..f5a98e2942b30d2e6b39f2fb00a17e3a01999a30 100644 (file)
@@ -630,7 +630,6 @@ static void __init lager_add_rsnd_device(void)
 static struct sh_mobile_sdhi_info sdhi0_info __initdata = {
        .tmio_caps      = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
                          MMC_CAP_POWER_OFF_CARD,
-       .tmio_caps2     = MMC_CAP2_NO_MULTI_READ,
        .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT |
                          TMIO_MMC_WRPROTECT_DISABLE,
 };
@@ -644,7 +643,6 @@ static struct resource sdhi0_resources[] __initdata = {
 static struct sh_mobile_sdhi_info sdhi2_info __initdata = {
        .tmio_caps      = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
                          MMC_CAP_POWER_OFF_CARD,
-       .tmio_caps2     = MMC_CAP2_NO_MULTI_READ,
        .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT |
                          TMIO_MMC_WRPROTECT_DISABLE,
 };
index 8a24b2be46ae34165a7c613b1d483097ce67a861..57fbff024dcd5dd6ccf23afb94e09d6b0ae47796 100644 (file)
@@ -12,6 +12,6 @@
 
 int __init shmobile_cpufreq_init(void)
 {
-       platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0);
+       platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
        return 0;
 }
index 69f70b7f7fb2ee406bcf0b0d75fd16c7378aabf8..82fe3d7f96624e3d68c3645ad7a1282dd308ceea 100644 (file)
@@ -87,7 +87,6 @@ static void r8a7779_init_pm_domain(struct r8a7779_pm_domain *r8a7779_pd)
        genpd->dev_ops.stop = pm_clk_suspend;
        genpd->dev_ops.start = pm_clk_resume;
        genpd->dev_ops.active_wakeup = pd_active_wakeup;
-       genpd->dev_irq_safe = true;
        genpd->power_off = pd_power_down;
        genpd->power_on = pd_power_up;
 
index a88079af7914afe0db35514b558d13e952c36e88..717e6413d29cb998cd067ece5cc715acc7bb1e93 100644 (file)
@@ -110,7 +110,6 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
        genpd->dev_ops.stop             = pm_clk_suspend;
        genpd->dev_ops.start            = pm_clk_resume;
        genpd->dev_ops.active_wakeup    = rmobile_pd_active_wakeup;
-       genpd->dev_irq_safe             = true;
        genpd->power_off                = rmobile_pd_power_down;
        genpd->power_on                 = rmobile_pd_power_up;
        __rmobile_pd_power_up(rmobile_pd, false);
index 613c476872eb06c5d6ba76c249ae2df888bf5e92..26f92c28d22b7c2c3e8a40ecaf1b02ca34bb04b5 100644 (file)
@@ -110,7 +110,7 @@ static void __init zynq_init_late(void)
  */
 static void __init zynq_init_machine(void)
 {
-       struct platform_device_info devinfo = { .name = "cpufreq-cpu0", };
+       struct platform_device_info devinfo = { .name = "cpufreq-dt", };
        struct soc_device_attribute *soc_dev_attr;
        struct soc_device *soc_dev;
        struct device *parent = NULL;
index 7a996aaa061e99fb5d5f3875da51890b0862b1c1..c245d903927fadfdee177b4d94b876ae2473618e 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/bootmem.h>
 #include <linux/module.h>
 #include <linux/mm.h>
+#include <linux/genalloc.h>
 #include <linux/gfp.h>
 #include <linux/errno.h>
 #include <linux/list.h>
@@ -298,57 +299,29 @@ static void *
 __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot,
        const void *caller)
 {
-       struct vm_struct *area;
-       unsigned long addr;
-
        /*
         * DMA allocation can be mapped to user space, so lets
         * set VM_USERMAP flags too.
         */
-       area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP,
-                                 caller);
-       if (!area)
-               return NULL;
-       addr = (unsigned long)area->addr;
-       area->phys_addr = __pfn_to_phys(page_to_pfn(page));
-
-       if (ioremap_page_range(addr, addr + size, area->phys_addr, prot)) {
-               vunmap((void *)addr);
-               return NULL;
-       }
-       return (void *)addr;
+       return dma_common_contiguous_remap(page, size,
+                       VM_ARM_DMA_CONSISTENT | VM_USERMAP,
+                       prot, caller);
 }
 
 static void __dma_free_remap(void *cpu_addr, size_t size)
 {
-       unsigned int flags = VM_ARM_DMA_CONSISTENT | VM_USERMAP;
-       struct vm_struct *area = find_vm_area(cpu_addr);
-       if (!area || (area->flags & flags) != flags) {
-               WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
-               return;
-       }
-       unmap_kernel_range((unsigned long)cpu_addr, size);
-       vunmap(cpu_addr);
+       dma_common_free_remap(cpu_addr, size,
+                       VM_ARM_DMA_CONSISTENT | VM_USERMAP);
 }
 
 #define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
+static struct gen_pool *atomic_pool;
 
-struct dma_pool {
-       size_t size;
-       spinlock_t lock;
-       unsigned long *bitmap;
-       unsigned long nr_pages;
-       void *vaddr;
-       struct page **pages;
-};
-
-static struct dma_pool atomic_pool = {
-       .size = DEFAULT_DMA_COHERENT_POOL_SIZE,
-};
+static size_t atomic_pool_size = DEFAULT_DMA_COHERENT_POOL_SIZE;
 
 static int __init early_coherent_pool(char *p)
 {
-       atomic_pool.size = memparse(p, &p);
+       atomic_pool_size = memparse(p, &p);
        return 0;
 }
 early_param("coherent_pool", early_coherent_pool);
@@ -358,14 +331,14 @@ void __init init_dma_coherent_pool_size(unsigned long size)
        /*
         * Catch any attempt to set the pool size too late.
         */
-       BUG_ON(atomic_pool.vaddr);
+       BUG_ON(atomic_pool);
 
        /*
         * Set architecture specific coherent pool size only if
         * it has not been changed by kernel command line parameter.
         */
-       if (atomic_pool.size == DEFAULT_DMA_COHERENT_POOL_SIZE)
-               atomic_pool.size = size;
+       if (atomic_pool_size == DEFAULT_DMA_COHERENT_POOL_SIZE)
+               atomic_pool_size = size;
 }
 
 /*
@@ -373,52 +346,44 @@ void __init init_dma_coherent_pool_size(unsigned long size)
  */
 static int __init atomic_pool_init(void)
 {
-       struct dma_pool *pool = &atomic_pool;
        pgprot_t prot = pgprot_dmacoherent(PAGE_KERNEL);
        gfp_t gfp = GFP_KERNEL | GFP_DMA;
-       unsigned long nr_pages = pool->size >> PAGE_SHIFT;
-       unsigned long *bitmap;
        struct page *page;
-       struct page **pages;
        void *ptr;
-       int bitmap_size = BITS_TO_LONGS(nr_pages) * sizeof(long);
 
-       bitmap = kzalloc(bitmap_size, GFP_KERNEL);
-       if (!bitmap)
-               goto no_bitmap;
-
-       pages = kzalloc(nr_pages * sizeof(struct page *), GFP_KERNEL);
-       if (!pages)
-               goto no_pages;
+       atomic_pool = gen_pool_create(PAGE_SHIFT, -1);
+       if (!atomic_pool)
+               goto out;
 
        if (dev_get_cma_area(NULL))
-               ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page,
-                                             atomic_pool_init);
+               ptr = __alloc_from_contiguous(NULL, atomic_pool_size, prot,
+                                             &page, atomic_pool_init);
        else
-               ptr = __alloc_remap_buffer(NULL, pool->size, gfp, prot, &page,
-                                          atomic_pool_init);
+               ptr = __alloc_remap_buffer(NULL, atomic_pool_size, gfp, prot,
+                                          &page, atomic_pool_init);
        if (ptr) {
-               int i;
-
-               for (i = 0; i < nr_pages; i++)
-                       pages[i] = page + i;
-
-               spin_lock_init(&pool->lock);
-               pool->vaddr = ptr;
-               pool->pages = pages;
-               pool->bitmap = bitmap;
-               pool->nr_pages = nr_pages;
-               pr_info("DMA: preallocated %u KiB pool for atomic coherent allocations\n",
-                      (unsigned)pool->size / 1024);
+               int ret;
+
+               ret = gen_pool_add_virt(atomic_pool, (unsigned long)ptr,
+                                       page_to_phys(page),
+                                       atomic_pool_size, -1);
+               if (ret)
+                       goto destroy_genpool;
+
+               gen_pool_set_algo(atomic_pool,
+                               gen_pool_first_fit_order_align,
+                               (void *)PAGE_SHIFT);
+               pr_info("DMA: preallocated %zd KiB pool for atomic coherent allocations\n",
+                      atomic_pool_size / 1024);
                return 0;
        }
 
-       kfree(pages);
-no_pages:
-       kfree(bitmap);
-no_bitmap:
-       pr_err("DMA: failed to allocate %u KiB pool for atomic coherent allocation\n",
-              (unsigned)pool->size / 1024);
+destroy_genpool:
+       gen_pool_destroy(atomic_pool);
+       atomic_pool = NULL;
+out:
+       pr_err("DMA: failed to allocate %zx KiB pool for atomic coherent allocation\n",
+              atomic_pool_size / 1024);
        return -ENOMEM;
 }
 /*
@@ -522,76 +487,36 @@ static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp,
 
 static void *__alloc_from_pool(size_t size, struct page **ret_page)
 {
-       struct dma_pool *pool = &atomic_pool;
-       unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
-       unsigned int pageno;
-       unsigned long flags;
+       unsigned long val;
        void *ptr = NULL;
-       unsigned long align_mask;
 
-       if (!pool->vaddr) {
+       if (!atomic_pool) {
                WARN(1, "coherent pool not initialised!\n");
                return NULL;
        }
 
-       /*
-        * Align the region allocation - allocations from pool are rather
-        * small, so align them to their order in pages, minimum is a page
-        * size. This helps reduce fragmentation of the DMA space.
-        */
-       align_mask = (1 << get_order(size)) - 1;
-
-       spin_lock_irqsave(&pool->lock, flags);
-       pageno = bitmap_find_next_zero_area(pool->bitmap, pool->nr_pages,
-                                           0, count, align_mask);
-       if (pageno < pool->nr_pages) {
-               bitmap_set(pool->bitmap, pageno, count);
-               ptr = pool->vaddr + PAGE_SIZE * pageno;
-               *ret_page = pool->pages[pageno];
-       } else {
-               pr_err_once("ERROR: %u KiB atomic DMA coherent pool is too small!\n"
-                           "Please increase it with coherent_pool= kernel parameter!\n",
-                           (unsigned)pool->size / 1024);
+       val = gen_pool_alloc(atomic_pool, size);
+       if (val) {
+               phys_addr_t phys = gen_pool_virt_to_phys(atomic_pool, val);
+
+               *ret_page = phys_to_page(phys);
+               ptr = (void *)val;
        }
-       spin_unlock_irqrestore(&pool->lock, flags);
 
        return ptr;
 }
 
 static bool __in_atomic_pool(void *start, size_t size)
 {
-       struct dma_pool *pool = &atomic_pool;
-       void *end = start + size;
-       void *pool_start = pool->vaddr;
-       void *pool_end = pool->vaddr + pool->size;
-
-       if (start < pool_start || start >= pool_end)
-               return false;
-
-       if (end <= pool_end)
-               return true;
-
-       WARN(1, "Wrong coherent size(%p-%p) from atomic pool(%p-%p)\n",
-            start, end - 1, pool_start, pool_end - 1);
-
-       return false;
+       return addr_in_gen_pool(atomic_pool, (unsigned long)start, size);
 }
 
 static int __free_from_pool(void *start, size_t size)
 {
-       struct dma_pool *pool = &atomic_pool;
-       unsigned long pageno, count;
-       unsigned long flags;
-
        if (!__in_atomic_pool(start, size))
                return 0;
 
-       pageno = (start - pool->vaddr) >> PAGE_SHIFT;
-       count = size >> PAGE_SHIFT;
-
-       spin_lock_irqsave(&pool->lock, flags);
-       bitmap_clear(pool->bitmap, pageno, count);
-       spin_unlock_irqrestore(&pool->lock, flags);
+       gen_pool_free(atomic_pool, (unsigned long)start, size);
 
        return 1;
 }
@@ -1271,29 +1196,8 @@ static void *
 __iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot,
                    const void *caller)
 {
-       unsigned int i, nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
-       struct vm_struct *area;
-       unsigned long p;
-
-       area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP,
-                                 caller);
-       if (!area)
-               return NULL;
-
-       area->pages = pages;
-       area->nr_pages = nr_pages;
-       p = (unsigned long)area->addr;
-
-       for (i = 0; i < nr_pages; i++) {
-               phys_addr_t phys = __pfn_to_phys(page_to_pfn(pages[i]));
-               if (ioremap_page_range(p, p + PAGE_SIZE, phys, prot))
-                       goto err;
-               p += PAGE_SIZE;
-       }
-       return area->addr;
-err:
-       unmap_kernel_range((unsigned long)area->addr, size);
-       vunmap(area->addr);
+       return dma_common_pages_remap(pages, size,
+                       VM_ARM_DMA_CONSISTENT | VM_USERMAP, prot, caller);
        return NULL;
 }
 
@@ -1355,11 +1259,13 @@ static int __iommu_remove_mapping(struct device *dev, dma_addr_t iova, size_t si
 
 static struct page **__atomic_get_pages(void *addr)
 {
-       struct dma_pool *pool = &atomic_pool;
-       struct page **pages = pool->pages;
-       int offs = (addr - pool->vaddr) >> PAGE_SHIFT;
+       struct page *page;
+       phys_addr_t phys;
+
+       phys = gen_pool_virt_to_phys(atomic_pool, (unsigned long)addr);
+       page = phys_to_page(phys);
 
-       return pages + offs;
+       return (struct page **)page;
 }
 
 static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs)
@@ -1501,8 +1407,8 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
        }
 
        if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) {
-               unmap_kernel_range((unsigned long)cpu_addr, size);
-               vunmap(cpu_addr);
+               dma_common_free_remap(cpu_addr, size,
+                       VM_ARM_DMA_CONSISTENT | VM_USERMAP);
        }
 
        __iommu_remove_mapping(dev, handle, size);
index 43d54f5b26b9b08edf95dd1b48b13ff6440451af..265b836b3bd1704d0265eebafd33d4f0d1e7ea45 100644 (file)
@@ -400,3 +400,18 @@ void __flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned l
         */
        __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
 }
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#ifdef CONFIG_HAVE_RCU_TABLE_FREE
+void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address,
+                         pmd_t *pmdp)
+{
+       pmd_t pmd = pmd_mksplitting(*pmdp);
+       VM_BUG_ON(address & ~PMD_MASK);
+       set_pmd_at(vma->vm_mm, address, pmdp, pmd);
+
+       /* dummy IPI to serialise against fast_gup */
+       kick_all_cpus_sync();
+}
+#endif /* CONFIG_HAVE_RCU_TABLE_FREE */
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
index 9221645dd192dbbfc0b0ae479642583487f99c76..92bba32d92304c4383d43bee8ef95f7d988602c6 100644 (file)
@@ -322,7 +322,7 @@ void __init arm_memblock_init(const struct machine_desc *mdesc)
         * reserve memory for DMA contigouos allocations,
         * must come from DMA area inside low memory
         */
-       dma_contiguous_reserve(min(arm_dma_limit, arm_lowmem_limit));
+       dma_contiguous_reserve(arm_dma_limit);
 
        arm_memblock_steal_permitted = false;
        memblock_dump_all();
index d0543d90db8d6ee556a0e93a4c06765c43b88543..ac9afde76dead843ed4a53b2e0da97e4a7a0bd72 100644 (file)
@@ -18,6 +18,7 @@ config ARM64
        select COMMON_CLK
        select CPU_PM if (SUSPEND || CPU_IDLE)
        select DCACHE_WORD_ACCESS
+       select GENERIC_ALLOCATOR
        select GENERIC_CLOCKEVENTS
        select GENERIC_CLOCKEVENTS_BROADCAST if SMP
        select GENERIC_CPU_AUTOPROBE
@@ -30,6 +31,7 @@ config ARM64
        select GENERIC_STRNCPY_FROM_USER
        select GENERIC_STRNLEN_USER
        select GENERIC_TIME_VSYSCALL
+       select HANDLE_DOMAIN_IRQ
        select HARDIRQS_SW_RESEND
        select HAVE_ARCH_AUDITSYSCALL
        select HAVE_ARCH_JUMP_LABEL
@@ -55,6 +57,7 @@ config ARM64
        select HAVE_PERF_EVENTS
        select HAVE_PERF_REGS
        select HAVE_PERF_USER_STACK_DUMP
+       select HAVE_RCU_TABLE_FREE
        select HAVE_SYSCALL_TRACEPOINTS
        select IRQ_DOMAIN
        select MODULES_USE_ELF_RELA
@@ -82,7 +85,7 @@ config MMU
        def_bool y
 
 config NO_IOPORT_MAP
-       def_bool y
+       def_bool y if !PCI
 
 config STACKTRACE_SUPPORT
        def_bool y
@@ -108,6 +111,9 @@ config GENERIC_CALIBRATE_DELAY
 config ZONE_DMA
        def_bool y
 
+config HAVE_GENERIC_RCU_GUP
+       def_bool y
+
 config ARCH_DMA_ADDR_T_64BIT
        def_bool y
 
@@ -162,6 +168,26 @@ menu "Bus support"
 config ARM_AMBA
        bool
 
+config PCI
+       bool "PCI support"
+       help
+         This feature enables support for PCI bus system. If you say Y
+         here, the kernel will include drivers and infrastructure code
+         to support PCI bus devices.
+
+config PCI_DOMAINS
+       def_bool PCI
+
+config PCI_DOMAINS_GENERIC
+       def_bool PCI
+
+config PCI_SYSCALL
+       def_bool PCI
+
+source "drivers/pci/Kconfig"
+source "drivers/pci/pcie/Kconfig"
+source "drivers/pci/hotplug/Kconfig"
+
 endmenu
 
 menu "Kernel Features"
@@ -323,7 +349,7 @@ config XEN_DOM0
        depends on XEN
 
 config XEN
-       bool "Xen guest support on ARM64 (EXPERIMENTAL)"
+       bool "Xen guest support on ARM64"
        depends on ARM64 && OF
        select SWIOTLB_XEN
        help
index 2ae782bbdf0f8a2a279d47c9632eb07c52001be8..8eb6d94c785154cb3c29dba770cde5fed6eaec50 100644 (file)
        };
 };
 
+&pcie0clk {
+       status = "ok";
+};
+
+&pcie0 {
+       status = "ok";
+};
+
 &serial0 {
        status = "ok";
 };
index d16cc03b7c5d6aaea59876f8fbec89310da2a478..87d3205e98d545fefb72d17f84ba51efef35a5b5 100644 (file)
                                enable-mask = <0x10>;
                                clock-output-names = "rngpkaclk";
                        };
+
+                       pcie0clk: pcie0clk@1f2bc000 {
+                               status = "disabled";
+                               compatible = "apm,xgene-device-clock";
+                               #clock-cells = <1>;
+                               clocks = <&socplldiv2 0>;
+                               reg = <0x0 0x1f2bc000 0x0 0x1000>;
+                               reg-names = "csr-reg";
+                               clock-output-names = "pcie0clk";
+                       };
+
+                       pcie1clk: pcie1clk@1f2cc000 {
+                               status = "disabled";
+                               compatible = "apm,xgene-device-clock";
+                               #clock-cells = <1>;
+                               clocks = <&socplldiv2 0>;
+                               reg = <0x0 0x1f2cc000 0x0 0x1000>;
+                               reg-names = "csr-reg";
+                               clock-output-names = "pcie1clk";
+                       };
+
+                       pcie2clk: pcie2clk@1f2dc000 {
+                               status = "disabled";
+                               compatible = "apm,xgene-device-clock";
+                               #clock-cells = <1>;
+                               clocks = <&socplldiv2 0>;
+                               reg = <0x0 0x1f2dc000 0x0 0x1000>;
+                               reg-names = "csr-reg";
+                               clock-output-names = "pcie2clk";
+                       };
+
+                       pcie3clk: pcie3clk@1f50c000 {
+                               status = "disabled";
+                               compatible = "apm,xgene-device-clock";
+                               #clock-cells = <1>;
+                               clocks = <&socplldiv2 0>;
+                               reg = <0x0 0x1f50c000 0x0 0x1000>;
+                               reg-names = "csr-reg";
+                               clock-output-names = "pcie3clk";
+                       };
+
+                       pcie4clk: pcie4clk@1f51c000 {
+                               status = "disabled";
+                               compatible = "apm,xgene-device-clock";
+                               #clock-cells = <1>;
+                               clocks = <&socplldiv2 0>;
+                               reg = <0x0 0x1f51c000 0x0 0x1000>;
+                               reg-names = "csr-reg";
+                               clock-output-names = "pcie4clk";
+                       };
+               };
+
+               pcie0: pcie@1f2b0000 {
+                       status = "disabled";
+                       device_type = "pci";
+                       compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+                       #interrupt-cells = <1>;
+                       #size-cells = <2>;
+                       #address-cells = <3>;
+                       reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+                               0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+                       reg-names = "csr", "cfg";
+                       ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+                                 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+                       dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+                                     0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+                       interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+                       interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+                                        0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+                                        0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+                                        0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+                       dma-coherent;
+                       clocks = <&pcie0clk 0>;
+               };
+
+               pcie1: pcie@1f2c0000 {
+                       status = "disabled";
+                       device_type = "pci";
+                       compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+                       #interrupt-cells = <1>;
+                       #size-cells = <2>;
+                       #address-cells = <3>;
+                       reg = < 0x00 0x1f2c0000 0x0 0x00010000   /* Controller registers */
+                               0xd0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+                       reg-names = "csr", "cfg";
+                       ranges = <0x01000000 0x0 0x00000000 0xd0 0x10000000 0x00 0x00010000   /* io  */
+                                 0x02000000 0x0 0x80000000 0xd1 0x80000000 0x00 0x80000000>; /* mem */
+                       dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+                                     0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+                       interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+                       interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc8 0x1
+                                        0x0 0x0 0x0 0x2 &gic 0x0 0xc9 0x1
+                                        0x0 0x0 0x0 0x3 &gic 0x0 0xca 0x1
+                                        0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
+                       dma-coherent;
+                       clocks = <&pcie1clk 0>;
+               };
+
+               pcie2: pcie@1f2d0000 {
+                       status = "disabled";
+                       device_type = "pci";
+                       compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+                       #interrupt-cells = <1>;
+                       #size-cells = <2>;
+                       #address-cells = <3>;
+                       reg =  < 0x00 0x1f2d0000 0x0 0x00010000   /* Controller registers */
+                                0x90 0xd0000000 0x0 0x00040000>; /* PCI config space */
+                       reg-names = "csr", "cfg";
+                       ranges = <0x01000000 0x0 0x00000000 0x90 0x10000000 0x0 0x00010000   /* io  */
+                                 0x02000000 0x0 0x80000000 0x91 0x80000000 0x0 0x80000000>; /* mem */
+                       dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+                                     0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+                       interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+                       interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xce 0x1
+                                        0x0 0x0 0x0 0x2 &gic 0x0 0xcf 0x1
+                                        0x0 0x0 0x0 0x3 &gic 0x0 0xd0 0x1
+                                        0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
+                       dma-coherent;
+                       clocks = <&pcie2clk 0>;
+               };
+
+               pcie3: pcie@1f500000 {
+                       status = "disabled";
+                       device_type = "pci";
+                       compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+                       #interrupt-cells = <1>;
+                       #size-cells = <2>;
+                       #address-cells = <3>;
+                       reg = < 0x00 0x1f500000 0x0 0x00010000   /* Controller registers */
+                               0xa0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+                       reg-names = "csr", "cfg";
+                       ranges = <0x01000000 0x0 0x00000000 0xa0 0x10000000 0x0 0x00010000   /* io   */
+                                 0x02000000 0x0 0x80000000 0xa1 0x80000000 0x0 0x80000000>; /* mem  */
+                       dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+                                     0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+                       interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+                       interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xd4 0x1
+                                        0x0 0x0 0x0 0x2 &gic 0x0 0xd5 0x1
+                                        0x0 0x0 0x0 0x3 &gic 0x0 0xd6 0x1
+                                        0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
+                       dma-coherent;
+                       clocks = <&pcie3clk 0>;
+               };
+
+               pcie4: pcie@1f510000 {
+                       status = "disabled";
+                       device_type = "pci";
+                       compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+                       #interrupt-cells = <1>;
+                       #size-cells = <2>;
+                       #address-cells = <3>;
+                       reg = < 0x00 0x1f510000 0x0 0x00010000   /* Controller registers */
+                               0xc0 0xd0000000 0x0 0x00200000>; /* PCI config space */
+                       reg-names = "csr", "cfg";
+                       ranges = <0x01000000 0x0 0x00000000 0xc0 0x10000000 0x0 0x00010000   /* io  */
+                                 0x02000000 0x0 0x80000000 0xc1 0x80000000 0x0 0x80000000>; /* mem */
+                       dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+                                     0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+                       interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+                       interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xda 0x1
+                                        0x0 0x0 0x0 0x2 &gic 0x0 0xdb 0x1
+                                        0x0 0x0 0x0 0x3 &gic 0x0 0xdc 0x1
+                                        0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
+                       dma-coherent;
+                       clocks = <&pcie4clk 0>;
                };
 
                serial0: serial@1c020000 {
index 0b3fcf86e6ba735b3b074a4a028c0ec94e62fbe1..774a7c85e70f8d75ae29a7268377dff3622e4158 100644 (file)
@@ -9,8 +9,8 @@ generic-y += current.h
 generic-y += delay.h
 generic-y += div64.h
 generic-y += dma.h
-generic-y += emergency-restart.h
 generic-y += early_ioremap.h
+generic-y += emergency-restart.h
 generic-y += errno.h
 generic-y += ftrace.h
 generic-y += hash.h
@@ -29,6 +29,7 @@ generic-y += mman.h
 generic-y += msgbuf.h
 generic-y += mutex.h
 generic-y += pci.h
+generic-y += pci-bridge.h
 generic-y += poll.h
 generic-y += preempt.h
 generic-y += resource.h
index 9400596a0f3972a91a8858da56e9d7097e4b9319..f19097134b02d6cc14f4a4f3fe4deb5a6225d6c0 100644 (file)
@@ -104,37 +104,6 @@ static inline void arch_timer_set_cntkctl(u32 cntkctl)
        asm volatile("msr       cntkctl_el1, %0" : : "r" (cntkctl));
 }
 
-static inline void arch_counter_set_user_access(void)
-{
-       u32 cntkctl = arch_timer_get_cntkctl();
-
-       /* Disable user access to the timers and the physical counter */
-       /* Also disable virtual event stream */
-       cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
-                       | ARCH_TIMER_USR_VT_ACCESS_EN
-                       | ARCH_TIMER_VIRT_EVT_EN
-                       | ARCH_TIMER_USR_PCT_ACCESS_EN);
-
-       /* Enable user access to the virtual counter */
-       cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN;
-
-       arch_timer_set_cntkctl(cntkctl);
-}
-
-static inline void arch_timer_evtstrm_enable(int divider)
-{
-       u32 cntkctl = arch_timer_get_cntkctl();
-       cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK;
-       /* Set the divider and enable virtual event stream */
-       cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT)
-                       | ARCH_TIMER_VIRT_EVT_EN;
-       arch_timer_set_cntkctl(cntkctl);
-       elf_hwcap |= HWCAP_EVTSTRM;
-#ifdef CONFIG_COMPAT
-       compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM;
-#endif
-}
-
 static inline u64 arch_counter_get_cntvct(void)
 {
        u64 cval;
index 0be67821f9ce0dc430981dcaf6bf1aeed4db7608..e8a3268a891c7cfc9ba799f9aca03cb29a257952 100644 (file)
@@ -47,8 +47,6 @@ static inline void ack_bad_irq(unsigned int irq)
        irq_err_count++;
 }
 
-extern void handle_IRQ(unsigned int, struct pt_regs *);
-
 /*
  * No arch-specific IRQ flags.
  */
index f771e8bcad4ab7d1dcea64629eb1844e20106b8b..79f1d519221f839eb8e6488d14e7978ce6a3cd22 100644 (file)
@@ -121,7 +121,8 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
 /*
  *  I/O port access primitives.
  */
-#define IO_SPACE_LIMIT         0xffff
+#define arch_has_dev_port()    (1)
+#define IO_SPACE_LIMIT         (SZ_32M - 1)
 #define PCI_IOBASE             ((void __iomem *)(MODULES_VADDR - SZ_32M))
 
 static inline u8 inb(unsigned long addr)
diff --git a/arch/arm64/include/asm/irq_work.h b/arch/arm64/include/asm/irq_work.h
new file mode 100644 (file)
index 0000000..8e24ef3
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __ASM_IRQ_WORK_H
+#define __ASM_IRQ_WORK_H
+
+#include <asm/smp.h>
+
+static inline bool arch_irq_work_has_interrupt(void)
+{
+       return !!__smp_cross_call;
+}
+
+#endif /* __ASM_IRQ_WORK_H */
diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h
new file mode 100644 (file)
index 0000000..872ba93
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef __ASM_PCI_H
+#define __ASM_PCI_H
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm-generic/pci-bridge.h>
+#include <asm-generic/pci-dma-compat.h>
+
+#define PCIBIOS_MIN_IO         0x1000
+#define PCIBIOS_MIN_MEM                0
+
+/*
+ * Set to 1 if the kernel should re-assign all PCI bus numbers
+ */
+#define pcibios_assign_all_busses() \
+       (pci_has_flag(PCI_REASSIGN_ALL_BUS))
+
+/*
+ * PCI address space differs from physical memory address space
+ */
+#define PCI_DMA_BUS_IS_PHYS    (0)
+
+extern int isa_dma_bridge_buggy;
+
+#ifdef CONFIG_PCI
+static inline int pci_proc_domain(struct pci_bus *bus)
+{
+       return 1;
+}
+#endif  /* CONFIG_PCI */
+
+#endif  /* __KERNEL__ */
+#endif  /* __ASM_PCI_H */
index d58e40cde88ecb85b86ab84f4c13bbbe1a620630..cefd3e825612640a061da464ac43e2a63cbefcea 100644 (file)
@@ -244,6 +244,16 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
 
 #define __HAVE_ARCH_PTE_SPECIAL
 
+static inline pte_t pud_pte(pud_t pud)
+{
+       return __pte(pud_val(pud));
+}
+
+static inline pmd_t pud_pmd(pud_t pud)
+{
+       return __pmd(pud_val(pud));
+}
+
 static inline pte_t pmd_pte(pmd_t pmd)
 {
        return __pte(pmd_val(pmd));
@@ -261,7 +271,13 @@ static inline pmd_t pte_pmd(pte_t pte)
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 #define pmd_trans_huge(pmd)    (pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT))
 #define pmd_trans_splitting(pmd)       pte_special(pmd_pte(pmd))
-#endif
+#ifdef CONFIG_HAVE_RCU_TABLE_FREE
+#define __HAVE_ARCH_PMDP_SPLITTING_FLUSH
+struct vm_area_struct;
+void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address,
+                         pmd_t *pmdp);
+#endif /* CONFIG_HAVE_RCU_TABLE_FREE */
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 #define pmd_young(pmd)         pte_young(pmd_pte(pmd))
 #define pmd_wrprotect(pmd)     pte_pmd(pte_wrprotect(pmd_pte(pmd)))
@@ -282,6 +298,7 @@ static inline pmd_t pte_pmd(pte_t pte)
 #define mk_pmd(page,prot)      pfn_pmd(page_to_pfn(page),prot)
 
 #define pmd_page(pmd)           pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK))
+#define pud_write(pud)         pte_write(pud_pte(pud))
 #define pud_pfn(pud)           (((pud_val(pud) & PUD_MASK) & PHYS_MASK) >> PAGE_SHIFT)
 
 #define set_pmd_at(mm, addr, pmdp, pmd)        set_pte_at(mm, addr, (pte_t *)pmdp, pmd_pte(pmd))
@@ -301,6 +318,8 @@ static inline int has_transparent_hugepage(void)
        __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN)
 #define pgprot_writecombine(prot) \
        __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
+#define pgprot_device(prot) \
+       __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN)
 #define __HAVE_PHYS_MEM_ACCESS_PROT
 struct file;
 extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
@@ -381,6 +400,8 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
        return (pmd_t *)pud_page_vaddr(*pud) + pmd_index(addr);
 }
 
+#define pud_page(pud)           pmd_page(pud_pmd(pud))
+
 #endif /* CONFIG_ARM64_PGTABLE_LEVELS > 2 */
 
 #if CONFIG_ARM64_PGTABLE_LEVELS > 3
index a498f2cd2c2ad606d612a2192d09c93475bb1ded..780f82c827b6e733da94fab939380364c9e2b279 100644 (file)
@@ -48,6 +48,8 @@ extern void smp_init_cpus(void);
  */
 extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int));
 
+extern void (*__smp_cross_call)(const struct cpumask *, unsigned int);
+
 /*
  * Called from the secondary holding pen, this is the secondary CPU entry point.
  */
index 62731ef9749a66faf30a267e1f6d6c2719aee3e3..a82c0c5c8b521dc652fe9c4ca92de19f07018543 100644 (file)
 
 #include <asm-generic/tlb.h>
 
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+
+#ifdef CONFIG_HAVE_RCU_TABLE_FREE
+
+#define tlb_remove_entry(tlb, entry)   tlb_remove_table(tlb, entry)
+static inline void __tlb_remove_table(void *_table)
+{
+       free_page_and_swap_cache((struct page *)_table);
+}
+#else
+#define tlb_remove_entry(tlb, entry)   tlb_remove_page(tlb, entry)
+#endif /* CONFIG_HAVE_RCU_TABLE_FREE */
+
 /*
  * There's three ways the TLB shootdown code is used:
  *  1. Unmapping a range of vmas.  See zap_page_range(), unmap_region().
@@ -88,7 +102,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
 {
        pgtable_page_dtor(pte);
        tlb_add_flush(tlb, addr);
-       tlb_remove_page(tlb, pte);
+       tlb_remove_entry(tlb, pte);
 }
 
 #if CONFIG_ARM64_PGTABLE_LEVELS > 2
@@ -96,7 +110,7 @@ static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
                                  unsigned long addr)
 {
        tlb_add_flush(tlb, addr);
-       tlb_remove_page(tlb, virt_to_page(pmdp));
+       tlb_remove_entry(tlb, virt_to_page(pmdp));
 }
 #endif
 
@@ -105,7 +119,7 @@ static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp,
                                  unsigned long addr)
 {
        tlb_add_flush(tlb, addr);
-       tlb_remove_page(tlb, virt_to_page(pudp));
+       tlb_remove_entry(tlb, virt_to_page(pudp));
 }
 #endif
 
index 6e9538c2d28a0fae8cf0de0a6908d6f58e7351bc..5bd029b436444f79719f714a9af92ffe27a911d5 100644 (file)
@@ -30,6 +30,7 @@ arm64-obj-$(CONFIG_CPU_IDLE)          += cpuidle.o
 arm64-obj-$(CONFIG_JUMP_LABEL)         += jump_label.o
 arm64-obj-$(CONFIG_KGDB)               += kgdb.o
 arm64-obj-$(CONFIG_EFI)                        += efi.o efi-stub.o efi-entry.o
+arm64-obj-$(CONFIG_PCI)                        += pci.o
 
 obj-y                                  += $(arm64-obj-y) vdso/
 obj-m                                  += $(arm64-obj-m)
index dfa6e3e74fddec289649c1baba921378f521f611..071a6ec13bd8e8beb8324c1ae8557dc20a085cd1 100644 (file)
@@ -40,33 +40,6 @@ int arch_show_interrupts(struct seq_file *p, int prec)
        return 0;
 }
 
-/*
- * handle_IRQ handles all hardware IRQ's.  Decoded IRQs should
- * not come via this function.  Instead, they should provide their
- * own 'handler'.  Used by platform code implementing C-based 1st
- * level decoding.
- */
-void handle_IRQ(unsigned int irq, struct pt_regs *regs)
-{
-       struct pt_regs *old_regs = set_irq_regs(regs);
-
-       irq_enter();
-
-       /*
-        * Some hardware gives randomly wrong interrupts.  Rather
-        * than crashing, do something sensible.
-        */
-       if (unlikely(irq >= nr_irqs)) {
-               pr_warn_ratelimited("Bad IRQ%u\n", irq);
-               ack_bad_irq(irq);
-       } else {
-               generic_handle_irq(irq);
-       }
-
-       irq_exit();
-       set_irq_regs(old_regs);
-}
-
 void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
 {
        if (handle_arch_irq)
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
new file mode 100644 (file)
index 0000000..ce5836c
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Code borrowed from powerpc/kernel/pci-common.c
+ *
+ * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#include <asm/pci-bridge.h>
+
+/*
+ * Called after each bus is probed, but before its children are examined
+ */
+void pcibios_fixup_bus(struct pci_bus *bus)
+{
+       /* nothing to do, expected to be removed in the future */
+}
+
+/*
+ * We don't have to worry about legacy ISA devices, so nothing to do here
+ */
+resource_size_t pcibios_align_resource(void *data, const struct resource *res,
+                               resource_size_t size, resource_size_t align)
+{
+       return res->start;
+}
+
+/*
+ * Try to assign the IRQ number from DT when adding a new device
+ */
+int pcibios_add_device(struct pci_dev *dev)
+{
+       dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
+
+       return 0;
+}
+
+
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+static bool dt_domain_found = false;
+
+void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
+{
+       int domain = of_get_pci_domain_nr(parent->of_node);
+
+       if (domain >= 0) {
+               dt_domain_found = true;
+       } else if (dt_domain_found == true) {
+               dev_err(parent, "Node %s is missing \"linux,pci-domain\" property in DT\n",
+                       parent->of_node->full_name);
+               return;
+       } else {
+               domain = pci_get_new_domain_nr();
+       }
+
+       bus->domain_nr = domain;
+}
+#endif
index 89f41f7d27dd2bc54da37bd850f5437f86b19630..c3065dbc4fa269bafbb0b93f5a458d0534ae2bc5 100644 (file)
@@ -72,7 +72,6 @@ void (*pm_power_off)(void);
 EXPORT_SYMBOL_GPL(pm_power_off);
 
 void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
-EXPORT_SYMBOL_GPL(arm_pm_restart);
 
 /*
  * This is our default idle handler.
@@ -154,6 +153,8 @@ void machine_restart(char *cmd)
        /* Now call the architecture specific reboot code. */
        if (arm_pm_restart)
                arm_pm_restart(reboot_mode, cmd);
+       else
+               do_kernel_restart(cmd);
 
        /*
         * Whoops - the architecture was unable to reboot.
index 474339718105823e9dc55201dccfeb017df7c588..b06d1d90ee8cb223c70612515a0bed75048cc806 100644 (file)
@@ -470,7 +470,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
        }
 }
 
-static void (*__smp_cross_call)(const struct cpumask *, unsigned int);
+void (*__smp_cross_call)(const struct cpumask *, unsigned int);
 
 void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
 {
index 2c71077cacfd7e6763829254621d7f976cad9f83..d9209420391381d41aa97665c3ce1dd08456c466 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/gfp.h>
 #include <linux/export.h>
 #include <linux/slab.h>
+#include <linux/genalloc.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-contiguous.h>
 #include <linux/vmalloc.h>
@@ -38,6 +39,54 @@ static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
        return prot;
 }
 
+static struct gen_pool *atomic_pool;
+
+#define DEFAULT_DMA_COHERENT_POOL_SIZE  SZ_256K
+static size_t atomic_pool_size = DEFAULT_DMA_COHERENT_POOL_SIZE;
+
+static int __init early_coherent_pool(char *p)
+{
+       atomic_pool_size = memparse(p, &p);
+       return 0;
+}
+early_param("coherent_pool", early_coherent_pool);
+
+static void *__alloc_from_pool(size_t size, struct page **ret_page)
+{
+       unsigned long val;
+       void *ptr = NULL;
+
+       if (!atomic_pool) {
+               WARN(1, "coherent pool not initialised!\n");
+               return NULL;
+       }
+
+       val = gen_pool_alloc(atomic_pool, size);
+       if (val) {
+               phys_addr_t phys = gen_pool_virt_to_phys(atomic_pool, val);
+
+               *ret_page = phys_to_page(phys);
+               ptr = (void *)val;
+       }
+
+       return ptr;
+}
+
+static bool __in_atomic_pool(void *start, size_t size)
+{
+       return addr_in_gen_pool(atomic_pool, (unsigned long)start, size);
+}
+
+static int __free_from_pool(void *start, size_t size)
+{
+       if (!__in_atomic_pool(start, size))
+               return 0;
+
+       gen_pool_free(atomic_pool, (unsigned long)start, size);
+
+       return 1;
+}
+
 static void *__dma_alloc_coherent(struct device *dev, size_t size,
                                  dma_addr_t *dma_handle, gfp_t flags,
                                  struct dma_attrs *attrs)
@@ -50,7 +99,7 @@ static void *__dma_alloc_coherent(struct device *dev, size_t size,
        if (IS_ENABLED(CONFIG_ZONE_DMA) &&
            dev->coherent_dma_mask <= DMA_BIT_MASK(32))
                flags |= GFP_DMA;
-       if (IS_ENABLED(CONFIG_DMA_CMA)) {
+       if (IS_ENABLED(CONFIG_DMA_CMA) && (flags & __GFP_WAIT)) {
                struct page *page;
 
                size = PAGE_ALIGN(size);
@@ -70,50 +119,54 @@ static void __dma_free_coherent(struct device *dev, size_t size,
                                void *vaddr, dma_addr_t dma_handle,
                                struct dma_attrs *attrs)
 {
+       bool freed;
+       phys_addr_t paddr = dma_to_phys(dev, dma_handle);
+
        if (dev == NULL) {
                WARN_ONCE(1, "Use an actual device structure for DMA allocation\n");
                return;
        }
 
-       if (IS_ENABLED(CONFIG_DMA_CMA)) {
-               phys_addr_t paddr = dma_to_phys(dev, dma_handle);
-
-               dma_release_from_contiguous(dev,
+       freed = dma_release_from_contiguous(dev,
                                        phys_to_page(paddr),
                                        size >> PAGE_SHIFT);
-       } else {
+       if (!freed)
                swiotlb_free_coherent(dev, size, vaddr, dma_handle);
-       }
 }
 
 static void *__dma_alloc_noncoherent(struct device *dev, size_t size,
                                     dma_addr_t *dma_handle, gfp_t flags,
                                     struct dma_attrs *attrs)
 {
-       struct page *page, **map;
+       struct page *page;
        void *ptr, *coherent_ptr;
-       int order, i;
 
        size = PAGE_ALIGN(size);
-       order = get_order(size);
+
+       if (!(flags & __GFP_WAIT)) {
+               struct page *page = NULL;
+               void *addr = __alloc_from_pool(size, &page);
+
+               if (addr)
+                       *dma_handle = phys_to_dma(dev, page_to_phys(page));
+
+               return addr;
+
+       }
 
        ptr = __dma_alloc_coherent(dev, size, dma_handle, flags, attrs);
        if (!ptr)
                goto no_mem;
-       map = kmalloc(sizeof(struct page *) << order, flags & ~GFP_DMA);
-       if (!map)
-               goto no_map;
 
        /* remove any dirty cache lines on the kernel alias */
        __dma_flush_range(ptr, ptr + size);
 
        /* create a coherent mapping */
        page = virt_to_page(ptr);
-       for (i = 0; i < (size >> PAGE_SHIFT); i++)
-               map[i] = page + i;
-       coherent_ptr = vmap(map, size >> PAGE_SHIFT, VM_MAP,
-                           __get_dma_pgprot(attrs, __pgprot(PROT_NORMAL_NC), false));
-       kfree(map);
+       coherent_ptr = dma_common_contiguous_remap(page, size, VM_USERMAP,
+                               __get_dma_pgprot(attrs,
+                                       __pgprot(PROT_NORMAL_NC), false),
+                                       NULL);
        if (!coherent_ptr)
                goto no_map;
 
@@ -132,6 +185,8 @@ static void __dma_free_noncoherent(struct device *dev, size_t size,
 {
        void *swiotlb_addr = phys_to_virt(dma_to_phys(dev, dma_handle));
 
+       if (__free_from_pool(vaddr, size))
+               return;
        vunmap(vaddr);
        __dma_free_coherent(dev, size, swiotlb_addr, dma_handle, attrs);
 }
@@ -307,6 +362,67 @@ EXPORT_SYMBOL(coherent_swiotlb_dma_ops);
 
 extern int swiotlb_late_init_with_default_size(size_t default_size);
 
+static int __init atomic_pool_init(void)
+{
+       pgprot_t prot = __pgprot(PROT_NORMAL_NC);
+       unsigned long nr_pages = atomic_pool_size >> PAGE_SHIFT;
+       struct page *page;
+       void *addr;
+       unsigned int pool_size_order = get_order(atomic_pool_size);
+
+       if (dev_get_cma_area(NULL))
+               page = dma_alloc_from_contiguous(NULL, nr_pages,
+                                                       pool_size_order);
+       else
+               page = alloc_pages(GFP_DMA, pool_size_order);
+
+       if (page) {
+               int ret;
+               void *page_addr = page_address(page);
+
+               memset(page_addr, 0, atomic_pool_size);
+               __dma_flush_range(page_addr, page_addr + atomic_pool_size);
+
+               atomic_pool = gen_pool_create(PAGE_SHIFT, -1);
+               if (!atomic_pool)
+                       goto free_page;
+
+               addr = dma_common_contiguous_remap(page, atomic_pool_size,
+                                       VM_USERMAP, prot, atomic_pool_init);
+
+               if (!addr)
+                       goto destroy_genpool;
+
+               ret = gen_pool_add_virt(atomic_pool, (unsigned long)addr,
+                                       page_to_phys(page),
+                                       atomic_pool_size, -1);
+               if (ret)
+                       goto remove_mapping;
+
+               gen_pool_set_algo(atomic_pool,
+                                 gen_pool_first_fit_order_align,
+                                 (void *)PAGE_SHIFT);
+
+               pr_info("DMA: preallocated %zu KiB pool for atomic allocations\n",
+                       atomic_pool_size / 1024);
+               return 0;
+       }
+       goto out;
+
+remove_mapping:
+       dma_common_free_remap(addr, atomic_pool_size, VM_USERMAP);
+destroy_genpool:
+       gen_pool_destroy(atomic_pool);
+       atomic_pool = NULL;
+free_page:
+       if (!dma_release_from_contiguous(NULL, page, nr_pages))
+               __free_pages(page, pool_size_order);
+out:
+       pr_err("DMA: failed to allocate %zu KiB pool for atomic coherent allocation\n",
+               atomic_pool_size / 1024);
+       return -ENOMEM;
+}
+
 static int __init swiotlb_late_init(void)
 {
        size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES << PAGE_SHIFT);
@@ -315,7 +431,17 @@ static int __init swiotlb_late_init(void)
 
        return swiotlb_late_init_with_default_size(swiotlb_size);
 }
-arch_initcall(swiotlb_late_init);
+
+static int __init arm64_dma_init(void)
+{
+       int ret = 0;
+
+       ret |= swiotlb_late_init();
+       ret |= atomic_pool_init();
+
+       return ret;
+}
+arch_initcall(arm64_dma_init);
 
 #define PREALLOC_DMA_DEBUG_ENTRIES     4096
 
index 0d64089d28b517c4feef39ba350284809a159159..b6f14e8d21213ed9e91d1521c9de4d3c21c86cae 100644 (file)
@@ -104,3 +104,19 @@ EXPORT_SYMBOL(flush_dcache_page);
  */
 EXPORT_SYMBOL(flush_cache_all);
 EXPORT_SYMBOL(flush_icache_range);
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#ifdef CONFIG_HAVE_RCU_TABLE_FREE
+void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address,
+                         pmd_t *pmdp)
+{
+       pmd_t pmd = pmd_mksplitting(*pmdp);
+
+       VM_BUG_ON(address & ~PMD_MASK);
+       set_pmd_at(vma->vm_mm, address, pmdp, pmd);
+
+       /* dummy IPI to serialise against fast_gup */
+       kick_all_cpus_sync();
+}
+#endif /* CONFIG_HAVE_RCU_TABLE_FREE */
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
index 00a0f3ccd6eb994a67071d8e544762475f1ca792..2a71b1cb984821418f1f0f5b84fb19c9bbd6e72f 100644 (file)
@@ -9,6 +9,7 @@ generic-y += exec.h
 generic-y += futex.h
 generic-y += hash.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += local.h
 generic-y += local64.h
 generic-y += mcs_spinlock.h
index 0d93b9a79ca9561399a152d25a3545a09496b858..46ed6bb9c6798a6ff3ff749f38b0f255e0b64d6e 100644 (file)
@@ -15,6 +15,7 @@ generic-y += hw_irq.h
 generic-y += ioctl.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += kmap_types.h
 generic-y += kvm_para.h
index 1e7290ef35258da56c5521ed23a1b637286b7342..1e1014df5e9e42aca573dfae5965e80250881eb6 100644 (file)
@@ -733,7 +733,6 @@ static struct platform_device bfin_mac_device = {
 
 static struct pata_platform_info bfin_pata_platform_data = {
        .ioport_shift = 2,
-       .irq_type = IRQF_TRIGGER_HIGH,
 };
 
 static struct resource bfin_pata_resources[] = {
@@ -750,7 +749,7 @@ static struct resource bfin_pata_resources[] = {
        {
                .start = PATA_INT,
                .end = PATA_INT,
-               .flags = IORESOURCE_IRQ,
+               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
        },
 };
 
index c7495dc74690db99f52bfa30c94c62dd5faae63c..d056db9e559279ac73395b343bb21a00fb260278 100644 (file)
@@ -587,7 +587,6 @@ static struct platform_device bfin_mac_device = {
 
 static struct pata_platform_info bfin_pata_platform_data = {
        .ioport_shift = 2,
-       .irq_type = IRQF_TRIGGER_HIGH,
 };
 
 static struct resource bfin_pata_resources[] = {
@@ -604,7 +603,7 @@ static struct resource bfin_pata_resources[] = {
        {
                .start = PATA_INT,
                .end = PATA_INT,
-               .flags = IORESOURCE_IRQ,
+               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
        },
 };
 
index de19b8a56007b60e8b2fd3c435ed870b6c5c173c..88a19fc9844d8395900dd94b48e6a22fec30767a 100644 (file)
@@ -2462,7 +2462,6 @@ static struct platform_device bfin_sport0_device = {
 #define PATA_INT       IRQ_PF5
 static struct pata_platform_info bfin_pata_platform_data = {
        .ioport_shift = 1,
-       .irq_flags = IRQF_TRIGGER_HIGH,
 };
 
 static struct resource bfin_pata_resources[] = {
@@ -2479,7 +2478,7 @@ static struct resource bfin_pata_resources[] = {
        {
                .start = PATA_INT,
                .end = PATA_INT,
-               .flags = IORESOURCE_IRQ,
+               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
        },
 };
 #elif defined(CF_IDE_NAND_CARD_USE_CF_IN_COMMON_MEMORY_MODE)
index 6b988ad653d8d1e69a1721ddfe4b7208bf7cebb9..ed309c9a62b676625750a40d37d79036a4800aa4 100644 (file)
@@ -589,7 +589,6 @@ static struct platform_device bfin_mac_device = {
 
 static struct pata_platform_info bfin_pata_platform_data = {
        .ioport_shift = 2,
-       .irq_type = IRQF_TRIGGER_HIGH,
 };
 
 static struct resource bfin_pata_resources[] = {
@@ -606,7 +605,7 @@ static struct resource bfin_pata_resources[] = {
        {
                .start = PATA_INT,
                .end = PATA_INT,
-               .flags = IORESOURCE_IRQ,
+               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
        },
 };
 
index e862f7823e68db1b5ea374a295982c23db2800c8..c6db52ba3a06653e8b6a22a277c607d82c69de46 100644 (file)
@@ -354,7 +354,6 @@ static struct platform_device bfin_sir0_device = {
 
 static struct pata_platform_info bfin_pata_platform_data = {
        .ioport_shift = 2,
-       .irq_type = IRQF_TRIGGER_HIGH,
 };
 
 static struct resource bfin_pata_resources[] = {
@@ -371,7 +370,7 @@ static struct resource bfin_pata_resources[] = {
        {
                .start = PATA_INT,
                .end = PATA_INT,
-               .flags = IORESOURCE_IRQ,
+               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
        },
 };
 
index 8dbdce8421b08c0e58322db87002ef314778cd1b..e77e0c1dbe75ee81a189f4b74702a7110c1328fa 100644 (file)
@@ -22,6 +22,7 @@ generic-y += ioctl.h
 generic-y += ioctls.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += kmap_types.h
 generic-y += local.h
index 31742dfadff903d0dd7a953e79f4c8cd2d780379..2ca489eaadd3092efa8bd6a9bba6a808719d193f 100644 (file)
@@ -8,12 +8,14 @@ generic-y += clkdev.h
 generic-y += cputime.h
 generic-y += exec.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += kvm_para.h
 generic-y += linkage.h
 generic-y += mcs_spinlock.h
 generic-y += module.h
 generic-y += preempt.h
 generic-y += scatterlist.h
+generic-y += sections.h
 generic-y += trace_clock.h
 generic-y += vga.h
 generic-y += xor.h
diff --git a/arch/cris/include/asm/sections.h b/arch/cris/include/asm/sections.h
deleted file mode 100644 (file)
index 2c998ce..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _CRIS_SECTIONS_H
-#define _CRIS_SECTIONS_H
-
-/* nothing to see, move along */
-#include <asm-generic/sections.h>
-
-#endif
index 5b73921b6e9d32f125366a07f0f1270b0d46e260..3caf05cabfc520a06506e6318203421ad7014f66 100644 (file)
@@ -3,6 +3,7 @@ generic-y += clkdev.h
 generic-y += cputime.h
 generic-y += exec.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += preempt.h
 generic-y += scatterlist.h
index 6554e78893f26bc88e96e103811e1f0881a3a5ca..ae8d423e79d9dbe5c9c33d6ce1a15c3c51140f56 100644 (file)
 /* Forward declaration, a strange C thing */
 struct task_struct;
 
-/*
- *  CPU type and hardware bug flags. Kept separately for each CPU.
- */
-struct cpuinfo_frv {
-#ifdef CONFIG_MMU
-       unsigned long   *pgd_quick;
-       unsigned long   *pte_quick;
-       unsigned long   pgtable_cache_sz;
-#endif
-} __cacheline_aligned;
-
-extern struct cpuinfo_frv __nongprelbss boot_cpu_data;
-
-#define cpu_data               (&boot_cpu_data)
-#define current_cpu_data       boot_cpu_data
-
 /*
  * Bus types
  */
index 2cc327a1ca44c51cbb455ddbf200532597c78131..091b2839be902dc488243394814d1a3c546a84b2 100644 (file)
@@ -107,25 +107,25 @@ static irqreturn_t fpga_interrupt(int irq, void *_mask)
 static struct irqaction fpga_irq[4]  = {
        [0] = {
                .handler        = fpga_interrupt,
-               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .flags          = IRQF_SHARED,
                .name           = "fpga.0",
                .dev_id         = (void *) 0x0028UL,
        },
        [1] = {
                .handler        = fpga_interrupt,
-               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .flags          = IRQF_SHARED,
                .name           = "fpga.1",
                .dev_id         = (void *) 0x0050UL,
        },
        [2] = {
                .handler        = fpga_interrupt,
-               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .flags          = IRQF_SHARED,
                .name           = "fpga.2",
                .dev_id         = (void *) 0x1c00UL,
        },
        [3] = {
                .handler        = fpga_interrupt,
-               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .flags          = IRQF_SHARED,
                .name           = "fpga.3",
                .dev_id         = (void *) 0x6386UL,
        }
index 95e4eb4f1f383adb5c5bf552552d64fb7175ad33..1f3015cf80f549c77df7e165527a30478d0eb0f4 100644 (file)
@@ -105,7 +105,6 @@ static irqreturn_t fpga_interrupt(int irq, void *_mask)
 static struct irqaction fpga_irq[1]  = {
        [0] = {
                .handler        = fpga_interrupt,
-               .flags          = IRQF_DISABLED,
                .name           = "fpga.0",
                .dev_id         = (void *) 0x0700UL,
        }
index ba648da0932daec3fc71da4feec67ac660a31e12..8ca5aa4ff5958346013f0fa42d0189f2f7d268ab 100644 (file)
@@ -118,13 +118,13 @@ static irqreturn_t mb93493_interrupt(int irq, void *_piqsr)
 static struct irqaction mb93493_irq[2]  = {
        [0] = {
                .handler        = mb93493_interrupt,
-               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .flags          = IRQF_SHARED,
                .name           = "mb93493.0",
                .dev_id         = (void *) __addr_MB93493_IQSR(0),
        },
        [1] = {
                .handler        = mb93493_interrupt,
-               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .flags          = IRQF_SHARED,
                .name           = "mb93493.1",
                .dev_id         = (void *) __addr_MB93493_IQSR(1),
        }
index 9f3a7a62d7873dc29caa8bf66eaa35a7489edf66..9f4a9a607dbe68fd8262cb373c256c5bf4079e7c 100644 (file)
@@ -104,8 +104,6 @@ unsigned long __nongprelbss dma_coherent_mem_end;
 unsigned long __initdata __sdram_old_base;
 unsigned long __initdata num_mappedpages;
 
-struct cpuinfo_frv __nongprelbss boot_cpu_data;
-
 char __initdata command_line[COMMAND_LINE_SIZE];
 char __initdata redboot_command_line[COMMAND_LINE_SIZE];
 
index b457de496b7052272e54857b6687f15d190365ad..332e00bf9d0688b14f29ff5393728e02bdb779d3 100644 (file)
@@ -44,7 +44,6 @@ static irqreturn_t timer_interrupt(int irq, void *dummy);
 
 static struct irqaction timer_irq  = {
        .handler = timer_interrupt,
-       .flags = IRQF_DISABLED,
        .name = "timer",
 };
 
index 0e69796b58c763d83e5b5eb99ed19cdfb951b9eb..5f234a5a23201319e26e9972abd1a31ab44f2d9c 100644 (file)
@@ -23,6 +23,7 @@ generic-y += ioctls.h
 generic-y += iomap.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += kmap_types.h
 generic-y += local.h
index e8317d2d6c8d4462fe2536e5863ba628e3e44194..747320be9d0e1c4a94bc72cad21ed3b44ab2c3f8 100644 (file)
@@ -2,6 +2,7 @@
 generic-y += clkdev.h
 generic-y += exec.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += kvm_para.h
 generic-y += mcs_spinlock.h
 generic-y += preempt.h
index c430f9198d1bfec6b50b9d6f21d2929ef55dcf45..8c3730c3c63d4f51e28e81c780a6ea1ea1aecf8f 100644 (file)
@@ -23,7 +23,7 @@ static int ia64_set_msi_irq_affinity(struct irq_data *idata,
        if (irq_prepare_move(irq, cpu))
                return -1;
 
-       get_cached_msi_msg(irq, &msg);
+       __get_cached_msi_msg(idata->msi_desc, &msg);
 
        addr = msg.address_lo;
        addr &= MSI_ADDR_DEST_ID_MASK;
index afc58d2799adb7a8b7ccf5d454d90a26fff0a3dc..446e7799928cd89316ca2384707a468ff8e973b8 100644 (file)
@@ -175,8 +175,8 @@ static int sn_set_msi_irq_affinity(struct irq_data *data,
         * Release XIO resources for the old MSI PCI address
         */
 
-       get_cached_msi_msg(irq, &msg);
-        sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
+       __get_cached_msi_msg(data->msi_desc, &msg);
+       sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
        pdev = sn_pdev->pdi_linux_pcidev;
        provider = SN_PCIDEV_BUSPROVIDER(pdev);
 
index accc10a3dc78f09bd7dab56a0907ed1be2925da9..3796801d6e0cbe4eb862365b7ce149ebb49ae543 100644 (file)
@@ -3,8 +3,10 @@ generic-y += clkdev.h
 generic-y += cputime.h
 generic-y += exec.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += module.h
 generic-y += preempt.h
 generic-y += scatterlist.h
+generic-y += sections.h
 generic-y += trace_clock.h
diff --git a/arch/m32r/include/asm/sections.h b/arch/m32r/include/asm/sections.h
deleted file mode 100644 (file)
index 5e5d21c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _M32R_SECTIONS_H
-#define _M32R_SECTIONS_H
-
-/* nothing to see, move along */
-#include <asm-generic/sections.h>
-
-#endif /* _M32R_SECTIONS_H */
index 1a15f81ea1bd9d6e5390f4fdb40560785c9830bd..093f2761aa51429e904cea297d14b3e405e9f65c 100644 (file)
@@ -134,7 +134,6 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
 
 static struct irqaction irq0 = {
        .handler = timer_interrupt,
-       .flags = IRQF_DISABLED,
        .name = "MFT2",
 };
 
index c67c94a2d67229a461239a4335def5d90d597abe..dbaf9f3065e8da6802e4beb32706a936264d597c 100644 (file)
@@ -11,6 +11,7 @@ generic-y += hw_irq.h
 generic-y += ioctl.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += kmap_types.h
 generic-y += kvm_para.h
index 3a480b3df0d6cfee0fa7a6e2da99f710d776d587..9aa01adb407fd3cfa25f6b1de395e01973392ff3 100644 (file)
@@ -376,7 +376,6 @@ cache_flush_060 (unsigned long addr, int scope, int cache, unsigned long len)
 asmlinkage int
 sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len)
 {
-       struct vm_area_struct *vma;
        int ret = -EINVAL;
 
        if (scope < FLUSH_SCOPE_LINE || scope > FLUSH_SCOPE_ALL ||
@@ -389,17 +388,21 @@ sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len)
                if (!capable(CAP_SYS_ADMIN))
                        goto out;
        } else {
+               struct vm_area_struct *vma;
+
+               /* Check for overflow.  */
+               if (addr + len < addr)
+                       goto out;
+
                /*
                 * Verify that the specified address region actually belongs
                 * to this process.
                 */
-               vma = find_vma (current->mm, addr);
                ret = -EINVAL;
-               /* Check for overflow.  */
-               if (addr + len < addr)
-                       goto out;
-               if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end)
-                       goto out;
+               down_read(&current->mm->mmap_sem);
+               vma = find_vma(current->mm, addr);
+               if (!vma || addr < vma->vm_start || addr + len > vma->vm_end)
+                       goto out_unlock;
        }
 
        if (CPU_IS_020_OR_030) {
@@ -429,7 +432,7 @@ sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len)
                        __asm__ __volatile__ ("movec %0, %%cacr" : : "r" (cacr));
                }
                ret = 0;
-               goto out;
+               goto out_unlock;
        } else {
            /*
             * 040 or 060: don't blindly trust 'scope', someone could
@@ -446,6 +449,8 @@ sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len)
                ret = cache_flush_060 (addr, scope, cache, len);
            }
        }
+out_unlock:
+       up_read(&current->mm->mmap_sem);
 out:
        return ret;
 }
index c29ead89a31778da9d5f217b6cc984c1176a6772..7b8111c8f937ba5d5f0820ad64e56cf1e5e4ef6b 100644 (file)
@@ -19,6 +19,7 @@ generic-y += ioctl.h
 generic-y += ioctls.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += kmap_types.h
 generic-y += kvm_para.h
index 27a3acda6c1904a05167b96efb006dcdd56f83ce..448143b8cabd1a8f5e5460d4c79461b0ef6b6bc6 100644 (file)
@@ -5,6 +5,7 @@ generic-y += cputime.h
 generic-y += device.h
 generic-y += exec.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += preempt.h
 generic-y += scatterlist.h
index 335e5290ec759f670aea360e53cd85293cdfa7f3..57012ef1f51ea102faf35f911009aa14ce846a98 100644 (file)
@@ -3,6 +3,7 @@ generic-y += cputime.h
 generic-y += current.h
 generic-y += emergency-restart.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += mutex.h
diff --git a/arch/mips/include/asm/suspend.h b/arch/mips/include/asm/suspend.h
deleted file mode 100644 (file)
index 3adac3b..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_SUSPEND_H
-#define __ASM_SUSPEND_H
-
-/* References to section boundaries */
-extern const void __nosave_begin, __nosave_end;
-
-#endif /* __ASM_SUSPEND_H */
index ab0c5d14c6f7289ac52554976ab7da9130cf4b41..63bbe07a1ccd24afb0857e82d88ea84052a669c3 100644 (file)
@@ -73,8 +73,7 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
         * wants.  Most devices only want 1, which will give
         * configured_private_bits and request_private_bits equal 0.
         */
-       pci_read_config_word(dev, desc->msi_attrib.pos + PCI_MSI_FLAGS,
-                            &control);
+       pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
 
        /*
         * If the number of private bits has been configured then use
@@ -176,8 +175,7 @@ msi_irq_allocated:
        /* Update the number of IRQs the device has available to it */
        control &= ~PCI_MSI_FLAGS_QSIZE;
        control |= request_private_bits << 4;
-       pci_write_config_word(dev, desc->msi_attrib.pos + PCI_MSI_FLAGS,
-                             control);
+       pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
 
        irq_set_msi_desc(irq, desc);
        write_msi_msg(irq, &msg);
index 521e5963df05f4f67038f52dd3b321851fa0e551..2129e67723ff38b6a1ef8fea8401886a063c8402 100644 (file)
@@ -7,7 +7,7 @@
  * Author: Hu Hongbing <huhb@lemote.com>
  *        Wu Zhangjin <wuzhangjin@gmail.com>
  */
-#include <asm/suspend.h>
+#include <asm/sections.h>
 #include <asm/fpu.h>
 #include <asm/dsp.h>
 
index 9ff200ae1c9aec94f77b683ac7d48d4761bea93b..2791b8641df64c4624fc480ef5ee3a37e36c3a77 100644 (file)
@@ -789,11 +789,11 @@ void __init txx9_iocled_init(unsigned long baseaddr,
        if (platform_device_add(pdev))
                goto out_pdev;
        return;
+
 out_pdev:
        platform_device_put(pdev);
 out_gpio:
-       if (gpiochip_remove(&iocled->chip))
-               return;
+       gpiochip_remove(&iocled->chip);
 out_unmap:
        iounmap(iocled->mmioaddr);
 out_free:
index ecbd6676bd338c70a5ad9d1d2ece7b28f996dc21..54a062cb9f2cb9236f4bdc9cef3e028777b11f31 100644 (file)
@@ -4,7 +4,9 @@ generic-y += clkdev.h
 generic-y += cputime.h
 generic-y += exec.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += preempt.h
 generic-y += scatterlist.h
+generic-y += sections.h
 generic-y += trace_clock.h
diff --git a/arch/mn10300/include/asm/sections.h b/arch/mn10300/include/asm/sections.h
deleted file mode 100644 (file)
index 2b8c516..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/sections.h>
index 88e83368bbf57f4e2ad8c9b1b685a1f063bd12a6..e5a693b16da2967ffc112acf0c7561d0e93d5e04 100644 (file)
@@ -8,6 +8,7 @@ config OPENRISC
        select OF
        select OF_EARLY_FLATTREE
        select IRQ_DOMAIN
+       select HANDLE_DOMAIN_IRQ
        select HAVE_MEMBLOCK
        select ARCH_REQUIRE_GPIOLIB
         select HAVE_ARCH_TRACEHOOK
index 480af0d9c2f5dbe20fb39dc6331648dacb043a97..89b61d7dc790ee7b0127072b1a66d181eae11522 100644 (file)
@@ -31,6 +31,7 @@ generic-y += ioctl.h
 generic-y += ioctls.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += kmap_types.h
 generic-y += kvm_para.h
index b84634cc95eb2fac7ee8b01d5e46ec855ae27075..d9eee0a2b7b4a866c760b5dbcb0a66a7d82c4d3f 100644 (file)
@@ -24,7 +24,6 @@
 
 #define NO_IRQ         (-1)
 
-void handle_IRQ(unsigned int, struct pt_regs *);
 extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
 
 #endif /* __ASM_OPENRISC_IRQ_H__ */
index 967eb143020311fce2d55dc0b5f0971ae2ffc9d3..35e478a93116802991b5242c1c497097399a222e 100644 (file)
@@ -48,18 +48,6 @@ void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
        handle_arch_irq = handle_irq;
 }
 
-void handle_IRQ(unsigned int irq, struct pt_regs *regs)
-{
-       struct pt_regs *old_regs = set_irq_regs(regs);
-
-       irq_enter();
-
-       generic_handle_irq(irq);
-
-       irq_exit();
-       set_irq_regs(old_regs);
-}
-
 void __irq_entry do_IRQ(struct pt_regs *regs)
 {
        handle_arch_irq(regs);
index ecf25e6678ad5a2f0e2bd63761699e85c9d2d759..ffb024b8423fa55a282fbfa05fbf4fb13464d4c4 100644 (file)
@@ -10,6 +10,7 @@ generic-y += exec.h
 generic-y += hash.h
 generic-y += hw_irq.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += kvm_para.h
 generic-y += local.h
index 4bc7b62fb4b68761341619d7bd8300dbb8786d76..88eace4e28c3dfbdbbe35261f0f03656f8616a1e 100644 (file)
@@ -147,6 +147,7 @@ config PPC
        select ARCH_USE_CMPXCHG_LOCKREF if PPC64
        select HAVE_ARCH_AUDITSYSCALL
        select ARCH_SUPPORTS_ATOMIC_RMW
+       select DCACHE_WORD_ACCESS if PPC64 && CPU_LITTLE_ENDIAN
 
 config GENERIC_CSUM
        def_bool CPU_LITTLE_ENDIAN
@@ -182,7 +183,7 @@ config SCHED_OMIT_FRAME_POINTER
 
 config ARCH_MAY_HAVE_PC_FDC
        bool
-       default !PPC_PSERIES || PCI
+       default PCI
 
 config PPC_OF
        def_bool y
@@ -287,6 +288,10 @@ config PPC_EMULATE_SSTEP
        bool
        default y if KPROBES || UPROBES || XMON || HAVE_HW_BREAKPOINT
 
+config ZONE_DMA32
+       bool
+       default y if PPC64
+
 source "init/Kconfig"
 
 source "kernel/Kconfig.freezer"
@@ -603,6 +608,10 @@ config PPC_SUBPAGE_PROT
          to set access permissions (read/write, readonly, or no access)
          on the 4k subpages of each 64k page.
 
+config PPC_COPRO_BASE
+       bool
+       default n
+
 config SCHED_SMT
        bool "SMT (Hyperthreading) scheduler support"
        depends on PPC64 && SMP
index 5687e299d0a5b3b58970804a04af057d3b6f64f4..132d9c681d6ae275ca6129a20fee4b9e55487840 100644 (file)
@@ -135,6 +135,7 @@ CFLAGS-$(CONFIG_POWER4_CPU) += $(call cc-option,-mcpu=power4)
 CFLAGS-$(CONFIG_POWER5_CPU) += $(call cc-option,-mcpu=power5)
 CFLAGS-$(CONFIG_POWER6_CPU) += $(call cc-option,-mcpu=power6)
 CFLAGS-$(CONFIG_POWER7_CPU) += $(call cc-option,-mcpu=power7)
+CFLAGS-$(CONFIG_POWER8_CPU) += $(call cc-option,-mcpu=power8)
 
 # Altivec option not allowed with e500mc64 in GCC.
 ifeq ($(CONFIG_ALTIVEC),y)
index ccc25eddbcb8b2ff8404d66cecbaec1e450f5a51..8a5bc1cfc6aa0fa16a52c2ef1be0ae3b18999966 100644 (file)
@@ -389,7 +389,12 @@ $(obj)/zImage:             $(addprefix $(obj)/, $(image-y))
 $(obj)/zImage.initrd:  $(addprefix $(obj)/, $(initrd-y))
        @rm -f $@; ln $< $@
 
+# Only install the vmlinux
 install: $(CONFIGURE) $(addprefix $(obj)/, $(image-y))
+       sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)"
+
+# Install the vmlinux and other built boot targets.
+zInstall: $(CONFIGURE) $(addprefix $(obj)/, $(image-y))
        sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $^
 
 # anything not in $(targets)
index 97479f0ce630317f97000a073702e5467335031d..aecee9690a88a0ec09320f5b43c9740bdfb2c4cf 100644 (file)
 /include/ "qoriq-gpio-3.dtsi"
 /include/ "qoriq-usb2-mph-0.dtsi"
        usb0: usb@210000 {
-               compatible = "fsl-usb2-mph-v2.4", "fsl-usb2-mph";
+               compatible = "fsl-usb2-mph-v2.5", "fsl-usb2-mph";
                fsl,iommu-parent = <&pamu1>;
                fsl,liodn-reg = <&guts 0x520>; /* USB1LIODNR */
                phy_type = "utmi";
        };
 /include/ "qoriq-usb2-dr-0.dtsi"
        usb1: usb@211000 {
-               compatible = "fsl-usb2-dr-v2.4", "fsl-usb2-dr";
+               compatible = "fsl-usb2-dr-v2.5", "fsl-usb2-dr";
                fsl,iommu-parent = <&pamu1>;
                fsl,liodn-reg = <&guts 0x524>; /* USB1LIODNR */
                dr_mode = "host";
index a3d582e0361ae706920a4eba4d8e50bcce177b58..7e2fc7cdce4833039a80f8fa650c7292a7b030e1 100644 (file)
 /include/ "qoriq-gpio-3.dtsi"
 /include/ "qoriq-usb2-mph-0.dtsi"
                usb0: usb@210000 {
-                       compatible = "fsl-usb2-mph-v2.4", "fsl-usb2-mph";
+                       compatible = "fsl-usb2-mph-v2.5", "fsl-usb2-mph";
                        phy_type = "utmi";
                        port0;
                };
 /include/ "qoriq-usb2-dr-0.dtsi"
                usb1: usb@211000 {
-                       compatible = "fsl-usb2-dr-v2.4", "fsl-usb2-dr";
+                       compatible = "fsl-usb2-dr-v2.5", "fsl-usb2-dr";
                        dr_mode = "host";
                        phy_type = "utmi";
                };
diff --git a/arch/powerpc/boot/dts/t1040rdb.dts b/arch/powerpc/boot/dts/t1040rdb.dts
new file mode 100644 (file)
index 0000000..79a0bed
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * T1040RDB Device Tree Source
+ *
+ * Copyright 2014 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/include/ "fsl/t104xsi-pre.dtsi"
+/include/ "t104xrdb.dtsi"
+
+/ {
+       model = "fsl,T1040RDB";
+       compatible = "fsl,T1040RDB";
+       ifc: localbus@ffe124000 {
+               cpld@3,0 {
+                       compatible = "fsl,t1040rdb-cpld";
+               };
+       };
+};
+
+/include/ "fsl/t1040si-post.dtsi"
diff --git a/arch/powerpc/boot/dts/t1042rdb.dts b/arch/powerpc/boot/dts/t1042rdb.dts
new file mode 100644 (file)
index 0000000..738c237
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * T1042RDB Device Tree Source
+ *
+ * Copyright 2014 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/include/ "fsl/t104xsi-pre.dtsi"
+/include/ "t104xrdb.dtsi"
+
+/ {
+       model = "fsl,T1042RDB";
+       compatible = "fsl,T1042RDB";
+       ifc: localbus@ffe124000 {
+               cpld@3,0 {
+                       compatible = "fsl,t1042rdb-cpld";
+               };
+       };
+};
+
+/include/ "fsl/t1042si-post.dtsi"
diff --git a/arch/powerpc/boot/dts/t1042rdb_pi.dts b/arch/powerpc/boot/dts/t1042rdb_pi.dts
new file mode 100644 (file)
index 0000000..634f751
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * T1042RDB_PI Device Tree Source
+ *
+ * Copyright 2014 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/include/ "fsl/t104xsi-pre.dtsi"
+/include/ "t104xrdb.dtsi"
+
+/ {
+       model = "fsl,T1042RDB_PI";
+       compatible = "fsl,T1042RDB_PI";
+       ifc: localbus@ffe124000 {
+               cpld@3,0 {
+                       compatible = "fsl,t1042rdb_pi-cpld";
+               };
+       };
+       soc: soc@ffe000000 {
+               i2c@118000 {
+                       rtc@68 {
+                               compatible = "dallas,ds1337";
+                               reg = <0x68>;
+                               interrupts = <0x2 0x1 0 0>;
+                       };
+               };
+       };
+};
+
+/include/ "fsl/t1042si-post.dtsi"
diff --git a/arch/powerpc/boot/dts/t104xrdb.dtsi b/arch/powerpc/boot/dts/t104xrdb.dtsi
new file mode 100644 (file)
index 0000000..1cf0f3c
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * T1040RDB/T1042RDB Device Tree Source
+ *
+ * Copyright 2014 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/ {
+
+       ifc: localbus@ffe124000 {
+               reg = <0xf 0xfe124000 0 0x2000>;
+               ranges = <0 0 0xf 0xe8000000 0x08000000
+                         2 0 0xf 0xff800000 0x00010000
+                         3 0 0xf 0xffdf0000 0x00008000>;
+
+               nor@0,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "cfi-flash";
+                       reg = <0x0 0x0 0x8000000>;
+                       bank-width = <2>;
+                       device-width = <1>;
+               };
+
+               nand@2,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "fsl,ifc-nand";
+                       reg = <0x2 0x0 0x10000>;
+               };
+
+               cpld@3,0 {
+                       reg = <3 0 0x300>;
+               };
+       };
+
+       memory {
+               device_type = "memory";
+       };
+
+       dcsr: dcsr@f00000000 {
+               ranges = <0x00000000 0xf 0x00000000 0x01072000>;
+       };
+
+       soc: soc@ffe000000 {
+               ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
+               reg = <0xf 0xfe000000 0 0x00001000>;
+
+               spi@110000 {
+                       flash@0 {
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               compatible = "micron,n25q512a";
+                               reg = <0>;
+                               spi-max-frequency = <10000000>; /* input clock */
+                       };
+               };
+
+               i2c@118100 {
+                       pca9546@77 {
+                               compatible = "nxp,pca9546";
+                               reg = <0x77>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                       };
+               };
+
+       };
+
+       pci0: pcie@ffe240000 {
+               reg = <0xf 0xfe240000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0x0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8000000 0x0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+
+       pci1: pcie@ffe250000 {
+               reg = <0xf 0xfe250000 0 0x10000>;
+               ranges = <0x02000000 0x0 0xe0000000 0xc 0x10000000 0x0 0x10000000
+                         0x01000000 0x0 0x00000000 0xf 0xf8010000 0x0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+
+       pci2: pcie@ffe260000 {
+               reg = <0xf 0xfe260000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x20000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+
+       pci3: pcie@ffe270000 {
+               reg = <0xf 0xfe270000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x30000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8030000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+};
index 45fd06cdc3e86639d3ddc829c4d44c8db0069bd0..7a7b3c879f96f83723c132a22c4367297215599a 100644 (file)
@@ -18,6 +18,7 @@ CONFIG_OPROFILE=m
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 # CONFIG_BLK_DEV_BSG is not set
+# CONFIG_PPC_POWERNV is not set
 # CONFIG_PPC_PSERIES is not set
 # CONFIG_PPC_PMAC is not set
 CONFIG_PPC_PS3=y
index 77d7bf3ca2acb014def979d68d21c7f94401ed10..acccbfde8a5094293c681aecf45fb3f32eea9e7b 100644 (file)
@@ -15,6 +15,7 @@ CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODVERSIONS=y
 CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_PPC_POWERNV is not set
 # CONFIG_PPC_PSERIES is not set
 # CONFIG_PPC_PMAC is not set
 CONFIG_PPC_CELLEB=y
index 6a3c58adf2530a45f312cc3df25d90934880b6ae..688e9e4d29a1a699af841aadb4ba1865cf442061 100644 (file)
@@ -165,6 +165,8 @@ CONFIG_NFS_FS=y
 CONFIG_NFS_V4=y
 CONFIG_ROOT_NFS=y
 CONFIG_NFSD=m
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_850=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_NLS_UTF8=m
 CONFIG_MAGIC_SYSRQ=y
index 269d6e47c67d2a998272f27545ec238766c68dc8..6db97e4414b2eabaa92b82af533f7facd646e675 100644 (file)
@@ -50,7 +50,6 @@ CONFIG_NET_IPIP=y
 CONFIG_IP_MROUTE=y
 CONFIG_IP_PIMSM_V1=y
 CONFIG_IP_PIMSM_V2=y
-CONFIG_ARPD=y
 CONFIG_INET_ESP=y
 # CONFIG_INET_XFRM_MODE_BEET is not set
 # CONFIG_INET_LRO is not set
@@ -60,33 +59,17 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_DEVTMPFS=y
 CONFIG_DEVTMPFS_MOUNT=y
 CONFIG_MTD=y
-CONFIG_MTD_OF_PARTS=y
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=y
-CONFIG_MTD_BLKDEVS=y
 CONFIG_MTD_BLOCK=y
 CONFIG_FTL=y
 CONFIG_MTD_CFI=y
-CONFIG_MTD_GEN_PROBE=y
-CONFIG_MTD_MAP_BANK_WIDTH_1=y
-CONFIG_MTD_MAP_BANK_WIDTH_2=y
-CONFIG_MTD_MAP_BANK_WIDTH_4=y
-CONFIG_MTD_CFI_I1=y
-CONFIG_MTD_CFI_I2=y
 CONFIG_MTD_CFI_INTELEXT=y
 CONFIG_MTD_CFI_AMDSTD=y
 CONFIG_MTD_PHYSMAP_OF=y
-CONFIG_MTD_M25P80=y
-CONFIG_MTD_CFI_UTIL=y
-CONFIG_MTD_NAND_ECC=y
 CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_IDS=y
 CONFIG_MTD_NAND_FSL_ELBC=y
 CONFIG_MTD_NAND_FSL_IFC=y
 CONFIG_MTD_UBI=y
-CONFIG_MTD_UBI_WL_THRESHOLD=4096
-CONFIG_MTD_UBI_BEB_RESERVE=1
-CONFIG_PROC_DEVICETREE=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_SIZE=131072
@@ -102,6 +85,7 @@ CONFIG_INPUT_FF_MEMLESS=m
 # CONFIG_INPUT_KEYBOARD is not set
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_SERIO_LIBPS2=y
+CONFIG_PPC_EPAPR_HV_BYTECHAN=y
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_SERIAL_8250_MANY_PORTS=y
@@ -115,7 +99,6 @@ CONFIG_SPI_GPIO=y
 CONFIG_SPI_FSL_SPI=y
 CONFIG_SPI_FSL_ESPI=y
 # CONFIG_HWMON is not set
-CONFIG_VIDEO_OUTPUT_CONTROL=y
 CONFIG_USB_HID=m
 CONFIG_USB=y
 CONFIG_USB_MON=y
@@ -124,14 +107,17 @@ CONFIG_USB_EHCI_FSL=y
 CONFIG_USB_STORAGE=y
 CONFIG_MMC=y
 CONFIG_MMC_SDHCI=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_DS1307=y
 CONFIG_RTC_DRV_DS1374=y
 CONFIG_RTC_DRV_DS3232=y
-CONFIG_EDAC=y
-CONFIG_EDAC_MM_EDAC=y
 CONFIG_DMADEVICES=y
 CONFIG_FSL_DMA=y
+CONFIG_VIRT_DRIVERS=y
+CONFIG_FSL_HV_MANAGER=y
+CONFIG_FSL_CORENET_CF=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
 CONFIG_ISO9660_FS=m
@@ -144,35 +130,24 @@ CONFIG_NTFS_FS=y
 CONFIG_PROC_KCORE=y
 CONFIG_TMPFS=y
 CONFIG_HUGETLBFS=y
-CONFIG_MISC_FILESYSTEMS=y
 CONFIG_JFFS2_FS=y
 CONFIG_JFFS2_FS_DEBUG=1
-CONFIG_JFFS2_FS_WRITEBUFFER=y
-CONFIG_JFFS2_ZLIB=y
-CONFIG_JFFS2_RTIME=y
 CONFIG_UBIFS_FS=y
-CONFIG_UBIFS_FS_XATTR=y
-CONFIG_UBIFS_FS_LZO=y
-CONFIG_UBIFS_FS_ZLIB=y
 CONFIG_NFS_FS=y
 CONFIG_NFS_V4=y
 CONFIG_ROOT_NFS=y
 CONFIG_NFSD=m
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_850=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_NLS_UTF8=m
 CONFIG_CRC_T10DIF=y
-CONFIG_CRC16=y
-CONFIG_ZLIB_DEFLATE=y
-CONFIG_LZO_COMPRESS=y
-CONFIG_LZO_DECOMPRESS=y
-CONFIG_CRYPTO_DEFLATE=y
-CONFIG_CRYPTO_LZO=y
+CONFIG_DEBUG_INFO=y
 CONFIG_FRAME_WARN=1024
-CONFIG_MAGIC_SYSRQ=y
 CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
 CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_HUNG_TASK=y
-CONFIG_DEBUG_INFO=y
 CONFIG_CRYPTO_NULL=y
 CONFIG_CRYPTO_PCBC=m
 CONFIG_CRYPTO_MD4=y
@@ -180,4 +155,3 @@ CONFIG_CRYPTO_SHA256=y
 CONFIG_CRYPTO_SHA512=y
 # CONFIG_CRYPTO_ANSI_CPRNG is not set
 CONFIG_CRYPTO_DEV_FSL_CAAM=y
-CONFIG_FSL_CORENET_CF=y
index 7594c5ac64818f6b01537bb0c0be3519c73bd455..6fab06f7f4117bb65ee7ee6594706edd281c6d65 100644 (file)
@@ -16,6 +16,7 @@ CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODVERSIONS=y
 CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_PPC_POWERNV is not set
 # CONFIG_PPC_PSERIES is not set
 CONFIG_CPU_FREQ=y
 CONFIG_CPU_FREQ_GOV_POWERSAVE=y
index c8b6a9ddb21bf813c448dfc4f847a3f611c23dd1..fbd9e4163311c060ee67bdbe35caba1083fc1601 100644 (file)
@@ -16,6 +16,7 @@ CONFIG_MODULE_UNLOAD=y
 CONFIG_MODVERSIONS=y
 CONFIG_MODULE_SRCVERSION_ALL=y
 # CONFIG_BLK_DEV_BSG is not set
+# CONFIG_PPC_POWERNV is not set
 # CONFIG_PPC_PSERIES is not set
 # CONFIG_PPC_PMAC is not set
 CONFIG_PPC_MAPLE=y
index fa1bfd37f1ec446551eafac579d9a3141738856e..d2c415489f724b4c3acdc6b8ea7d0f8b83ff5341 100644 (file)
@@ -213,7 +213,6 @@ CONFIG_RTC_DRV_DS1307=y
 CONFIG_RTC_DRV_DS1374=y
 CONFIG_RTC_DRV_DS3232=y
 CONFIG_RTC_DRV_CMOS=y
-CONFIG_RTC_DRV_DS1307=y
 CONFIG_DMADEVICES=y
 CONFIG_FSL_DMA=y
 # CONFIG_NET_DMA is not set
@@ -227,6 +226,9 @@ CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=y
 CONFIG_NTFS_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_850=y
+CONFIG_NLS_ISO8859_1=y
 CONFIG_PROC_KCORE=y
 CONFIG_TMPFS=y
 CONFIG_HUGETLBFS=y
index 0b452ebd8b3d7d8b0569a9196470ee205993278d..87460083dbc7a508fcd567db4b1ba7fa5728a40d 100644 (file)
@@ -214,7 +214,6 @@ CONFIG_RTC_DRV_DS1307=y
 CONFIG_RTC_DRV_DS1374=y
 CONFIG_RTC_DRV_DS3232=y
 CONFIG_RTC_DRV_CMOS=y
-CONFIG_RTC_DRV_DS1307=y
 CONFIG_DMADEVICES=y
 CONFIG_FSL_DMA=y
 # CONFIG_NET_DMA is not set
@@ -228,6 +227,9 @@ CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=y
 CONFIG_NTFS_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_850=y
+CONFIG_NLS_ISO8859_1=y
 CONFIG_PROC_KCORE=y
 CONFIG_TMPFS=y
 CONFIG_HUGETLBFS=y
index 35595ea74ff4208c48a16a0e5252eb6716150178..fc58aa8a89e498b859f8d53e6ae19adade127678 100644 (file)
@@ -145,6 +145,9 @@ CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=y
 CONFIG_NTFS_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_850=y
+CONFIG_NLS_ISO8859_1=y
 CONFIG_PROC_KCORE=y
 CONFIG_TMPFS=y
 CONFIG_ADFS_FS=m
index e5e7838af0088f04b8faf5f40d8204962bd0bb6f..3e72c8c06a0dc7d3c9cb9470066dc33f93cc894f 100644 (file)
@@ -14,6 +14,7 @@ CONFIG_MODULE_UNLOAD=y
 # CONFIG_BLK_DEV_BSG is not set
 CONFIG_PARTITION_ADVANCED=y
 CONFIG_MAC_PARTITION=y
+# CONFIG_PPC_POWERNV is not set
 # CONFIG_PPC_PSERIES is not set
 # CONFIG_PPC_PMAC is not set
 CONFIG_PPC_PASEMI=y
index 36518870e6b29f75811b0b7ec4cddcb7859c04b5..20bc5e2d368d05601fd267ac81c2b375c9f95e6d 100644 (file)
@@ -50,6 +50,7 @@ CONFIG_HZ_100=y
 CONFIG_BINFMT_MISC=m
 CONFIG_PPC_TRANSACTIONAL_MEM=y
 CONFIG_KEXEC=y
+CONFIG_CRASH_DUMP=y
 CONFIG_IRQ_ALL_CPUS=y
 CONFIG_MEMORY_HOTREMOVE=y
 CONFIG_SCHED_SMT=y
index 7f23f162ce9c0c40a9e29eebba12cdd1db4c6704..31e8f59aff38894c0b16629b2ce7cc18fecbfb2c 100644 (file)
@@ -1,6 +1,7 @@
 
 generic-y += clkdev.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += preempt.h
 generic-y += rwsem.h
index 3eb53d741070d69fe3505b694d04b8a40303a71e..3a39283333c3cdbba5139f1c97f83d7f36815ed9 100644 (file)
@@ -133,7 +133,6 @@ extern int do_page_fault(struct pt_regs *, unsigned long, unsigned long);
 extern void bad_page_fault(struct pt_regs *, unsigned long, int);
 extern void _exception(int, struct pt_regs *, int, unsigned long);
 extern void die(const char *, struct pt_regs *, long);
-extern void print_backtrace(unsigned long *);
 
 #endif /* !__ASSEMBLY__ */
 
diff --git a/arch/powerpc/include/asm/copro.h b/arch/powerpc/include/asm/copro.h
new file mode 100644 (file)
index 0000000..ce216df
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#ifndef _ASM_POWERPC_COPRO_H
+#define _ASM_POWERPC_COPRO_H
+
+struct copro_slb
+{
+       u64 esid, vsid;
+};
+
+int copro_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
+                         unsigned long dsisr, unsigned *flt);
+
+int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb);
+
+
+#ifdef CONFIG_PPC_COPRO_BASE
+void copro_flush_all_slbs(struct mm_struct *mm);
+#else
+static inline void copro_flush_all_slbs(struct mm_struct *mm) {}
+#endif
+#endif /* _ASM_POWERPC_COPRO_H */
index 150866b2a3fe07337f38905511a73cd726e07689..894d538f356783336f6addf6468ab941cafd0d02 100644 (file)
@@ -135,6 +135,7 @@ static inline int dma_supported(struct device *dev, u64 mask)
 
 extern int dma_set_mask(struct device *dev, u64 dma_mask);
 extern int __dma_set_mask(struct device *dev, u64 dma_mask);
+extern u64 __dma_get_required_mask(struct device *dev);
 
 #define dma_alloc_coherent(d,s,h,f)    dma_alloc_attrs(d,s,h,f,NULL)
 
index 9983c3d26bcaa1f4e5572f434deaeb05868c4758..3b260efbfbf9833a5d23263b64f50c833564b74a 100644 (file)
@@ -146,6 +146,11 @@ static inline struct pci_dev *eeh_dev_to_pci_dev(struct eeh_dev *edev)
        return edev ? edev->pdev : NULL;
 }
 
+static inline struct eeh_pe *eeh_dev_to_pe(struct eeh_dev* edev)
+{
+       return edev ? edev->pe : NULL;
+}
+
 /* Return values from eeh_ops::next_error */
 enum {
        EEH_NEXT_ERR_NONE = 0,
@@ -167,6 +172,7 @@ enum {
 #define EEH_OPT_ENABLE         1       /* EEH enable   */
 #define EEH_OPT_THAW_MMIO      2       /* MMIO enable  */
 #define EEH_OPT_THAW_DMA       3       /* DMA enable   */
+#define EEH_OPT_FREEZE_PE      4       /* Freeze PE    */
 #define EEH_STATE_UNAVAILABLE  (1 << 0)        /* State unavailable    */
 #define EEH_STATE_NOT_SUPPORT  (1 << 1)        /* EEH not supported    */
 #define EEH_STATE_RESET_ACTIVE (1 << 2)        /* Active reset         */
@@ -198,6 +204,8 @@ struct eeh_ops {
        int (*wait_state)(struct eeh_pe *pe, int max_wait);
        int (*get_log)(struct eeh_pe *pe, int severity, char *drv_log, unsigned long len);
        int (*configure_bridge)(struct eeh_pe *pe);
+       int (*err_inject)(struct eeh_pe *pe, int type, int func,
+                         unsigned long addr, unsigned long mask);
        int (*read_config)(struct device_node *dn, int where, int size, u32 *val);
        int (*write_config)(struct device_node *dn, int where, int size, u32 val);
        int (*next_error)(struct eeh_pe **pe);
@@ -269,8 +277,7 @@ void eeh_dev_phb_init_dynamic(struct pci_controller *phb);
 int eeh_init(void);
 int __init eeh_ops_register(struct eeh_ops *ops);
 int __exit eeh_ops_unregister(const char *name);
-unsigned long eeh_check_failure(const volatile void __iomem *token,
-                               unsigned long val);
+int eeh_check_failure(const volatile void __iomem *token);
 int eeh_dev_check_failure(struct eeh_dev *edev);
 void eeh_addr_cache_build(void);
 void eeh_add_device_early(struct device_node *);
@@ -279,6 +286,8 @@ void eeh_add_device_late(struct pci_dev *);
 void eeh_add_device_tree_late(struct pci_bus *);
 void eeh_add_sysfs_files(struct pci_bus *);
 void eeh_remove_device(struct pci_dev *);
+int eeh_unfreeze_pe(struct eeh_pe *pe, bool sw_state);
+int eeh_pe_reset_and_recover(struct eeh_pe *pe);
 int eeh_dev_open(struct pci_dev *pdev);
 void eeh_dev_release(struct pci_dev *pdev);
 struct eeh_pe *eeh_iommu_group_to_pe(struct iommu_group *group);
@@ -321,9 +330,9 @@ static inline void *eeh_dev_init(struct device_node *dn, void *data)
 
 static inline void eeh_dev_phb_init_dynamic(struct pci_controller *phb) { }
 
-static inline unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
+static inline int eeh_check_failure(const volatile void __iomem *token)
 {
-       return val;
+       return 0;
 }
 
 #define eeh_dev_check_failure(x) (0)
@@ -354,7 +363,7 @@ static inline u8 eeh_readb(const volatile void __iomem *addr)
 {
        u8 val = in_8(addr);
        if (EEH_POSSIBLE_ERROR(val, u8))
-               return eeh_check_failure(addr, val);
+               eeh_check_failure(addr);
        return val;
 }
 
@@ -362,7 +371,7 @@ static inline u16 eeh_readw(const volatile void __iomem *addr)
 {
        u16 val = in_le16(addr);
        if (EEH_POSSIBLE_ERROR(val, u16))
-               return eeh_check_failure(addr, val);
+               eeh_check_failure(addr);
        return val;
 }
 
@@ -370,7 +379,7 @@ static inline u32 eeh_readl(const volatile void __iomem *addr)
 {
        u32 val = in_le32(addr);
        if (EEH_POSSIBLE_ERROR(val, u32))
-               return eeh_check_failure(addr, val);
+               eeh_check_failure(addr);
        return val;
 }
 
@@ -378,7 +387,7 @@ static inline u64 eeh_readq(const volatile void __iomem *addr)
 {
        u64 val = in_le64(addr);
        if (EEH_POSSIBLE_ERROR(val, u64))
-               return eeh_check_failure(addr, val);
+               eeh_check_failure(addr);
        return val;
 }
 
@@ -386,7 +395,7 @@ static inline u16 eeh_readw_be(const volatile void __iomem *addr)
 {
        u16 val = in_be16(addr);
        if (EEH_POSSIBLE_ERROR(val, u16))
-               return eeh_check_failure(addr, val);
+               eeh_check_failure(addr);
        return val;
 }
 
@@ -394,7 +403,7 @@ static inline u32 eeh_readl_be(const volatile void __iomem *addr)
 {
        u32 val = in_be32(addr);
        if (EEH_POSSIBLE_ERROR(val, u32))
-               return eeh_check_failure(addr, val);
+               eeh_check_failure(addr);
        return val;
 }
 
@@ -402,7 +411,7 @@ static inline u64 eeh_readq_be(const volatile void __iomem *addr)
 {
        u64 val = in_be64(addr);
        if (EEH_POSSIBLE_ERROR(val, u64))
-               return eeh_check_failure(addr, val);
+               eeh_check_failure(addr);
        return val;
 }
 
@@ -416,7 +425,7 @@ static inline void eeh_memcpy_fromio(void *dest, const
         * were copied. Check all four bytes.
         */
        if (n >= 4 && EEH_POSSIBLE_ERROR(*((u32 *)(dest + n - 4)), u32))
-               eeh_check_failure(src, *((u32 *)(dest + n - 4)));
+               eeh_check_failure(src);
 }
 
 /* in-string eeh macros */
@@ -425,7 +434,7 @@ static inline void eeh_readsb(const volatile void __iomem *addr, void * buf,
 {
        _insb(addr, buf, ns);
        if (EEH_POSSIBLE_ERROR((*(((u8*)buf)+ns-1)), u8))
-               eeh_check_failure(addr, *(u8*)buf);
+               eeh_check_failure(addr);
 }
 
 static inline void eeh_readsw(const volatile void __iomem *addr, void * buf,
@@ -433,7 +442,7 @@ static inline void eeh_readsw(const volatile void __iomem *addr, void * buf,
 {
        _insw(addr, buf, ns);
        if (EEH_POSSIBLE_ERROR((*(((u16*)buf)+ns-1)), u16))
-               eeh_check_failure(addr, *(u16*)buf);
+               eeh_check_failure(addr);
 }
 
 static inline void eeh_readsl(const volatile void __iomem *addr, void * buf,
@@ -441,7 +450,7 @@ static inline void eeh_readsl(const volatile void __iomem *addr, void * buf,
 {
        _insl(addr, buf, nl);
        if (EEH_POSSIBLE_ERROR((*(((u32*)buf)+nl-1)), u32))
-               eeh_check_failure(addr, *(u32*)buf);
+               eeh_check_failure(addr);
 }
 
 #endif /* CONFIG_PPC64 */
index 5b0c98bd46abb346c2a6966265ff767651f3de4f..1cb39c96d1558fcded9b86d7aa5e3a5512f929cc 100644 (file)
@@ -95,7 +95,6 @@ extern volatile struct Hydra __iomem *Hydra;
 #define HYDRA_INT_SPARE                19
 
 extern int hydra_init(void);
-extern void macio_adb_init(void);
 
 #endif /* __KERNEL__ */
 
index 41f13cec8a8fcd01bbd2e02f3cfc50083b3addba..e8e3a0a04eb079b179d3a67c47641a0ad95b07da 100644 (file)
@@ -31,11 +31,6 @@ extern atomic_t ppc_n_lost_interrupts;
 
 extern irq_hw_number_t virq_to_hw(unsigned int virq);
 
-/**
- * irq_early_init - Init irq remapping subsystem
- */
-extern void irq_early_init(void);
-
 static __inline__ int irq_canonicalize(int irq)
 {
        return irq;
index 16d7e33d35e970c742585f9c19fb0fc62b61f0ba..19c36cba37c4acac5e63e7c4d8a093b4dbe65781 100644 (file)
@@ -81,7 +81,6 @@ extern void default_machine_crash_shutdown(struct pt_regs *regs);
 extern int crash_shutdown_register(crash_shutdown_t handler);
 extern int crash_shutdown_unregister(crash_shutdown_t handler);
 
-extern void machine_kexec_simple(struct kimage *image);
 extern void crash_kexec_secondary(struct pt_regs *regs);
 extern int overlaps_crashkernel(unsigned long start, unsigned long size);
 extern void reserve_crashkernel(void);
index b125ceab149c0a8e428263093334d752ead4bdfa..307347f8ddbdc808f49ca4cbc4825a5fbe8c5c99 100644 (file)
@@ -136,8 +136,6 @@ struct machdep_calls {
        int             (*pci_setup_phb)(struct pci_controller *host);
 
 #ifdef CONFIG_PCI_MSI
-       int             (*msi_check_device)(struct pci_dev* dev,
-                                           int nvec, int type);
        int             (*setup_msi_irqs)(struct pci_dev *dev,
                                          int nvec, int type);
        void            (*teardown_msi_irqs)(struct pci_dev *dev);
@@ -330,8 +328,6 @@ extern struct machdep_calls *machine_id;
 
 extern void probe_machine(void);
 
-extern char cmd_line[COMMAND_LINE_SIZE];
-
 #ifdef CONFIG_PPC_PMAC
 /*
  * Power macintoshes have either a CUDA, PMU or SMU controlling
index d76514487d6f8f19c5409941dafa970b21426118..aeebc94b2bced66d4d5762077629362ece591103 100644 (file)
@@ -190,6 +190,13 @@ static inline unsigned int mmu_psize_to_shift(unsigned int mmu_psize)
 
 #ifndef __ASSEMBLY__
 
+static inline int slb_vsid_shift(int ssize)
+{
+       if (ssize == MMU_SEGSIZE_256M)
+               return SLB_VSID_SHIFT;
+       return SLB_VSID_SHIFT_1T;
+}
+
 static inline int segment_shift(int ssize)
 {
        if (ssize == MMU_SEGSIZE_256M)
@@ -317,6 +324,7 @@ extern int __hash_page_64K(unsigned long ea, unsigned long access,
                           unsigned int local, int ssize);
 struct mm_struct;
 unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap);
+extern int hash_page_mm(struct mm_struct *mm, unsigned long ea, unsigned long access, unsigned long trap);
 extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap);
 int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
                     pte_t *ptep, unsigned long trap, int local, int ssize,
@@ -342,6 +350,8 @@ extern void hash_failure_debug(unsigned long ea, unsigned long access,
 extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend,
                             unsigned long pstart, unsigned long prot,
                             int psize, int ssize);
+int htab_remove_mapping(unsigned long vstart, unsigned long vend,
+                       int psize, int ssize);
 extern void add_gpage(u64 addr, u64 page_size, unsigned long number_of_pages);
 extern void demote_segment_4k(struct mm_struct *mm, unsigned long addr);
 
index 86055e598269ebc9c4ba2c52ea3b6f59a27599e2..9124b0ede1fc7f531a97d369169706024741ce17 100644 (file)
@@ -135,6 +135,7 @@ struct opal_sg_list {
 #define OPAL_FLASH_MANAGE                      77
 #define OPAL_FLASH_UPDATE                      78
 #define OPAL_RESYNC_TIMEBASE                   79
+#define OPAL_CHECK_TOKEN                       80
 #define OPAL_DUMP_INIT                         81
 #define OPAL_DUMP_INFO                         82
 #define OPAL_DUMP_READ                         83
@@ -146,7 +147,9 @@ struct opal_sg_list {
 #define OPAL_GET_PARAM                         89
 #define OPAL_SET_PARAM                         90
 #define OPAL_DUMP_RESEND                       91
+#define OPAL_PCI_SET_PHB_CXL_MODE              93
 #define OPAL_DUMP_INFO2                                94
+#define OPAL_PCI_ERR_INJECT                    96
 #define OPAL_PCI_EEH_FREEZE_SET                        97
 #define OPAL_HANDLE_HMI                                98
 #define OPAL_REGISTER_DUMP_REGION              101
@@ -199,6 +202,35 @@ enum OpalPciErrorSeverity {
        OPAL_EEH_SEV_INF        = 5
 };
 
+enum OpalErrinjectType {
+       OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR        = 0,
+       OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64      = 1,
+};
+
+enum OpalErrinjectFunc {
+       /* IOA bus specific errors */
+       OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR    = 0,
+       OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_DATA    = 1,
+       OPAL_ERR_INJECT_FUNC_IOA_LD_IO_ADDR     = 2,
+       OPAL_ERR_INJECT_FUNC_IOA_LD_IO_DATA     = 3,
+       OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_ADDR    = 4,
+       OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_DATA    = 5,
+       OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_ADDR    = 6,
+       OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_DATA    = 7,
+       OPAL_ERR_INJECT_FUNC_IOA_ST_IO_ADDR     = 8,
+       OPAL_ERR_INJECT_FUNC_IOA_ST_IO_DATA     = 9,
+       OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_ADDR    = 10,
+       OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_DATA    = 11,
+       OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_ADDR    = 12,
+       OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_DATA    = 13,
+       OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_MASTER  = 14,
+       OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_TARGET  = 15,
+       OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_ADDR    = 16,
+       OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_DATA    = 17,
+       OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_MASTER  = 18,
+       OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET  = 19,
+};
+
 enum OpalShpcAction {
        OPAL_SHPC_GET_LINK_STATE = 0,
        OPAL_SHPC_GET_SLOT_STATE = 1
@@ -356,9 +388,12 @@ enum OpalM64EnableAction {
 };
 
 enum OpalPciResetScope {
-       OPAL_PHB_COMPLETE = 1, OPAL_PCI_LINK = 2, OPAL_PHB_ERROR = 3,
-       OPAL_PCI_HOT_RESET = 4, OPAL_PCI_FUNDAMENTAL_RESET = 5,
-       OPAL_PCI_IODA_TABLE_RESET = 6,
+       OPAL_RESET_PHB_COMPLETE         = 1,
+       OPAL_RESET_PCI_LINK             = 2,
+       OPAL_RESET_PHB_ERROR            = 3,
+       OPAL_RESET_PCI_HOT              = 4,
+       OPAL_RESET_PCI_FUNDAMENTAL      = 5,
+       OPAL_RESET_PCI_IODA_TABLE       = 6
 };
 
 enum OpalPciReinitScope {
@@ -819,6 +854,8 @@ int64_t opal_pci_eeh_freeze_clear(uint64_t phb_id, uint64_t pe_number,
                                  uint64_t eeh_action_token);
 int64_t opal_pci_eeh_freeze_set(uint64_t phb_id, uint64_t pe_number,
                                uint64_t eeh_action_token);
+int64_t opal_pci_err_inject(uint64_t phb_id, uint32_t pe_no, uint32_t type,
+                           uint32_t func, uint64_t addr, uint64_t mask);
 int64_t opal_pci_shpc(uint64_t phb_id, uint64_t shpc_action, uint8_t *state);
 
 
@@ -887,6 +924,7 @@ int64_t opal_pci_next_error(uint64_t phb_id, __be64 *first_frozen_pe,
                            __be16 *pci_error_type, __be16 *severity);
 int64_t opal_pci_poll(uint64_t phb_id);
 int64_t opal_return_cpu(void);
+int64_t opal_check_token(uint64_t token);
 int64_t opal_reinit_cpus(uint64_t flags);
 
 int64_t opal_xscom_read(uint32_t gcid, uint64_t pcb_addr, __be64 *val);
@@ -924,6 +962,7 @@ int64_t opal_sensor_read(uint32_t sensor_hndl, int token, __be32 *sensor_data);
 int64_t opal_handle_hmi(void);
 int64_t opal_register_dump_region(uint32_t id, uint64_t start, uint64_t end);
 int64_t opal_unregister_dump_region(uint32_t id);
+int64_t opal_pci_set_phb_cxl_mode(uint64_t phb_id, uint64_t mode, uint64_t pe_number);
 
 /* Internal functions */
 extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
index 88693cef4f3d60545f17881000451f71319477e6..d908a46d05c0b1be8bbb5b35a90425465fd5d3aa 100644 (file)
 
 typedef unsigned long pte_basic_t;
 
-static __inline__ void clear_page(void *addr)
+static inline void clear_page(void *addr)
 {
-       unsigned long lines, line_size;
-
-       line_size = ppc64_caches.dline_size;
-       lines = ppc64_caches.dlines_per_page;
-
-       __asm__ __volatile__(
+       unsigned long iterations;
+       unsigned long onex, twox, fourx, eightx;
+
+       iterations = ppc64_caches.dlines_per_page / 8;
+
+       /*
+        * Some verisions of gcc use multiply instructions to
+        * calculate the offsets so lets give it a hand to
+        * do better.
+        */
+       onex = ppc64_caches.dline_size;
+       twox = onex << 1;
+       fourx = onex << 2;
+       eightx = onex << 3;
+
+       asm volatile(
        "mtctr  %1      # clear_page\n\
-1:      dcbz   0,%0\n\
-       add     %0,%0,%3\n\
+       .balign 16\n\
+1:     dcbz    0,%0\n\
+       dcbz    %3,%0\n\
+       dcbz    %4,%0\n\
+       dcbz    %5,%0\n\
+       dcbz    %6,%0\n\
+       dcbz    %7,%0\n\
+       dcbz    %8,%0\n\
+       dcbz    %9,%0\n\
+       add     %0,%0,%10\n\
        bdnz+   1b"
-        : "=r" (addr)
-        : "r" (lines), "0" (addr), "r" (line_size)
+       : "=&r" (addr)
+       : "r" (iterations), "0" (addr), "b" (onex), "b" (twox),
+               "b" (twox+onex), "b" (fourx), "b" (fourx+onex),
+               "b" (twox+fourx), "b" (eightx-onex), "r" (eightx)
        : "ctr", "memory");
 }
 
@@ -104,7 +124,6 @@ extern unsigned long slice_get_unmapped_area(unsigned long addr,
 extern unsigned int get_slice_psize(struct mm_struct *mm,
                                    unsigned long addr);
 
-extern void slice_init_context(struct mm_struct *mm, unsigned int psize);
 extern void slice_set_user_psize(struct mm_struct *mm, unsigned int psize);
 extern void slice_set_range_psize(struct mm_struct *mm, unsigned long start,
                                  unsigned long len, unsigned int psize);
index 47edde8c3556221b7371b225f32e1065b4b2fdb8..945e47adf7db633ae13fc5003e948f19e38fbb08 100644 (file)
@@ -8,8 +8,6 @@
 #include <linux/threads.h>
 #include <asm/io.h>                    /* For sub-arch specific PPC_PIN_SIZE */
 
-extern unsigned long va_to_phys(unsigned long address);
-extern pte_t *va_to_pte(unsigned long address);
 extern unsigned long ioremap_bot;
 
 #ifdef CONFIG_44x
@@ -50,10 +48,10 @@ extern int icache_44x_need_flush;
 #define FIRST_USER_ADDRESS     0
 
 #define pte_ERROR(e) \
-       printk("%s:%d: bad pte %llx.\n", __FILE__, __LINE__, \
+       pr_err("%s:%d: bad pte %llx.\n", __FILE__, __LINE__, \
                (unsigned long long)pte_val(e))
 #define pgd_ERROR(e) \
-       printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
+       pr_err("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
 
 /*
  * This is the bottom of the PKMAP area with HIGHMEM or an arbitrary
index 12798c9d4b4ba5401c77c41bc605e722a11b6266..7b935683f2684687813f470765091bcec064b219 100644 (file)
@@ -64,7 +64,7 @@
     (((addr) >> PUD_SHIFT) & (PTRS_PER_PUD - 1)))
 
 #define pud_ERROR(e) \
-       printk("%s:%d: bad pud %08lx.\n", __FILE__, __LINE__, pud_val(e))
+       pr_err("%s:%d: bad pud %08lx.\n", __FILE__, __LINE__, pud_val(e))
 
 /*
  * On all 4K setups, remap_4k_pfn() equates to remap_pfn_range() */
index 7b3d54fae46f92a80dcaddf301a2ea2687beafba..ae153c40ab7c7e9a552731e3b5673c1cd0efdd75 100644 (file)
@@ -328,11 +328,11 @@ static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry)
 #define pte_same(A,B)  (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HPTEFLAGS) == 0)
 
 #define pte_ERROR(e) \
-       printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
+       pr_err("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
 #define pmd_ERROR(e) \
-       printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
+       pr_err("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
 #define pgd_ERROR(e) \
-       printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
+       pr_err("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
 
 /* Encode and de-code a swap entry */
 #define __swp_type(entry)      (((entry).val >> 1) & 0x3f)
index d98c1ecc32665444c2b046cf57952f45bdfea9fa..316f9a5da173f1653fe03a4a8cbc43749f85516a 100644 (file)
@@ -4,6 +4,7 @@
 
 #ifndef __ASSEMBLY__
 #include <linux/mmdebug.h>
+#include <linux/mmzone.h>
 #include <asm/processor.h>             /* For TASK_SIZE */
 #include <asm/mmu.h>
 #include <asm/page.h>
@@ -38,10 +39,9 @@ static inline int pte_none(pte_t pte)                { return (pte_val(pte) & ~_PTE_NONE_MASK)
 static inline pgprot_t pte_pgprot(pte_t pte)   { return __pgprot(pte_val(pte) & PAGE_PROT_BITS); }
 
 #ifdef CONFIG_NUMA_BALANCING
-
 static inline int pte_present(pte_t pte)
 {
-       return pte_val(pte) & (_PAGE_PRESENT | _PAGE_NUMA);
+       return pte_val(pte) & _PAGE_NUMA_MASK;
 }
 
 #define pte_present_nonuma pte_present_nonuma
@@ -50,37 +50,6 @@ static inline int pte_present_nonuma(pte_t pte)
        return pte_val(pte) & (_PAGE_PRESENT);
 }
 
-#define pte_numa pte_numa
-static inline int pte_numa(pte_t pte)
-{
-       return (pte_val(pte) &
-               (_PAGE_NUMA|_PAGE_PRESENT)) == _PAGE_NUMA;
-}
-
-#define pte_mknonnuma pte_mknonnuma
-static inline pte_t pte_mknonnuma(pte_t pte)
-{
-       pte_val(pte) &= ~_PAGE_NUMA;
-       pte_val(pte) |=  _PAGE_PRESENT | _PAGE_ACCESSED;
-       return pte;
-}
-
-#define pte_mknuma pte_mknuma
-static inline pte_t pte_mknuma(pte_t pte)
-{
-       /*
-        * We should not set _PAGE_NUMA on non present ptes. Also clear the
-        * present bit so that hash_page will return 1 and we collect this
-        * as numa fault.
-        */
-       if (pte_present(pte)) {
-               pte_val(pte) |= _PAGE_NUMA;
-               pte_val(pte) &= ~_PAGE_PRESENT;
-       } else
-               VM_BUG_ON(1);
-       return pte;
-}
-
 #define ptep_set_numa ptep_set_numa
 static inline void ptep_set_numa(struct mm_struct *mm, unsigned long addr,
                                 pte_t *ptep)
@@ -92,12 +61,6 @@ static inline void ptep_set_numa(struct mm_struct *mm, unsigned long addr,
        return;
 }
 
-#define pmd_numa pmd_numa
-static inline int pmd_numa(pmd_t pmd)
-{
-       return pte_numa(pmd_pte(pmd));
-}
-
 #define pmdp_set_numa pmdp_set_numa
 static inline void pmdp_set_numa(struct mm_struct *mm, unsigned long addr,
                                 pmd_t *pmdp)
@@ -109,16 +72,21 @@ static inline void pmdp_set_numa(struct mm_struct *mm, unsigned long addr,
        return;
 }
 
-#define pmd_mknonnuma pmd_mknonnuma
-static inline pmd_t pmd_mknonnuma(pmd_t pmd)
+/*
+ * Generic NUMA pte helpers expect pteval_t and pmdval_t types to exist
+ * which was inherited from x86. For the purposes of powerpc pte_basic_t and
+ * pmd_t are equivalent
+ */
+#define pteval_t pte_basic_t
+#define pmdval_t pmd_t
+static inline pteval_t ptenuma_flags(pte_t pte)
 {
-       return pte_pmd(pte_mknonnuma(pmd_pte(pmd)));
+       return pte_val(pte) & _PAGE_NUMA_MASK;
 }
 
-#define pmd_mknuma pmd_mknuma
-static inline pmd_t pmd_mknuma(pmd_t pmd)
+static inline pmdval_t pmdnuma_flags(pmd_t pmd)
 {
-       return pte_pmd(pte_mknuma(pmd_pte(pmd)));
+       return pmd_val(pmd) & _PAGE_NUMA_MASK;
 }
 
 # else
@@ -281,6 +249,8 @@ extern unsigned long empty_zero_page[];
 
 extern pgd_t swapper_pg_dir[];
 
+void limit_zone_pfn(enum zone_type zone, unsigned long max_pfn);
+int dma_pfn_limit_to_zone(u64 pfn_limit);
 extern void paging_init(void);
 
 /*
index 12c32c5f533d924428714301f7482493df00e3c8..67859edbf8fd39aba27c8f8b7503533d6d9a3271 100644 (file)
@@ -273,7 +273,7 @@ static inline long plpar_set_mode(unsigned long mflags, unsigned long resource,
 static inline long enable_reloc_on_exceptions(void)
 {
        /* mflags = 3: Exceptions at 0xC000000000004000 */
-       return plpar_set_mode(3, 3, 0, 0);
+       return plpar_set_mode(3, H_SET_MODE_RESOURCE_ADDR_TRANS_MODE, 0, 0);
 }
 
 /*
@@ -284,7 +284,7 @@ static inline long enable_reloc_on_exceptions(void)
  * returns H_SUCCESS.
  */
 static inline long disable_reloc_on_exceptions(void) {
-       return plpar_set_mode(0, 3, 0, 0);
+       return plpar_set_mode(0, H_SET_MODE_RESOURCE_ADDR_TRANS_MODE, 0, 0);
 }
 
 /*
@@ -297,7 +297,7 @@ static inline long disable_reloc_on_exceptions(void) {
 static inline long enable_big_endian_exceptions(void)
 {
        /* mflags = 0: big endian exceptions */
-       return plpar_set_mode(0, 4, 0, 0);
+       return plpar_set_mode(0, H_SET_MODE_RESOURCE_LE, 0, 0);
 }
 
 /*
@@ -310,17 +310,17 @@ static inline long enable_big_endian_exceptions(void)
 static inline long enable_little_endian_exceptions(void)
 {
        /* mflags = 1: little endian exceptions */
-       return plpar_set_mode(1, 4, 0, 0);
+       return plpar_set_mode(1, H_SET_MODE_RESOURCE_LE, 0, 0);
 }
 
 static inline long plapr_set_ciabr(unsigned long ciabr)
 {
-       return plpar_set_mode(0, 1, ciabr, 0);
+       return plpar_set_mode(0, H_SET_MODE_RESOURCE_SET_CIABR, ciabr, 0);
 }
 
 static inline long plapr_set_watchpoint0(unsigned long dawr0, unsigned long dawrx0)
 {
-       return plpar_set_mode(0, 2, dawr0, dawrx0);
+       return plpar_set_mode(0, H_SET_MODE_RESOURCE_SET_DAWR, dawr0, dawrx0);
 }
 
 #endif /* _ASM_POWERPC_PLPAR_WRAPPERS_H */
diff --git a/arch/powerpc/include/asm/pnv-pci.h b/arch/powerpc/include/asm/pnv-pci.h
new file mode 100644 (file)
index 0000000..f09a22f
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#ifndef _ASM_PNV_PCI_H
+#define _ASM_PNV_PCI_H
+
+#include <linux/pci.h>
+#include <misc/cxl.h>
+
+int pnv_phb_to_cxl(struct pci_dev *dev);
+int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq,
+                          unsigned int virq);
+int pnv_cxl_alloc_hwirqs(struct pci_dev *dev, int num);
+void pnv_cxl_release_hwirqs(struct pci_dev *dev, int hwirq, int num);
+int pnv_cxl_get_irq_count(struct pci_dev *dev);
+struct device_node *pnv_pci_to_phb_node(struct pci_dev *dev);
+
+#ifdef CONFIG_CXL_BASE
+int pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs,
+                              struct pci_dev *dev, int num);
+void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs,
+                                 struct pci_dev *dev);
+#endif
+
+#endif
index 74b79f07f0412bbebd160cc4b14ab8d08d583278..7f436ba1b56f5d2246fed43825c41a2581fb7662 100644 (file)
@@ -76,8 +76,6 @@ void of_parse_dma_window(struct device_node *dn, const __be32 *dma_window,
                         unsigned long *busno, unsigned long *phys,
                         unsigned long *size);
 
-extern void kdump_move_device_tree(void);
-
 extern void of_instantiate_rtc(void);
 
 extern int of_get_ibm_chip_id(struct device_node *np);
index 8d1569c290428e82db7d03cec9bca66c66dcf35b..e040c3595129e41f502ba873502a1571b0076b9e 100644 (file)
@@ -98,6 +98,11 @@ extern unsigned long bad_call_to_PMD_PAGE_SIZE(void);
                         _PAGE_USER | _PAGE_ACCESSED | \
                         _PAGE_RW | _PAGE_HWWRITE | _PAGE_DIRTY | _PAGE_EXEC)
 
+#ifdef CONFIG_NUMA_BALANCING
+/* Mask of bits that distinguish present and numa ptes */
+#define _PAGE_NUMA_MASK (_PAGE_NUMA|_PAGE_PRESENT)
+#endif
+
 /*
  * We define 2 sets of base prot bits, one for basic pages (ie,
  * cacheable kernel and user pages) and one for non cacheable
index 0c0505956a296bd83a185042a04e4e096880a04c..fe3f9488f321e5ec20448ac92c6e6a3e16d4fc77 100644 (file)
  * 32-bit 8xx:
  *     - SPRG0 scratch for exception vectors
  *     - SPRG1 scratch for exception vectors
- *     - SPRG2 apparently unused but initialized
+ *     - SPRG2 scratch for exception vectors
  *
  */
 #ifdef CONFIG_PPC64
 #ifdef CONFIG_8xx
 #define SPRN_SPRG_SCRATCH0     SPRN_SPRG0
 #define SPRN_SPRG_SCRATCH1     SPRN_SPRG1
+#define SPRN_SPRG_SCRATCH2     SPRN_SPRG2
 #endif
 
 
index b1d2deceeedbc1f24030a302e2f190ff8929084b..ec800f28fec5249e2478f718260d05ca87176c12 100644 (file)
@@ -13,7 +13,6 @@
 #ifndef ASM_PPC_RIO_H
 #define ASM_PPC_RIO_H
 
-extern void platform_rio_init(void);
 #ifdef CONFIG_FSL_RIO
 extern int fsl_rio_mcheck_exception(struct pt_regs *);
 #else
index 37b7ca39ec9f1b5ec2d3e574a8806de34ccff555..a6e6e2bf9d15a5be729d1190c511c1b8eb503393 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/workqueue.h>
 #include <linux/device.h>
 #include <linux/mutex.h>
+#include <asm/reg.h>
+#include <asm/copro.h>
 
 #define LS_SIZE (256 * 1024)
 #define LS_ADDR_MASK (LS_SIZE - 1)
@@ -277,9 +279,6 @@ void spu_remove_dev_attr(struct device_attribute *attr);
 int spu_add_dev_attr_group(struct attribute_group *attrs);
 void spu_remove_dev_attr_group(struct attribute_group *attrs);
 
-int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
-               unsigned long dsisr, unsigned *flt);
-
 /*
  * Notifier blocks:
  *
index f593b0f9b6278ee5625173931bb19eb72fb3d894..d3a42cc45a8286023b47f98403229e02f8e1f238 100644 (file)
@@ -25,3 +25,65 @@ struct pt_regs;
 
 /* Emulate instructions that cause a transfer of control. */
 extern int emulate_step(struct pt_regs *regs, unsigned int instr);
+
+enum instruction_type {
+       COMPUTE,                /* arith/logical/CR op, etc. */
+       LOAD,
+       LOAD_MULTI,
+       LOAD_FP,
+       LOAD_VMX,
+       LOAD_VSX,
+       STORE,
+       STORE_MULTI,
+       STORE_FP,
+       STORE_VMX,
+       STORE_VSX,
+       LARX,
+       STCX,
+       BRANCH,
+       MFSPR,
+       MTSPR,
+       CACHEOP,
+       BARRIER,
+       SYSCALL,
+       MFMSR,
+       MTMSR,
+       RFI,
+       INTERRUPT,
+       UNKNOWN
+};
+
+#define INSTR_TYPE_MASK        0x1f
+
+/* Load/store flags, ORed in with type */
+#define SIGNEXT                0x20
+#define UPDATE         0x40    /* matches bit in opcode 31 instructions */
+#define BYTEREV                0x80
+
+/* Cacheop values, ORed in with type */
+#define CACHEOP_MASK   0x700
+#define DCBST          0
+#define DCBF           0x100
+#define DCBTST         0x200
+#define DCBT           0x300
+#define ICBI           0x400
+
+/* Size field in type word */
+#define SIZE(n)                ((n) << 8)
+#define GETSIZE(w)     ((w) >> 8)
+
+#define MKOP(t, f, s)  ((t) | (f) | SIZE(s))
+
+struct instruction_op {
+       int type;
+       int reg;
+       unsigned long val;
+       /* For LOAD/STORE/LARX/STCX */
+       unsigned long ea;
+       int update_reg;
+       /* For MFSPR */
+       int spr;
+};
+
+extern int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
+                        unsigned int instr);
index f8b60793b7a908f508904619ad2a8a023f6e82a1..d531d9e173ef8c2e86bfa0a5f352264f75062a5c 100644 (file)
 extern u32 tsi108_pci_cfg_base;
 /* Exported functions */
 
-extern int tsi108_bridge_init(struct pci_controller *hose, uint phys_csr_base);
-extern unsigned long tsi108_get_mem_size(void);
-extern unsigned long tsi108_get_cpu_clk(void);
-extern unsigned long tsi108_get_sdc_clk(void);
 extern int tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfn,
                                      int offset, int len, u32 val);
 extern int tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn,
index b51fba10e733d453ac29e0c535ff419d480f206d..78f2675f2aacb3e4f08258270e604af40103a62c 100644 (file)
@@ -52,7 +52,6 @@ extern void __init udbg_init_44x_as1(void);
 extern void __init udbg_init_40x_realmode(void);
 extern void __init udbg_init_cpm(void);
 extern void __init udbg_init_usbgecko(void);
-extern void __init udbg_init_wsp(void);
 extern void __init udbg_init_memcons(void);
 extern void __init udbg_init_ehv_bc(void);
 extern void __init udbg_init_ps3gelic(void);
index 9a5c928bb3c64ea8cb021da323c46b1d59dc6446..5b3a903adae6d761effa550a802ed5d6a2aeb656 100644 (file)
@@ -42,32 +42,65 @@ static inline bool has_zero(unsigned long val, unsigned long *data, const struct
 
 #else
 
+#ifdef CONFIG_64BIT
+
+/* unused */
 struct word_at_a_time {
-       const unsigned long one_bits, high_bits;
 };
 
-#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) }
+#define WORD_AT_A_TIME_CONSTANTS { }
 
-#ifdef CONFIG_64BIT
+/* This will give us 0xff for a NULL char and 0x00 elsewhere */
+static inline unsigned long has_zero(unsigned long a, unsigned long *bits, const struct word_at_a_time *c)
+{
+       unsigned long ret;
+       unsigned long zero = 0;
 
-/* Alan Modra's little-endian strlen tail for 64-bit */
-#define create_zero_mask(mask) (mask)
+       asm("cmpb %0,%1,%2" : "=r" (ret) : "r" (a), "r" (zero));
+       *bits = ret;
 
-static inline unsigned long find_zero(unsigned long mask)
+       return ret;
+}
+
+static inline unsigned long prep_zero_mask(unsigned long a, unsigned long bits, const struct word_at_a_time *c)
+{
+       return bits;
+}
+
+/* Alan Modra's little-endian strlen tail for 64-bit */
+static inline unsigned long create_zero_mask(unsigned long bits)
 {
        unsigned long leading_zero_bits;
        long trailing_zero_bit_mask;
 
-       asm ("addi %1,%2,-1\n\t"
-            "andc %1,%1,%2\n\t"
-            "popcntd %0,%1"
-            : "=r" (leading_zero_bits), "=&r" (trailing_zero_bit_mask)
-            : "r" (mask));
-       return leading_zero_bits >> 3;
+       asm("addi       %1,%2,-1\n\t"
+           "andc       %1,%1,%2\n\t"
+           "popcntd    %0,%1"
+               : "=r" (leading_zero_bits), "=&r" (trailing_zero_bit_mask)
+               : "r" (bits));
+
+       return leading_zero_bits;
+}
+
+static inline unsigned long find_zero(unsigned long mask)
+{
+       return mask >> 3;
+}
+
+/* This assumes that we never ask for an all 1s bitmask */
+static inline unsigned long zero_bytemask(unsigned long mask)
+{
+       return (1UL << mask) - 1;
 }
 
 #else  /* 32-bit case */
 
+struct word_at_a_time {
+       const unsigned long one_bits, high_bits;
+};
+
+#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) }
+
 /*
  * This is largely generic for little-endian machines, but the
  * optimal byte mask counting is probably going to be something
@@ -96,8 +129,6 @@ static inline unsigned long find_zero(unsigned long mask)
        return count_masked_bytes(mask);
 }
 
-#endif
-
 /* Return nonzero if it has a zero */
 static inline unsigned long has_zero(unsigned long a, unsigned long *bits, const struct word_at_a_time *c)
 {
@@ -114,6 +145,59 @@ static inline unsigned long prep_zero_mask(unsigned long a, unsigned long bits,
 /* The mask we created is directly usable as a bytemask */
 #define zero_bytemask(mask) (mask)
 
+#endif /* CONFIG_64BIT */
+
+#endif /* __BIG_ENDIAN__ */
+
+/*
+ * We use load_unaligned_zero() in a selftest, which builds a userspace
+ * program. Some linker scripts seem to discard the .fixup section, so allow
+ * the test code to use a different section name.
+ */
+#ifndef FIXUP_SECTION
+#define FIXUP_SECTION ".fixup"
+#endif
+
+static inline unsigned long load_unaligned_zeropad(const void *addr)
+{
+       unsigned long ret, offset, tmp;
+
+       asm(
+       "1:     " PPC_LL "%[ret], 0(%[addr])\n"
+       "2:\n"
+       ".section " FIXUP_SECTION ",\"ax\"\n"
+       "3:     "
+#ifdef __powerpc64__
+       "clrrdi         %[tmp], %[addr], 3\n\t"
+       "clrlsldi       %[offset], %[addr], 61, 3\n\t"
+       "ld             %[ret], 0(%[tmp])\n\t"
+#ifdef __BIG_ENDIAN__
+       "sld            %[ret], %[ret], %[offset]\n\t"
+#else
+       "srd            %[ret], %[ret], %[offset]\n\t"
 #endif
+#else
+       "clrrwi         %[tmp], %[addr], 2\n\t"
+       "clrlslwi       %[offset], %[addr], 30, 3\n\t"
+       "lwz            %[ret], 0(%[tmp])\n\t"
+#ifdef __BIG_ENDIAN__
+       "slw            %[ret], %[ret], %[offset]\n\t"
+#else
+       "srw            %[ret], %[ret], %[offset]\n\t"
+#endif
+#endif
+       "b      2b\n"
+       ".previous\n"
+       ".section __ex_table,\"a\"\n\t"
+               PPC_LONG_ALIGN "\n\t"
+               PPC_LONG "1b,3b\n"
+       ".previous"
+       : [tmp] "=&b" (tmp), [offset] "=&r" (offset), [ret] "=&r" (ret)
+       : [addr] "b" (addr), "m" (*(unsigned long *)addr));
+
+       return ret;
+}
+
+#undef FIXUP_SECTION
 
 #endif /* _ASM_WORD_AT_A_TIME_H */
index 282d43a0c85566927755dc71d6a2ce9b5bd40d4a..0d050ea37a0489a90eae1d844be6400e4eb9965d 100644 (file)
@@ -29,6 +29,7 @@
 /* Native ICP */
 #ifdef CONFIG_PPC_ICP_NATIVE
 extern int icp_native_init(void);
+extern void icp_native_flush_interrupt(void);
 #else
 static inline int icp_native_init(void) { return -ENODEV; }
 #endif
index 670c312d914e64508ddcf8599d639663504603f6..502cf69b6c89e30c75545ac34cc4092b4af80416 100644 (file)
@@ -93,6 +93,9 @@ obj-$(CONFIG_PPC32)           += entry_32.o setup_32.o
 obj-$(CONFIG_PPC64)            += dma-iommu.o iommu.o
 obj-$(CONFIG_KGDB)             += kgdb.o
 obj-$(CONFIG_MODULES)          += ppc_ksyms.o
+ifeq ($(CONFIG_PPC32),y)
+obj-$(CONFIG_MODULES)          += ppc_ksyms_32.o
+endif
 obj-$(CONFIG_BOOTX_TEXT)       += btext.o
 obj-$(CONFIG_SMP)              += smp.o
 obj-$(CONFIG_KPROBES)          += kprobes.o
index 7a13f378ca2c7b1c715b9719e01329f99bf2ef2e..c78e6dac4d7de2842c1d679691373eb53762057a 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/crash_dump.h>
 #include <linux/bootmem.h>
+#include <linux/io.h>
 #include <linux/memblock.h>
 #include <asm/code-patching.h>
 #include <asm/kdump.h>
index bd1a2aba599f59aaee4d31d9d971f5afb9d5bca4..735979764cd4ce6b9100051b88e947b02bc4f9b2 100644 (file)
@@ -106,10 +106,14 @@ int __init swiotlb_setup_bus_notifier(void)
        return 0;
 }
 
-void swiotlb_detect_4g(void)
+void __init swiotlb_detect_4g(void)
 {
-       if ((memblock_end_of_DRAM() - 1) > 0xffffffff)
+       if ((memblock_end_of_DRAM() - 1) > 0xffffffff) {
                ppc_swiotlb_enable = 1;
+#ifdef CONFIG_ZONE_DMA32
+               limit_zone_pfn(ZONE_DMA32, (1ULL << 32) >> PAGE_SHIFT);
+#endif
+       }
 }
 
 static int __init swiotlb_late_init(void)
index ee78f6e49d64bddc78d58382b73008841896b38a..adac9dc54aeed2c748ae62a7a3cd1ad9fad8eb2f 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/vio.h>
 #include <asm/bug.h>
 #include <asm/machdep.h>
+#include <asm/swiotlb.h>
 
 /*
  * Generic direct DMA implementation
  * default the offset is PCI_DRAM_OFFSET.
  */
 
+static u64 __maybe_unused get_pfn_limit(struct device *dev)
+{
+       u64 pfn = (dev->coherent_dma_mask >> PAGE_SHIFT) + 1;
+       struct dev_archdata __maybe_unused *sd = &dev->archdata;
+
+#ifdef CONFIG_SWIOTLB
+       if (sd->max_direct_dma_addr && sd->dma_ops == &swiotlb_dma_ops)
+               pfn = min_t(u64, pfn, sd->max_direct_dma_addr >> PAGE_SHIFT);
+#endif
+
+       return pfn;
+}
 
 void *dma_direct_alloc_coherent(struct device *dev, size_t size,
                                dma_addr_t *dma_handle, gfp_t flag,
@@ -40,6 +53,26 @@ void *dma_direct_alloc_coherent(struct device *dev, size_t size,
 #else
        struct page *page;
        int node = dev_to_node(dev);
+       u64 pfn = get_pfn_limit(dev);
+       int zone;
+
+       zone = dma_pfn_limit_to_zone(pfn);
+       if (zone < 0) {
+               dev_err(dev, "%s: No suitable zone for pfn %#llx\n",
+                       __func__, pfn);
+               return NULL;
+       }
+
+       switch (zone) {
+       case ZONE_DMA:
+               flag |= GFP_DMA;
+               break;
+#ifdef CONFIG_ZONE_DMA32
+       case ZONE_DMA32:
+               flag |= GFP_DMA32;
+               break;
+#endif
+       };
 
        /* ignore region specifiers */
        flag  &= ~(__GFP_HIGHMEM);
@@ -202,6 +235,7 @@ int __dma_set_mask(struct device *dev, u64 dma_mask)
        *dev->dma_mask = dma_mask;
        return 0;
 }
+
 int dma_set_mask(struct device *dev, u64 dma_mask)
 {
        if (ppc_md.dma_set_mask)
@@ -210,13 +244,10 @@ int dma_set_mask(struct device *dev, u64 dma_mask)
 }
 EXPORT_SYMBOL(dma_set_mask);
 
-u64 dma_get_required_mask(struct device *dev)
+u64 __dma_get_required_mask(struct device *dev)
 {
        struct dma_map_ops *dma_ops = get_dma_ops(dev);
 
-       if (ppc_md.dma_get_required_mask)
-               return ppc_md.dma_get_required_mask(dev);
-
        if (unlikely(dma_ops == NULL))
                return 0;
 
@@ -225,6 +256,14 @@ u64 dma_get_required_mask(struct device *dev)
 
        return DMA_BIT_MASK(8 * sizeof(dma_addr_t));
 }
+
+u64 dma_get_required_mask(struct device *dev)
+{
+       if (ppc_md.dma_get_required_mask)
+               return ppc_md.dma_get_required_mask(dev);
+
+       return __dma_get_required_mask(dev);
+}
 EXPORT_SYMBOL_GPL(dma_get_required_mask);
 
 static int __init dma_init(void)
index 59a64f8dc85f4cbd771e68b72ebaf8019429eeb3..d543e4179c18cc7737569c8a0e5ef5e8ebf48e14 100644 (file)
@@ -117,7 +117,7 @@ static DEFINE_MUTEX(eeh_dev_mutex);
  * not dynamically alloced, so that it ends up in RMO where RTAS
  * can access it.
  */
-#define EEH_PCI_REGS_LOG_LEN 4096
+#define EEH_PCI_REGS_LOG_LEN 8192
 static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
 
 /*
@@ -148,16 +148,12 @@ static int __init eeh_setup(char *str)
 }
 __setup("eeh=", eeh_setup);
 
-/**
- * eeh_gather_pci_data - Copy assorted PCI config space registers to buff
- * @edev: device to report data for
- * @buf: point to buffer in which to log
- * @len: amount of room in buffer
- *
- * This routine captures assorted PCI configuration space data,
- * and puts them into a buffer for RTAS error logging.
+/*
+ * This routine captures assorted PCI configuration space data
+ * for the indicated PCI device, and puts them into a buffer
+ * for RTAS error logging.
  */
-static size_t eeh_gather_pci_data(struct eeh_dev *edev, char *buf, size_t len)
+static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
 {
        struct device_node *dn = eeh_dev_to_of_node(edev);
        u32 cfg;
@@ -255,6 +251,19 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char *buf, size_t len)
        return n;
 }
 
+static void *eeh_dump_pe_log(void *data, void *flag)
+{
+       struct eeh_pe *pe = data;
+       struct eeh_dev *edev, *tmp;
+       size_t *plen = flag;
+
+       eeh_pe_for_each_dev(pe, edev, tmp)
+               *plen += eeh_dump_dev_log(edev, pci_regs_buf + *plen,
+                                         EEH_PCI_REGS_LOG_LEN - *plen);
+
+       return NULL;
+}
+
 /**
  * eeh_slot_error_detail - Generate combined log including driver log and error log
  * @pe: EEH PE
@@ -268,7 +277,6 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char *buf, size_t len)
 void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
 {
        size_t loglen = 0;
-       struct eeh_dev *edev, *tmp;
 
        /*
         * When the PHB is fenced or dead, it's pointless to collect
@@ -286,10 +294,7 @@ void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
                eeh_pe_restore_bars(pe);
 
                pci_regs_buf[0] = 0;
-               eeh_pe_for_each_dev(pe, edev, tmp) {
-                       loglen += eeh_gather_pci_data(edev, pci_regs_buf + loglen,
-                                                     EEH_PCI_REGS_LOG_LEN - loglen);
-               }
+               eeh_pe_traverse(pe, eeh_dump_pe_log, &loglen);
        }
 
        eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);
@@ -410,7 +415,7 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
        }
        dn = eeh_dev_to_of_node(edev);
        dev = eeh_dev_to_pci_dev(edev);
-       pe = edev->pe;
+       pe = eeh_dev_to_pe(edev);
 
        /* Access to IO BARs might get this far and still not want checking. */
        if (!pe) {
@@ -542,17 +547,16 @@ EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
 
 /**
  * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
- * @token: I/O token, should be address in the form 0xA....
- * @val: value, should be all 1's (XXX why do we need this arg??)
+ * @token: I/O address
  *
- * Check for an EEH failure at the given token address.  Call this
+ * Check for an EEH failure at the given I/O address. Call this
  * routine if the result of a read was all 0xff's and you want to
- * find out if this is due to an EEH slot freeze event.  This routine
+ * find out if this is due to an EEH slot freeze event. This routine
  * will query firmware for the EEH status.
  *
  * Note this routine is safe to call in an interrupt context.
  */
-unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
+int eeh_check_failure(const volatile void __iomem *token)
 {
        unsigned long addr;
        struct eeh_dev *edev;
@@ -562,13 +566,11 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
        edev = eeh_addr_cache_get_dev(addr);
        if (!edev) {
                eeh_stats.no_device++;
-               return val;
+               return 0;
        }
 
-       eeh_dev_check_failure(edev);
-       return val;
+       return eeh_dev_check_failure(edev);
 }
-
 EXPORT_SYMBOL(eeh_check_failure);
 
 
@@ -582,25 +584,51 @@ EXPORT_SYMBOL(eeh_check_failure);
  */
 int eeh_pci_enable(struct eeh_pe *pe, int function)
 {
-       int rc, flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
+       int active_flag, rc;
 
        /*
         * pHyp doesn't allow to enable IO or DMA on unfrozen PE.
         * Also, it's pointless to enable them on unfrozen PE. So
-        * we have the check here.
+        * we have to check before enabling IO or DMA.
         */
-       if (function == EEH_OPT_THAW_MMIO ||
-           function == EEH_OPT_THAW_DMA) {
+       switch (function) {
+       case EEH_OPT_THAW_MMIO:
+               active_flag = EEH_STATE_MMIO_ACTIVE;
+               break;
+       case EEH_OPT_THAW_DMA:
+               active_flag = EEH_STATE_DMA_ACTIVE;
+               break;
+       case EEH_OPT_DISABLE:
+       case EEH_OPT_ENABLE:
+       case EEH_OPT_FREEZE_PE:
+               active_flag = 0;
+               break;
+       default:
+               pr_warn("%s: Invalid function %d\n",
+                       __func__, function);
+               return -EINVAL;
+       }
+
+       /*
+        * Check if IO or DMA has been enabled before
+        * enabling them.
+        */
+       if (active_flag) {
                rc = eeh_ops->get_state(pe, NULL);
                if (rc < 0)
                        return rc;
 
-               /* Needn't to enable or already enabled */
-               if ((rc == EEH_STATE_NOT_SUPPORT) ||
-                   ((rc & flags) == flags))
+               /* Needn't enable it at all */
+               if (rc == EEH_STATE_NOT_SUPPORT)
+                       return 0;
+
+               /* It's already enabled */
+               if (rc & active_flag)
                        return 0;
        }
 
+
+       /* Issue the request */
        rc = eeh_ops->set_option(pe, function);
        if (rc)
                pr_warn("%s: Unexpected state change %d on "
@@ -608,17 +636,17 @@ int eeh_pci_enable(struct eeh_pe *pe, int function)
                        __func__, function, pe->phb->global_number,
                        pe->addr, rc);
 
-       rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
-       if (rc <= 0)
-               return rc;
+       /* Check if the request is finished successfully */
+       if (active_flag) {
+               rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
+               if (rc <= 0)
+                       return rc;
 
-       if ((function == EEH_OPT_THAW_MMIO) &&
-           (rc & EEH_STATE_MMIO_ENABLED))
-               return 0;
+               if (rc & active_flag)
+                       return 0;
 
-       if ((function == EEH_OPT_THAW_DMA) &&
-           (rc & EEH_STATE_DMA_ENABLED))
-               return 0;
+               return -EIO;
+       }
 
        return rc;
 }
@@ -634,7 +662,7 @@ int eeh_pci_enable(struct eeh_pe *pe, int function)
 int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
 {
        struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
-       struct eeh_pe *pe = edev->pe;
+       struct eeh_pe *pe = eeh_dev_to_pe(edev);
 
        if (!pe) {
                pr_err("%s: No PE found on PCI device %s\n",
@@ -645,14 +673,18 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
        switch (state) {
        case pcie_deassert_reset:
                eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
+               eeh_pe_state_clear(pe, EEH_PE_RESET);
                break;
        case pcie_hot_reset:
+               eeh_pe_state_mark(pe, EEH_PE_RESET);
                eeh_ops->reset(pe, EEH_RESET_HOT);
                break;
        case pcie_warm_reset:
+               eeh_pe_state_mark(pe, EEH_PE_RESET);
                eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
                break;
        default:
+               eeh_pe_state_clear(pe, EEH_PE_RESET);
                return -EINVAL;
        };
 
@@ -1141,6 +1173,85 @@ void eeh_remove_device(struct pci_dev *dev)
        edev->mode &= ~EEH_DEV_SYSFS;
 }
 
+int eeh_unfreeze_pe(struct eeh_pe *pe, bool sw_state)
+{
+       int ret;
+
+       ret = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
+       if (ret) {
+               pr_warn("%s: Failure %d enabling IO on PHB#%x-PE#%x\n",
+                       __func__, ret, pe->phb->global_number, pe->addr);
+               return ret;
+       }
+
+       ret = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
+       if (ret) {
+               pr_warn("%s: Failure %d enabling DMA on PHB#%x-PE#%x\n",
+                       __func__, ret, pe->phb->global_number, pe->addr);
+               return ret;
+       }
+
+       /* Clear software isolated state */
+       if (sw_state && (pe->state & EEH_PE_ISOLATED))
+               eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
+
+       return ret;
+}
+
+
+static struct pci_device_id eeh_reset_ids[] = {
+       { PCI_DEVICE(0x19a2, 0x0710) }, /* Emulex, BE     */
+       { PCI_DEVICE(0x10df, 0xe220) }, /* Emulex, Lancer */
+       { 0 }
+};
+
+static int eeh_pe_change_owner(struct eeh_pe *pe)
+{
+       struct eeh_dev *edev, *tmp;
+       struct pci_dev *pdev;
+       struct pci_device_id *id;
+       int flags, ret;
+
+       /* Check PE state */
+       flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
+       ret = eeh_ops->get_state(pe, NULL);
+       if (ret < 0 || ret == EEH_STATE_NOT_SUPPORT)
+               return 0;
+
+       /* Unfrozen PE, nothing to do */
+       if ((ret & flags) == flags)
+               return 0;
+
+       /* Frozen PE, check if it needs PE level reset */
+       eeh_pe_for_each_dev(pe, edev, tmp) {
+               pdev = eeh_dev_to_pci_dev(edev);
+               if (!pdev)
+                       continue;
+
+               for (id = &eeh_reset_ids[0]; id->vendor != 0; id++) {
+                       if (id->vendor != PCI_ANY_ID &&
+                           id->vendor != pdev->vendor)
+                               continue;
+                       if (id->device != PCI_ANY_ID &&
+                           id->device != pdev->device)
+                               continue;
+                       if (id->subvendor != PCI_ANY_ID &&
+                           id->subvendor != pdev->subsystem_vendor)
+                               continue;
+                       if (id->subdevice != PCI_ANY_ID &&
+                           id->subdevice != pdev->subsystem_device)
+                               continue;
+
+                       goto reset;
+               }
+       }
+
+       return eeh_unfreeze_pe(pe, true);
+
+reset:
+       return eeh_pe_reset_and_recover(pe);
+}
+
 /**
  * eeh_dev_open - Increase count of pass through devices for PE
  * @pdev: PCI device
@@ -1153,6 +1264,7 @@ void eeh_remove_device(struct pci_dev *dev)
 int eeh_dev_open(struct pci_dev *pdev)
 {
        struct eeh_dev *edev;
+       int ret = -ENODEV;
 
        mutex_lock(&eeh_dev_mutex);
 
@@ -1165,6 +1277,16 @@ int eeh_dev_open(struct pci_dev *pdev)
        if (!edev || !edev->pe)
                goto out;
 
+       /*
+        * The PE might have been put into frozen state, but we
+        * didn't detect that yet. The passed through PCI devices
+        * in frozen PE won't work properly. Clear the frozen state
+        * in advance.
+        */
+       ret = eeh_pe_change_owner(edev->pe);
+       if (ret)
+               goto out;
+
        /* Increase PE's pass through count */
        atomic_inc(&edev->pe->pass_dev_cnt);
        mutex_unlock(&eeh_dev_mutex);
@@ -1172,7 +1294,7 @@ int eeh_dev_open(struct pci_dev *pdev)
        return 0;
 out:
        mutex_unlock(&eeh_dev_mutex);
-       return -ENODEV;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(eeh_dev_open);
 
@@ -1202,6 +1324,7 @@ void eeh_dev_release(struct pci_dev *pdev)
        /* Decrease PE's pass through count */
        atomic_dec(&edev->pe->pass_dev_cnt);
        WARN_ON(atomic_read(&edev->pe->pass_dev_cnt) < 0);
+       eeh_pe_change_owner(edev->pe);
 out:
        mutex_unlock(&eeh_dev_mutex);
 }
@@ -1281,8 +1404,10 @@ int eeh_pe_set_option(struct eeh_pe *pe, int option)
         */
        switch (option) {
        case EEH_OPT_ENABLE:
-               if (eeh_enabled())
+               if (eeh_enabled()) {
+                       ret = eeh_pe_change_owner(pe);
                        break;
+               }
                ret = -EIO;
                break;
        case EEH_OPT_DISABLE:
@@ -1294,7 +1419,7 @@ int eeh_pe_set_option(struct eeh_pe *pe, int option)
                        break;
                }
 
-               ret = eeh_ops->set_option(pe, option);
+               ret = eeh_pci_enable(pe, option);
                break;
        default:
                pr_debug("%s: Option %d out of range (%d, %d)\n",
@@ -1345,6 +1470,36 @@ int eeh_pe_get_state(struct eeh_pe *pe)
 }
 EXPORT_SYMBOL_GPL(eeh_pe_get_state);
 
+static int eeh_pe_reenable_devices(struct eeh_pe *pe)
+{
+       struct eeh_dev *edev, *tmp;
+       struct pci_dev *pdev;
+       int ret = 0;
+
+       /* Restore config space */
+       eeh_pe_restore_bars(pe);
+
+       /*
+        * Reenable PCI devices as the devices passed
+        * through are always enabled before the reset.
+        */
+       eeh_pe_for_each_dev(pe, edev, tmp) {
+               pdev = eeh_dev_to_pci_dev(edev);
+               if (!pdev)
+                       continue;
+
+               ret = pci_reenable_device(pdev);
+               if (ret) {
+                       pr_warn("%s: Failure %d reenabling %s\n",
+                               __func__, ret, pci_name(pdev));
+                       return ret;
+               }
+       }
+
+       /* The PE is still in frozen state */
+       return eeh_unfreeze_pe(pe, true);
+}
+
 /**
  * eeh_pe_reset - Issue PE reset according to specified type
  * @pe: EEH PE
@@ -1368,23 +1523,22 @@ int eeh_pe_reset(struct eeh_pe *pe, int option)
        switch (option) {
        case EEH_RESET_DEACTIVATE:
                ret = eeh_ops->reset(pe, option);
+               eeh_pe_state_clear(pe, EEH_PE_RESET);
                if (ret)
                        break;
 
-               /*
-                * The PE is still in frozen state and we need to clear
-                * that. It's good to clear frozen state after deassert
-                * to avoid messy IO access during reset, which might
-                * cause recursive frozen PE.
-                */
-               ret = eeh_ops->set_option(pe, EEH_OPT_THAW_MMIO);
-               if (!ret)
-                       ret = eeh_ops->set_option(pe, EEH_OPT_THAW_DMA);
-               if (!ret)
-                       eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
+               ret = eeh_pe_reenable_devices(pe);
                break;
        case EEH_RESET_HOT:
        case EEH_RESET_FUNDAMENTAL:
+               /*
+                * Proactively freeze the PE to drop all MMIO access
+                * during reset, which should be banned as it's always
+                * cause recursive EEH error.
+                */
+               eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
+
+               eeh_pe_state_mark(pe, EEH_PE_RESET);
                ret = eeh_ops->reset(pe, option);
                break;
        default:
@@ -1413,9 +1567,6 @@ int eeh_pe_configure(struct eeh_pe *pe)
        if (!pe)
                return -ENODEV;
 
-       /* Restore config space for the affected devices */
-       eeh_pe_restore_bars(pe);
-
        return ret;
 }
 EXPORT_SYMBOL_GPL(eeh_pe_configure);
index 6a0dcee8e931f6584ac5066b8997e114dbc2cba9..3fd514f8e4b2a8e300b87171378185260dc901b3 100644 (file)
@@ -180,6 +180,22 @@ static bool eeh_dev_removed(struct eeh_dev *edev)
        return false;
 }
 
+static void *eeh_dev_save_state(void *data, void *userdata)
+{
+       struct eeh_dev *edev = data;
+       struct pci_dev *pdev;
+
+       if (!edev)
+               return NULL;
+
+       pdev = eeh_dev_to_pci_dev(edev);
+       if (!pdev)
+               return NULL;
+
+       pci_save_state(pdev);
+       return NULL;
+}
+
 /**
  * eeh_report_error - Report pci error to each device driver
  * @data: eeh device
@@ -303,6 +319,22 @@ static void *eeh_report_reset(void *data, void *userdata)
        return NULL;
 }
 
+static void *eeh_dev_restore_state(void *data, void *userdata)
+{
+       struct eeh_dev *edev = data;
+       struct pci_dev *pdev;
+
+       if (!edev)
+               return NULL;
+
+       pdev = eeh_dev_to_pci_dev(edev);
+       if (!pdev)
+               return NULL;
+
+       pci_restore_state(pdev);
+       return NULL;
+}
+
 /**
  * eeh_report_resume - Tell device to resume normal operations
  * @data: eeh device
@@ -450,38 +482,82 @@ static void *eeh_pe_detach_dev(void *data, void *userdata)
 static void *__eeh_clear_pe_frozen_state(void *data, void *flag)
 {
        struct eeh_pe *pe = (struct eeh_pe *)data;
-       int i, rc;
+       bool *clear_sw_state = flag;
+       int i, rc = 1;
 
-       for (i = 0; i < 3; i++) {
-               rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
-               if (rc)
-                       continue;
-               rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
-               if (!rc)
-                       break;
-       }
+       for (i = 0; rc && i < 3; i++)
+               rc = eeh_unfreeze_pe(pe, clear_sw_state);
 
-       /* The PE has been isolated, clear it */
+       /* Stop immediately on any errors */
        if (rc) {
-               pr_warn("%s: Can't clear frozen PHB#%x-PE#%x (%d)\n",
-                       __func__, pe->phb->global_number, pe->addr, rc);
+               pr_warn("%s: Failure %d unfreezing PHB#%x-PE#%x\n",
+                       __func__, rc, pe->phb->global_number, pe->addr);
                return (void *)pe;
        }
 
        return NULL;
 }
 
-static int eeh_clear_pe_frozen_state(struct eeh_pe *pe)
+static int eeh_clear_pe_frozen_state(struct eeh_pe *pe,
+                                    bool clear_sw_state)
 {
        void *rc;
 
-       rc = eeh_pe_traverse(pe, __eeh_clear_pe_frozen_state, NULL);
+       rc = eeh_pe_traverse(pe, __eeh_clear_pe_frozen_state, &clear_sw_state);
        if (!rc)
                eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
 
        return rc ? -EIO : 0;
 }
 
+int eeh_pe_reset_and_recover(struct eeh_pe *pe)
+{
+       int result, ret;
+
+       /* Bail if the PE is being recovered */
+       if (pe->state & EEH_PE_RECOVERING)
+               return 0;
+
+       /* Put the PE into recovery mode */
+       eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
+
+       /* Save states */
+       eeh_pe_dev_traverse(pe, eeh_dev_save_state, NULL);
+
+       /* Report error */
+       eeh_pe_dev_traverse(pe, eeh_report_error, &result);
+
+       /* Issue reset */
+       eeh_pe_state_mark(pe, EEH_PE_RESET);
+       ret = eeh_reset_pe(pe);
+       if (ret) {
+               eeh_pe_state_clear(pe, EEH_PE_RECOVERING | EEH_PE_RESET);
+               return ret;
+       }
+       eeh_pe_state_clear(pe, EEH_PE_RESET);
+
+       /* Unfreeze the PE */
+       ret = eeh_clear_pe_frozen_state(pe, true);
+       if (ret) {
+               eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
+               return ret;
+       }
+
+       /* Notify completion of reset */
+       eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
+
+       /* Restore device state */
+       eeh_pe_dev_traverse(pe, eeh_dev_restore_state, NULL);
+
+       /* Resume */
+       eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
+
+       /* Clear recovery mode */
+       eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
+
+       return 0;
+}
+
 /**
  * eeh_reset_device - Perform actual reset of a pci slot
  * @pe: EEH PE
@@ -540,7 +616,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
        eeh_pe_state_clear(pe, EEH_PE_RESET);
 
        /* Clear frozen state */
-       rc = eeh_clear_pe_frozen_state(pe);
+       rc = eeh_clear_pe_frozen_state(pe, false);
        if (rc)
                return rc;
 
index 00e3844525a6d479b4e69c70222588369589594c..53dd0915e6907165aaed9740575d4c8f7b7a5c5a 100644 (file)
@@ -428,7 +428,7 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
        }
 
        /* Remove the EEH device */
-       pe = edev->pe;
+       pe = eeh_dev_to_pe(edev);
        edev->pe = NULL;
        list_del(&edev->list);
 
@@ -584,6 +584,8 @@ static void *__eeh_pe_state_clear(void *data, void *flag)
 {
        struct eeh_pe *pe = (struct eeh_pe *)data;
        int state = *((int *)flag);
+       struct eeh_dev *edev, *tmp;
+       struct pci_dev *pdev;
 
        /* Keep the state of permanently removed PE intact */
        if ((pe->freeze_count > EEH_MAX_ALLOWED_FREEZES) &&
@@ -592,9 +594,22 @@ static void *__eeh_pe_state_clear(void *data, void *flag)
 
        pe->state &= ~state;
 
-       /* Clear check count since last isolation */
-       if (state & EEH_PE_ISOLATED)
-               pe->check_count = 0;
+       /*
+        * Special treatment on clearing isolated state. Clear
+        * check count since last isolation and put all affected
+        * devices to normal state.
+        */
+       if (!(state & EEH_PE_ISOLATED))
+               return NULL;
+
+       pe->check_count = 0;
+       eeh_pe_for_each_dev(pe, edev, tmp) {
+               pdev = eeh_dev_to_pci_dev(edev);
+               if (!pdev)
+                       continue;
+
+               pdev->error_state = pci_channel_io_normal;
+       }
 
        return NULL;
 }
index e2595ba4b720123ad89908b26bf9f9eeff35255c..f19b1e5cb06096e2bd9b68b8cd620669c8943cac 100644 (file)
@@ -54,6 +54,43 @@ EEH_SHOW_ATTR(eeh_mode,            mode,            "0x%x");
 EEH_SHOW_ATTR(eeh_config_addr,     config_addr,     "0x%x");
 EEH_SHOW_ATTR(eeh_pe_config_addr,  pe_config_addr,  "0x%x");
 
+static ssize_t eeh_pe_state_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
+       int state;
+
+       if (!edev || !edev->pe)
+               return -ENODEV;
+
+       state = eeh_ops->get_state(edev->pe, NULL);
+       return sprintf(buf, "%0x08x %0x08x\n",
+                      state, edev->pe->state);
+}
+
+static ssize_t eeh_pe_state_store(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
+
+       if (!edev || !edev->pe)
+               return -ENODEV;
+
+       /* Nothing to do if it's not frozen */
+       if (!(edev->pe->state & EEH_PE_ISOLATED))
+               return count;
+
+       if (eeh_unfreeze_pe(edev->pe, true))
+               return -EIO;
+
+       return count;
+}
+
+static DEVICE_ATTR_RW(eeh_pe_state);
+
 void eeh_sysfs_add_device(struct pci_dev *pdev)
 {
        struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
@@ -68,9 +105,10 @@ void eeh_sysfs_add_device(struct pci_dev *pdev)
        rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
        rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
        rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
+       rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_state);
 
        if (rc)
-               printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
+               pr_warn("EEH: Unable to create sysfs entries\n");
        else if (edev)
                edev->mode |= EEH_DEV_SYSFS;
 }
@@ -92,6 +130,7 @@ void eeh_sysfs_remove_device(struct pci_dev *pdev)
        device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
        device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
        device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
+       device_remove_file(&pdev->dev, &dev_attr_eeh_pe_state);
 
        if (edev)
                edev->mode &= ~EEH_DEV_SYSFS;
index 7ee876d2adb57c6cc1e78d1d3204ab240238c02d..fafff8dbd5d990175874d9ca09e40a44085be525 100644 (file)
@@ -104,12 +104,15 @@ turn_on_mmu:
  * task's thread_struct.
  */
 #define EXCEPTION_PROLOG       \
-       mtspr   SPRN_SPRG_SCRATCH0,r10; \
-       mtspr   SPRN_SPRG_SCRATCH1,r11; \
-       mfcr    r10;            \
+       EXCEPTION_PROLOG_0;     \
        EXCEPTION_PROLOG_1;     \
        EXCEPTION_PROLOG_2
 
+#define EXCEPTION_PROLOG_0     \
+       mtspr   SPRN_SPRG_SCRATCH0,r10; \
+       mtspr   SPRN_SPRG_SCRATCH1,r11; \
+       mfcr    r10
+
 #define EXCEPTION_PROLOG_1     \
        mfspr   r11,SPRN_SRR1;          /* check whether user or kernel */ \
        andi.   r11,r11,MSR_PR; \
@@ -144,6 +147,14 @@ turn_on_mmu:
        SAVE_4GPRS(3, r11);     \
        SAVE_2GPRS(7, r11)
 
+/*
+ * Exception exit code.
+ */
+#define EXCEPTION_EPILOG_0     \
+       mtcr    r10;            \
+       mfspr   r10,SPRN_SPRG_SCRATCH0; \
+       mfspr   r11,SPRN_SPRG_SCRATCH1
+
 /*
  * Note: code which follows this uses cr0.eq (set if from kernel),
  * r11, r12 (SRR0), and r9 (SRR1).
@@ -293,16 +304,8 @@ InstructionTLBMiss:
 #ifdef CONFIG_8xx_CPU6
        stw     r3, 8(r0)
 #endif
-       DO_8xx_CPU6(0x3f80, r3)
-       mtspr   SPRN_M_TW, r10  /* Save a couple of working registers */
-       mfcr    r10
-#ifdef CONFIG_8xx_CPU6
-       stw     r10, 0(r0)
-       stw     r11, 4(r0)
-#else
-       mtspr   SPRN_DAR, r10
-       mtspr   SPRN_SPRG2, r11
-#endif
+       EXCEPTION_PROLOG_0
+       mtspr   SPRN_SPRG_SCRATCH2, r10
        mfspr   r10, SPRN_SRR0  /* Get effective address of fault */
 #ifdef CONFIG_8xx_CPU15
        addi    r11, r10, 0x1000
@@ -359,18 +362,11 @@ InstructionTLBMiss:
        mtspr   SPRN_MI_RPN, r10        /* Update TLB entry */
 
        /* Restore registers */
-#ifndef CONFIG_8xx_CPU6
-       mfspr   r10, SPRN_DAR
-       mtcr    r10
-       mtspr   SPRN_DAR, r11   /* Tag DAR */
-       mfspr   r11, SPRN_SPRG2
-#else
-       lwz     r11, 0(r0)
-       mtcr    r11
-       lwz     r11, 4(r0)
+#ifdef CONFIG_8xx_CPU6
        lwz     r3, 8(r0)
 #endif
-       mfspr   r10, SPRN_M_TW
+       mfspr   r10, SPRN_SPRG_SCRATCH2
+       EXCEPTION_EPILOG_0
        rfi
 2:
        mfspr   r11, SPRN_SRR1
@@ -381,19 +377,11 @@ InstructionTLBMiss:
        mtspr   SPRN_SRR1, r11
 
        /* Restore registers */
-#ifndef CONFIG_8xx_CPU6
-       mfspr   r10, SPRN_DAR
-       mtcr    r10
-       li      r11, 0x00f0
-       mtspr   SPRN_DAR, r11   /* Tag DAR */
-       mfspr   r11, SPRN_SPRG2
-#else
-       lwz     r11, 0(r0)
-       mtcr    r11
-       lwz     r11, 4(r0)
+#ifdef CONFIG_8xx_CPU6
        lwz     r3, 8(r0)
 #endif
-       mfspr   r10, SPRN_M_TW
+       mfspr   r10, SPRN_SPRG_SCRATCH2
+       EXCEPTION_EPILOG_0
        b       InstructionAccess
 
        . = 0x1200
@@ -401,16 +389,8 @@ DataStoreTLBMiss:
 #ifdef CONFIG_8xx_CPU6
        stw     r3, 8(r0)
 #endif
-       DO_8xx_CPU6(0x3f80, r3)
-       mtspr   SPRN_M_TW, r10  /* Save a couple of working registers */
-       mfcr    r10
-#ifdef CONFIG_8xx_CPU6
-       stw     r10, 0(r0)
-       stw     r11, 4(r0)
-#else
-       mtspr   SPRN_DAR, r10
-       mtspr   SPRN_SPRG2, r11
-#endif
+       EXCEPTION_PROLOG_0
+       mtspr   SPRN_SPRG_SCRATCH2, r10
        mfspr   r10, SPRN_M_TWB /* Get level 1 table entry address */
 
        /* If we are faulting a kernel address, we have to use the
@@ -483,19 +463,12 @@ DataStoreTLBMiss:
        mtspr   SPRN_MD_RPN, r10        /* Update TLB entry */
 
        /* Restore registers */
-#ifndef CONFIG_8xx_CPU6
-       mfspr   r10, SPRN_DAR
-       mtcr    r10
-       mtspr   SPRN_DAR, r11   /* Tag DAR */
-       mfspr   r11, SPRN_SPRG2
-#else
-       mtspr   SPRN_DAR, r11   /* Tag DAR */
-       lwz     r11, 0(r0)
-       mtcr    r11
-       lwz     r11, 4(r0)
+#ifdef CONFIG_8xx_CPU6
        lwz     r3, 8(r0)
 #endif
-       mfspr   r10, SPRN_M_TW
+       mtspr   SPRN_DAR, r11   /* Tag DAR */
+       mfspr   r10, SPRN_SPRG_SCRATCH2
+       EXCEPTION_EPILOG_0
        rfi
 
 /* This is an instruction TLB error on the MPC8xx.  This could be due
@@ -507,35 +480,18 @@ InstructionTLBError:
        b       InstructionAccess
 
 /* This is the data TLB error on the MPC8xx.  This could be due to
- * many reasons, including a dirty update to a pte.  We can catch that
- * one here, but anything else is an error.  First, we track down the
- * Linux pte.  If it is valid, write access is allowed, but the
- * page dirty bit is not set, we will set it and reload the TLB.  For
- * any other case, we bail out to a higher level function that can
- * handle it.
+ * many reasons, including a dirty update to a pte.  We bail out to
+ * a higher level function that can handle it.
  */
        . = 0x1400
 DataTLBError:
-#ifdef CONFIG_8xx_CPU6
-       stw     r3, 8(r0)
-#endif
-       DO_8xx_CPU6(0x3f80, r3)
-       mtspr   SPRN_M_TW, r10  /* Save a couple of working registers */
-       mfcr    r10
-       stw     r10, 0(r0)
-       stw     r11, 4(r0)
+       EXCEPTION_PROLOG_0
 
-       mfspr   r10, SPRN_DAR
-       cmpwi   cr0, r10, 0x00f0
+       mfspr   r11, SPRN_DAR
+       cmpwi   cr0, r11, 0x00f0
        beq-    FixupDAR        /* must be a buggy dcbX, icbi insn. */
-DARFixed:/* Return from dcbx instruction bug workaround, r10 holds value of DAR */
-       mfspr   r10, SPRN_M_TW  /* Restore registers */
-       lwz     r11, 0(r0)
-       mtcr    r11
-       lwz     r11, 4(r0)
-#ifdef CONFIG_8xx_CPU6
-       lwz     r3, 8(r0)
-#endif
+DARFixed:/* Return from dcbx instruction bug workaround */
+       EXCEPTION_EPILOG_0
        b       DataAccess
 
        EXCEPTION(0x1500, Trap_15, unknown_exception, EXC_XFER_EE)
@@ -559,11 +515,15 @@ DARFixed:/* Return from dcbx instruction bug workaround, r10 holds value of DAR
 
 /* This is the procedure to calculate the data EA for buggy dcbx,dcbi instructions
  * by decoding the registers used by the dcbx instruction and adding them.
- * DAR is set to the calculated address and r10 also holds the EA on exit.
+ * DAR is set to the calculated address.
  */
  /* define if you don't want to use self modifying code */
 #define NO_SELF_MODIFYING_CODE
 FixupDAR:/* Entry point for dcbx workaround. */
+#ifdef CONFIG_8xx_CPU6
+       stw     r3, 8(r0)
+#endif
+       mtspr   SPRN_SPRG_SCRATCH2, r10
        /* fetch instruction from memory. */
        mfspr   r10, SPRN_SRR0
        andis.  r11, r10, 0x8000        /* Address >= 0x80000000 */
@@ -579,16 +539,17 @@ FixupDAR:/* Entry point for dcbx workaround. */
        mtspr   SPRN_MD_TWC, r11        /* Load pte table base address */
        mfspr   r11, SPRN_MD_TWC        /* ....and get the pte address */
        lwz     r11, 0(r11)     /* Get the pte */
+#ifdef CONFIG_8xx_CPU6
+       lwz     r3, 8(r0)       /* restore r3 from memory */
+#endif
        /* concat physical page address(r11) and page offset(r10) */
        rlwimi  r11, r10, 0, 20, 31
        lwz     r11,0(r11)
 /* Check if it really is a dcbx instruction. */
 /* dcbt and dcbtst does not generate DTLB Misses/Errors,
  * no need to include them here */
-       srwi    r10, r11, 26    /* check if major OP code is 31 */
-       cmpwi   cr0, r10, 31
-       bne-    141f
-       rlwinm  r10, r11, 0, 21, 30
+       xoris   r10, r11, 0x7c00        /* check if major OP code is 31 */
+       rlwinm  r10, r10, 0, 21, 5
        cmpwi   cr0, r10, 2028  /* Is dcbz? */
        beq+    142f
        cmpwi   cr0, r10, 940   /* Is dcbi? */
@@ -599,16 +560,13 @@ FixupDAR:/* Entry point for dcbx workaround. */
        beq+    142f
        cmpwi   cr0, r10, 1964  /* Is icbi? */
        beq+    142f
-141:   mfspr   r10, SPRN_DAR   /* r10 must hold DAR at exit */
+141:   mfspr   r10,SPRN_SPRG_SCRATCH2
        b       DARFixed        /* Nope, go back to normal TLB processing */
 
 144:   mfspr   r10, SPRN_DSISR
        rlwinm  r10, r10,0,7,5  /* Clear store bit for buggy dcbst insn */
        mtspr   SPRN_DSISR, r10
 142:   /* continue, it was a dcbx, dcbi instruction. */
-#ifdef CONFIG_8xx_CPU6
-       lwz     r3, 8(r0)       /* restore r3 from memory */
-#endif
 #ifndef NO_SELF_MODIFYING_CODE
        andis.  r10,r11,0x1f    /* test if reg RA is r0 */
        li      r10,modified_instr@l
@@ -619,14 +577,15 @@ FixupDAR:/* Entry point for dcbx workaround. */
        stw     r11,0(r10)      /* store add/and instruction */
        dcbf    0,r10           /* flush new instr. to memory. */
        icbi    0,r10           /* invalidate instr. cache line */
-       lwz     r11, 4(r0)      /* restore r11 from memory */
-       mfspr   r10, SPRN_M_TW  /* restore r10 from M_TW */
+       mfspr   r11, SPRN_SPRG_SCRATCH1 /* restore r11 */
+       mfspr   r10, SPRN_SPRG_SCRATCH0 /* restore r10 */
        isync                   /* Wait until new instr is loaded from memory */
 modified_instr:
        .space  4               /* this is where the add instr. is stored */
        bne+    143f
        subf    r10,r0,r10      /* r10=r10-r0, only if reg RA is r0 */
 143:   mtdar   r10             /* store faulting EA in DAR */
+       mfspr   r10,SPRN_SPRG_SCRATCH2
        b       DARFixed        /* Go back to normal TLB handling */
 #else
        mfctr   r10
@@ -680,13 +639,16 @@ modified_instr:
        mfdar   r11
        mtctr   r11                     /* restore ctr reg from DAR */
        mtdar   r10                     /* save fault EA to DAR */
+       mfspr   r10,SPRN_SPRG_SCRATCH2
        b       DARFixed                /* Go back to normal TLB handling */
 
        /* special handling for r10,r11 since these are modified already */
-153:   lwz     r11, 4(r0)      /* load r11 from memory */
-       b       155f
-154:   mfspr   r11, SPRN_M_TW  /* load r10 from M_TW */
-155:   add     r10, r10, r11   /* add it */
+153:   mfspr   r11, SPRN_SPRG_SCRATCH1 /* load r11 from SPRN_SPRG_SCRATCH1 */
+       add     r10, r10, r11   /* add it */
+       mfctr   r11             /* restore r11 */
+       b       151b
+154:   mfspr   r11, SPRN_SPRG_SCRATCH0 /* load r10 from SPRN_SPRG_SCRATCH0 */
+       add     r10, r10, r11   /* add it */
        mfctr   r11             /* restore r11 */
        b       151b
 #endif
index 0bb5918faaaf2c008f9e3ae25a7ae3d1c7ad4f21..1f7d84e2e8b219e42d5e40c00a90ff5544c02a3a 100644 (file)
@@ -293,7 +293,7 @@ out:
 /*
  * Handle single-step exceptions following a DABR hit.
  */
-int __kprobes single_step_dabr_instruction(struct die_args *args)
+static int __kprobes single_step_dabr_instruction(struct die_args *args)
 {
        struct pt_regs *regs = args->regs;
        struct perf_event *bp = NULL;
index 1114d13ac19f6ec5e0294a2d197e2db3ec8e6814..ac86c53e25428baaad22800ae1cedb2775680732 100644 (file)
@@ -55,7 +55,7 @@ static struct device ibmebus_bus_device = { /* fake "parent" device */
 struct bus_type ibmebus_bus_type;
 
 /* These devices will automatically be added to the bus during init */
-static struct of_device_id __initdata ibmebus_matches[] = {
+static const struct of_device_id ibmebus_matches[] __initconst = {
        { .compatible = "IBM,lhca" },
        { .compatible = "IBM,lhea" },
        {},
index be05841396cff00d6095e8a9edecd6e4fed7ffc3..c0754bbf81181c93c36cd8e1b12776b5ab729463 100644 (file)
@@ -73,7 +73,7 @@ _GLOBAL(power7_powersave_common)
 
        /* Check if something happened while soft-disabled */
        lbz     r0,PACAIRQHAPPENED(r13)
-       cmpwi   cr0,r0,0
+       andi.   r0,r0,~PACA_IRQ_HARD_DIS@l
        beq     1f
        cmpwi   cr0,r4,0
        beq     1f
index 4c5891de162e2de7bff803f6139853c6ef066221..8eb857f216c1ba1404a4e3b153a2077b7f7880c4 100644 (file)
@@ -444,13 +444,13 @@ void migrate_irqs(void)
 
                cpumask_and(mask, data->affinity, map);
                if (cpumask_any(mask) >= nr_cpu_ids) {
-                       printk("Breaking affinity for irq %i\n", irq);
+                       pr_warn("Breaking affinity for irq %i\n", irq);
                        cpumask_copy(mask, map);
                }
                if (chip->irq_set_affinity)
                        chip->irq_set_affinity(data, mask, true);
                else if (desc->action && !(warned++))
-                       printk("Cannot set affinity for irq %i\n", irq);
+                       pr_err("Cannot set affinity for irq %i\n", irq);
        }
 
        free_cpumask_var(mask);
@@ -470,7 +470,7 @@ static inline void check_stack_overflow(void)
 
        /* check for stack overflow: is there less than 2KB free? */
        if (unlikely(sp < (sizeof(struct thread_info) + 2048))) {
-               printk("do_IRQ: stack overflow: %ld\n",
+               pr_err("do_IRQ: stack overflow: %ld\n",
                        sp - sizeof(struct thread_info));
                dump_stack();
        }
index 936258881c98ccaee684998a3d05b2485c7fce0c..7b750c4ed5c73a5a1e7a4dfa8aa2e6055ae933d3 100644 (file)
@@ -35,7 +35,7 @@ static struct legacy_serial_info {
        phys_addr_t                     taddr;
 } legacy_serial_infos[MAX_LEGACY_SERIAL_PORTS];
 
-static struct of_device_id legacy_serial_parents[] __initdata = {
+static const struct of_device_id legacy_serial_parents[] __initconst = {
        {.type = "soc",},
        {.type = "tsi-bridge",},
        {.type = "opb", },
index 6cff040bf4565ae618e801a7159e2e5871b2c1e1..c94d2e018d843dc9d9054561252bef19f7672516 100644 (file)
@@ -15,6 +15,9 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/moduleloader.h>
 #include <linux/elf.h>
 #include <linux/sort.h>
 #include <asm/setup.h>
 
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(fmt , ...)
-#endif
-
 /* Count how many different relocations (different symbol, different
    addend) */
 static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num)
@@ -121,8 +118,8 @@ static unsigned long get_plt_size(const Elf32_Ehdr *hdr,
                        continue;
 
                if (sechdrs[i].sh_type == SHT_RELA) {
-                       DEBUGP("Found relocations in section %u\n", i);
-                       DEBUGP("Ptr: %p.  Number: %u\n",
+                       pr_debug("Found relocations in section %u\n", i);
+                       pr_debug("Ptr: %p.  Number: %u\n",
                               (void *)hdr + sechdrs[i].sh_offset,
                               sechdrs[i].sh_size / sizeof(Elf32_Rela));
 
@@ -161,7 +158,7 @@ int module_frob_arch_sections(Elf32_Ehdr *hdr,
                        me->arch.core_plt_section = i;
        }
        if (!me->arch.core_plt_section || !me->arch.init_plt_section) {
-               printk("Module doesn't contain .plt or .init.plt sections.\n");
+               pr_err("Module doesn't contain .plt or .init.plt sections.\n");
                return -ENOEXEC;
        }
 
@@ -189,7 +186,7 @@ static uint32_t do_plt_call(void *location,
 {
        struct ppc_plt_entry *entry;
 
-       DEBUGP("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location);
+       pr_debug("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location);
        /* Init, or core PLT? */
        if (location >= mod->module_core
            && location < mod->module_core + mod->core_size)
@@ -208,7 +205,7 @@ static uint32_t do_plt_call(void *location,
        entry->jump[2] = 0x7d8903a6;                    /* mtctr r12 */
        entry->jump[3] = 0x4e800420;                    /* bctr */
 
-       DEBUGP("Initialized plt for 0x%x at %p\n", val, entry);
+       pr_debug("Initialized plt for 0x%x at %p\n", val, entry);
        return (uint32_t)entry;
 }
 
@@ -224,7 +221,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
        uint32_t *location;
        uint32_t value;
 
-       DEBUGP("Applying ADD relocate section %u to %u\n", relsec,
+       pr_debug("Applying ADD relocate section %u to %u\n", relsec,
               sechdrs[relsec].sh_info);
        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
                /* This is where to make the change */
@@ -268,17 +265,17 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
                                                    sechdrs, module);
 
                        /* Only replace bits 2 through 26 */
-                       DEBUGP("REL24 value = %08X. location = %08X\n",
+                       pr_debug("REL24 value = %08X. location = %08X\n",
                               value, (uint32_t)location);
-                       DEBUGP("Location before: %08X.\n",
+                       pr_debug("Location before: %08X.\n",
                               *(uint32_t *)location);
                        *(uint32_t *)location
                                = (*(uint32_t *)location & ~0x03fffffc)
                                | ((value - (uint32_t)location)
                                   & 0x03fffffc);
-                       DEBUGP("Location after: %08X.\n",
+                       pr_debug("Location after: %08X.\n",
                               *(uint32_t *)location);
-                       DEBUGP("ie. jump to %08X+%08X = %08X\n",
+                       pr_debug("ie. jump to %08X+%08X = %08X\n",
                               *(uint32_t *)location & 0x03fffffc,
                               (uint32_t)location,
                               (*(uint32_t *)location & 0x03fffffc)
@@ -291,7 +288,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
                        break;
 
                default:
-                       printk("%s: unknown ADD relocation: %u\n",
+                       pr_err("%s: unknown ADD relocation: %u\n",
                               module->name,
                               ELF32_R_TYPE(rela[i].r_info));
                        return -ENOEXEC;
index d807ee626af9c32a1a258c673c508e6bb9519081..68384514506b7725346d4b1cefd61048459efa24 100644 (file)
@@ -15,6 +15,9 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/elf.h>
 #include <linux/moduleloader.h>
    Using a magic allocator which places modules within 32MB solves
    this, and makes other things simpler.  Anton?
    --RR.  */
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(fmt , ...)
-#endif
 
 #if defined(_CALL_ELF) && _CALL_ELF == 2
 #define R2_STACK_OFFSET 24
@@ -279,8 +277,8 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
        /* Every relocated section... */
        for (i = 1; i < hdr->e_shnum; i++) {
                if (sechdrs[i].sh_type == SHT_RELA) {
-                       DEBUGP("Found relocations in section %u\n", i);
-                       DEBUGP("Ptr: %p.  Number: %lu\n",
+                       pr_debug("Found relocations in section %u\n", i);
+                       pr_debug("Ptr: %p.  Number: %Lu\n",
                               (void *)sechdrs[i].sh_addr,
                               sechdrs[i].sh_size / sizeof(Elf64_Rela));
 
@@ -304,7 +302,7 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
        relocs++;
 #endif
 
-       DEBUGP("Looks like a total of %lu stubs, max\n", relocs);
+       pr_debug("Looks like a total of %lu stubs, max\n", relocs);
        return relocs * sizeof(struct ppc64_stub_entry);
 }
 
@@ -390,7 +388,7 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
        }
 
        if (!me->arch.stubs_section) {
-               printk("%s: doesn't contain .stubs.\n", me->name);
+               pr_err("%s: doesn't contain .stubs.\n", me->name);
                return -ENOEXEC;
        }
 
@@ -434,11 +432,11 @@ static inline int create_stub(Elf64_Shdr *sechdrs,
        /* Stub uses address relative to r2. */
        reladdr = (unsigned long)entry - my_r2(sechdrs, me);
        if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
-               printk("%s: Address %p of stub out of range of %p.\n",
+               pr_err("%s: Address %p of stub out of range of %p.\n",
                       me->name, (void *)reladdr, (void *)my_r2);
                return 0;
        }
-       DEBUGP("Stub %p get data from reladdr %li\n", entry, reladdr);
+       pr_debug("Stub %p get data from reladdr %li\n", entry, reladdr);
 
        entry->jump[0] |= PPC_HA(reladdr);
        entry->jump[1] |= PPC_LO(reladdr);
@@ -477,7 +475,7 @@ static unsigned long stub_for_addr(Elf64_Shdr *sechdrs,
 static int restore_r2(u32 *instruction, struct module *me)
 {
        if (*instruction != PPC_INST_NOP) {
-               printk("%s: Expect noop after relocate, got %08x\n",
+               pr_err("%s: Expect noop after relocate, got %08x\n",
                       me->name, *instruction);
                return 0;
        }
@@ -498,7 +496,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
        unsigned long *location;
        unsigned long value;
 
-       DEBUGP("Applying ADD relocate section %u to %u\n", relsec,
+       pr_debug("Applying ADD relocate section %u to %u\n", relsec,
               sechdrs[relsec].sh_info);
 
        /* First time we're called, we can fix up .TOC. */
@@ -519,7 +517,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
                sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
                        + ELF64_R_SYM(rela[i].r_info);
 
-               DEBUGP("RELOC at %p: %li-type as %s (%lu) + %li\n",
+               pr_debug("RELOC at %p: %li-type as %s (0x%lx) + %li\n",
                       location, (long)ELF64_R_TYPE(rela[i].r_info),
                       strtab + sym->st_name, (unsigned long)sym->st_value,
                       (long)rela[i].r_addend);
@@ -546,7 +544,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
                        /* Subtract TOC pointer */
                        value -= my_r2(sechdrs, me);
                        if (value + 0x8000 > 0xffff) {
-                               printk("%s: bad TOC16 relocation (%lu)\n",
+                               pr_err("%s: bad TOC16 relocation (0x%lx)\n",
                                       me->name, value);
                                return -ENOEXEC;
                        }
@@ -567,7 +565,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
                        /* Subtract TOC pointer */
                        value -= my_r2(sechdrs, me);
                        if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
-                               printk("%s: bad TOC16_DS relocation (%lu)\n",
+                               pr_err("%s: bad TOC16_DS relocation (0x%lx)\n",
                                       me->name, value);
                                return -ENOEXEC;
                        }
@@ -580,7 +578,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
                        /* Subtract TOC pointer */
                        value -= my_r2(sechdrs, me);
                        if ((value & 3) != 0) {
-                               printk("%s: bad TOC16_LO_DS relocation (%lu)\n",
+                               pr_err("%s: bad TOC16_LO_DS relocation (0x%lx)\n",
                                       me->name, value);
                                return -ENOEXEC;
                        }
@@ -613,7 +611,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
                        /* Convert value to relative */
                        value -= (unsigned long)location;
                        if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
-                               printk("%s: REL24 %li out of range!\n",
+                               pr_err("%s: REL24 %li out of range!\n",
                                       me->name, (long int)value);
                                return -ENOEXEC;
                        }
@@ -655,7 +653,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
                        break;
 
                default:
-                       printk("%s: Unknown ADD relocation: %lu\n",
+                       pr_err("%s: Unknown ADD relocation: %lu\n",
                               me->name,
                               (unsigned long)ELF64_R_TYPE(rela[i].r_info));
                        return -ENOEXEC;
index 8bbc12d20f5c9a11346874bb336fac33c7dcc6bc..71bd161640cfc8d101e03117582229823d365676 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <asm/machdep.h>
 
-int arch_msi_check_device(struct pci_dev* dev, int nvec, int type)
+int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 {
        if (!ppc_md.setup_msi_irqs || !ppc_md.teardown_msi_irqs) {
                pr_debug("msi: Platform doesn't provide MSI callbacks.\n");
@@ -24,16 +24,6 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type)
        if (type == PCI_CAP_ID_MSI && nvec > 1)
                return 1;
 
-       if (ppc_md.msi_check_device) {
-               pr_debug("msi: Using platform check routine.\n");
-               return ppc_md.msi_check_device(dev, nvec, type);
-       }
-
-        return 0;
-}
-
-int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
-{
        return ppc_md.setup_msi_irqs(dev, nvec, type);
 }
 
index 28b898e681850ab22996a2043d46c867e96232be..34f7c9b7cd96b78a7ddf338a61f2d20a5fd16611 100644 (file)
@@ -567,7 +567,7 @@ static int __init nvram_init(void)
        return rc;
 }
 
-void __exit nvram_cleanup(void)
+static void __exit nvram_cleanup(void)
 {
         misc_deregister( &nvram_dev );
 }
index a7b743076720d3a2fc04dad98be34b0d55b40978..f87bc1b4bdda60c6bf68a966eb8496e9498da8b7 100644 (file)
@@ -97,7 +97,7 @@ static int of_pci_phb_probe(struct platform_device *dev)
        return 0;
 }
 
-static struct of_device_id of_pci_phb_ids[] = {
+static const struct of_device_id of_pci_phb_ids[] = {
        { .type = "pci", },
        { .type = "pcix", },
        { .type = "pcie", },
index b2814e23e1edceacba1d2c659a2c8b73be907b3b..bd70a51d5747594906f24911d46f8a7b2abfb489 100644 (file)
@@ -1140,7 +1140,7 @@ static int reparent_resources(struct resource *parent,
  *         as well.
  */
 
-void pcibios_allocate_bus_resources(struct pci_bus *bus)
+static void pcibios_allocate_bus_resources(struct pci_bus *bus)
 {
        struct pci_bus *b;
        int i;
@@ -1561,7 +1561,6 @@ EARLY_PCI_OP(write, byte, u8)
 EARLY_PCI_OP(write, word, u16)
 EARLY_PCI_OP(write, dword, u32)
 
-extern int pci_bus_find_capability (struct pci_bus *bus, unsigned int devfn, int cap);
 int early_find_capability(struct pci_controller *hose, int bus, int devfn,
                          int cap)
 {
index 44562aa97f1611f5bcf1a26b56a425efa0c7a33e..e6245e9c7d8d7d54022a408e66e64d74d795757a 100644 (file)
@@ -38,7 +38,7 @@ static u32 get_int_prop(struct device_node *np, const char *name, u32 def)
  * @addr0: value of 1st cell of a device tree PCI address.
  * @bridge: Set this flag if the address is from a bridge 'ranges' property
  */
-unsigned int pci_parse_of_flags(u32 addr0, int bridge)
+static unsigned int pci_parse_of_flags(u32 addr0, int bridge)
 {
        unsigned int flags = 0;
 
index 48d17d6fca5b6e43ab1c77759f04a87f90c5aa2c..c4dfff6c2719ca5bfc6a17c9faf204fa836cad7f 100644 (file)
-#include <linux/export.h>
-#include <linux/threads.h>
-#include <linux/smp.h>
-#include <linux/sched.h>
-#include <linux/elfcore.h>
-#include <linux/string.h>
-#include <linux/interrupt.h>
-#include <linux/screen_info.h>
-#include <linux/vt_kern.h>
-#include <linux/nvram.h>
-#include <linux/irq.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
+#include <linux/ftrace.h>
+#include <linux/mm.h>
 
-#include <asm/page.h>
 #include <asm/processor.h>
-#include <asm/cacheflush.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <linux/atomic.h>
-#include <asm/checksum.h>
-#include <asm/pgtable.h>
-#include <asm/tlbflush.h>
-#include <linux/adb.h>
-#include <linux/cuda.h>
-#include <linux/pmu.h>
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
-#include <asm/irq.h>
-#include <asm/pmac_feature.h>
-#include <asm/dma.h>
-#include <asm/machdep.h>
-#include <asm/hw_irq.h>
-#include <asm/nvram.h>
-#include <asm/mmu_context.h>
-#include <asm/backlight.h>
-#include <asm/time.h>
-#include <asm/cputable.h>
-#include <asm/btext.h>
-#include <asm/div64.h>
-#include <asm/signal.h>
-#include <asm/dcr.h>
-#include <asm/ftrace.h>
 #include <asm/switch_to.h>
+#include <asm/cacheflush.h>
 #include <asm/epapr_hcalls.h>
 
-#ifdef CONFIG_PPC32
-extern void transfer_to_handler(void);
-extern void do_IRQ(struct pt_regs *regs);
-extern void machine_check_exception(struct pt_regs *regs);
-extern void alignment_exception(struct pt_regs *regs);
-extern void program_check_exception(struct pt_regs *regs);
-extern void single_step_exception(struct pt_regs *regs);
-extern int sys_sigreturn(struct pt_regs *regs);
+EXPORT_SYMBOL(flush_dcache_range);
+EXPORT_SYMBOL(flush_icache_range);
 
-EXPORT_SYMBOL(clear_pages);
-EXPORT_SYMBOL(ISA_DMA_THRESHOLD);
-EXPORT_SYMBOL(DMA_MODE_READ);
-EXPORT_SYMBOL(DMA_MODE_WRITE);
+EXPORT_SYMBOL(empty_zero_page);
 
-EXPORT_SYMBOL(transfer_to_handler);
-EXPORT_SYMBOL(do_IRQ);
-EXPORT_SYMBOL(machine_check_exception);
-EXPORT_SYMBOL(alignment_exception);
-EXPORT_SYMBOL(program_check_exception);
-EXPORT_SYMBOL(single_step_exception);
-EXPORT_SYMBOL(sys_sigreturn);
-#endif
+long long __bswapdi2(long long);
+EXPORT_SYMBOL(__bswapdi2);
 
 #ifdef CONFIG_FUNCTION_TRACER
 EXPORT_SYMBOL(_mcount);
 #endif
 
-EXPORT_SYMBOL(strcpy);
-EXPORT_SYMBOL(strncpy);
-EXPORT_SYMBOL(strcat);
-EXPORT_SYMBOL(strlen);
-EXPORT_SYMBOL(strcmp);
-EXPORT_SYMBOL(strncmp);
-
-#ifndef CONFIG_GENERIC_CSUM
-EXPORT_SYMBOL(csum_partial);
-EXPORT_SYMBOL(csum_partial_copy_generic);
-EXPORT_SYMBOL(ip_fast_csum);
-EXPORT_SYMBOL(csum_tcpudp_magic);
-#endif
-
-EXPORT_SYMBOL(__copy_tofrom_user);
-EXPORT_SYMBOL(__clear_user);
-EXPORT_SYMBOL(copy_page);
-
-#if defined(CONFIG_PCI) && defined(CONFIG_PPC32)
-EXPORT_SYMBOL(isa_io_base);
-EXPORT_SYMBOL(isa_mem_base);
-EXPORT_SYMBOL(pci_dram_offset);
-#endif /* CONFIG_PCI */
-
-EXPORT_SYMBOL(start_thread);
-
 #ifdef CONFIG_PPC_FPU
 EXPORT_SYMBOL(giveup_fpu);
 EXPORT_SYMBOL(load_fp_state);
 EXPORT_SYMBOL(store_fp_state);
 #endif
+
 #ifdef CONFIG_ALTIVEC
 EXPORT_SYMBOL(giveup_altivec);
 EXPORT_SYMBOL(load_vr_state);
 EXPORT_SYMBOL(store_vr_state);
-#endif /* CONFIG_ALTIVEC */
-#ifdef CONFIG_VSX
-EXPORT_SYMBOL(giveup_vsx);
-EXPORT_SYMBOL_GPL(__giveup_vsx);
-#endif /* CONFIG_VSX */
-#ifdef CONFIG_SPE
-EXPORT_SYMBOL(giveup_spe);
-#endif /* CONFIG_SPE */
-
-#ifndef CONFIG_PPC64
-EXPORT_SYMBOL(flush_instruction_cache);
 #endif
-EXPORT_SYMBOL(flush_dcache_range);
-EXPORT_SYMBOL(flush_icache_range);
 
-#ifdef CONFIG_SMP
-#ifdef CONFIG_PPC32
-EXPORT_SYMBOL(smp_hw_index);
-#endif
-#endif
-
-#ifdef CONFIG_ADB
-EXPORT_SYMBOL(adb_request);
-EXPORT_SYMBOL(adb_register);
-EXPORT_SYMBOL(adb_unregister);
-EXPORT_SYMBOL(adb_poll);
-EXPORT_SYMBOL(adb_try_handler_change);
-#endif /* CONFIG_ADB */
-#ifdef CONFIG_ADB_CUDA
-EXPORT_SYMBOL(cuda_request);
-EXPORT_SYMBOL(cuda_poll);
-#endif /* CONFIG_ADB_CUDA */
-EXPORT_SYMBOL(to_tm);
-
-#ifdef CONFIG_PPC32
-long long __ashrdi3(long long, int);
-long long __ashldi3(long long, int);
-long long __lshrdi3(long long, int);
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__lshrdi3);
-int __ucmpdi2(unsigned long long, unsigned long long);
-EXPORT_SYMBOL(__ucmpdi2);
-int __cmpdi2(long long, long long);
-EXPORT_SYMBOL(__cmpdi2);
-#endif
-long long __bswapdi2(long long);
-EXPORT_SYMBOL(__bswapdi2);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(memcmp);
-EXPORT_SYMBOL(memchr);
-
-#if defined(CONFIG_FB_VGA16_MODULE)
-EXPORT_SYMBOL(screen_info);
-#endif
-
-#ifdef CONFIG_PPC32
-EXPORT_SYMBOL(timer_interrupt);
-EXPORT_SYMBOL(tb_ticks_per_jiffy);
-EXPORT_SYMBOL(cacheable_memcpy);
-EXPORT_SYMBOL(cacheable_memzero);
-#endif
-
-#ifdef CONFIG_PPC32
-EXPORT_SYMBOL(switch_mmu_context);
-#endif
-
-#ifdef CONFIG_PPC_STD_MMU_32
-extern long mol_trampoline;
-EXPORT_SYMBOL(mol_trampoline); /* For MOL */
-EXPORT_SYMBOL(flush_hash_pages); /* For MOL */
-#ifdef CONFIG_SMP
-extern int mmu_hash_lock;
-EXPORT_SYMBOL(mmu_hash_lock); /* For MOL */
-#endif /* CONFIG_SMP */
-extern long *intercept_table;
-EXPORT_SYMBOL(intercept_table);
-#endif /* CONFIG_PPC_STD_MMU_32 */
-#ifdef CONFIG_PPC_DCR_NATIVE
-EXPORT_SYMBOL(__mtdcr);
-EXPORT_SYMBOL(__mfdcr);
-#endif
-EXPORT_SYMBOL(empty_zero_page);
-
-#ifdef CONFIG_PPC64
-EXPORT_SYMBOL(__arch_hweight8);
-EXPORT_SYMBOL(__arch_hweight16);
-EXPORT_SYMBOL(__arch_hweight32);
-EXPORT_SYMBOL(__arch_hweight64);
+#ifdef CONFIG_VSX
+EXPORT_SYMBOL_GPL(__giveup_vsx);
 #endif
 
-#ifdef CONFIG_PPC_BOOK3S_64
-EXPORT_SYMBOL_GPL(mmu_psize_defs);
+#ifdef CONFIG_SPE
+EXPORT_SYMBOL(giveup_spe);
 #endif
 
 #ifdef CONFIG_EPAPR_PARAVIRT
diff --git a/arch/powerpc/kernel/ppc_ksyms_32.c b/arch/powerpc/kernel/ppc_ksyms_32.c
new file mode 100644 (file)
index 0000000..30ddd8a
--- /dev/null
@@ -0,0 +1,61 @@
+#include <linux/export.h>
+#include <linux/smp.h>
+
+#include <asm/page.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hw_irq.h>
+#include <asm/time.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/dcr.h>
+
+EXPORT_SYMBOL(clear_pages);
+EXPORT_SYMBOL(ISA_DMA_THRESHOLD);
+EXPORT_SYMBOL(DMA_MODE_READ);
+EXPORT_SYMBOL(DMA_MODE_WRITE);
+
+#if defined(CONFIG_PCI)
+EXPORT_SYMBOL(isa_io_base);
+EXPORT_SYMBOL(isa_mem_base);
+EXPORT_SYMBOL(pci_dram_offset);
+#endif
+
+#ifdef CONFIG_SMP
+EXPORT_SYMBOL(smp_hw_index);
+#endif
+
+long long __ashrdi3(long long, int);
+long long __ashldi3(long long, int);
+long long __lshrdi3(long long, int);
+int __ucmpdi2(unsigned long long, unsigned long long);
+int __cmpdi2(long long, long long);
+EXPORT_SYMBOL(__ashrdi3);
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__lshrdi3);
+EXPORT_SYMBOL(__ucmpdi2);
+EXPORT_SYMBOL(__cmpdi2);
+
+EXPORT_SYMBOL(timer_interrupt);
+EXPORT_SYMBOL(tb_ticks_per_jiffy);
+
+EXPORT_SYMBOL(switch_mmu_context);
+
+#ifdef CONFIG_PPC_STD_MMU_32
+extern long mol_trampoline;
+EXPORT_SYMBOL(mol_trampoline); /* For MOL */
+EXPORT_SYMBOL(flush_hash_pages); /* For MOL */
+#ifdef CONFIG_SMP
+extern int mmu_hash_lock;
+EXPORT_SYMBOL(mmu_hash_lock); /* For MOL */
+#endif /* CONFIG_SMP */
+extern long *intercept_table;
+EXPORT_SYMBOL(intercept_table);
+#endif /* CONFIG_PPC_STD_MMU_32 */
+
+#ifdef CONFIG_PPC_DCR_NATIVE
+EXPORT_SYMBOL(__mtdcr);
+EXPORT_SYMBOL(__mfdcr);
+#endif
+
+EXPORT_SYMBOL(flush_instruction_cache);
index bf44ae962ab82206bb148c674dd2dbb7f093d1fd..aa1df89c8b2a8165411d588af394ea85f833265b 100644 (file)
@@ -228,6 +228,7 @@ void giveup_vsx(struct task_struct *tsk)
        giveup_altivec_maybe_transactional(tsk);
        __giveup_vsx(tsk);
 }
+EXPORT_SYMBOL(giveup_vsx);
 
 void flush_vsx_to_thread(struct task_struct *tsk)
 {
@@ -1316,6 +1317,7 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
        current->thread.tm_tfiar = 0;
 #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
 }
+EXPORT_SYMBOL(start_thread);
 
 #define PR_FP_ALL_EXCEPT (PR_FP_EXC_DIV | PR_FP_EXC_OVF | PR_FP_EXC_UND \
                | PR_FP_EXC_RES | PR_FP_EXC_INV)
index 4e139f8a69effa0a403a2e6d75b0fe7d7e268e3d..099f27e6d1b07e148c0b2c25d8f377d1d4f23654 100644 (file)
@@ -386,8 +386,9 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
        return 0;
 }
 
-int __init early_init_dt_scan_chosen_ppc(unsigned long node, const char *uname,
-                                        int depth, void *data)
+static int __init early_init_dt_scan_chosen_ppc(unsigned long node,
+                                               const char *uname,
+                                               int depth, void *data)
 {
        const unsigned long *lprop; /* All these set by kernel, so no need to convert endian */
 
@@ -641,6 +642,10 @@ void __init early_init_devtree(void *params)
 
        DBG(" -> early_init_devtree(%p)\n", params);
 
+       /* Too early to BUG_ON(), do it by hand */
+       if (!early_init_dt_verify(params))
+               panic("BUG: Failed verifying flat device tree, bad version?");
+
        /* Setup flat device-tree pointer */
        initial_boot_params = params;
 
@@ -663,14 +668,12 @@ void __init early_init_devtree(void *params)
         * device-tree, including the platform type, initrd location and
         * size, TCE reserve, and more ...
         */
-       of_scan_flat_dt(early_init_dt_scan_chosen_ppc, cmd_line);
+       of_scan_flat_dt(early_init_dt_scan_chosen_ppc, boot_command_line);
 
        /* Scan memory nodes and rebuild MEMBLOCKs */
        of_scan_flat_dt(early_init_dt_scan_root, NULL);
        of_scan_flat_dt(early_init_dt_scan_memory_ppc, NULL);
 
-       /* Save command line for /proc/cmdline and then parse parameters */
-       strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE);
        parse_early_param();
 
        /* make sure we've parsed cmdline for mem= before this */
index fe8e54b9ef7db5bddd01b3917741cadbc0746fa3..12640f7e726b29ce5ac9d256c589ce758a2521e8 100644 (file)
@@ -50,24 +50,14 @@ do
        done
 
        # ignore register save/restore funcitons
-       if [ "${UNDEF:0:9}" = "_restgpr_" ]; then
+       case $UNDEF in
+       _restgpr_*|_restgpr0_*|_rest32gpr_*)
                OK=1
-       fi
-       if [ "${UNDEF:0:10}" = "_restgpr0_" ]; then
-               OK=1
-       fi
-       if [ "${UNDEF:0:11}" = "_rest32gpr_" ]; then
-               OK=1
-       fi
-       if [ "${UNDEF:0:9}" = "_savegpr_" ]; then
+               ;;
+       _savegpr_*|_savegpr0_*|_save32gpr_*)
                OK=1
-       fi
-       if [ "${UNDEF:0:10}" = "_savegpr0_" ]; then
-               OK=1
-       fi
-       if [ "${UNDEF:0:11}" = "_save32gpr_" ]; then
-               OK=1
-       fi
+               ;;
+       esac
 
        if [ $OK -eq 0 ]; then
                ERROR=1
index 2e3d2bf536c5662c00f02e07cf36cd1e4b111825..cdb404ea34688b581a788694ed1f8da43aed1f91 100644 (file)
@@ -932,7 +932,7 @@ void ptrace_triggered(struct perf_event *bp,
 }
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
 
-int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
+static int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
                               unsigned long data)
 {
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
index e736387fee6af3ee5b92ed6407b4f2e7fb38024b..5a2c049c1c61461a50546a05082338158bdd7d08 100644 (file)
@@ -286,7 +286,7 @@ static void prrn_work_fn(struct work_struct *work)
 
 static DECLARE_WORK(prrn_work, prrn_work_fn);
 
-void prrn_schedule_update(u32 scope)
+static void prrn_schedule_update(u32 scope)
 {
        flush_work(&prrn_work);
        prrn_update_scope = scope;
index 1b0e26013a62d3c9b2853b429b9f5fedad7fc1ed..1362cd62b3fa5c32fdcc27d3ba3e5ee0e21fd974 100644 (file)
@@ -81,8 +81,6 @@ EXPORT_SYMBOL_GPL(boot_cpuid);
 
 unsigned long klimit = (unsigned long) _end;
 
-char cmd_line[COMMAND_LINE_SIZE];
-
 /*
  * This still seems to be needed... -- paulus
  */ 
@@ -94,6 +92,9 @@ struct screen_info screen_info = {
        .orig_video_isVGA = 1,
        .orig_video_points = 16
 };
+#if defined(CONFIG_FB_VGA16_MODULE)
+EXPORT_SYMBOL(screen_info);
+#endif
 
 /* Variables required to store legacy IO irq routing */
 int of_i8042_kbd_irq;
@@ -382,7 +383,7 @@ void __init check_for_initrd(void)
                initrd_start = initrd_end = 0;
 
        if (initrd_start)
-               printk("Found initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end);
+               pr_info("Found initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end);
 
        DBG(" <- check_for_initrd()\n");
 #endif /* CONFIG_BLK_DEV_INITRD */
index ea4fda60e57b93f18163b487a4bd2a56d43715ba..07831ed0d9efd12c86dce4b6346661b7e9fa216f 100644 (file)
@@ -268,7 +268,7 @@ static void __init exc_lvl_early_init(void)
 /* Warning, IO base is not yet inited */
 void __init setup_arch(char **cmdline_p)
 {
-       *cmdline_p = cmd_line;
+       *cmdline_p = boot_command_line;
 
        /* so udelay does something sensible, assume <= 1000 bogomips */
        loops_per_jiffy = 500000000 / HZ;
index 75d62d63fe684ad77b33efac9ac2a13d59222ac7..cd07d79ad21cac659d9966fb09e2ac41fa13a374 100644 (file)
@@ -525,21 +525,31 @@ void __init setup_system(void)
        printk("Starting Linux PPC64 %s\n", init_utsname()->version);
 
        printk("-----------------------------------------------------\n");
-       printk("ppc64_pft_size                = 0x%llx\n", ppc64_pft_size);
-       printk("physicalMemorySize            = 0x%llx\n", memblock_phys_mem_size());
+       printk("ppc64_pft_size    = 0x%llx\n", ppc64_pft_size);
+       printk("phys_mem_size     = 0x%llx\n", memblock_phys_mem_size());
+
        if (ppc64_caches.dline_size != 0x80)
-               printk("ppc64_caches.dcache_line_size = 0x%x\n",
-                      ppc64_caches.dline_size);
+               printk("dcache_line_size  = 0x%x\n", ppc64_caches.dline_size);
        if (ppc64_caches.iline_size != 0x80)
-               printk("ppc64_caches.icache_line_size = 0x%x\n",
-                      ppc64_caches.iline_size);
+               printk("icache_line_size  = 0x%x\n", ppc64_caches.iline_size);
+
+       printk("cpu_features      = 0x%016lx\n", cur_cpu_spec->cpu_features);
+       printk("  possible        = 0x%016lx\n", CPU_FTRS_POSSIBLE);
+       printk("  always          = 0x%016lx\n", CPU_FTRS_ALWAYS);
+       printk("cpu_user_features = 0x%08x 0x%08x\n", cur_cpu_spec->cpu_user_features,
+               cur_cpu_spec->cpu_user_features2);
+       printk("mmu_features      = 0x%08x\n", cur_cpu_spec->mmu_features);
+       printk("firmware_features = 0x%016lx\n", powerpc_firmware_features);
+
 #ifdef CONFIG_PPC_STD_MMU_64
        if (htab_address)
-               printk("htab_address                  = 0x%p\n", htab_address);
-       printk("htab_hash_mask                = 0x%lx\n", htab_hash_mask);
-#endif /* CONFIG_PPC_STD_MMU_64 */
+               printk("htab_address      = 0x%p\n", htab_address);
+
+       printk("htab_hash_mask    = 0x%lx\n", htab_hash_mask);
+#endif
+
        if (PHYSICAL_START > 0)
-               printk("physical_start                = 0x%llx\n",
+               printk("physical_start    = 0x%llx\n",
                       (unsigned long long)PHYSICAL_START);
        printk("-----------------------------------------------------\n");
 
@@ -657,7 +667,7 @@ void __init setup_arch(char **cmdline_p)
 {
        ppc64_boot_msg(0x12, "Setup Arch");
 
-       *cmdline_p = cmd_line;
+       *cmdline_p = boot_command_line;
 
        /*
         * Set cache line size based on type of cpu as a default.
index a0738af4aba6b80b3d356935d3bcf5c3b61a4f48..71e186d5f331a3320259e4d0ad432d29944a684e 100644 (file)
@@ -52,6 +52,7 @@
 #endif
 #include <asm/vdso.h>
 #include <asm/debug.h>
+#include <asm/kexec.h>
 
 #ifdef DEBUG
 #include <asm/udbg.h>
@@ -379,8 +380,11 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
                /*
                 * numa_node_id() works after this.
                 */
-               set_cpu_numa_node(cpu, numa_cpu_lookup_table[cpu]);
-               set_cpu_numa_mem(cpu, local_memory_node(numa_cpu_lookup_table[cpu]));
+               if (cpu_present(cpu)) {
+                       set_cpu_numa_node(cpu, numa_cpu_lookup_table[cpu]);
+                       set_cpu_numa_mem(cpu,
+                               local_memory_node(numa_cpu_lookup_table[cpu]));
+               }
        }
 
        cpumask_set_cpu(boot_cpuid, cpu_sibling_mask(boot_cpuid));
@@ -728,6 +732,9 @@ void start_secondary(void *unused)
        }
        traverse_core_siblings(cpu, true);
 
+       set_numa_node(numa_cpu_lookup_table[cpu]);
+       set_numa_mem(local_memory_node(numa_cpu_lookup_table[cpu]));
+
        smp_wmb();
        notify_cpu_starting(cpu);
        set_cpu_online(cpu, true);
index 0167d53da30cbbbb1e61c8cdb8768db35e4b3e7a..a531154cc0f3a43f06edbba75e3207ab92b105d5 100644 (file)
@@ -9,9 +9,7 @@
 
 #include <linux/mm.h>
 #include <asm/page.h>
-
-/* References to section boundaries */
-extern const void __nosave_begin, __nosave_end;
+#include <asm/sections.h>
 
 /*
  *     pfn_is_nosave - check if given pfn is in the 'nosave' section
index 368ab374d33c6b315c4a553a613d10f7d453970b..7505599c2593cc63fd89709fad21e9e5af92c906 100644 (file)
@@ -479,7 +479,7 @@ void arch_irq_work_raise(void)
 
 #endif /* CONFIG_IRQ_WORK */
 
-void __timer_interrupt(void)
+static void __timer_interrupt(void)
 {
        struct pt_regs *regs = get_irq_regs();
        u64 *next_tb = &__get_cpu_var(decrementers_next_tb);
@@ -643,7 +643,7 @@ static int __init get_freq(char *name, int cells, unsigned long *val)
        return found;
 }
 
-void start_cpu_decrementer(void)
+static void start_cpu_decrementer(void)
 {
 #if defined(CONFIG_BOOKE) || defined(CONFIG_40x)
        /* Clear any pending timer interrupts */
@@ -1024,6 +1024,7 @@ void to_tm(int tim, struct rtc_time * tm)
         */
        GregorianDay(tm);
 }
+EXPORT_SYMBOL(to_tm);
 
 /*
  * Divide a 128-bit dividend by a 32-bit divisor, leaving a 128 bit
index 59fa2de9546d7fb0ba8ca05729ad78e75e0a9e88..9f342f134ae486d2ea7be1b656202d9957713c8a 100644 (file)
@@ -10,7 +10,7 @@ CFLAGS_REMOVE_code-patching.o = -pg
 CFLAGS_REMOVE_feature-fixups.o = -pg
 
 obj-y                  := string.o alloc.o \
-                          crtsavres.o
+                          crtsavres.o ppc_ksyms.o
 obj-$(CONFIG_PPC32)    += div64.o copy_32.o
 obj-$(CONFIG_HAS_IOMEM)        += devres.o
 
index 7a8a7487cee8dde9d06aa86fff3bd32bdd54433e..7ce3870d7ddd1054898421647c54819ead09e20e 100644 (file)
@@ -164,7 +164,7 @@ static long calc_offset(struct fixup_entry *entry, unsigned int *p)
        return (unsigned long)p - (unsigned long)entry;
 }
 
-void test_basic_patching(void)
+static void test_basic_patching(void)
 {
        extern unsigned int ftr_fixup_test1;
        extern unsigned int end_ftr_fixup_test1;
diff --git a/arch/powerpc/lib/ppc_ksyms.c b/arch/powerpc/lib/ppc_ksyms.c
new file mode 100644 (file)
index 0000000..f993959
--- /dev/null
@@ -0,0 +1,39 @@
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/bitops.h>
+#include <net/checksum.h>
+
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memchr);
+#ifdef CONFIG_PPC32
+EXPORT_SYMBOL(cacheable_memcpy);
+EXPORT_SYMBOL(cacheable_memzero);
+#endif
+
+EXPORT_SYMBOL(strcpy);
+EXPORT_SYMBOL(strncpy);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strncmp);
+
+#ifndef CONFIG_GENERIC_CSUM
+EXPORT_SYMBOL(csum_partial);
+EXPORT_SYMBOL(csum_partial_copy_generic);
+EXPORT_SYMBOL(ip_fast_csum);
+EXPORT_SYMBOL(csum_tcpudp_magic);
+#endif
+
+EXPORT_SYMBOL(__copy_tofrom_user);
+EXPORT_SYMBOL(__clear_user);
+EXPORT_SYMBOL(copy_page);
+
+#ifdef CONFIG_PPC64
+EXPORT_SYMBOL(__arch_hweight8);
+EXPORT_SYMBOL(__arch_hweight16);
+EXPORT_SYMBOL(__arch_hweight32);
+EXPORT_SYMBOL(__arch_hweight64);
+#endif
index 5c09f365c84276161b75fd524fe270b2d43a1db2..54651fc2d4124dc7fd9b80fa5fbc3d551e7b8f06 100644 (file)
@@ -98,13 +98,8 @@ static unsigned long __kprobes dform_ea(unsigned int instr, struct pt_regs *regs
 
        ra = (instr >> 16) & 0x1f;
        ea = (signed short) instr;              /* sign-extend */
-       if (ra) {
+       if (ra)
                ea += regs->gpr[ra];
-               if (instr & 0x04000000) {               /* update forms */
-                       if ((instr>>26) != 47)          /* stmw is not an update form */
-                               regs->gpr[ra] = ea;
-               }
-       }
 
        return truncate_if_32bit(regs->msr, ea);
 }
@@ -120,11 +115,8 @@ static unsigned long __kprobes dsform_ea(unsigned int instr, struct pt_regs *reg
 
        ra = (instr >> 16) & 0x1f;
        ea = (signed short) (instr & ~3);       /* sign-extend */
-       if (ra) {
+       if (ra)
                ea += regs->gpr[ra];
-               if ((instr & 3) == 1)           /* update forms */
-                       regs->gpr[ra] = ea;
-       }
 
        return truncate_if_32bit(regs->msr, ea);
 }
@@ -133,8 +125,8 @@ static unsigned long __kprobes dsform_ea(unsigned int instr, struct pt_regs *reg
 /*
  * Calculate effective address for an X-form instruction
  */
-static unsigned long __kprobes xform_ea(unsigned int instr, struct pt_regs *regs,
-                                    int do_update)
+static unsigned long __kprobes xform_ea(unsigned int instr,
+                                       struct pt_regs *regs)
 {
        int ra, rb;
        unsigned long ea;
@@ -142,11 +134,8 @@ static unsigned long __kprobes xform_ea(unsigned int instr, struct pt_regs *regs
        ra = (instr >> 16) & 0x1f;
        rb = (instr >> 11) & 0x1f;
        ea = regs->gpr[rb];
-       if (ra) {
+       if (ra)
                ea += regs->gpr[ra];
-               if (do_update)          /* update forms */
-                       regs->gpr[ra] = ea;
-       }
 
        return truncate_if_32bit(regs->msr, ea);
 }
@@ -611,6 +600,23 @@ static void __kprobes do_cmp_unsigned(struct pt_regs *regs, unsigned long v1,
        regs->ccr = (regs->ccr & ~(0xf << shift)) | (crval << shift);
 }
 
+static int __kprobes trap_compare(long v1, long v2)
+{
+       int ret = 0;
+
+       if (v1 < v2)
+               ret |= 0x10;
+       else if (v1 > v2)
+               ret |= 0x08;
+       else
+               ret |= 0x04;
+       if ((unsigned long)v1 < (unsigned long)v2)
+               ret |= 0x02;
+       else if ((unsigned long)v1 > (unsigned long)v2)
+               ret |= 0x01;
+       return ret;
+}
+
 /*
  * Elements of 32-bit rotate and mask instructions.
  */
@@ -627,26 +633,27 @@ static void __kprobes do_cmp_unsigned(struct pt_regs *regs, unsigned long v1,
 #define ROTATE(x, n)   ((n) ? (((x) << (n)) | ((x) >> (8 * sizeof(long) - (n)))) : (x))
 
 /*
- * Emulate instructions that cause a transfer of control,
- * loads and stores, and a few other instructions.
- * Returns 1 if the step was emulated, 0 if not,
- * or -1 if the instruction is one that should not be stepped,
- * such as an rfid, or a mtmsrd that would clear MSR_RI.
+ * Decode an instruction, and execute it if that can be done just by
+ * modifying *regs (i.e. integer arithmetic and logical instructions,
+ * branches, and barrier instructions).
+ * Returns 1 if the instruction has been executed, or 0 if not.
+ * Sets *op to indicate what the instruction does.
  */
-int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
+int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs,
+                           unsigned int instr)
 {
        unsigned int opcode, ra, rb, rd, spr, u;
        unsigned long int imm;
        unsigned long int val, val2;
-       unsigned long int ea;
-       unsigned int cr, mb, me, sh;
-       int err;
-       unsigned long old_ra, val3;
+       unsigned int mb, me, sh;
        long ival;
 
+       op->type = COMPUTE;
+
        opcode = instr >> 26;
        switch (opcode) {
        case 16:        /* bc */
+               op->type = BRANCH;
                imm = (signed short)(instr & 0xfffc);
                if ((instr & 2) == 0)
                        imm += regs->nip;
@@ -659,26 +666,14 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
                return 1;
 #ifdef CONFIG_PPC64
        case 17:        /* sc */
-               /*
-                * N.B. this uses knowledge about how the syscall
-                * entry code works.  If that is changed, this will
-                * need to be changed also.
-                */
-               if (regs->gpr[0] == 0x1ebe &&
-                   cpu_has_feature(CPU_FTR_REAL_LE)) {
-                       regs->msr ^= MSR_LE;
-                       goto instr_done;
-               }
-               regs->gpr[9] = regs->gpr[13];
-               regs->gpr[10] = MSR_KERNEL;
-               regs->gpr[11] = regs->nip + 4;
-               regs->gpr[12] = regs->msr & MSR_MASK;
-               regs->gpr[13] = (unsigned long) get_paca();
-               regs->nip = (unsigned long) &system_call_common;
-               regs->msr = MSR_KERNEL;
-               return 1;
+               if ((instr & 0xfe2) == 2)
+                       op->type = SYSCALL;
+               else
+                       op->type = UNKNOWN;
+               return 0;
 #endif
        case 18:        /* b */
+               op->type = BRANCH;
                imm = instr & 0x03fffffc;
                if (imm & 0x02000000)
                        imm -= 0x04000000;
@@ -691,8 +686,16 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
                return 1;
        case 19:
                switch ((instr >> 1) & 0x3ff) {
+               case 0:         /* mcrf */
+                       rd = (instr >> 21) & 0x1c;
+                       ra = (instr >> 16) & 0x1c;
+                       val = (regs->ccr >> ra) & 0xf;
+                       regs->ccr = (regs->ccr & ~(0xfUL << rd)) | (val << rd);
+                       goto instr_done;
+
                case 16:        /* bclr */
                case 528:       /* bcctr */
+                       op->type = BRANCH;
                        imm = (instr & 0x400)? regs->ctr: regs->link;
                        regs->nip = truncate_if_32bit(regs->msr, regs->nip + 4);
                        imm = truncate_if_32bit(regs->msr, imm);
@@ -703,9 +706,13 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
                        return 1;
 
                case 18:        /* rfid, scary */
-                       return -1;
+                       if (regs->msr & MSR_PR)
+                               goto priv;
+                       op->type = RFI;
+                       return 0;
 
                case 150:       /* isync */
+                       op->type = BARRIER;
                        isync();
                        goto instr_done;
 
@@ -731,6 +738,7 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
        case 31:
                switch ((instr >> 1) & 0x3ff) {
                case 598:       /* sync */
+                       op->type = BARRIER;
 #ifdef __powerpc64__
                        switch ((instr >> 21) & 3) {
                        case 1:         /* lwsync */
@@ -745,6 +753,7 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
                        goto instr_done;
 
                case 854:       /* eieio */
+                       op->type = BARRIER;
                        eieio();
                        goto instr_done;
                }
@@ -760,6 +769,17 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
        rb = (instr >> 11) & 0x1f;
 
        switch (opcode) {
+#ifdef __powerpc64__
+       case 2:         /* tdi */
+               if (rd & trap_compare(regs->gpr[ra], (short) instr))
+                       goto trap;
+               goto instr_done;
+#endif
+       case 3:         /* twi */
+               if (rd & trap_compare((int)regs->gpr[ra], (short) instr))
+                       goto trap;
+               goto instr_done;
+
        case 7:         /* mulli */
                regs->gpr[rd] = regs->gpr[ra] * (short) instr;
                goto instr_done;
@@ -908,35 +928,44 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
 
        case 31:
                switch ((instr >> 1) & 0x3ff) {
+               case 4:         /* tw */
+                       if (rd == 0x1f ||
+                           (rd & trap_compare((int)regs->gpr[ra],
+                                              (int)regs->gpr[rb])))
+                               goto trap;
+                       goto instr_done;
+#ifdef __powerpc64__
+               case 68:        /* td */
+                       if (rd & trap_compare(regs->gpr[ra], regs->gpr[rb]))
+                               goto trap;
+                       goto instr_done;
+#endif
                case 83:        /* mfmsr */
                        if (regs->msr & MSR_PR)
-                               break;
-                       regs->gpr[rd] = regs->msr & MSR_MASK;
-                       goto instr_done;
+                               goto priv;
+                       op->type = MFMSR;
+                       op->reg = rd;
+                       return 0;
                case 146:       /* mtmsr */
                        if (regs->msr & MSR_PR)
-                               break;
-                       imm = regs->gpr[rd];
-                       if ((imm & MSR_RI) == 0)
-                               /* can't step mtmsr that would clear MSR_RI */
-                               return -1;
-                       regs->msr = imm;
-                       goto instr_done;
+                               goto priv;
+                       op->type = MTMSR;
+                       op->reg = rd;
+                       op->val = 0xffffffff & ~(MSR_ME | MSR_LE);
+                       return 0;
 #ifdef CONFIG_PPC64
                case 178:       /* mtmsrd */
-                       /* only MSR_EE and MSR_RI get changed if bit 15 set */
-                       /* mtmsrd doesn't change MSR_HV and MSR_ME */
                        if (regs->msr & MSR_PR)
-                               break;
-                       imm = (instr & 0x10000)? 0x8002: 0xefffffffffffefffUL;
-                       imm = (regs->msr & MSR_MASK & ~imm)
-                               | (regs->gpr[rd] & imm);
-                       if ((imm & MSR_RI) == 0)
-                               /* can't step mtmsrd that would clear MSR_RI */
-                               return -1;
-                       regs->msr = imm;
-                       goto instr_done;
+                               goto priv;
+                       op->type = MTMSR;
+                       op->reg = rd;
+                       /* only MSR_EE and MSR_RI get changed if bit 15 set */
+                       /* mtmsrd doesn't change MSR_HV, MSR_ME or MSR_LE */
+                       imm = (instr & 0x10000)? 0x8002: 0xefffffffffffeffeUL;
+                       op->val = imm;
+                       return 0;
 #endif
+
                case 19:        /* mfcr */
                        regs->gpr[rd] = regs->ccr;
                        regs->gpr[rd] &= 0xffffffffUL;
@@ -954,33 +983,43 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
                        goto instr_done;
 
                case 339:       /* mfspr */
-                       spr = (instr >> 11) & 0x3ff;
+                       spr = ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0);
                        switch (spr) {
-                       case 0x20:      /* mfxer */
+                       case SPRN_XER:  /* mfxer */
                                regs->gpr[rd] = regs->xer;
                                regs->gpr[rd] &= 0xffffffffUL;
                                goto instr_done;
-                       case 0x100:     /* mflr */
+                       case SPRN_LR:   /* mflr */
                                regs->gpr[rd] = regs->link;
                                goto instr_done;
-                       case 0x120:     /* mfctr */
+                       case SPRN_CTR:  /* mfctr */
                                regs->gpr[rd] = regs->ctr;
                                goto instr_done;
+                       default:
+                               op->type = MFSPR;
+                               op->reg = rd;
+                               op->spr = spr;
+                               return 0;
                        }
                        break;
 
                case 467:       /* mtspr */
-                       spr = (instr >> 11) & 0x3ff;
+                       spr = ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0);
                        switch (spr) {
-                       case 0x20:      /* mtxer */
+                       case SPRN_XER:  /* mtxer */
                                regs->xer = (regs->gpr[rd] & 0xffffffffUL);
                                goto instr_done;
-                       case 0x100:     /* mtlr */
+                       case SPRN_LR:   /* mtlr */
                                regs->link = regs->gpr[rd];
                                goto instr_done;
-                       case 0x120:     /* mtctr */
+                       case SPRN_CTR:  /* mtctr */
                                regs->ctr = regs->gpr[rd];
                                goto instr_done;
+                       default:
+                               op->type = MTSPR;
+                               op->val = regs->gpr[rd];
+                               op->spr = spr;
+                               return 0;
                        }
                        break;
 
@@ -1257,294 +1296,242 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
  * Cache instructions
  */
                case 54:        /* dcbst */
-                       ea = xform_ea(instr, regs, 0);
-                       if (!address_ok(regs, ea, 8))
-                               return 0;
-                       err = 0;
-                       __cacheop_user_asmx(ea, err, "dcbst");
-                       if (err)
-                               return 0;
-                       goto instr_done;
+                       op->type = MKOP(CACHEOP, DCBST, 0);
+                       op->ea = xform_ea(instr, regs);
+                       return 0;
 
                case 86:        /* dcbf */
-                       ea = xform_ea(instr, regs, 0);
-                       if (!address_ok(regs, ea, 8))
-                               return 0;
-                       err = 0;
-                       __cacheop_user_asmx(ea, err, "dcbf");
-                       if (err)
-                               return 0;
-                       goto instr_done;
+                       op->type = MKOP(CACHEOP, DCBF, 0);
+                       op->ea = xform_ea(instr, regs);
+                       return 0;
 
                case 246:       /* dcbtst */
-                       if (rd == 0) {
-                               ea = xform_ea(instr, regs, 0);
-                               prefetchw((void *) ea);
-                       }
-                       goto instr_done;
+                       op->type = MKOP(CACHEOP, DCBTST, 0);
+                       op->ea = xform_ea(instr, regs);
+                       op->reg = rd;
+                       return 0;
 
                case 278:       /* dcbt */
-                       if (rd == 0) {
-                               ea = xform_ea(instr, regs, 0);
-                               prefetch((void *) ea);
-                       }
-                       goto instr_done;
+                       op->type = MKOP(CACHEOP, DCBTST, 0);
+                       op->ea = xform_ea(instr, regs);
+                       op->reg = rd;
+                       return 0;
 
+               case 982:       /* icbi */
+                       op->type = MKOP(CACHEOP, ICBI, 0);
+                       op->ea = xform_ea(instr, regs);
+                       return 0;
                }
                break;
        }
 
        /*
-        * Following cases are for loads and stores, so bail out
-        * if we're in little-endian mode.
+        * Loads and stores.
         */
-       if (regs->msr & MSR_LE)
-               return 0;
-
-       /*
-        * Save register RA in case it's an update form load or store
-        * and the access faults.
-        */
-       old_ra = regs->gpr[ra];
+       op->type = UNKNOWN;
+       op->update_reg = ra;
+       op->reg = rd;
+       op->val = regs->gpr[rd];
+       u = (instr >> 20) & UPDATE;
 
        switch (opcode) {
        case 31:
-               u = instr & 0x40;
+               u = instr & UPDATE;
+               op->ea = xform_ea(instr, regs);
                switch ((instr >> 1) & 0x3ff) {
                case 20:        /* lwarx */
-                       ea = xform_ea(instr, regs, 0);
-                       if (ea & 3)
-                               break;          /* can't handle misaligned */
-                       err = -EFAULT;
-                       if (!address_ok(regs, ea, 4))
-                               goto ldst_done;
-                       err = 0;
-                       __get_user_asmx(val, ea, err, "lwarx");
-                       if (!err)
-                               regs->gpr[rd] = val;
-                       goto ldst_done;
+                       op->type = MKOP(LARX, 0, 4);
+                       break;
 
                case 150:       /* stwcx. */
-                       ea = xform_ea(instr, regs, 0);
-                       if (ea & 3)
-                               break;          /* can't handle misaligned */
-                       err = -EFAULT;
-                       if (!address_ok(regs, ea, 4))
-                               goto ldst_done;
-                       err = 0;
-                       __put_user_asmx(regs->gpr[rd], ea, err, "stwcx.", cr);
-                       if (!err)
-                               regs->ccr = (regs->ccr & 0x0fffffff) |
-                                       (cr & 0xe0000000) |
-                                       ((regs->xer >> 3) & 0x10000000);
-                       goto ldst_done;
+                       op->type = MKOP(STCX, 0, 4);
+                       break;
 
 #ifdef __powerpc64__
                case 84:        /* ldarx */
-                       ea = xform_ea(instr, regs, 0);
-                       if (ea & 7)
-                               break;          /* can't handle misaligned */
-                       err = -EFAULT;
-                       if (!address_ok(regs, ea, 8))
-                               goto ldst_done;
-                       err = 0;
-                       __get_user_asmx(val, ea, err, "ldarx");
-                       if (!err)
-                               regs->gpr[rd] = val;
-                       goto ldst_done;
+                       op->type = MKOP(LARX, 0, 8);
+                       break;
 
                case 214:       /* stdcx. */
-                       ea = xform_ea(instr, regs, 0);
-                       if (ea & 7)
-                               break;          /* can't handle misaligned */
-                       err = -EFAULT;
-                       if (!address_ok(regs, ea, 8))
-                               goto ldst_done;
-                       err = 0;
-                       __put_user_asmx(regs->gpr[rd], ea, err, "stdcx.", cr);
-                       if (!err)
-                               regs->ccr = (regs->ccr & 0x0fffffff) |
-                                       (cr & 0xe0000000) |
-                                       ((regs->xer >> 3) & 0x10000000);
-                       goto ldst_done;
+                       op->type = MKOP(STCX, 0, 8);
+                       break;
 
                case 21:        /* ldx */
                case 53:        /* ldux */
-                       err = read_mem(&regs->gpr[rd], xform_ea(instr, regs, u),
-                                      8, regs);
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, u, 8);
+                       break;
 #endif
 
                case 23:        /* lwzx */
                case 55:        /* lwzux */
-                       err = read_mem(&regs->gpr[rd], xform_ea(instr, regs, u),
-                                      4, regs);
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, u, 4);
+                       break;
 
                case 87:        /* lbzx */
                case 119:       /* lbzux */
-                       err = read_mem(&regs->gpr[rd], xform_ea(instr, regs, u),
-                                      1, regs);
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, u, 1);
+                       break;
 
 #ifdef CONFIG_ALTIVEC
                case 103:       /* lvx */
                case 359:       /* lvxl */
                        if (!(regs->msr & MSR_VEC))
-                               break;
-                       ea = xform_ea(instr, regs, 0);
-                       err = do_vec_load(rd, do_lvx, ea, regs);
-                       goto ldst_done;
+                               goto vecunavail;
+                       op->type = MKOP(LOAD_VMX, 0, 16);
+                       break;
 
                case 231:       /* stvx */
                case 487:       /* stvxl */
                        if (!(regs->msr & MSR_VEC))
-                               break;
-                       ea = xform_ea(instr, regs, 0);
-                       err = do_vec_store(rd, do_stvx, ea, regs);
-                       goto ldst_done;
+                               goto vecunavail;
+                       op->type = MKOP(STORE_VMX, 0, 16);
+                       break;
 #endif /* CONFIG_ALTIVEC */
 
 #ifdef __powerpc64__
                case 149:       /* stdx */
                case 181:       /* stdux */
-                       val = regs->gpr[rd];
-                       err = write_mem(val, xform_ea(instr, regs, u), 8, regs);
-                       goto ldst_done;
+                       op->type = MKOP(STORE, u, 8);
+                       break;
 #endif
 
                case 151:       /* stwx */
                case 183:       /* stwux */
-                       val = regs->gpr[rd];
-                       err = write_mem(val, xform_ea(instr, regs, u), 4, regs);
-                       goto ldst_done;
+                       op->type = MKOP(STORE, u, 4);
+                       break;
 
                case 215:       /* stbx */
                case 247:       /* stbux */
-                       val = regs->gpr[rd];
-                       err = write_mem(val, xform_ea(instr, regs, u), 1, regs);
-                       goto ldst_done;
+                       op->type = MKOP(STORE, u, 1);
+                       break;
 
                case 279:       /* lhzx */
                case 311:       /* lhzux */
-                       err = read_mem(&regs->gpr[rd], xform_ea(instr, regs, u),
-                                      2, regs);
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, u, 2);
+                       break;
 
 #ifdef __powerpc64__
                case 341:       /* lwax */
                case 373:       /* lwaux */
-                       err = read_mem(&regs->gpr[rd], xform_ea(instr, regs, u),
-                                      4, regs);
-                       if (!err)
-                               regs->gpr[rd] = (signed int) regs->gpr[rd];
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, SIGNEXT | u, 4);
+                       break;
 #endif
 
                case 343:       /* lhax */
                case 375:       /* lhaux */
-                       err = read_mem(&regs->gpr[rd], xform_ea(instr, regs, u),
-                                      2, regs);
-                       if (!err)
-                               regs->gpr[rd] = (signed short) regs->gpr[rd];
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, SIGNEXT | u, 2);
+                       break;
 
                case 407:       /* sthx */
                case 439:       /* sthux */
-                       val = regs->gpr[rd];
-                       err = write_mem(val, xform_ea(instr, regs, u), 2, regs);
-                       goto ldst_done;
+                       op->type = MKOP(STORE, u, 2);
+                       break;
 
 #ifdef __powerpc64__
                case 532:       /* ldbrx */
-                       err = read_mem(&val, xform_ea(instr, regs, 0), 8, regs);
-                       if (!err)
-                               regs->gpr[rd] = byterev_8(val);
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, BYTEREV, 8);
+                       break;
 
 #endif
+               case 533:       /* lswx */
+                       op->type = MKOP(LOAD_MULTI, 0, regs->xer & 0x7f);
+                       break;
 
                case 534:       /* lwbrx */
-                       err = read_mem(&val, xform_ea(instr, regs, 0), 4, regs);
-                       if (!err)
-                               regs->gpr[rd] = byterev_4(val);
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, BYTEREV, 4);
+                       break;
+
+               case 597:       /* lswi */
+                       if (rb == 0)
+                               rb = 32;        /* # bytes to load */
+                       op->type = MKOP(LOAD_MULTI, 0, rb);
+                       op->ea = 0;
+                       if (ra)
+                               op->ea = truncate_if_32bit(regs->msr,
+                                                          regs->gpr[ra]);
+                       break;
 
 #ifdef CONFIG_PPC_FPU
                case 535:       /* lfsx */
                case 567:       /* lfsux */
                        if (!(regs->msr & MSR_FP))
-                               break;
-                       ea = xform_ea(instr, regs, u);
-                       err = do_fp_load(rd, do_lfs, ea, 4, regs);
-                       goto ldst_done;
+                               goto fpunavail;
+                       op->type = MKOP(LOAD_FP, u, 4);
+                       break;
 
                case 599:       /* lfdx */
                case 631:       /* lfdux */
                        if (!(regs->msr & MSR_FP))
-                               break;
-                       ea = xform_ea(instr, regs, u);
-                       err = do_fp_load(rd, do_lfd, ea, 8, regs);
-                       goto ldst_done;
+                               goto fpunavail;
+                       op->type = MKOP(LOAD_FP, u, 8);
+                       break;
 
                case 663:       /* stfsx */
                case 695:       /* stfsux */
                        if (!(regs->msr & MSR_FP))
-                               break;
-                       ea = xform_ea(instr, regs, u);
-                       err = do_fp_store(rd, do_stfs, ea, 4, regs);
-                       goto ldst_done;
+                               goto fpunavail;
+                       op->type = MKOP(STORE_FP, u, 4);
+                       break;
 
                case 727:       /* stfdx */
                case 759:       /* stfdux */
                        if (!(regs->msr & MSR_FP))
-                               break;
-                       ea = xform_ea(instr, regs, u);
-                       err = do_fp_store(rd, do_stfd, ea, 8, regs);
-                       goto ldst_done;
+                               goto fpunavail;
+                       op->type = MKOP(STORE_FP, u, 8);
+                       break;
 #endif
 
 #ifdef __powerpc64__
                case 660:       /* stdbrx */
-                       val = byterev_8(regs->gpr[rd]);
-                       err = write_mem(val, xform_ea(instr, regs, 0), 8, regs);
-                       goto ldst_done;
+                       op->type = MKOP(STORE, BYTEREV, 8);
+                       op->val = byterev_8(regs->gpr[rd]);
+                       break;
 
 #endif
+               case 661:       /* stswx */
+                       op->type = MKOP(STORE_MULTI, 0, regs->xer & 0x7f);
+                       break;
+
                case 662:       /* stwbrx */
-                       val = byterev_4(regs->gpr[rd]);
-                       err = write_mem(val, xform_ea(instr, regs, 0), 4, regs);
-                       goto ldst_done;
+                       op->type = MKOP(STORE, BYTEREV, 4);
+                       op->val = byterev_4(regs->gpr[rd]);
+                       break;
+
+               case 725:
+                       if (rb == 0)
+                               rb = 32;        /* # bytes to store */
+                       op->type = MKOP(STORE_MULTI, 0, rb);
+                       op->ea = 0;
+                       if (ra)
+                               op->ea = truncate_if_32bit(regs->msr,
+                                                          regs->gpr[ra]);
+                       break;
 
                case 790:       /* lhbrx */
-                       err = read_mem(&val, xform_ea(instr, regs, 0), 2, regs);
-                       if (!err)
-                               regs->gpr[rd] = byterev_2(val);
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, BYTEREV, 2);
+                       break;
 
                case 918:       /* sthbrx */
-                       val = byterev_2(regs->gpr[rd]);
-                       err = write_mem(val, xform_ea(instr, regs, 0), 2, regs);
-                       goto ldst_done;
+                       op->type = MKOP(STORE, BYTEREV, 2);
+                       op->val = byterev_2(regs->gpr[rd]);
+                       break;
 
 #ifdef CONFIG_VSX
                case 844:       /* lxvd2x */
                case 876:       /* lxvd2ux */
                        if (!(regs->msr & MSR_VSX))
-                               break;
-                       rd |= (instr & 1) << 5;
-                       ea = xform_ea(instr, regs, u);
-                       err = do_vsx_load(rd, do_lxvd2x, ea, regs);
-                       goto ldst_done;
+                               goto vsxunavail;
+                       op->reg = rd | ((instr & 1) << 5);
+                       op->type = MKOP(LOAD_VSX, u, 16);
+                       break;
 
                case 972:       /* stxvd2x */
                case 1004:      /* stxvd2ux */
                        if (!(regs->msr & MSR_VSX))
-                               break;
-                       rd |= (instr & 1) << 5;
-                       ea = xform_ea(instr, regs, u);
-                       err = do_vsx_store(rd, do_stxvd2x, ea, regs);
-                       goto ldst_done;
+                               goto vsxunavail;
+                       op->reg = rd | ((instr & 1) << 5);
+                       op->type = MKOP(STORE_VSX, u, 16);
+                       break;
 
 #endif /* CONFIG_VSX */
                }
@@ -1552,178 +1539,123 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
 
        case 32:        /* lwz */
        case 33:        /* lwzu */
-               err = read_mem(&regs->gpr[rd], dform_ea(instr, regs), 4, regs);
-               goto ldst_done;
+               op->type = MKOP(LOAD, u, 4);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 34:        /* lbz */
        case 35:        /* lbzu */
-               err = read_mem(&regs->gpr[rd], dform_ea(instr, regs), 1, regs);
-               goto ldst_done;
+               op->type = MKOP(LOAD, u, 1);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 36:        /* stw */
-               val = regs->gpr[rd];
-               err = write_mem(val, dform_ea(instr, regs), 4, regs);
-               goto ldst_done;
-
        case 37:        /* stwu */
-               val = regs->gpr[rd];
-               val3 = dform_ea(instr, regs);
-               /*
-                * For PPC32 we always use stwu to change stack point with r1. So
-                * this emulated store may corrupt the exception frame, now we
-                * have to provide the exception frame trampoline, which is pushed
-                * below the kprobed function stack. So we only update gpr[1] but
-                * don't emulate the real store operation. We will do real store
-                * operation safely in exception return code by checking this flag.
-                */
-               if ((ra == 1) && !(regs->msr & MSR_PR) \
-                       && (val3 >= (regs->gpr[1] - STACK_INT_FRAME_SIZE))) {
-#ifdef CONFIG_PPC32
-                       /*
-                        * Check if we will touch kernel sack overflow
-                        */
-                       if (val3 - STACK_INT_FRAME_SIZE <= current->thread.ksp_limit) {
-                               printk(KERN_CRIT "Can't kprobe this since Kernel stack overflow.\n");
-                               err = -EINVAL;
-                               break;
-                       }
-#endif /* CONFIG_PPC32 */
-                       /*
-                        * Check if we already set since that means we'll
-                        * lose the previous value.
-                        */
-                       WARN_ON(test_thread_flag(TIF_EMULATE_STACK_STORE));
-                       set_thread_flag(TIF_EMULATE_STACK_STORE);
-                       err = 0;
-               } else
-                       err = write_mem(val, val3, 4, regs);
-               goto ldst_done;
+               op->type = MKOP(STORE, u, 4);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 38:        /* stb */
        case 39:        /* stbu */
-               val = regs->gpr[rd];
-               err = write_mem(val, dform_ea(instr, regs), 1, regs);
-               goto ldst_done;
+               op->type = MKOP(STORE, u, 1);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 40:        /* lhz */
        case 41:        /* lhzu */
-               err = read_mem(&regs->gpr[rd], dform_ea(instr, regs), 2, regs);
-               goto ldst_done;
+               op->type = MKOP(LOAD, u, 2);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 42:        /* lha */
        case 43:        /* lhau */
-               err = read_mem(&regs->gpr[rd], dform_ea(instr, regs), 2, regs);
-               if (!err)
-                       regs->gpr[rd] = (signed short) regs->gpr[rd];
-               goto ldst_done;
+               op->type = MKOP(LOAD, SIGNEXT | u, 2);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 44:        /* sth */
        case 45:        /* sthu */
-               val = regs->gpr[rd];
-               err = write_mem(val, dform_ea(instr, regs), 2, regs);
-               goto ldst_done;
+               op->type = MKOP(STORE, u, 2);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 46:        /* lmw */
-               ra = (instr >> 16) & 0x1f;
                if (ra >= rd)
                        break;          /* invalid form, ra in range to load */
-               ea = dform_ea(instr, regs);
-               do {
-                       err = read_mem(&regs->gpr[rd], ea, 4, regs);
-                       if (err)
-                               return 0;
-                       ea += 4;
-               } while (++rd < 32);
-               goto instr_done;
+               op->type = MKOP(LOAD_MULTI, 0, 4 * (32 - rd));
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 47:        /* stmw */
-               ea = dform_ea(instr, regs);
-               do {
-                       err = write_mem(regs->gpr[rd], ea, 4, regs);
-                       if (err)
-                               return 0;
-                       ea += 4;
-               } while (++rd < 32);
-               goto instr_done;
+               op->type = MKOP(STORE_MULTI, 0, 4 * (32 - rd));
+               op->ea = dform_ea(instr, regs);
+               break;
 
 #ifdef CONFIG_PPC_FPU
        case 48:        /* lfs */
        case 49:        /* lfsu */
                if (!(regs->msr & MSR_FP))
-                       break;
-               ea = dform_ea(instr, regs);
-               err = do_fp_load(rd, do_lfs, ea, 4, regs);
-               goto ldst_done;
+                       goto fpunavail;
+               op->type = MKOP(LOAD_FP, u, 4);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 50:        /* lfd */
        case 51:        /* lfdu */
                if (!(regs->msr & MSR_FP))
-                       break;
-               ea = dform_ea(instr, regs);
-               err = do_fp_load(rd, do_lfd, ea, 8, regs);
-               goto ldst_done;
+                       goto fpunavail;
+               op->type = MKOP(LOAD_FP, u, 8);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 52:        /* stfs */
        case 53:        /* stfsu */
                if (!(regs->msr & MSR_FP))
-                       break;
-               ea = dform_ea(instr, regs);
-               err = do_fp_store(rd, do_stfs, ea, 4, regs);
-               goto ldst_done;
+                       goto fpunavail;
+               op->type = MKOP(STORE_FP, u, 4);
+               op->ea = dform_ea(instr, regs);
+               break;
 
        case 54:        /* stfd */
        case 55:        /* stfdu */
                if (!(regs->msr & MSR_FP))
-                       break;
-               ea = dform_ea(instr, regs);
-               err = do_fp_store(rd, do_stfd, ea, 8, regs);
-               goto ldst_done;
+                       goto fpunavail;
+               op->type = MKOP(STORE_FP, u, 8);
+               op->ea = dform_ea(instr, regs);
+               break;
 #endif
 
 #ifdef __powerpc64__
        case 58:        /* ld[u], lwa */
+               op->ea = dsform_ea(instr, regs);
                switch (instr & 3) {
                case 0:         /* ld */
-                       err = read_mem(&regs->gpr[rd], dsform_ea(instr, regs),
-                                      8, regs);
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, 0, 8);
+                       break;
                case 1:         /* ldu */
-                       err = read_mem(&regs->gpr[rd], dsform_ea(instr, regs),
-                                      8, regs);
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, UPDATE, 8);
+                       break;
                case 2:         /* lwa */
-                       err = read_mem(&regs->gpr[rd], dsform_ea(instr, regs),
-                                      4, regs);
-                       if (!err)
-                               regs->gpr[rd] = (signed int) regs->gpr[rd];
-                       goto ldst_done;
+                       op->type = MKOP(LOAD, SIGNEXT, 4);
+                       break;
                }
                break;
 
        case 62:        /* std[u] */
-               val = regs->gpr[rd];
+               op->ea = dsform_ea(instr, regs);
                switch (instr & 3) {
                case 0:         /* std */
-                       err = write_mem(val, dsform_ea(instr, regs), 8, regs);
-                       goto ldst_done;
+                       op->type = MKOP(STORE, 0, 8);
+                       break;
                case 1:         /* stdu */
-                       err = write_mem(val, dsform_ea(instr, regs), 8, regs);
-                       goto ldst_done;
+                       op->type = MKOP(STORE, UPDATE, 8);
+                       break;
                }
                break;
 #endif /* __powerpc64__ */
 
        }
-       err = -EINVAL;
-
- ldst_done:
-       if (err) {
-               regs->gpr[ra] = old_ra;
-               return 0;       /* invoke DSI if -EFAULT? */
-       }
- instr_done:
-       regs->nip = truncate_if_32bit(regs->msr, regs->nip + 4);
-       return 1;
+       return 0;
 
  logical_done:
        if (instr & 1)
@@ -1733,5 +1665,349 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
  arith_done:
        if (instr & 1)
                set_cr0(regs, rd);
-       goto instr_done;
+
+ instr_done:
+       regs->nip = truncate_if_32bit(regs->msr, regs->nip + 4);
+       return 1;
+
+ priv:
+       op->type = INTERRUPT | 0x700;
+       op->val = SRR1_PROGPRIV;
+       return 0;
+
+ trap:
+       op->type = INTERRUPT | 0x700;
+       op->val = SRR1_PROGTRAP;
+       return 0;
+
+#ifdef CONFIG_PPC_FPU
+ fpunavail:
+       op->type = INTERRUPT | 0x800;
+       return 0;
+#endif
+
+#ifdef CONFIG_ALTIVEC
+ vecunavail:
+       op->type = INTERRUPT | 0xf20;
+       return 0;
+#endif
+
+#ifdef CONFIG_VSX
+ vsxunavail:
+       op->type = INTERRUPT | 0xf40;
+       return 0;
+#endif
+}
+EXPORT_SYMBOL_GPL(analyse_instr);
+
+/*
+ * For PPC32 we always use stwu with r1 to change the stack pointer.
+ * So this emulated store may corrupt the exception frame, now we
+ * have to provide the exception frame trampoline, which is pushed
+ * below the kprobed function stack. So we only update gpr[1] but
+ * don't emulate the real store operation. We will do real store
+ * operation safely in exception return code by checking this flag.
+ */
+static __kprobes int handle_stack_update(unsigned long ea, struct pt_regs *regs)
+{
+#ifdef CONFIG_PPC32
+       /*
+        * Check if we will touch kernel stack overflow
+        */
+       if (ea - STACK_INT_FRAME_SIZE <= current->thread.ksp_limit) {
+               printk(KERN_CRIT "Can't kprobe this since kernel stack would overflow.\n");
+               return -EINVAL;
+       }
+#endif /* CONFIG_PPC32 */
+       /*
+        * Check if we already set since that means we'll
+        * lose the previous value.
+        */
+       WARN_ON(test_thread_flag(TIF_EMULATE_STACK_STORE));
+       set_thread_flag(TIF_EMULATE_STACK_STORE);
+       return 0;
+}
+
+static __kprobes void do_signext(unsigned long *valp, int size)
+{
+       switch (size) {
+       case 2:
+               *valp = (signed short) *valp;
+               break;
+       case 4:
+               *valp = (signed int) *valp;
+               break;
+       }
+}
+
+static __kprobes void do_byterev(unsigned long *valp, int size)
+{
+       switch (size) {
+       case 2:
+               *valp = byterev_2(*valp);
+               break;
+       case 4:
+               *valp = byterev_4(*valp);
+               break;
+#ifdef __powerpc64__
+       case 8:
+               *valp = byterev_8(*valp);
+               break;
+#endif
+       }
+}
+
+/*
+ * Emulate instructions that cause a transfer of control,
+ * loads and stores, and a few other instructions.
+ * Returns 1 if the step was emulated, 0 if not,
+ * or -1 if the instruction is one that should not be stepped,
+ * such as an rfid, or a mtmsrd that would clear MSR_RI.
+ */
+int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
+{
+       struct instruction_op op;
+       int r, err, size;
+       unsigned long val;
+       unsigned int cr;
+       int i, rd, nb;
+
+       r = analyse_instr(&op, regs, instr);
+       if (r != 0)
+               return r;
+
+       err = 0;
+       size = GETSIZE(op.type);
+       switch (op.type & INSTR_TYPE_MASK) {
+       case CACHEOP:
+               if (!address_ok(regs, op.ea, 8))
+                       return 0;
+               switch (op.type & CACHEOP_MASK) {
+               case DCBST:
+                       __cacheop_user_asmx(op.ea, err, "dcbst");
+                       break;
+               case DCBF:
+                       __cacheop_user_asmx(op.ea, err, "dcbf");
+                       break;
+               case DCBTST:
+                       if (op.reg == 0)
+                               prefetchw((void *) op.ea);
+                       break;
+               case DCBT:
+                       if (op.reg == 0)
+                               prefetch((void *) op.ea);
+                       break;
+               case ICBI:
+                       __cacheop_user_asmx(op.ea, err, "icbi");
+                       break;
+               }
+               if (err)
+                       return 0;
+               goto instr_done;
+
+       case LARX:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               if (op.ea & (size - 1))
+                       break;          /* can't handle misaligned */
+               err = -EFAULT;
+               if (!address_ok(regs, op.ea, size))
+                       goto ldst_done;
+               err = 0;
+               switch (size) {
+               case 4:
+                       __get_user_asmx(val, op.ea, err, "lwarx");
+                       break;
+               case 8:
+                       __get_user_asmx(val, op.ea, err, "ldarx");
+                       break;
+               default:
+                       return 0;
+               }
+               if (!err)
+                       regs->gpr[op.reg] = val;
+               goto ldst_done;
+
+       case STCX:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               if (op.ea & (size - 1))
+                       break;          /* can't handle misaligned */
+               err = -EFAULT;
+               if (!address_ok(regs, op.ea, size))
+                       goto ldst_done;
+               err = 0;
+               switch (size) {
+               case 4:
+                       __put_user_asmx(op.val, op.ea, err, "stwcx.", cr);
+                       break;
+               case 8:
+                       __put_user_asmx(op.val, op.ea, err, "stdcx.", cr);
+                       break;
+               default:
+                       return 0;
+               }
+               if (!err)
+                       regs->ccr = (regs->ccr & 0x0fffffff) |
+                               (cr & 0xe0000000) |
+                               ((regs->xer >> 3) & 0x10000000);
+               goto ldst_done;
+
+       case LOAD:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               err = read_mem(&regs->gpr[op.reg], op.ea, size, regs);
+               if (!err) {
+                       if (op.type & SIGNEXT)
+                               do_signext(&regs->gpr[op.reg], size);
+                       if (op.type & BYTEREV)
+                               do_byterev(&regs->gpr[op.reg], size);
+               }
+               goto ldst_done;
+
+       case LOAD_FP:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               if (size == 4)
+                       err = do_fp_load(op.reg, do_lfs, op.ea, size, regs);
+               else
+                       err = do_fp_load(op.reg, do_lfd, op.ea, size, regs);
+               goto ldst_done;
+
+#ifdef CONFIG_ALTIVEC
+       case LOAD_VMX:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               err = do_vec_load(op.reg, do_lvx, op.ea & ~0xfUL, regs);
+               goto ldst_done;
+#endif
+#ifdef CONFIG_VSX
+       case LOAD_VSX:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               err = do_vsx_load(op.reg, do_lxvd2x, op.ea, regs);
+               goto ldst_done;
+#endif
+       case LOAD_MULTI:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               rd = op.reg;
+               for (i = 0; i < size; i += 4) {
+                       nb = size - i;
+                       if (nb > 4)
+                               nb = 4;
+                       err = read_mem(&regs->gpr[rd], op.ea, nb, regs);
+                       if (err)
+                               return 0;
+                       if (nb < 4)     /* left-justify last bytes */
+                               regs->gpr[rd] <<= 32 - 8 * nb;
+                       op.ea += 4;
+                       ++rd;
+               }
+               goto instr_done;
+
+       case STORE:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               if ((op.type & UPDATE) && size == sizeof(long) &&
+                   op.reg == 1 && op.update_reg == 1 &&
+                   !(regs->msr & MSR_PR) &&
+                   op.ea >= regs->gpr[1] - STACK_INT_FRAME_SIZE) {
+                       err = handle_stack_update(op.ea, regs);
+                       goto ldst_done;
+               }
+               err = write_mem(op.val, op.ea, size, regs);
+               goto ldst_done;
+
+       case STORE_FP:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               if (size == 4)
+                       err = do_fp_store(op.reg, do_stfs, op.ea, size, regs);
+               else
+                       err = do_fp_store(op.reg, do_stfd, op.ea, size, regs);
+               goto ldst_done;
+
+#ifdef CONFIG_ALTIVEC
+       case STORE_VMX:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               err = do_vec_store(op.reg, do_stvx, op.ea & ~0xfUL, regs);
+               goto ldst_done;
+#endif
+#ifdef CONFIG_VSX
+       case STORE_VSX:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               err = do_vsx_store(op.reg, do_stxvd2x, op.ea, regs);
+               goto ldst_done;
+#endif
+       case STORE_MULTI:
+               if (regs->msr & MSR_LE)
+                       return 0;
+               rd = op.reg;
+               for (i = 0; i < size; i += 4) {
+                       val = regs->gpr[rd];
+                       nb = size - i;
+                       if (nb > 4)
+                               nb = 4;
+                       else
+                               val >>= 32 - 8 * nb;
+                       err = write_mem(val, op.ea, nb, regs);
+                       if (err)
+                               return 0;
+                       op.ea += 4;
+                       ++rd;
+               }
+               goto instr_done;
+
+       case MFMSR:
+               regs->gpr[op.reg] = regs->msr & MSR_MASK;
+               goto instr_done;
+
+       case MTMSR:
+               val = regs->gpr[op.reg];
+               if ((val & MSR_RI) == 0)
+                       /* can't step mtmsr[d] that would clear MSR_RI */
+                       return -1;
+               /* here op.val is the mask of bits to change */
+               regs->msr = (regs->msr & ~op.val) | (val & op.val);
+               goto instr_done;
+
+#ifdef CONFIG_PPC64
+       case SYSCALL:   /* sc */
+               /*
+                * N.B. this uses knowledge about how the syscall
+                * entry code works.  If that is changed, this will
+                * need to be changed also.
+                */
+               if (regs->gpr[0] == 0x1ebe &&
+                   cpu_has_feature(CPU_FTR_REAL_LE)) {
+                       regs->msr ^= MSR_LE;
+                       goto instr_done;
+               }
+               regs->gpr[9] = regs->gpr[13];
+               regs->gpr[10] = MSR_KERNEL;
+               regs->gpr[11] = regs->nip + 4;
+               regs->gpr[12] = regs->msr & MSR_MASK;
+               regs->gpr[13] = (unsigned long) get_paca();
+               regs->nip = (unsigned long) &system_call_common;
+               regs->msr = MSR_KERNEL;
+               return 1;
+
+       case RFI:
+               return -1;
+#endif
+       }
+       return 0;
+
+ ldst_done:
+       if (err)
+               return 0;
+       if (op.type & UPDATE)
+               regs->gpr[op.update_reg] = op.ea;
+
+ instr_done:
+       regs->nip = truncate_if_32bit(regs->msr, regs->nip + 4);
+       return 1;
 }
index d0130fff20e532791fec298cd07ad16e59b010b1..325e861616a175b65e72db96514d2f84e087d3d3 100644 (file)
@@ -34,3 +34,4 @@ obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += hugepage-hash64.o
 obj-$(CONFIG_PPC_SUBPAGE_PROT) += subpage-prot.o
 obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o
 obj-$(CONFIG_HIGHMEM)          += highmem.o
+obj-$(CONFIG_PPC_COPRO_BASE)   += copro_fault.o
similarity index 56%
rename from arch/powerpc/platforms/cell/spu_fault.c
rename to arch/powerpc/mm/copro_fault.c
index 641e7273d75ae687335716c5b4e87c9afcdb98c7..0f9939e693df6b8422a7b87301f7dcd250816c34 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * SPU mm fault handler
+ * CoProcessor (SPU/AFU) mm fault handler
  *
  * (C) Copyright IBM Deutschland Entwicklung GmbH 2007
  *
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/export.h>
-
+#include <asm/reg.h>
+#include <asm/copro.h>
 #include <asm/spu.h>
-#include <asm/spu_csa.h>
+#include <misc/cxl.h>
 
 /*
  * This ought to be kept in sync with the powerpc specific do_page_fault
  * function. Currently, there are a few corner cases that we haven't had
  * to handle fortunately.
  */
-int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
+int copro_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
                unsigned long dsisr, unsigned *flt)
 {
        struct vm_area_struct *vma;
@@ -58,12 +59,12 @@ int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
                        goto out_unlock;
        }
 
-       is_write = dsisr & MFC_DSISR_ACCESS_PUT;
+       is_write = dsisr & DSISR_ISSTORE;
        if (is_write) {
                if (!(vma->vm_flags & VM_WRITE))
                        goto out_unlock;
        } else {
-               if (dsisr & MFC_DSISR_ACCESS_DENIED)
+               if (dsisr & DSISR_PROTFAULT)
                        goto out_unlock;
                if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
                        goto out_unlock;
@@ -91,4 +92,58 @@ out_unlock:
        up_read(&mm->mmap_sem);
        return ret;
 }
-EXPORT_SYMBOL_GPL(spu_handle_mm_fault);
+EXPORT_SYMBOL_GPL(copro_handle_mm_fault);
+
+int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb)
+{
+       u64 vsid;
+       int psize, ssize;
+
+       slb->esid = (ea & ESID_MASK) | SLB_ESID_V;
+
+       switch (REGION_ID(ea)) {
+       case USER_REGION_ID:
+               pr_devel("%s: 0x%llx -- USER_REGION_ID\n", __func__, ea);
+               psize = get_slice_psize(mm, ea);
+               ssize = user_segment_size(ea);
+               vsid = get_vsid(mm->context.id, ea, ssize);
+               break;
+       case VMALLOC_REGION_ID:
+               pr_devel("%s: 0x%llx -- VMALLOC_REGION_ID\n", __func__, ea);
+               if (ea < VMALLOC_END)
+                       psize = mmu_vmalloc_psize;
+               else
+                       psize = mmu_io_psize;
+               ssize = mmu_kernel_ssize;
+               vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
+               break;
+       case KERNEL_REGION_ID:
+               pr_devel("%s: 0x%llx -- KERNEL_REGION_ID\n", __func__, ea);
+               psize = mmu_linear_psize;
+               ssize = mmu_kernel_ssize;
+               vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
+               break;
+       default:
+               pr_debug("%s: invalid region access at %016llx\n", __func__, ea);
+               return 1;
+       }
+
+       vsid = (vsid << slb_vsid_shift(ssize)) | SLB_VSID_USER;
+
+       vsid |= mmu_psize_defs[psize].sllp |
+               ((ssize == MMU_SEGSIZE_1T) ? SLB_VSID_B_1T : 0);
+
+       slb->vsid = vsid;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(copro_calculate_slb);
+
+void copro_flush_all_slbs(struct mm_struct *mm)
+{
+#ifdef CONFIG_SPU_BASE
+       spu_flush_all_slbs(mm);
+#endif
+       cxl_slbia(mm);
+}
+EXPORT_SYMBOL_GPL(copro_flush_all_slbs);
index 51ab9e7e6c391b9497a08730a7b5e4c625c96304..24b3f4949df40b984d1a3398646d4f1be6b5d322 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/magic.h>
 #include <linux/ratelimit.h>
 #include <linux/context_tracking.h>
+#include <linux/hugetlb.h>
 
 #include <asm/firmware.h>
 #include <asm/page.h>
@@ -114,22 +115,37 @@ static int store_updates_sp(struct pt_regs *regs)
 #define MM_FAULT_CONTINUE      -1
 #define MM_FAULT_ERR(sig)      (sig)
 
-static int do_sigbus(struct pt_regs *regs, unsigned long address)
+static int do_sigbus(struct pt_regs *regs, unsigned long address,
+                    unsigned int fault)
 {
        siginfo_t info;
+       unsigned int lsb = 0;
 
        up_read(&current->mm->mmap_sem);
 
-       if (user_mode(regs)) {
-               current->thread.trap_nr = BUS_ADRERR;
-               info.si_signo = SIGBUS;
-               info.si_errno = 0;
-               info.si_code = BUS_ADRERR;
-               info.si_addr = (void __user *)address;
-               force_sig_info(SIGBUS, &info, current);
-               return MM_FAULT_RETURN;
+       if (!user_mode(regs))
+               return MM_FAULT_ERR(SIGBUS);
+
+       current->thread.trap_nr = BUS_ADRERR;
+       info.si_signo = SIGBUS;
+       info.si_errno = 0;
+       info.si_code = BUS_ADRERR;
+       info.si_addr = (void __user *)address;
+#ifdef CONFIG_MEMORY_FAILURE
+       if (fault & (VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)) {
+               pr_err("MCE: Killing %s:%d due to hardware memory corruption fault at %lx\n",
+                       current->comm, current->pid, address);
+               info.si_code = BUS_MCEERR_AR;
        }
-       return MM_FAULT_ERR(SIGBUS);
+
+       if (fault & VM_FAULT_HWPOISON_LARGE)
+               lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault));
+       if (fault & VM_FAULT_HWPOISON)
+               lsb = PAGE_SHIFT;
+#endif
+       info.si_addr_lsb = lsb;
+       force_sig_info(SIGBUS, &info, current);
+       return MM_FAULT_RETURN;
 }
 
 static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)
@@ -170,11 +186,8 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)
                return MM_FAULT_RETURN;
        }
 
-       /* Bus error. x86 handles HWPOISON here, we'll add this if/when
-        * we support the feature in HW
-        */
-       if (fault & VM_FAULT_SIGBUS)
-               return do_sigbus(regs, addr);
+       if (fault & (VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE))
+               return do_sigbus(regs, addr, fault);
 
        /* We don't understand the fault code, this is fatal */
        BUG();
index afc0a8295f84c7097217855fae59f62b1ed6149e..ae4962a06476eed53eb4f6bfa84fc283f59197c4 100644 (file)
@@ -29,6 +29,8 @@
 #include <asm/kexec.h>
 #include <asm/ppc-opcode.h>
 
+#include <misc/cxl.h>
+
 #ifdef DEBUG_LOW
 #define DBG_LOW(fmt...) udbg_printf(fmt)
 #else
@@ -149,9 +151,11 @@ static inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize)
 static inline void tlbie(unsigned long vpn, int psize, int apsize,
                         int ssize, int local)
 {
-       unsigned int use_local = local && mmu_has_feature(MMU_FTR_TLBIEL);
+       unsigned int use_local;
        int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
 
+       use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) && !cxl_ctx_in_use();
+
        if (use_local)
                use_local = mmu_psize_defs[psize].tlbiel;
        if (lock_tlbie && !use_local)
index daee7f4e5a14ca0048a7dfee9f2f07921529fced..d5339a3b99458d191123e56c94a6ef3df0b48fc3 100644 (file)
@@ -51,7 +51,7 @@
 #include <asm/cacheflush.h>
 #include <asm/cputable.h>
 #include <asm/sections.h>
-#include <asm/spu.h>
+#include <asm/copro.h>
 #include <asm/udbg.h>
 #include <asm/code-patching.h>
 #include <asm/fadump.h>
@@ -92,12 +92,14 @@ extern unsigned long dart_tablebase;
 
 static unsigned long _SDR1;
 struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT];
+EXPORT_SYMBOL_GPL(mmu_psize_defs);
 
 struct hash_pte *htab_address;
 unsigned long htab_size_bytes;
 unsigned long htab_hash_mask;
 EXPORT_SYMBOL_GPL(htab_hash_mask);
 int mmu_linear_psize = MMU_PAGE_4K;
+EXPORT_SYMBOL_GPL(mmu_linear_psize);
 int mmu_virtual_psize = MMU_PAGE_4K;
 int mmu_vmalloc_psize = MMU_PAGE_4K;
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
@@ -105,6 +107,7 @@ int mmu_vmemmap_psize = MMU_PAGE_4K;
 #endif
 int mmu_io_psize = MMU_PAGE_4K;
 int mmu_kernel_ssize = MMU_SEGSIZE_256M;
+EXPORT_SYMBOL_GPL(mmu_kernel_ssize);
 int mmu_highuser_ssize = MMU_SEGSIZE_256M;
 u16 mmu_slb_size = 64;
 EXPORT_SYMBOL_GPL(mmu_slb_size);
@@ -333,70 +336,69 @@ static int __init htab_dt_scan_page_sizes(unsigned long node,
                return 0;
 
        prop = of_get_flat_dt_prop(node, "ibm,segment-page-sizes", &size);
-       if (prop != NULL) {
-               pr_info("Page sizes from device-tree:\n");
-               size /= 4;
-               cur_cpu_spec->mmu_features &= ~(MMU_FTR_16M_PAGE);
-               while(size > 0) {
-                       unsigned int base_shift = be32_to_cpu(prop[0]);
-                       unsigned int slbenc = be32_to_cpu(prop[1]);
-                       unsigned int lpnum = be32_to_cpu(prop[2]);
-                       struct mmu_psize_def *def;
-                       int idx, base_idx;
-
-                       size -= 3; prop += 3;
-                       base_idx = get_idx_from_shift(base_shift);
-                       if (base_idx < 0) {
-                               /*
-                                * skip the pte encoding also
-                                */
-                               prop += lpnum * 2; size -= lpnum * 2;
+       if (!prop)
+               return 0;
+
+       pr_info("Page sizes from device-tree:\n");
+       size /= 4;
+       cur_cpu_spec->mmu_features &= ~(MMU_FTR_16M_PAGE);
+       while(size > 0) {
+               unsigned int base_shift = be32_to_cpu(prop[0]);
+               unsigned int slbenc = be32_to_cpu(prop[1]);
+               unsigned int lpnum = be32_to_cpu(prop[2]);
+               struct mmu_psize_def *def;
+               int idx, base_idx;
+
+               size -= 3; prop += 3;
+               base_idx = get_idx_from_shift(base_shift);
+               if (base_idx < 0) {
+                       /* skip the pte encoding also */
+                       prop += lpnum * 2; size -= lpnum * 2;
+                       continue;
+               }
+               def = &mmu_psize_defs[base_idx];
+               if (base_idx == MMU_PAGE_16M)
+                       cur_cpu_spec->mmu_features |= MMU_FTR_16M_PAGE;
+
+               def->shift = base_shift;
+               if (base_shift <= 23)
+                       def->avpnm = 0;
+               else
+                       def->avpnm = (1 << (base_shift - 23)) - 1;
+               def->sllp = slbenc;
+               /*
+                * We don't know for sure what's up with tlbiel, so
+                * for now we only set it for 4K and 64K pages
+                */
+               if (base_idx == MMU_PAGE_4K || base_idx == MMU_PAGE_64K)
+                       def->tlbiel = 1;
+               else
+                       def->tlbiel = 0;
+
+               while (size > 0 && lpnum) {
+                       unsigned int shift = be32_to_cpu(prop[0]);
+                       int penc  = be32_to_cpu(prop[1]);
+
+                       prop += 2; size -= 2;
+                       lpnum--;
+
+                       idx = get_idx_from_shift(shift);
+                       if (idx < 0)
                                continue;
-                       }
-                       def = &mmu_psize_defs[base_idx];
-                       if (base_idx == MMU_PAGE_16M)
-                               cur_cpu_spec->mmu_features |= MMU_FTR_16M_PAGE;
-
-                       def->shift = base_shift;
-                       if (base_shift <= 23)
-                               def->avpnm = 0;
-                       else
-                               def->avpnm = (1 << (base_shift - 23)) - 1;
-                       def->sllp = slbenc;
-                       /*
-                        * We don't know for sure what's up with tlbiel, so
-                        * for now we only set it for 4K and 64K pages
-                        */
-                       if (base_idx == MMU_PAGE_4K || base_idx == MMU_PAGE_64K)
-                               def->tlbiel = 1;
-                       else
-                               def->tlbiel = 0;
-
-                       while (size > 0 && lpnum) {
-                               unsigned int shift = be32_to_cpu(prop[0]);
-                               int penc  = be32_to_cpu(prop[1]);
-
-                               prop += 2; size -= 2;
-                               lpnum--;
-
-                               idx = get_idx_from_shift(shift);
-                               if (idx < 0)
-                                       continue;
-
-                               if (penc == -1)
-                                       pr_err("Invalid penc for base_shift=%d "
-                                              "shift=%d\n", base_shift, shift);
-
-                               def->penc[idx] = penc;
-                               pr_info("base_shift=%d: shift=%d, sllp=0x%04lx,"
-                                       " avpnm=0x%08lx, tlbiel=%d, penc=%d\n",
-                                       base_shift, shift, def->sllp,
-                                       def->avpnm, def->tlbiel, def->penc[idx]);
-                       }
+
+                       if (penc == -1)
+                               pr_err("Invalid penc for base_shift=%d "
+                                      "shift=%d\n", base_shift, shift);
+
+                       def->penc[idx] = penc;
+                       pr_info("base_shift=%d: shift=%d, sllp=0x%04lx,"
+                               " avpnm=0x%08lx, tlbiel=%d, penc=%d\n",
+                               base_shift, shift, def->sllp,
+                               def->avpnm, def->tlbiel, def->penc[idx]);
                }
-               return 1;
        }
-       return 0;
+
+       return 1;
 }
 
 #ifdef CONFIG_HUGETLB_PAGE
@@ -867,7 +869,7 @@ unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap)
 }
 
 #ifdef CONFIG_PPC_MM_SLICES
-unsigned int get_paca_psize(unsigned long addr)
+static unsigned int get_paca_psize(unsigned long addr)
 {
        u64 lpsizes;
        unsigned char *hpsizes;
@@ -901,10 +903,8 @@ void demote_segment_4k(struct mm_struct *mm, unsigned long addr)
        if (get_slice_psize(mm, addr) == MMU_PAGE_4K)
                return;
        slice_set_range_psize(mm, addr, 1, MMU_PAGE_4K);
-#ifdef CONFIG_SPU_BASE
-       spu_flush_all_slbs(mm);
-#endif
-       if (get_paca_psize(addr) != MMU_PAGE_4K) {
+       copro_flush_all_slbs(mm);
+       if ((get_paca_psize(addr) != MMU_PAGE_4K) && (current->mm == mm)) {
                get_paca()->context = mm->context;
                slb_flush_and_rebolt();
        }
@@ -989,12 +989,11 @@ static void check_paca_psize(unsigned long ea, struct mm_struct *mm,
  * -1 - critical hash insertion error
  * -2 - access not permitted by subpage protection mechanism
  */
-int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
+int hash_page_mm(struct mm_struct *mm, unsigned long ea, unsigned long access, unsigned long trap)
 {
        enum ctx_state prev_state = exception_enter();
        pgd_t *pgdir;
        unsigned long vsid;
-       struct mm_struct *mm;
        pte_t *ptep;
        unsigned hugeshift;
        const struct cpumask *tmp;
@@ -1008,7 +1007,6 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
        switch (REGION_ID(ea)) {
        case USER_REGION_ID:
                user_region = 1;
-               mm = current->mm;
                if (! mm) {
                        DBG_LOW(" user region with no mm !\n");
                        rc = 1;
@@ -1019,7 +1017,6 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
                vsid = get_vsid(mm->context.id, ea, ssize);
                break;
        case VMALLOC_REGION_ID:
-               mm = &init_mm;
                vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
                if (ea < VMALLOC_END)
                        psize = mmu_vmalloc_psize;
@@ -1104,7 +1101,8 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
                        WARN_ON(1);
                }
 #endif
-               check_paca_psize(ea, mm, psize, user_region);
+               if (current->mm == mm)
+                       check_paca_psize(ea, mm, psize, user_region);
 
                goto bail;
        }
@@ -1141,13 +1139,12 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
                               "to 4kB pages because of "
                               "non-cacheable mapping\n");
                        psize = mmu_vmalloc_psize = MMU_PAGE_4K;
-#ifdef CONFIG_SPU_BASE
-                       spu_flush_all_slbs(mm);
-#endif
+                       copro_flush_all_slbs(mm);
                }
        }
 
-       check_paca_psize(ea, mm, psize, user_region);
+       if (current->mm == mm)
+               check_paca_psize(ea, mm, psize, user_region);
 #endif /* CONFIG_PPC_64K_PAGES */
 
 #ifdef CONFIG_PPC_HAS_HASH_64K
@@ -1182,6 +1179,17 @@ bail:
        exception_exit(prev_state);
        return rc;
 }
+EXPORT_SYMBOL_GPL(hash_page_mm);
+
+int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
+{
+       struct mm_struct *mm = current->mm;
+
+       if (REGION_ID(ea) == VMALLOC_REGION_ID)
+               mm = &init_mm;
+
+       return hash_page_mm(mm, ea, access, trap);
+}
 EXPORT_SYMBOL_GPL(hash_page);
 
 void hash_preload(struct mm_struct *mm, unsigned long ea,
index cff59f1bec238bd41fbcd246d6a732ccc357e1ce..cad68ff8eca5322990cfa56ee46003ae530e1d39 100644 (file)
@@ -106,11 +106,11 @@ unsigned long __max_low_memory = MAX_LOW_MEM;
 void MMU_setup(void)
 {
        /* Check for nobats option (used in mapin_ram). */
-       if (strstr(cmd_line, "nobats")) {
+       if (strstr(boot_command_line, "nobats")) {
                __map_without_bats = 1;
        }
 
-       if (strstr(cmd_line, "noltlbs")) {
+       if (strstr(boot_command_line, "noltlbs")) {
                __map_without_ltlbs = 1;
        }
 #ifdef CONFIG_DEBUG_PAGEALLOC
index 253b4b971c8afa261d110d653e15495a8c55dc6c..3481556a1880407db79fe32527289040eb9b6d77 100644 (file)
@@ -233,9 +233,6 @@ static void __meminit vmemmap_create_mapping(unsigned long start,
 }
 
 #ifdef CONFIG_MEMORY_HOTPLUG
-extern int htab_remove_mapping(unsigned long vstart, unsigned long vend,
-                       int psize, int ssize);
-
 static void vmemmap_remove_mapping(unsigned long start,
                                   unsigned long page_size)
 {
index e0f7a189c48ea440dc2e05ac659eb46895490590..8ebaac75c940ad7c5e9752a71c7dd7894e2bdedf 100644 (file)
@@ -260,6 +260,60 @@ static int __init mark_nonram_nosave(void)
        }
        return 0;
 }
+#else /* CONFIG_NEED_MULTIPLE_NODES */
+static int __init mark_nonram_nosave(void)
+{
+       return 0;
+}
+#endif
+
+static bool zone_limits_final;
+
+static unsigned long max_zone_pfns[MAX_NR_ZONES] = {
+       [0 ... MAX_NR_ZONES - 1] = ~0UL
+};
+
+/*
+ * Restrict the specified zone and all more restrictive zones
+ * to be below the specified pfn.  May not be called after
+ * paging_init().
+ */
+void __init limit_zone_pfn(enum zone_type zone, unsigned long pfn_limit)
+{
+       int i;
+
+       if (WARN_ON(zone_limits_final))
+               return;
+
+       for (i = zone; i >= 0; i--) {
+               if (max_zone_pfns[i] > pfn_limit)
+                       max_zone_pfns[i] = pfn_limit;
+       }
+}
+
+/*
+ * Find the least restrictive zone that is entirely below the
+ * specified pfn limit.  Returns < 0 if no suitable zone is found.
+ *
+ * pfn_limit must be u64 because it can exceed 32 bits even on 32-bit
+ * systems -- the DMA limit can be higher than any possible real pfn.
+ */
+int dma_pfn_limit_to_zone(u64 pfn_limit)
+{
+       enum zone_type top_zone = ZONE_NORMAL;
+       int i;
+
+#ifdef CONFIG_HIGHMEM
+       top_zone = ZONE_HIGHMEM;
+#endif
+
+       for (i = top_zone; i >= 0; i--) {
+               if (max_zone_pfns[i] <= pfn_limit)
+                       return i;
+       }
+
+       return -EPERM;
+}
 
 /*
  * paging_init() sets up the page tables - in fact we've already done this.
@@ -268,7 +322,7 @@ void __init paging_init(void)
 {
        unsigned long long total_ram = memblock_phys_mem_size();
        phys_addr_t top_of_ram = memblock_end_of_DRAM();
-       unsigned long max_zone_pfns[MAX_NR_ZONES];
+       enum zone_type top_zone;
 
 #ifdef CONFIG_PPC32
        unsigned long v = __fix_to_virt(__end_of_fixed_addresses - 1);
@@ -290,18 +344,20 @@ void __init paging_init(void)
               (unsigned long long)top_of_ram, total_ram);
        printk(KERN_DEBUG "Memory hole size: %ldMB\n",
               (long int)((top_of_ram - total_ram) >> 20));
-       memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
+
 #ifdef CONFIG_HIGHMEM
-       max_zone_pfns[ZONE_DMA] = lowmem_end_addr >> PAGE_SHIFT;
-       max_zone_pfns[ZONE_HIGHMEM] = top_of_ram >> PAGE_SHIFT;
+       top_zone = ZONE_HIGHMEM;
+       limit_zone_pfn(ZONE_NORMAL, lowmem_end_addr >> PAGE_SHIFT);
 #else
-       max_zone_pfns[ZONE_DMA] = top_of_ram >> PAGE_SHIFT;
+       top_zone = ZONE_NORMAL;
 #endif
+
+       limit_zone_pfn(top_zone, top_of_ram >> PAGE_SHIFT);
+       zone_limits_final = true;
        free_area_init_nodes(max_zone_pfns);
 
        mark_nonram_nosave();
 }
-#endif /* ! CONFIG_NEED_MULTIPLE_NODES */
 
 static void __init register_page_bootmem_info(void)
 {
index d7737a542fd7d5f5af82bde3ec417262575175af..649666d5d1c20520ed554a3375c11393660871e1 100644 (file)
@@ -538,7 +538,7 @@ static int of_drconf_to_nid_single(struct of_drconf_cell *drmem,
  */
 static int numa_setup_cpu(unsigned long lcpu)
 {
-       int nid;
+       int nid = -1;
        struct device_node *cpu;
 
        /*
@@ -555,19 +555,21 @@ static int numa_setup_cpu(unsigned long lcpu)
 
        if (!cpu) {
                WARN_ON(1);
-               nid = 0;
-               goto out;
+               if (cpu_present(lcpu))
+                       goto out_present;
+               else
+                       goto out;
        }
 
        nid = of_node_to_nid_single(cpu);
 
+out_present:
        if (nid < 0 || !node_online(nid))
                nid = first_online_node;
-out:
-       map_cpu_to_node(lcpu, nid);
 
+       map_cpu_to_node(lcpu, nid);
        of_node_put(cpu);
-
+out:
        return nid;
 }
 
@@ -1127,20 +1129,11 @@ void __init do_init_bootmem(void)
         * even before we online them, so that we can use cpu_to_{node,mem}
         * early in boot, cf. smp_prepare_cpus().
         */
-       for_each_possible_cpu(cpu) {
-               cpu_numa_callback(&ppc64_numa_nb, CPU_UP_PREPARE,
-                                 (void *)(unsigned long)cpu);
+       for_each_present_cpu(cpu) {
+               numa_setup_cpu((unsigned long)cpu);
        }
 }
 
-void __init paging_init(void)
-{
-       unsigned long max_zone_pfns[MAX_NR_ZONES];
-       memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
-       max_zone_pfns[ZONE_DMA] = memblock_end_of_DRAM() >> PAGE_SHIFT;
-       free_area_init_nodes(max_zone_pfns);
-}
-
 static int __init early_numa(char *p)
 {
        if (!p)
index c695943a513cc638621c830f537ea903ccfa3b86..c90e602677c94f38e084957a78ab386cba89b037 100644 (file)
@@ -48,7 +48,7 @@ static inline int pte_looks_normal(pte_t pte)
            (_PAGE_PRESENT | _PAGE_USER);
 }
 
-struct page * maybe_pte_to_page(pte_t pte)
+static struct page *maybe_pte_to_page(pte_t pte)
 {
        unsigned long pfn = pte_pfn(pte);
        struct page *page;
index 0399a6702958dd933f8581cf8b75cfe16a184bb3..6e450ca6652684eede4bb00f17ebb3e9a74e91d5 100644 (file)
@@ -46,9 +46,6 @@ static inline unsigned long mk_esid_data(unsigned long ea, int ssize,
        return (ea & slb_esid_mask(ssize)) | SLB_ESID_V | slot;
 }
 
-#define slb_vsid_shift(ssize)  \
-       ((ssize) == MMU_SEGSIZE_256M? SLB_VSID_SHIFT: SLB_VSID_SHIFT_1T)
-
 static inline unsigned long mk_vsid_data(unsigned long ea, int ssize,
                                         unsigned long flags)
 {
index b0c75cc15efc673a0ff8f22e0c0e6ef8dc18a038..8d7bda94d1969b2325073315875a05545922b0fc 100644 (file)
 #include <linux/err.h>
 #include <linux/spinlock.h>
 #include <linux/export.h>
+#include <linux/hugetlb.h>
 #include <asm/mman.h>
 #include <asm/mmu.h>
-#include <asm/spu.h>
+#include <asm/copro.h>
+#include <asm/hugetlb.h>
 
 /* some sanity checks */
 #if (PGTABLE_RANGE >> 43) > SLICE_MASK_SIZE
@@ -232,9 +234,7 @@ static void slice_convert(struct mm_struct *mm, struct slice_mask mask, int psiz
 
        spin_unlock_irqrestore(&slice_convert_lock, flags);
 
-#ifdef CONFIG_SPU_BASE
-       spu_flush_all_slbs(mm);
-#endif
+       copro_flush_all_slbs(mm);
 }
 
 /*
@@ -671,9 +671,7 @@ void slice_set_psize(struct mm_struct *mm, unsigned long address,
 
        spin_unlock_irqrestore(&slice_convert_lock, flags);
 
-#ifdef CONFIG_SPU_BASE
-       spu_flush_all_slbs(mm);
-#endif
+       copro_flush_all_slbs(mm);
 }
 
 void slice_set_range_psize(struct mm_struct *mm, unsigned long start,
index f75301f2c85fd2960acd21ee427527614934585e..6adf55fa5d88c2cb55b13dc99cfbdaecba7dcff3 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/processor.h>
 #include <asm/uaccess.h>
 #include <asm/compat.h>
+#include <asm/oprofile_impl.h>
 
 #define STACK_SP(STACK)                *(STACK)
 
index b7cd00b0171ed9776603969e2af25d5abe3f66a9..a6995d4e93d43d29a34d3c4f833097530f13ebf8 100644 (file)
@@ -59,9 +59,9 @@ struct cpu_hw_events {
        struct  perf_branch_entry       bhrb_entries[BHRB_MAX_ENTRIES];
 };
 
-DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
+static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
 
-struct power_pmu *ppmu;
+static struct power_pmu *ppmu;
 
 /*
  * Normally, to ignore kernel events we set the FCS (freeze counters
@@ -124,7 +124,7 @@ static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw)
 
 static inline void power_pmu_bhrb_enable(struct perf_event *event) {}
 static inline void power_pmu_bhrb_disable(struct perf_event *event) {}
-void power_pmu_flush_branch_stack(void) {}
+static void power_pmu_flush_branch_stack(void) {}
 static inline void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) {}
 static void pmao_restore_workaround(bool ebb) { }
 #endif /* CONFIG_PPC32 */
@@ -375,7 +375,7 @@ static void power_pmu_bhrb_disable(struct perf_event *event)
 /* Called from ctxsw to prevent one process's branch entries to
  * mingle with the other process's entries during context switch.
  */
-void power_pmu_flush_branch_stack(void)
+static void power_pmu_flush_branch_stack(void)
 {
        if (ppmu->bhrb_nr)
                power_pmu_bhrb_reset();
@@ -408,7 +408,7 @@ static __u64 power_pmu_bhrb_to(u64 addr)
 }
 
 /* Processing BHRB entries */
-void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
+static void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
 {
        u64 val;
        u64 addr;
@@ -1573,7 +1573,7 @@ static void power_pmu_stop(struct perf_event *event, int ef_flags)
  * Set the flag to make pmu::enable() not perform the
  * schedulability test, it will be performed at commit time
  */
-void power_pmu_start_txn(struct pmu *pmu)
+static void power_pmu_start_txn(struct pmu *pmu)
 {
        struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
 
@@ -1587,7 +1587,7 @@ void power_pmu_start_txn(struct pmu *pmu)
  * Clear the flag and pmu::enable() will perform the
  * schedulability test.
  */
-void power_pmu_cancel_txn(struct pmu *pmu)
+static void power_pmu_cancel_txn(struct pmu *pmu)
 {
        struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
 
@@ -1600,7 +1600,7 @@ void power_pmu_cancel_txn(struct pmu *pmu)
  * Perform the group schedulability test as a whole
  * Return 0 if success
  */
-int power_pmu_commit_txn(struct pmu *pmu)
+static int power_pmu_commit_txn(struct pmu *pmu)
 {
        struct cpu_hw_events *cpuhw;
        long i, n;
@@ -1888,7 +1888,7 @@ ssize_t power_events_sysfs_show(struct device *dev,
        return sprintf(page, "event=0x%02llx\n", pmu_attr->id);
 }
 
-struct pmu power_pmu = {
+static struct pmu power_pmu = {
        .pmu_enable     = power_pmu_enable,
        .pmu_disable    = power_pmu_disable,
        .event_init     = power_pmu_event_init,
index 70d4f748b54bead98a8889ea3de6d344dbb3ca31..6c8710dd90c9b12d7e746113b43fdc9231c8b8b3 100644 (file)
@@ -75,86 +75,6 @@ static struct attribute_group format_group = {
 
 static struct kmem_cache *hv_page_cache;
 
-/*
- * read_offset_data - copy data from one buffer to another while treating the
- *                    source buffer as a small view on the total avaliable
- *                    source data.
- *
- * @dest: buffer to copy into
- * @dest_len: length of @dest in bytes
- * @requested_offset: the offset within the source data we want. Must be > 0
- * @src: buffer to copy data from
- * @src_len: length of @src in bytes
- * @source_offset: the offset in the sorce data that (src,src_len) refers to.
- *                 Must be > 0
- *
- * returns the number of bytes copied.
- *
- * The following ascii art shows the various buffer possitioning we need to
- * handle, assigns some arbitrary varibles to points on the buffer, and then
- * shows how we fiddle with those values to get things we care about (copy
- * start in src and copy len)
- *
- * s = @src buffer
- * d = @dest buffer
- * '.' areas in d are written to.
- *
- *                       u
- *   x         w        v  z
- * d           |.........|
- * s |----------------------|
- *
- *                      u
- *   x         w       z     v
- * d           |........------|
- * s |------------------|
- *
- *   x         w        u,z,v
- * d           |........|
- * s |------------------|
- *
- *   x,w                u,v,z
- * d |..................|
- * s |------------------|
- *
- *   x        u
- *   w        v                z
- * d |........|
- * s |------------------|
- *
- *   x      z   w      v
- * d            |------|
- * s |------|
- *
- * x = source_offset
- * w = requested_offset
- * z = source_offset + src_len
- * v = requested_offset + dest_len
- *
- * w_offset_in_s = w - x = requested_offset - source_offset
- * z_offset_in_s = z - x = src_len
- * v_offset_in_s = v - x = request_offset + dest_len - src_len
- */
-static ssize_t read_offset_data(void *dest, size_t dest_len,
-                               loff_t requested_offset, void *src,
-                               size_t src_len, loff_t source_offset)
-{
-       size_t w_offset_in_s = requested_offset - source_offset;
-       size_t z_offset_in_s = src_len;
-       size_t v_offset_in_s = requested_offset + dest_len - src_len;
-       size_t u_offset_in_s = min(z_offset_in_s, v_offset_in_s);
-       size_t copy_len = u_offset_in_s - w_offset_in_s;
-
-       if (requested_offset < 0 || source_offset < 0)
-               return -EINVAL;
-
-       if (z_offset_in_s <= w_offset_in_s)
-               return 0;
-
-       memcpy(dest, src + w_offset_in_s, copy_len);
-       return copy_len;
-}
-
 static unsigned long h_get_24x7_catalog_page_(unsigned long phys_4096,
                                              unsigned long version,
                                              unsigned long index)
@@ -183,8 +103,10 @@ static ssize_t catalog_read(struct file *filp, struct kobject *kobj,
 {
        unsigned long hret;
        ssize_t ret = 0;
-       size_t catalog_len = 0, catalog_page_len = 0, page_count = 0;
+       size_t catalog_len = 0, catalog_page_len = 0;
        loff_t page_offset = 0;
+       loff_t offset_in_page;
+       size_t copy_len;
        uint64_t catalog_version_num = 0;
        void *page = kmem_cache_alloc(hv_page_cache, GFP_USER);
        struct hv_24x7_catalog_page_0 *page_0 = page;
@@ -202,7 +124,7 @@ static ssize_t catalog_read(struct file *filp, struct kobject *kobj,
        catalog_len = catalog_page_len * 4096;
 
        page_offset = offset / 4096;
-       page_count  = count  / 4096;
+       offset_in_page = offset % 4096;
 
        if (page_offset >= catalog_page_len)
                goto e_free;
@@ -216,8 +138,13 @@ static ssize_t catalog_read(struct file *filp, struct kobject *kobj,
                }
        }
 
-       ret = read_offset_data(buf, count, offset,
-                               page, 4096, page_offset * 4096);
+       copy_len = 4096 - offset_in_page;
+       if (copy_len > count)
+               copy_len = count;
+
+       memcpy(buf, page+offset_in_page, copy_len);
+       ret = copy_len;
+
 e_free:
        if (hret)
                pr_err("h_get_24x7_catalog_page(ver=%lld, page=%lld) failed:"
@@ -225,9 +152,9 @@ e_free:
                       catalog_version_num, page_offset, hret);
        kmem_cache_free(hv_page_cache, page);
 
-       pr_devel("catalog_read: offset=%lld(%lld) count=%zu(%zu) catalog_len=%zu(%zu) => %zd\n",
-                       offset, page_offset, count, page_count, catalog_len,
-                       catalog_page_len, ret);
+       pr_devel("catalog_read: offset=%lld(%lld) count=%zu "
+                       "catalog_len=%zu(%zu) => %zd\n", offset, page_offset,
+                       count, catalog_len, catalog_page_len, ret);
 
        return ret;
 }
@@ -294,7 +221,7 @@ static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix,
                                         u16 lpar, u64 *res,
                                         bool success_expected)
 {
-       unsigned long ret;
+       unsigned long ret = -ENOMEM;
 
        /*
         * request_buffer and result_buffer are not required to be 4k aligned,
@@ -304,7 +231,27 @@ static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix,
        struct reqb {
                struct hv_24x7_request_buffer buf;
                struct hv_24x7_request req;
-       } __packed __aligned(4096) request_buffer = {
+       } __packed *request_buffer;
+
+       struct {
+               struct hv_24x7_data_result_buffer buf;
+               struct hv_24x7_result res;
+               struct hv_24x7_result_element elem;
+               __be64 result;
+       } __packed *result_buffer;
+
+       BUILD_BUG_ON(sizeof(*request_buffer) > 4096);
+       BUILD_BUG_ON(sizeof(*result_buffer) > 4096);
+
+       request_buffer = kmem_cache_zalloc(hv_page_cache, GFP_USER);
+       if (!request_buffer)
+               goto out;
+
+       result_buffer = kmem_cache_zalloc(hv_page_cache, GFP_USER);
+       if (!result_buffer)
+               goto out_free_request_buffer;
+
+       *request_buffer = (struct reqb) {
                .buf = {
                        .interface_version = HV_24X7_IF_VERSION_CURRENT,
                        .num_requests = 1,
@@ -320,28 +267,27 @@ static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix,
                }
        };
 
-       struct resb {
-               struct hv_24x7_data_result_buffer buf;
-               struct hv_24x7_result res;
-               struct hv_24x7_result_element elem;
-               __be64 result;
-       } __packed __aligned(4096) result_buffer = {};
-
        ret = plpar_hcall_norets(H_GET_24X7_DATA,
-                       virt_to_phys(&request_buffer), sizeof(request_buffer),
-                       virt_to_phys(&result_buffer),  sizeof(result_buffer));
+                       virt_to_phys(request_buffer), sizeof(*request_buffer),
+                       virt_to_phys(result_buffer),  sizeof(*result_buffer));
 
        if (ret) {
                if (success_expected)
-                       pr_err_ratelimited("hcall failed: %d %#x %#x %d => 0x%lx (%ld) detail=0x%x failing ix=%x\n",
-                                       domain, offset, ix, lpar,
-                                       ret, ret,
-                                       result_buffer.buf.detailed_rc,
-                                       result_buffer.buf.failing_request_ix);
-               return ret;
+                       pr_err_ratelimited("hcall failed: %d %#x %#x %d => "
+                               "0x%lx (%ld) detail=0x%x failing ix=%x\n",
+                               domain, offset, ix, lpar, ret, ret,
+                               result_buffer->buf.detailed_rc,
+                               result_buffer->buf.failing_request_ix);
+               goto out_free_result_buffer;
        }
 
-       *res = be64_to_cpu(result_buffer.result);
+       *res = be64_to_cpu(result_buffer->result);
+
+out_free_result_buffer:
+       kfree(result_buffer);
+out_free_request_buffer:
+       kfree(request_buffer);
+out:
        return ret;
 }
 
index b0389bbe4f94328206eb00bf4d384e6c9c9fbc93..ddc12a1926ef0a189dc7e7d591c87cf5b80cbf2b 100644 (file)
@@ -49,7 +49,7 @@ static void __iomem *bcsr_regs;
 /* there's more, can't be bothered typing them tho */
 
 
-static __initdata struct of_device_id ep405_of_bus[] = {
+static const struct of_device_id ep405_of_bus[] __initconst = {
        { .compatible = "ibm,plb3", },
        { .compatible = "ibm,opb", },
        { .compatible = "ibm,ebc", },
index 8f3920e5a046c4df771ecd30371154a84e813d82..b0c46375dd9567d9b85d19688ab1f0bb5b102fd3 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/init.h>
 #include <linux/of_platform.h>
 
-static __initdata struct of_device_id ppc40x_of_bus[] = {
+static const struct of_device_id ppc40x_of_bus[] __initconst = {
        { .compatible = "ibm,plb3", },
        { .compatible = "ibm,plb4", },
        { .compatible = "ibm,opb", },
index d0fc6866b00ca04884a2a39bb1d0922763e5a6a0..9aa7ae2f4164ba45c51f0522aac1bb5ccccc032d 100644 (file)
@@ -17,7 +17,7 @@
 #include <asm/xilinx_pci.h>
 #include <asm/ppc4xx.h>
 
-static struct of_device_id xilinx_of_bus_ids[] __initdata = {
+static const struct of_device_id xilinx_of_bus_ids[] __initconst = {
        { .compatible = "xlnx,plb-v46-1.00.a", },
        { .compatible = "xlnx,plb-v34-1.01.a", },
        { .compatible = "xlnx,plb-v34-1.02.a", },
index 8b691df72f745232b85a349b702c4a72d6869288..f7ac2d0fcb4461023371b08d3ab67e2770b33a47 100644 (file)
@@ -28,7 +28,7 @@
 #include <asm/pci-bridge.h>
 #include <asm/ppc4xx.h>
 
-static __initdata struct of_device_id walnut_of_bus[] = {
+static const struct of_device_id walnut_of_bus[] __initconst = {
        { .compatible = "ibm,plb3", },
        { .compatible = "ibm,opb", },
        { .compatible = "ibm,ebc", },
index 4d88f6a19058cc253dcd885eec86e077f7441295..82f2da28cd2775c4051a60d3f4014e25148f892c 100644 (file)
@@ -215,9 +215,9 @@ config AKEBONO
        select NET_VENDOR_IBM
        select IBM_EMAC_EMAC4
        select IBM_EMAC_RGMII_WOL
-       select USB
-       select USB_OHCI_HCD_PLATFORM
-       select USB_EHCI_HCD_PLATFORM
+       select USB if USB_SUPPORT
+       select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
+       select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
        select MMC_SDHCI
        select MMC_SDHCI_PLTFM
        select MMC_SDHCI_OF_476GTR
index e300dd4c89bff9964ecfaf72734fbf9c08a03352..22ca5430c9cb5614256bc2cd939d628fb4ad8bd2 100644 (file)
@@ -33,7 +33,7 @@
 
 #define BCSR_USB_EN    0x11
 
-static __initdata struct of_device_id ppc460ex_of_bus[] = {
+static const struct of_device_id ppc460ex_of_bus[] __initconst = {
        { .compatible = "ibm,plb4", },
        { .compatible = "ibm,opb", },
        { .compatible = "ibm,ebc", },
index 6a4232bbdf88a35e9d4c1baa76eb37a620c7a8be..ae893226392dbf4f5494a95a55070ac758e7c685 100644 (file)
@@ -28,7 +28,7 @@
 #include <asm/pci-bridge.h>
 #include <asm/ppc4xx.h>
 
-static __initdata struct of_device_id ebony_of_bus[] = {
+static const struct of_device_id ebony_of_bus[] __initconst = {
        { .compatible = "ibm,plb4", },
        { .compatible = "ibm,opb", },
        { .compatible = "ibm,ebc", },
index 4241bc825800d72450bc994bacf2a4203fd9aa63..c7c6758b3cfe56ff8a8c919adb6921d4b382c69d 100644 (file)
@@ -32,7 +32,7 @@
 #include <asm/mpic.h>
 #include <asm/mmu.h>
 
-static __initdata struct of_device_id iss4xx_of_bus[] = {
+static const struct of_device_id iss4xx_of_bus[] __initconst = {
        { .compatible = "ibm,plb4", },
        { .compatible = "ibm,plb6", },
        { .compatible = "ibm,opb", },
index 3ffb915446e39bdf74f716395a6a05f8e559c632..573c3d2689c616981186c3c083b28d73c950e1e2 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/init.h>
 #include <linux/of_platform.h>
 
-static __initdata struct of_device_id ppc44x_of_bus[] = {
+static const struct of_device_id ppc44x_of_bus[] __initconst = {
        { .compatible = "ibm,plb4", },
        { .compatible = "ibm,opb", },
        { .compatible = "ibm,ebc", },
index 33986c1a05da2eaa6e1722dac53de62a2586f304..58db9d0839698fa65429e40703f196d416f7d02a 100644 (file)
@@ -38,7 +38,7 @@
 #include <linux/pci.h>
 #include <linux/i2c.h>
 
-static struct of_device_id ppc47x_of_bus[] __initdata = {
+static const struct of_device_id ppc47x_of_bus[] __initconst = {
        { .compatible = "ibm,plb4", },
        { .compatible = "ibm,plb6", },
        { .compatible = "ibm,opb", },
index 9e09b835758bc85479531469cfb2dff02b2e4e0d..3ee4a03c14969cccbdea835ed96287cb50d65377 100644 (file)
@@ -29,7 +29,7 @@
 #include <asm/ppc4xx.h>
 #include <linux/i2c.h>
 
-static __initdata struct of_device_id sam440ep_of_bus[] = {
+static const struct of_device_id sam440ep_of_bus[] __initconst = {
        { .compatible = "ibm,plb4", },
        { .compatible = "ibm,opb", },
        { .compatible = "ibm,ebc", },
index cf96ccaa760cd98ca9efc3cedf12f653e7e8f43d..ad272c17c640f561a1ed790c3fd40bc7f6359d9b 100644 (file)
@@ -21,7 +21,7 @@
 #include <asm/ppc4xx.h>
 #include "44x.h"
 
-static struct of_device_id xilinx_of_bus_ids[] __initdata = {
+static const struct of_device_id xilinx_of_bus_ids[] __initconst = {
        { .compatible = "simple-bus", },
        { .compatible = "xlnx,plb-v46-1.00.a", },
        { .compatible = "xlnx,plb-v46-1.02.a", },
index 3a104284b338f700d1ec56478dcb679859db514f..501333cf42cf194743528e0017477a095bbf3bfb 100644 (file)
@@ -28,7 +28,7 @@
 #include <asm/dma.h>
 
 
-static __initdata struct of_device_id warp_of_bus[] = {
+static const struct of_device_id warp_of_bus[] __initconst = {
        { .compatible = "ibm,plb4", },
        { .compatible = "ibm,opb", },
        { .compatible = "ibm,ebc", },
index adb95f03d4d4b20002992977120bd8a27073dc88..e996e007bc44cef80ab30541f4b045bd5e87158c 100644 (file)
@@ -337,7 +337,7 @@ void __init mpc512x_init_IRQ(void)
 /*
  * Nodes to do bus probe on, soc and localbus
  */
-static struct of_device_id __initdata of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .compatible = "fsl,mpc5121-immr", },
        { .compatible = "fsl,mpc5121-localbus", },
        { .compatible = "fsl,mpc5121-mbx", },
index 1843bc9320118b84d47cc2e44a5a083b891de6c0..7492de3cf6d058bb38e56bca84dd4f34be6b51e3 100644 (file)
  */
 
 /* mpc5200 device tree match tables */
-static struct of_device_id mpc5200_cdm_ids[] __initdata = {
+static const struct of_device_id mpc5200_cdm_ids[] __initconst = {
        { .compatible = "fsl,mpc5200-cdm", },
        { .compatible = "mpc5200-cdm", },
        {}
 };
 
-static struct of_device_id mpc5200_gpio_ids[] __initdata = {
+static const struct of_device_id mpc5200_gpio_ids[] __initconst = {
        { .compatible = "fsl,mpc5200-gpio", },
        { .compatible = "mpc5200-gpio", },
        {}
index 070d315dd6cd18359a4b62bb0927ca3681f84998..32cae33c4266b990d2dc88d89cc6d6df8ae9a9df 100644 (file)
@@ -30,7 +30,7 @@
 #include <asm/machdep.h>
 #include <asm/mpc52xx.h>
 
-static struct of_device_id mpc5200_gpio_ids[] __initdata = {
+static const struct of_device_id mpc5200_gpio_ids[] __initconst = {
        { .compatible = "fsl,mpc5200-gpio", },
        { .compatible = "mpc5200-gpio", },
        {}
index d7e94f49532a156f78f3fc2da2d98b73e60c0554..26993826a797683e0fea881fc7f6f1c11fa0c703 100644 (file)
 #include <asm/mpc52xx.h>
 
 /* MPC5200 device tree match tables */
-static struct of_device_id mpc52xx_xlb_ids[] __initdata = {
+static const struct of_device_id mpc52xx_xlb_ids[] __initconst = {
        { .compatible = "fsl,mpc5200-xlb", },
        { .compatible = "mpc5200-xlb", },
        {}
 };
-static struct of_device_id mpc52xx_bus_ids[] __initdata = {
+static const struct of_device_id mpc52xx_bus_ids[] __initconst = {
        { .compatible = "fsl,mpc5200-immr", },
        { .compatible = "fsl,mpc5200b-immr", },
        { .compatible = "simple-bus", },
@@ -108,21 +108,21 @@ void __init mpc52xx_declare_of_platform_devices(void)
 /*
  * match tables used by mpc52xx_map_common_devices()
  */
-static struct of_device_id mpc52xx_gpt_ids[] __initdata = {
+static const struct of_device_id mpc52xx_gpt_ids[] __initconst = {
        { .compatible = "fsl,mpc5200-gpt", },
        { .compatible = "mpc5200-gpt", }, /* old */
        {}
 };
-static struct of_device_id mpc52xx_cdm_ids[] __initdata = {
+static const struct of_device_id mpc52xx_cdm_ids[] __initconst = {
        { .compatible = "fsl,mpc5200-cdm", },
        { .compatible = "mpc5200-cdm", }, /* old */
        {}
 };
-static const struct of_device_id mpc52xx_gpio_simple[] = {
+static const struct of_device_id mpc52xx_gpio_simple[] __initconst = {
        { .compatible = "fsl,mpc5200-gpio", },
        {}
 };
-static const struct of_device_id mpc52xx_gpio_wkup[] = {
+static const struct of_device_id mpc52xx_gpio_wkup[] __initconst = {
        { .compatible = "fsl,mpc5200-gpio-wkup", },
        {}
 };
index 37f7a89c10f2581c0366606d471623cc26bf4997..f8f0081759fb7941ab70de2994c07ffb70e8ec5d 100644 (file)
@@ -564,7 +564,7 @@ static int mpc52xx_lpbfifo_remove(struct platform_device *op)
        return 0;
 }
 
-static struct of_device_id mpc52xx_lpbfifo_match[] = {
+static const struct of_device_id mpc52xx_lpbfifo_match[] = {
        { .compatible = "fsl,mpc5200-lpbfifo", },
        {},
 };
index 2898b737deb79e5015c7658568a696254d6f23b5..2944bc84b9d6fc790bc2b3ece7878cdf1ff208ba 100644 (file)
 
 
 /* MPC5200 device tree match tables */
-static struct of_device_id mpc52xx_pic_ids[] __initdata = {
+static const struct of_device_id mpc52xx_pic_ids[] __initconst = {
        { .compatible = "fsl,mpc5200-pic", },
        { .compatible = "mpc5200-pic", },
        {}
 };
-static struct of_device_id mpc52xx_sdma_ids[] __initdata = {
+static const struct of_device_id mpc52xx_sdma_ids[] __initconst = {
        { .compatible = "fsl,mpc5200-bestcomm", },
        { .compatible = "mpc5200-bestcomm", },
        {}
index 79799b29ffe21912dfa437885d2affaf8e916912..3d0c3a01143de70d8bac28a1e884fed0c64cfe24 100644 (file)
@@ -298,7 +298,7 @@ static void __init ep8248e_setup_arch(void)
                ppc_md.progress("ep8248e_setup_arch(), finish", 0);
 }
 
-static  __initdata struct of_device_id of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .compatible = "simple-bus", },
        { .compatible = "fsl,ep8248e-bcsr", },
        {},
index 058cc1895c886588a2a7886fd3f4510d5f6b5af1..387b446f416111982ab16390e16ffea65eeb5642 100644 (file)
@@ -180,7 +180,7 @@ static void __init km82xx_setup_arch(void)
                ppc_md.progress("km82xx_setup_arch(), finish", 0);
 }
 
-static  __initdata struct of_device_id of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .compatible = "simple-bus", },
        {},
 };
index 6a14cf50f4a27ec21409018f435c48361979321d..d24deacf07d0796120b60517cf7ea849a2bb5630 100644 (file)
@@ -181,7 +181,7 @@ static void __init mpc8272_ads_setup_arch(void)
                ppc_md.progress("mpc8272_ads_setup_arch(), finish", 0);
 }
 
-static struct of_device_id __initdata of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .name = "soc", },
        { .name = "cpm", },
        { .name = "localbus", },
index e5f82ec8df17b54d8b8bac693074c741207a649e..3a5164ad10ad73a77a1a77df7c50b7e3420a6732 100644 (file)
@@ -168,7 +168,7 @@ static int __init pq2fads_probe(void)
        return of_flat_dt_is_compatible(root, "fsl,pq2fads");
 }
 
-static struct of_device_id __initdata of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .name = "soc", },
        { .name = "cpm", },
        { .name = "localbus", },
index e238b6a55b1593e08f43e029bf6b4f9af1a7836f..463fa91ee5b663034c9859139e8ab6a11d9a921c 100644 (file)
@@ -141,7 +141,8 @@ static int mcu_gpiochip_add(struct mcu *mcu)
 
 static int mcu_gpiochip_remove(struct mcu *mcu)
 {
-       return gpiochip_remove(&mcu->gc);
+       gpiochip_remove(&mcu->gc);
+       return 0;
 }
 
 static int mcu_probe(struct i2c_client *client, const struct i2c_device_id *id)
@@ -213,7 +214,7 @@ static const struct i2c_device_id mcu_ids[] = {
 };
 MODULE_DEVICE_TABLE(i2c, mcu_ids);
 
-static struct of_device_id mcu_of_match_table[] = {
+static const struct of_device_id mcu_of_match_table[] = {
        { .compatible = "fsl,mcu-mpc8349emitx", },
        { },
 };
index 125336f750c6969273d70d09e0eeefc39cb5a0c5..ef9d01a049c16755ddf2616f4a9babfec0867de5 100644 (file)
@@ -114,7 +114,7 @@ void __init mpc83xx_ipic_and_qe_init_IRQ(void)
 }
 #endif /* CONFIG_QUICC_ENGINE */
 
-static struct of_device_id __initdata of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .type = "soc", },
        { .compatible = "soc", },
        { .compatible = "simple-bus" },
index a494fa57bdf9b1ef78060531e78729251a472aca..80aea8c4b5a3b2c96d5a2f03490568085d07075b 100644 (file)
@@ -38,7 +38,7 @@
 
 #include "mpc83xx.h"
 
-static struct of_device_id __initdata mpc834x_itx_ids[] = {
+static const struct of_device_id mpc834x_itx_ids[] __initconst = {
        { .compatible = "fsl,pq2pro-localbus", },
        {},
 };
index 4b4c081df94db168e480e10bff1d427debb1c6bb..eeb80e25214df75acbdb83696fad8e5503aac161 100644 (file)
@@ -321,7 +321,7 @@ static const struct platform_suspend_ops mpc83xx_suspend_ops = {
        .end = mpc83xx_suspend_end,
 };
 
-static struct of_device_id pmc_match[];
+static const struct of_device_id pmc_match[];
 static int pmc_probe(struct platform_device *ofdev)
 {
        const struct of_device_id *match;
@@ -420,7 +420,7 @@ static struct pmc_type pmc_types[] = {
        }
 };
 
-static struct of_device_id pmc_match[] = {
+static const struct of_device_id pmc_match[] = {
        {
                .compatible = "fsl,mpc8313-pmc",
                .data = &pmc_types[0],
index 0c1e6903597e18fdee65acc64af163df49848d7f..f22635a71d0117f1a62e441b5a666026376c30b1 100644 (file)
@@ -276,7 +276,7 @@ config CORENET_GENERIC
          For 64bit kernel, the following boards are supported:
            T208x QDS/RDB, T4240 QDS/RDB and B4 QDS
          The following boards are supported for both 32bit and 64bit kernel:
-           P5020 DS, P5040 DS and T104xQDS
+           P5020 DS, P5040 DS and T104xQDS/RDB
 
 endif # FSL_SOC_BOOKE
 
index b564b5e23f7c315d9fb88e2d0c7f5374f9aa24e8..4a9ad871a168e6452e365d512ae20a95578ce216 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "mpc85xx.h"
 
-static struct of_device_id __initdata mpc85xx_common_ids[] = {
+static const struct of_device_id mpc85xx_common_ids[] __initconst = {
        { .type = "soc", },
        { .compatible = "soc", },
        { .compatible = "simple-bus", },
index d22dd85e50bf316891fbb806adf62e171ec24d70..e56b89a792eda51bffd3cfe0e8c9791da7c306b4 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/time.h>
 #include <asm/machdep.h>
 #include <asm/pci-bridge.h>
+#include <asm/pgtable.h>
 #include <asm/ppc-pci.h>
 #include <mm/mmu_decl.h>
 #include <asm/prom.h>
@@ -67,6 +68,16 @@ void __init corenet_gen_setup_arch(void)
 
        swiotlb_detect_4g();
 
+#if defined(CONFIG_FSL_PCI) && defined(CONFIG_ZONE_DMA32)
+       /*
+        * Inbound windows don't cover the full lower 4 GiB
+        * due to conflicts with PCICSRBAR and outbound windows,
+        * so limit the DMA32 zone to 2 GiB, to allow consistent
+        * allocations to succeed.
+        */
+       limit_zone_pfn(ZONE_DMA32, 1UL << (31 - PAGE_SHIFT));
+#endif
+
        pr_info("%s board\n", ppc_md.name);
 
        mpc85xx_qe_init();
@@ -129,6 +140,9 @@ static const char * const boards[] __initconst = {
        "fsl,B4220QDS",
        "fsl,T1040QDS",
        "fsl,T1042QDS",
+       "fsl,T1040RDB",
+       "fsl,T1042RDB",
+       "fsl,T1042RDB_PI",
        "keymile,kmcoge4",
        NULL
 };
index 3daff7c635693a0e716e246a6be037d360aca662..12019f17f297c7e216e53f3fa06aec53601bf2d3 100644 (file)
@@ -59,7 +59,7 @@ static void ppa8548_show_cpuinfo(struct seq_file *m)
        seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f));
 }
 
-static struct of_device_id __initdata of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .name = "soc", },
        { .type = "soc", },
        { .compatible = "simple-bus", },
index 7f26732935496381f92446600f30262dccb4215a..8ad2fe6f200a5b0b5956a61969ca86f6d47fb645 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/kernel.h>
 #include <linux/of_fdt.h>
 #include <asm/machdep.h>
+#include <asm/pgtable.h>
 #include <asm/time.h>
 #include <asm/udbg.h>
 #include <asm/mpic.h>
@@ -44,6 +45,15 @@ static void __init qemu_e500_setup_arch(void)
 
        fsl_pci_assign_primary();
        swiotlb_detect_4g();
+#if defined(CONFIG_FSL_PCI) && defined(CONFIG_ZONE_DMA32)
+       /*
+        * Inbound windows don't cover the full lower 4 GiB
+        * due to conflicts with PCICSRBAR and outbound windows,
+        * so limit the DMA32 zone to 2 GiB, to allow consistent
+        * allocations to succeed.
+        */
+       limit_zone_pfn(ZONE_DMA32, 1UL << (31 - PAGE_SHIFT));
+#endif
        mpc85xx_smp_init();
 }
 
index bb75add670844e3f2e990d0a21e1c48a0ac42c20..8162b0412117a18171cccea8dd7cb61d7b7ef4ac 100644 (file)
@@ -24,7 +24,7 @@
 
 static struct device_node *halt_node;
 
-static struct of_device_id child_match[] = {
+static const struct of_device_id child_match[] = {
        {
                .compatible = "sgy,gpio-halt",
        },
@@ -147,7 +147,7 @@ static int gpio_halt_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct of_device_id gpio_halt_match[] = {
+static const struct of_device_id gpio_halt_match[] = {
        /* We match on the gpio bus itself and scan the children since they
         * wont be matched against us. We know the bus wont match until it
         * has been registered too. */
index c23f3443880ac96d138c1483120802559a5ee75b..bf17933b20f32c315caf1393d0e666c88cf8876a 100644 (file)
@@ -213,7 +213,7 @@ static long __init mpc86xx_time_init(void)
        return 0;
 }
 
-static __initdata struct of_device_id of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .compatible = "simple-bus", },
        { .compatible = "gianfar", },
        { .compatible = "fsl,mpc8641-pcie", },
index 8a6ac20686ea2b0218c89059521788b7377a0ad9..8facf5873866ad94e583cdae37ad42b6573a4d3a 100644 (file)
@@ -200,7 +200,7 @@ static long __init mpc86xx_time_init(void)
        return 0;
 }
 
-static __initdata struct of_device_id of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .compatible = "simple-bus", },
        { .compatible = "gianfar", },
        { .compatible = "fsl,mpc8641-pcie", },
index 06c72636f2992d5ac3a632cc28ab05ccb8b6048f..8c9058df5642d13eb30e8e6c66e14523c2dd3d4e 100644 (file)
@@ -190,7 +190,7 @@ static long __init mpc86xx_time_init(void)
        return 0;
 }
 
-static __initdata struct of_device_id of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .compatible = "simple-bus", },
        { .compatible = "gianfar", },
        { .compatible = "fsl,mpc8641-pcie", },
index d479d68fbb2bc5017b00b161975533be0c7181cf..55413a547ea8eb0779f28f00835c78fc8b68d75c 100644 (file)
@@ -85,7 +85,7 @@ static void __init mpc8610_suspend_init(void)
 static inline void mpc8610_suspend_init(void) { }
 #endif /* CONFIG_SUSPEND */
 
-static struct of_device_id __initdata mpc8610_ids[] = {
+static const struct of_device_id mpc8610_ids[] __initconst = {
        { .compatible = "fsl,mpc8610-immr", },
        { .compatible = "fsl,mpc8610-guts", },
        { .compatible = "simple-bus", },
index e8bf3fae56060dfa2645701db57839e8092f8e28..07ccb1b0cc7de168cc46eec53c8723a8c43491d1 100644 (file)
@@ -127,7 +127,7 @@ mpc86xx_time_init(void)
        return 0;
 }
 
-static __initdata struct of_device_id of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .compatible = "simple-bus", },
        { .compatible = "fsl,srio", },
        { .compatible = "gianfar", },
index b47a8fd0f3d30ad4d2d3e1bfa45a43761bc9e8f1..6810b71d54a795803a6a8cdc6d021af84ebf4b21 100644 (file)
@@ -92,7 +92,7 @@ mpc86xx_time_init(void)
        return 0;
 }
 
-static __initdata struct of_device_id of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .compatible = "simple-bus", },
        { .compatible = "gianfar", },
        { .compatible = "fsl,mpc8641-pcie", },
index 82363e98f50e4bc05240df0c147e65a3168b2c65..61cae4c1edb8532f47acd9fa549eda858aebdcc3 100644 (file)
@@ -92,7 +92,7 @@ static int __init adder875_probe(void)
        return of_flat_dt_is_compatible(root, "analogue-and-micro,adder875");
 }
 
-static __initdata struct of_device_id of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .compatible = "simple-bus", },
        {},
 };
index e62166681d0811113680965696944c8c23514db3..2bedeb7d5f8f143b97183cb8cb725911b449ca4a 100644 (file)
@@ -147,7 +147,7 @@ static int __init ep88xc_probe(void)
        return of_flat_dt_is_compatible(root, "fsl,ep88xc");
 }
 
-static struct of_device_id __initdata of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .name = "soc", },
        { .name = "cpm", },
        { .name = "localbus", },
index 63084640c5c5af872aba11a94979324cec02ccd6..78180c5e73ffc293f9122d147a3981194ff6574e 100644 (file)
@@ -122,7 +122,7 @@ static int __init mpc86xads_probe(void)
        return of_flat_dt_is_compatible(root, "fsl,mpc866ads");
 }
 
-static struct of_device_id __initdata of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .name = "soc", },
        { .name = "cpm", },
        { .name = "localbus", },
index 5921dcb498fd2bdf5957563a143f68b9463fe791..4d62bf9dc7894fba257488dbe1ec482be32e2cf5 100644 (file)
@@ -197,7 +197,7 @@ static int __init mpc885ads_probe(void)
        return of_flat_dt_is_compatible(root, "fsl,mpc885ads");
 }
 
-static struct of_device_id __initdata of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .name = "soc", },
        { .name = "cpm", },
        { .name = "localbus", },
index dda607807def7d8a782c212ae74dc270858abc5d..bee47a2b23e66b9f487adc565aa3d879c4ae5296 100644 (file)
@@ -124,7 +124,7 @@ static int __init tqm8xx_probe(void)
        return of_flat_dt_is_compatible(node, "tqc,tqm8xx");
 }
 
-static struct of_device_id __initdata of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .name = "soc", },
        { .name = "cpm", },
        { .name = "localbus", },
index 7d9ee3d8c61810fdd0fc5cbd17ca8721623f346d..76483e3acd60e432a83d568b49924224eec83bbf 100644 (file)
@@ -116,6 +116,12 @@ config POWER6_CPU
 config POWER7_CPU
        bool "POWER7"
        depends on PPC_BOOK3S_64
+       select ARCH_HAS_FAST_MULTIPLIER
+
+config POWER8_CPU
+       bool "POWER8"
+       depends on PPC_BOOK3S_64
+       select ARCH_HAS_FAST_MULTIPLIER
 
 config E5500_CPU
        bool "Freescale e5500"
index 9978f594cac013a5f94095846a4e98816784f2a7..870b6dbd4d18affa403b7dc0a8037da0c29a58e4 100644 (file)
@@ -86,6 +86,7 @@ config SPU_FS_64K_LS
 config SPU_BASE
        bool
        default n
+       select PPC_COPRO_BASE
 
 config CBE_RAS
        bool "RAS features for bare metal Cell BE"
index fe053e7c73ee95ae21dc618232c19190930c309a..2d16884f67b9dbca7d56752d9cc5a1711eaf1d24 100644 (file)
@@ -20,7 +20,7 @@ spu-manage-$(CONFIG_PPC_CELL_COMMON)  += spu_manage.o
 
 obj-$(CONFIG_SPU_BASE)                 += spu_callbacks.o spu_base.o \
                                           spu_notify.o \
-                                          spu_syscalls.o spu_fault.o \
+                                          spu_syscalls.o \
                                           $(spu-priv1-y) \
                                           $(spu-manage-y) \
                                           spufs/
index 85825b5401e51d936b81c99b8342b2f2a08cf6c1..862b32702d2933d062e33ccf20df8eff7bfc9d41 100644 (file)
@@ -199,14 +199,6 @@ out_error:
        return msic;
 }
 
-static int axon_msi_check_device(struct pci_dev *dev, int nvec, int type)
-{
-       if (!find_msi_translator(dev))
-               return -ENODEV;
-
-       return 0;
-}
-
 static int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg)
 {
        struct device_node *dn;
@@ -416,7 +408,6 @@ static int axon_msi_probe(struct platform_device *device)
 
        ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
        ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
-       ppc_md.msi_check_device = axon_msi_check_device;
 
        axon_msi_debug_setup(dn, msic);
 
index 173568140a32ec0df09c0269107c1caa7ec035c6..2b98a36ef8fb740ac7b087ff1cb856400af841d3 100644 (file)
@@ -454,7 +454,7 @@ static struct celleb_phb_spec celleb_fake_pci_spec __initdata = {
        .setup = celleb_setup_fake_pci,
 };
 
-static struct of_device_id celleb_phb_match[] __initdata = {
+static const struct of_device_id celleb_phb_match[] __initconst = {
        {
                .name = "pci-pseudo",
                .data = &celleb_fake_pci_spec,
index 1d5a4d8ddad9dd221f110ba3768db00134055bad..34e8ce2976aac3ff3d2ee7ac5500cda0698367d9 100644 (file)
@@ -102,7 +102,7 @@ static void __init celleb_setup_arch_common(void)
 #endif
 }
 
-static struct of_device_id celleb_bus_ids[] __initdata = {
+static const struct of_device_id celleb_bus_ids[] __initconst = {
        { .type = "scc", },
        { .type = "ioif", },    /* old style */
        {},
index 2930d1e81a05c0f960e08e344ee4ff6601a3bbf4..ffcbd242e6693de80a632be5100f1c8d7b1fd072 100644 (file)
@@ -76,10 +76,6 @@ static LIST_HEAD(spu_full_list);
 static DEFINE_SPINLOCK(spu_full_list_lock);
 static DEFINE_MUTEX(spu_full_list_mutex);
 
-struct spu_slb {
-       u64 esid, vsid;
-};
-
 void spu_invalidate_slbs(struct spu *spu)
 {
        struct spu_priv2 __iomem *priv2 = spu->priv2;
@@ -149,7 +145,7 @@ static void spu_restart_dma(struct spu *spu)
        }
 }
 
-static inline void spu_load_slb(struct spu *spu, int slbe, struct spu_slb *slb)
+static inline void spu_load_slb(struct spu *spu, int slbe, struct copro_slb *slb)
 {
        struct spu_priv2 __iomem *priv2 = spu->priv2;
 
@@ -167,45 +163,12 @@ static inline void spu_load_slb(struct spu *spu, int slbe, struct spu_slb *slb)
 
 static int __spu_trap_data_seg(struct spu *spu, unsigned long ea)
 {
-       struct mm_struct *mm = spu->mm;
-       struct spu_slb slb;
-       int psize;
-
-       pr_debug("%s\n", __func__);
-
-       slb.esid = (ea & ESID_MASK) | SLB_ESID_V;
+       struct copro_slb slb;
+       int ret;
 
-       switch(REGION_ID(ea)) {
-       case USER_REGION_ID:
-#ifdef CONFIG_PPC_MM_SLICES
-               psize = get_slice_psize(mm, ea);
-#else
-               psize = mm->context.user_psize;
-#endif
-               slb.vsid = (get_vsid(mm->context.id, ea, MMU_SEGSIZE_256M)
-                               << SLB_VSID_SHIFT) | SLB_VSID_USER;
-               break;
-       case VMALLOC_REGION_ID:
-               if (ea < VMALLOC_END)
-                       psize = mmu_vmalloc_psize;
-               else
-                       psize = mmu_io_psize;
-               slb.vsid = (get_kernel_vsid(ea, MMU_SEGSIZE_256M)
-                               << SLB_VSID_SHIFT) | SLB_VSID_KERNEL;
-               break;
-       case KERNEL_REGION_ID:
-               psize = mmu_linear_psize;
-               slb.vsid = (get_kernel_vsid(ea, MMU_SEGSIZE_256M)
-                               << SLB_VSID_SHIFT) | SLB_VSID_KERNEL;
-               break;
-       default:
-               /* Future: support kernel segments so that drivers
-                * can use SPUs.
-                */
-               pr_debug("invalid region access at %016lx\n", ea);
-               return 1;
-       }
-       slb.vsid |= mmu_psize_defs[psize].sllp;
+       ret = copro_calculate_slb(spu->mm, ea, &slb);
+       if (ret)
+               return ret;
 
        spu_load_slb(spu, spu->slb_replace, &slb);
 
@@ -253,7 +216,7 @@ static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr)
        return 0;
 }
 
-static void __spu_kernel_slb(void *addr, struct spu_slb *slb)
+static void __spu_kernel_slb(void *addr, struct copro_slb *slb)
 {
        unsigned long ea = (unsigned long)addr;
        u64 llp;
@@ -272,7 +235,7 @@ static void __spu_kernel_slb(void *addr, struct spu_slb *slb)
  * Given an array of @nr_slbs SLB entries, @slbs, return non-zero if the
  * address @new_addr is present.
  */
-static inline int __slb_present(struct spu_slb *slbs, int nr_slbs,
+static inline int __slb_present(struct copro_slb *slbs, int nr_slbs,
                void *new_addr)
 {
        unsigned long ea = (unsigned long)new_addr;
@@ -297,7 +260,7 @@ static inline int __slb_present(struct spu_slb *slbs, int nr_slbs,
 void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa,
                void *code, int code_size)
 {
-       struct spu_slb slbs[4];
+       struct copro_slb slbs[4];
        int i, nr_slbs = 0;
        /* start and end addresses of both mappings */
        void *addrs[] = {
index 8cb6260cc80fa66231a4e7d5205ebb94e3a9b9a2..e45894a081185bc92142ad4b75a2eec9138e5fe0 100644 (file)
@@ -138,7 +138,7 @@ int spufs_handle_class1(struct spu_context *ctx)
        if (ctx->state == SPU_STATE_RUNNABLE)
                ctx->spu->stats.hash_flt++;
 
-       /* we must not hold the lock when entering spu_handle_mm_fault */
+       /* we must not hold the lock when entering copro_handle_mm_fault */
        spu_release(ctx);
 
        access = (_PAGE_PRESENT | _PAGE_USER);
@@ -149,7 +149,7 @@ int spufs_handle_class1(struct spu_context *ctx)
 
        /* hashing failed, so try the actual fault handler */
        if (ret)
-               ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt);
+               ret = copro_handle_mm_fault(current->mm, ea, dsisr, &flt);
 
        /*
         * This is nasty: we need the state_mutex for all the bookkeeping even
index 7044fd36197b90a42af0a3196aabc5c9bd2e971b..5b77b1919fd21b5007f5dadc10e1f8a2129553aa 100644 (file)
@@ -258,7 +258,7 @@ static void chrp_init_early(void)
        struct device_node *node;
        const char *property;
 
-       if (strstr(cmd_line, "console="))
+       if (strstr(boot_command_line, "console="))
                return;
        /* find the boot console from /chosen/stdout */
        if (!of_chosen)
index a138e14bad2e7292565bf807be59e784e1e77f89..bd4ba5d7d568f3064675e51ea46c389ede739e5a 100644 (file)
@@ -90,7 +90,7 @@ define_machine(gamecube) {
 };
 
 
-static struct of_device_id gamecube_of_bus[] = {
+static const struct of_device_id gamecube_of_bus[] = {
        { .compatible = "nintendo,flipper", },
        { },
 };
index 455e7c08742290a6bfb175dc3f3d730829c5545b..168e1d80b2e5dd58ace20584c265b9c539c3d7b1 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "mpc10x.h"
 
-static __initdata struct of_device_id of_bus_ids[] = {
+static const struct of_device_id of_bus_ids[] __initconst = {
        { .type = "soc", },
        { .compatible = "simple-bus", },
        {},
index 25e3bfb64efbe7d0b9a5e8b0e5ad26d2affd1394..1613303177e64e6b3b60830c90b6be50a56e01a7 100644 (file)
@@ -149,7 +149,7 @@ static int __init mvme5100_add_bridge(struct device_node *dev)
        return 0;
 }
 
-static struct of_device_id mvme5100_of_bus_ids[] __initdata = {
+static const struct of_device_id mvme5100_of_bus_ids[] __initconst = {
        { .compatible = "hawk-bridge", },
        {},
 };
index c458b60d14c4f78064485f209a026438e73f1e9a..d572833ebd00cc3e27b1a7671292335f2f2507b5 100644 (file)
@@ -24,7 +24,7 @@
 #include "mpc10x.h"
 
 
-static __initdata struct of_device_id storcenter_of_bus[] = {
+static const struct of_device_id storcenter_of_bus[] __initconst = {
        { .name = "soc", },
        {},
 };
index 6d8dadf19f0b84e95cd58fbed9bfbdd1101f3126..388e29bab8f61a3fa673efd13ab517383ef6e302 100644 (file)
@@ -235,7 +235,7 @@ define_machine(wii) {
        .machine_shutdown       = wii_shutdown,
 };
 
-static struct of_device_id wii_of_bus[] = {
+static const struct of_device_id wii_of_bus[] = {
        { .compatible = "nintendo,hollywood", },
        { },
 };
index 15adee544638b6c11fcea4f0a8c7d813403496c5..ada33358950dcd582d106c068724c2ddb892cc95 100644 (file)
@@ -290,7 +290,7 @@ static int gpio_mdio_remove(struct platform_device *dev)
        return 0;
 }
 
-static struct of_device_id gpio_mdio_match[] =
+static const struct of_device_id gpio_mdio_match[] =
 {
        {
                .compatible      = "gpio-mdio",
index 8c54de6d8ec426e11e7a84ee7193159176f0b482..d71b2c7e84031ba4c94e41664b365eb14c1e24b5 100644 (file)
@@ -393,7 +393,7 @@ static inline void pasemi_pcmcia_init(void)
 #endif
 
 
-static struct of_device_id pasemi_bus_ids[] = {
+static const struct of_device_id pasemi_bus_ids[] = {
        /* Unfortunately needed for legacy firmwares */
        { .type = "localbus", },
        { .type = "sdc", },
index 141f8899a633f04182d65179c410b53a78a74109..b127a29ac526dd0e27e5f5a7b487c0d58c2ae9c1 100644 (file)
@@ -336,7 +336,7 @@ static void __init pmac_setup_arch(void)
 #endif
 
 #ifdef CONFIG_ADB
-       if (strstr(cmd_line, "adb_sync")) {
+       if (strstr(boot_command_line, "adb_sync")) {
                extern int __adb_probe_sync;
                __adb_probe_sync = 1;
        }
@@ -460,7 +460,7 @@ pmac_halt(void)
 static void __init pmac_init_early(void)
 {
        /* Enable early btext debug if requested */
-       if (strstr(cmd_line, "btextdbg")) {
+       if (strstr(boot_command_line, "btextdbg")) {
                udbg_adb_init_early();
                register_early_udbg_console();
        }
@@ -469,8 +469,8 @@ static void __init pmac_init_early(void)
        pmac_feature_init();
 
        /* Initialize debug stuff */
-       udbg_scc_init(!!strstr(cmd_line, "sccdbg"));
-       udbg_adb_init(!!strstr(cmd_line, "btextdbg"));
+       udbg_scc_init(!!strstr(boot_command_line, "sccdbg"));
+       udbg_adb_init(!!strstr(boot_command_line, "btextdbg"));
 
 #ifdef CONFIG_PPC64
        iommu_init_early_dart();
index c945bed4dc9e7439c929350effb3b583d3f3a740..426814a2ede34db7f83c6405a123aa4317a3008c 100644 (file)
@@ -66,6 +66,54 @@ static struct notifier_block ioda_eeh_nb = {
 };
 
 #ifdef CONFIG_DEBUG_FS
+static ssize_t ioda_eeh_ei_write(struct file *filp,
+                                const char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+       struct pci_controller *hose = filp->private_data;
+       struct pnv_phb *phb = hose->private_data;
+       struct eeh_dev *edev;
+       struct eeh_pe *pe;
+       int pe_no, type, func;
+       unsigned long addr, mask;
+       char buf[50];
+       int ret;
+
+       if (!phb->eeh_ops || !phb->eeh_ops->err_inject)
+               return -ENXIO;
+
+       ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count);
+       if (!ret)
+               return -EFAULT;
+
+       /* Retrieve parameters */
+       ret = sscanf(buf, "%x:%x:%x:%lx:%lx",
+                    &pe_no, &type, &func, &addr, &mask);
+       if (ret != 5)
+               return -EINVAL;
+
+       /* Retrieve PE */
+       edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+       if (!edev)
+               return -ENOMEM;
+       edev->phb = hose;
+       edev->pe_config_addr = pe_no;
+       pe = eeh_pe_get(edev);
+       kfree(edev);
+       if (!pe)
+               return -ENODEV;
+
+       /* Do error injection */
+       ret = phb->eeh_ops->err_inject(pe, type, func, addr, mask);
+       return ret < 0 ? ret : count;
+}
+
+static const struct file_operations ioda_eeh_ei_fops = {
+       .open   = simple_open,
+       .llseek = no_llseek,
+       .write  = ioda_eeh_ei_write,
+};
+
 static int ioda_eeh_dbgfs_set(void *data, int offset, u64 val)
 {
        struct pci_controller *hose = data;
@@ -152,6 +200,10 @@ static int ioda_eeh_post_init(struct pci_controller *hose)
        if (!phb->has_dbgfs && phb->dbgfs) {
                phb->has_dbgfs = 1;
 
+               debugfs_create_file("err_injct", 0200,
+                                   phb->dbgfs, hose,
+                                   &ioda_eeh_ei_fops);
+
                debugfs_create_file("err_injct_outbound", 0600,
                                    phb->dbgfs, hose,
                                    &ioda_eeh_outb_dbgfs_ops);
@@ -189,6 +241,7 @@ static int ioda_eeh_set_option(struct eeh_pe *pe, int option)
 {
        struct pci_controller *hose = pe->phb;
        struct pnv_phb *phb = hose->private_data;
+       bool freeze_pe = false;
        int enable, ret = 0;
        s64 rc;
 
@@ -212,6 +265,10 @@ static int ioda_eeh_set_option(struct eeh_pe *pe, int option)
        case EEH_OPT_THAW_DMA:
                enable = OPAL_EEH_ACTION_CLEAR_FREEZE_DMA;
                break;
+       case EEH_OPT_FREEZE_PE:
+               freeze_pe = true;
+               enable = OPAL_EEH_ACTION_SET_FREEZE_ALL;
+               break;
        default:
                pr_warn("%s: Invalid option %d\n",
                        __func__, option);
@@ -219,17 +276,35 @@ static int ioda_eeh_set_option(struct eeh_pe *pe, int option)
        }
 
        /* If PHB supports compound PE, to handle it */
-       if (phb->unfreeze_pe) {
-               ret = phb->unfreeze_pe(phb, pe->addr, enable);
+       if (freeze_pe) {
+               if (phb->freeze_pe) {
+                       phb->freeze_pe(phb, pe->addr);
+               } else {
+                       rc = opal_pci_eeh_freeze_set(phb->opal_id,
+                                                    pe->addr,
+                                                    enable);
+                       if (rc != OPAL_SUCCESS) {
+                               pr_warn("%s: Failure %lld freezing "
+                                       "PHB#%x-PE#%x\n",
+                                       __func__, rc,
+                                       phb->hose->global_number, pe->addr);
+                               ret = -EIO;
+                       }
+               }
        } else {
-               rc = opal_pci_eeh_freeze_clear(phb->opal_id,
-                                              pe->addr,
-                                              enable);
-               if (rc != OPAL_SUCCESS) {
-                       pr_warn("%s: Failure %lld enable %d for PHB#%x-PE#%x\n",
-                               __func__, rc, option, phb->hose->global_number,
-                               pe->addr);
-                       ret = -EIO;
+               if (phb->unfreeze_pe) {
+                       ret = phb->unfreeze_pe(phb, pe->addr, enable);
+               } else {
+                       rc = opal_pci_eeh_freeze_clear(phb->opal_id,
+                                                      pe->addr,
+                                                      enable);
+                       if (rc != OPAL_SUCCESS) {
+                               pr_warn("%s: Failure %lld enable %d "
+                                       "for PHB#%x-PE#%x\n",
+                                       __func__, rc, option,
+                                       phb->hose->global_number, pe->addr);
+                               ret = -EIO;
+                       }
                }
        }
 
@@ -439,11 +514,11 @@ int ioda_eeh_phb_reset(struct pci_controller *hose, int option)
        if (option == EEH_RESET_FUNDAMENTAL ||
            option == EEH_RESET_HOT)
                rc = opal_pci_reset(phb->opal_id,
-                               OPAL_PHB_COMPLETE,
+                               OPAL_RESET_PHB_COMPLETE,
                                OPAL_ASSERT_RESET);
        else if (option == EEH_RESET_DEACTIVATE)
                rc = opal_pci_reset(phb->opal_id,
-                               OPAL_PHB_COMPLETE,
+                               OPAL_RESET_PHB_COMPLETE,
                                OPAL_DEASSERT_RESET);
        if (rc < 0)
                goto out;
@@ -483,15 +558,15 @@ static int ioda_eeh_root_reset(struct pci_controller *hose, int option)
         */
        if (option == EEH_RESET_FUNDAMENTAL)
                rc = opal_pci_reset(phb->opal_id,
-                               OPAL_PCI_FUNDAMENTAL_RESET,
+                               OPAL_RESET_PCI_FUNDAMENTAL,
                                OPAL_ASSERT_RESET);
        else if (option == EEH_RESET_HOT)
                rc = opal_pci_reset(phb->opal_id,
-                               OPAL_PCI_HOT_RESET,
+                               OPAL_RESET_PCI_HOT,
                                OPAL_ASSERT_RESET);
        else if (option == EEH_RESET_DEACTIVATE)
                rc = opal_pci_reset(phb->opal_id,
-                               OPAL_PCI_HOT_RESET,
+                               OPAL_RESET_PCI_HOT,
                                OPAL_DEASSERT_RESET);
        if (rc < 0)
                goto out;
@@ -607,6 +682,31 @@ static int ioda_eeh_reset(struct eeh_pe *pe, int option)
        if (pe->type & EEH_PE_PHB) {
                ret = ioda_eeh_phb_reset(hose, option);
        } else {
+               struct pnv_phb *phb;
+               s64 rc;
+
+               /*
+                * The frozen PE might be caused by PAPR error injection
+                * registers, which are expected to be cleared after hitting
+                * frozen PE as stated in the hardware spec. Unfortunately,
+                * that's not true on P7IOC. So we have to clear it manually
+                * to avoid recursive EEH errors during recovery.
+                */
+               phb = hose->private_data;
+               if (phb->model == PNV_PHB_MODEL_P7IOC &&
+                   (option == EEH_RESET_HOT ||
+                   option == EEH_RESET_FUNDAMENTAL)) {
+                       rc = opal_pci_reset(phb->opal_id,
+                                           OPAL_RESET_PHB_ERROR,
+                                           OPAL_ASSERT_RESET);
+                       if (rc != OPAL_SUCCESS) {
+                               pr_warn("%s: Failure %lld clearing "
+                                       "error injection registers\n",
+                                       __func__, rc);
+                               return -EIO;
+                       }
+               }
+
                bus = eeh_pe_bus_get(pe);
                if (pci_is_root_bus(bus) ||
                    pci_is_root_bus(bus->parent))
@@ -628,8 +728,8 @@ static int ioda_eeh_reset(struct eeh_pe *pe, int option)
  * Retrieve error log, which contains log from device driver
  * and firmware.
  */
-int ioda_eeh_get_log(struct eeh_pe *pe, int severity,
-                    char *drv_log, unsigned long len)
+static int ioda_eeh_get_log(struct eeh_pe *pe, int severity,
+                           char *drv_log, unsigned long len)
 {
        pnv_pci_dump_phb_diag_data(pe->phb, pe->data);
 
@@ -650,6 +750,49 @@ static int ioda_eeh_configure_bridge(struct eeh_pe *pe)
        return 0;
 }
 
+static int ioda_eeh_err_inject(struct eeh_pe *pe, int type, int func,
+                              unsigned long addr, unsigned long mask)
+{
+       struct pci_controller *hose = pe->phb;
+       struct pnv_phb *phb = hose->private_data;
+       s64 ret;
+
+       /* Sanity check on error type */
+       if (type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR &&
+           type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) {
+               pr_warn("%s: Invalid error type %d\n",
+                       __func__, type);
+               return -ERANGE;
+       }
+
+       if (func < OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR ||
+           func > OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET) {
+               pr_warn("%s: Invalid error function %d\n",
+                       __func__, func);
+               return -ERANGE;
+       }
+
+       /* Firmware supports error injection ? */
+       if (!opal_check_token(OPAL_PCI_ERR_INJECT)) {
+               pr_warn("%s: Firmware doesn't support error injection\n",
+                       __func__);
+               return -ENXIO;
+       }
+
+       /* Do error injection */
+       ret = opal_pci_err_inject(phb->opal_id, pe->addr,
+                                 type, func, addr, mask);
+       if (ret != OPAL_SUCCESS) {
+               pr_warn("%s: Failure %lld injecting error "
+                       "%d-%d to PHB#%x-PE#%x\n",
+                       __func__, ret, type, func,
+                       hose->global_number, pe->addr);
+               return -EIO;
+       }
+
+       return 0;
+}
+
 static void ioda_eeh_hub_diag_common(struct OpalIoP7IOCErrorData *data)
 {
        /* GEM */
@@ -743,14 +886,12 @@ static int ioda_eeh_get_pe(struct pci_controller *hose,
         * the master PE because slave PE is invisible
         * to EEH core.
         */
-       if (phb->get_pe_state) {
-               pnv_pe = &phb->ioda.pe_array[pe_no];
-               if (pnv_pe->flags & PNV_IODA_PE_SLAVE) {
-                       pnv_pe = pnv_pe->master;
-                       WARN_ON(!pnv_pe ||
-                               !(pnv_pe->flags & PNV_IODA_PE_MASTER));
-                       pe_no = pnv_pe->pe_number;
-               }
+       pnv_pe = &phb->ioda.pe_array[pe_no];
+       if (pnv_pe->flags & PNV_IODA_PE_SLAVE) {
+               pnv_pe = pnv_pe->master;
+               WARN_ON(!pnv_pe ||
+                       !(pnv_pe->flags & PNV_IODA_PE_MASTER));
+               pe_no = pnv_pe->pe_number;
        }
 
        /* Find the PE according to PE# */
@@ -761,15 +902,37 @@ static int ioda_eeh_get_pe(struct pci_controller *hose,
        if (!dev_pe)
                return -EEXIST;
 
-       /*
-        * At this point, we're sure the compound PE should
-        * be put into frozen state.
-        */
+       /* Freeze the (compound) PE */
        *pe = dev_pe;
-       if (phb->freeze_pe &&
-           !(dev_pe->state & EEH_PE_ISOLATED))
+       if (!(dev_pe->state & EEH_PE_ISOLATED))
                phb->freeze_pe(phb, pe_no);
 
+       /*
+        * At this point, we're sure the (compound) PE should
+        * have been frozen. However, we still need poke until
+        * hitting the frozen PE on top level.
+        */
+       dev_pe = dev_pe->parent;
+       while (dev_pe && !(dev_pe->type & EEH_PE_PHB)) {
+               int ret;
+               int active_flags = (EEH_STATE_MMIO_ACTIVE |
+                                   EEH_STATE_DMA_ACTIVE);
+
+               ret = eeh_ops->get_state(dev_pe, NULL);
+               if (ret <= 0 || (ret & active_flags) == active_flags) {
+                       dev_pe = dev_pe->parent;
+                       continue;
+               }
+
+               /* Frozen parent PE */
+               *pe = dev_pe;
+               if (!(dev_pe->state & EEH_PE_ISOLATED))
+                       phb->freeze_pe(phb, dev_pe->addr);
+
+               /* Next one */
+               dev_pe = dev_pe->parent;
+       }
+
        return 0;
 }
 
@@ -971,5 +1134,6 @@ struct pnv_eeh_ops ioda_eeh_ops = {
        .reset                  = ioda_eeh_reset,
        .get_log                = ioda_eeh_get_log,
        .configure_bridge       = ioda_eeh_configure_bridge,
+       .err_inject             = ioda_eeh_err_inject,
        .next_error             = ioda_eeh_next_error
 };
index fd7a16f855edffd1315b055800b7e4de6ab28fc3..3e89cbf5588547660ed9f3543735c6b644ae77e9 100644 (file)
@@ -358,6 +358,31 @@ static int powernv_eeh_configure_bridge(struct eeh_pe *pe)
        return ret;
 }
 
+/**
+ * powernv_pe_err_inject - Inject specified error to the indicated PE
+ * @pe: the indicated PE
+ * @type: error type
+ * @func: specific error type
+ * @addr: address
+ * @mask: address mask
+ *
+ * The routine is called to inject specified error, which is
+ * determined by @type and @func, to the indicated PE for
+ * testing purpose.
+ */
+static int powernv_eeh_err_inject(struct eeh_pe *pe, int type, int func,
+                                 unsigned long addr, unsigned long mask)
+{
+       struct pci_controller *hose = pe->phb;
+       struct pnv_phb *phb = hose->private_data;
+       int ret = -EEXIST;
+
+       if (phb->eeh_ops && phb->eeh_ops->err_inject)
+               ret = phb->eeh_ops->err_inject(pe, type, func, addr, mask);
+
+       return ret;
+}
+
 /**
  * powernv_eeh_next_error - Retrieve next EEH error to handle
  * @pe: Affected PE
@@ -414,6 +439,7 @@ static struct eeh_ops powernv_eeh_ops = {
        .wait_state             = powernv_eeh_wait_state,
        .get_log                = powernv_eeh_get_log,
        .configure_bridge       = powernv_eeh_configure_bridge,
+       .err_inject             = powernv_eeh_err_inject,
        .read_config            = pnv_pci_cfg_read,
        .write_config           = pnv_pci_cfg_write,
        .next_error             = powernv_eeh_next_error,
index 85bb8fff7947f9d752d4f3adddbc0b99e76b78be..23260f7dfa7aeca7446dcfcc6e59f5ced346e6b1 100644 (file)
@@ -112,7 +112,7 @@ static ssize_t init_dump_show(struct dump_obj *dump_obj,
                              struct dump_attribute *attr,
                              char *buf)
 {
-       return sprintf(buf, "1 - initiate dump\n");
+       return sprintf(buf, "1 - initiate Service Processor(FSP) dump\n");
 }
 
 static int64_t dump_fips_init(uint8_t type)
@@ -121,7 +121,7 @@ static int64_t dump_fips_init(uint8_t type)
 
        rc = opal_dump_init(type);
        if (rc)
-               pr_warn("%s: Failed to initiate FipS dump (%d)\n",
+               pr_warn("%s: Failed to initiate FSP dump (%d)\n",
                        __func__, rc);
        return rc;
 }
@@ -131,8 +131,12 @@ static ssize_t init_dump_store(struct dump_obj *dump_obj,
                               const char *buf,
                               size_t count)
 {
-       dump_fips_init(DUMP_TYPE_FSP);
-       pr_info("%s: Initiated FSP dump\n", __func__);
+       int rc;
+
+       rc = dump_fips_init(DUMP_TYPE_FSP);
+       if (rc == OPAL_SUCCESS)
+               pr_info("%s: Initiated FSP dump\n", __func__);
+
        return count;
 }
 
@@ -297,7 +301,7 @@ static ssize_t dump_attr_read(struct file *filep, struct kobject *kobj,
                         * and rely on userspace to ask us to try
                         * again.
                         */
-                       pr_info("%s: Platform dump partially read.ID = 0x%x\n",
+                       pr_info("%s: Platform dump partially read. ID = 0x%x\n",
                                __func__, dump->id);
                        return -EIO;
                }
@@ -423,6 +427,10 @@ void __init opal_platform_dump_init(void)
 {
        int rc;
 
+       /* ELOG not supported by firmware */
+       if (!opal_check_token(OPAL_DUMP_READ))
+               return;
+
        dump_kset = kset_create_and_add("dump", NULL, opal_kobj);
        if (!dump_kset) {
                pr_warn("%s: Failed to create dump kset\n", __func__);
index bbdb3ffaab98f165a63b000961a26c3db41cc921..518fe95dbf24ec6bde07bf1f7c3923db0680b361 100644 (file)
@@ -295,6 +295,10 @@ int __init opal_elog_init(void)
 {
        int rc = 0;
 
+       /* ELOG not supported by firmware */
+       if (!opal_check_token(OPAL_ELOG_READ))
+               return -1;
+
        elog_kset = kset_create_and_add("elog", NULL, opal_kobj);
        if (!elog_kset) {
                pr_warn("%s: failed to create elog kset\n", __func__);
index ad4b31df779a389ceb83b1c62d77a850e9c1b749..dd2c285ad1708d755b5a1950558566149e837839 100644 (file)
@@ -191,6 +191,7 @@ static ssize_t lpc_debug_read(struct file *filp, char __user *ubuf,
 {
        struct lpc_debugfs_entry *lpc = filp->private_data;
        u32 data, pos, len, todo;
+       __be32 bedata;
        int rc;
 
        if (!access_ok(VERIFY_WRITE, ubuf, count))
@@ -213,9 +214,10 @@ static ssize_t lpc_debug_read(struct file *filp, char __user *ubuf,
                                len = 2;
                }
                rc = opal_lpc_read(opal_lpc_chip_id, lpc->lpc_type, pos,
-                                  &data, len);
+                                  &bedata, len);
                if (rc)
                        return -ENXIO;
+               data = be32_to_cpu(bedata);
                switch(len) {
                case 4:
                        rc = __put_user((u32)data, (u32 __user *)ubuf);
index acd9f7e96678256e2620b24494fded8e90b08ebb..f9896fd5d04afa1acc62f6ca6e82e25edce65d85 100644 (file)
@@ -78,7 +78,7 @@ void __init opal_nvram_init(void)
        }
        nvram_size = be32_to_cpup(nbytes_p);
 
-       printk(KERN_INFO "OPAL nvram setup, %u bytes\n", nvram_size);
+       pr_info("OPAL nvram setup, %u bytes\n", nvram_size);
        of_node_put(np);
 
        ppc_md.nvram_read = opal_nvram_read;
index b1885db8fdf34bfe068f1712d486f6884dc50d0e..499707ddaa9c79bc94dc900c725d828224209a0d 100644 (file)
@@ -42,6 +42,9 @@ unsigned long __init opal_get_boot_time(void)
        __be64 __h_m_s_ms;
        long rc = OPAL_BUSY;
 
+       if (!opal_check_token(OPAL_RTC_READ))
+               goto out;
+
        while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
                rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
                if (rc == OPAL_BUSY_EVENT)
@@ -49,16 +52,18 @@ unsigned long __init opal_get_boot_time(void)
                else
                        mdelay(10);
        }
-       if (rc != OPAL_SUCCESS) {
-               ppc_md.get_rtc_time = NULL;
-               ppc_md.set_rtc_time = NULL;
-               return 0;
-       }
+       if (rc != OPAL_SUCCESS)
+               goto out;
+
        y_m_d = be32_to_cpu(__y_m_d);
        h_m_s_ms = be64_to_cpu(__h_m_s_ms);
        opal_to_tm(y_m_d, h_m_s_ms, &tm);
        return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
                      tm.tm_hour, tm.tm_min, tm.tm_sec);
+out:
+       ppc_md.get_rtc_time = NULL;
+       ppc_md.set_rtc_time = NULL;
+       return 0;
 }
 
 void opal_get_rtc_time(struct rtc_time *tm)
index d8a000a9988b035db116aa446792a3968426e13d..ae14c40b4b1c8b0d119a905be191f1153b32fb6a 100644 (file)
@@ -2,7 +2,7 @@
 #include <linux/jump_label.h>
 #include <asm/trace.h>
 
-#ifdef CONFIG_JUMP_LABEL
+#ifdef HAVE_JUMP_LABEL
 struct static_key opal_tracepoint_key = STATIC_KEY_INIT;
 
 void opal_tracepoint_regfunc(void)
index 2e6ce1b8dc8ffcdd0d21cd86034c9e86e745880b..e9e2450c1fddd3bb6988084fb38039a8b3c3300b 100644 (file)
@@ -184,6 +184,7 @@ OPAL_CALL(opal_register_exception_handler,  OPAL_REGISTER_OPAL_EXCEPTION_HANDLER)
 OPAL_CALL(opal_pci_eeh_freeze_status,          OPAL_PCI_EEH_FREEZE_STATUS);
 OPAL_CALL(opal_pci_eeh_freeze_clear,           OPAL_PCI_EEH_FREEZE_CLEAR);
 OPAL_CALL(opal_pci_eeh_freeze_set,             OPAL_PCI_EEH_FREEZE_SET);
+OPAL_CALL(opal_pci_err_inject,                 OPAL_PCI_ERR_INJECT);
 OPAL_CALL(opal_pci_shpc,                       OPAL_PCI_SHPC);
 OPAL_CALL(opal_pci_phb_mmio_enable,            OPAL_PCI_PHB_MMIO_ENABLE);
 OPAL_CALL(opal_pci_set_phb_mem_window,         OPAL_PCI_SET_PHB_MEM_WINDOW);
@@ -232,6 +233,7 @@ OPAL_CALL(opal_validate_flash,                      OPAL_FLASH_VALIDATE);
 OPAL_CALL(opal_manage_flash,                   OPAL_FLASH_MANAGE);
 OPAL_CALL(opal_update_flash,                   OPAL_FLASH_UPDATE);
 OPAL_CALL(opal_resync_timebase,                        OPAL_RESYNC_TIMEBASE);
+OPAL_CALL(opal_check_token,                    OPAL_CHECK_TOKEN);
 OPAL_CALL(opal_dump_init,                      OPAL_DUMP_INIT);
 OPAL_CALL(opal_dump_info,                      OPAL_DUMP_INFO);
 OPAL_CALL(opal_dump_info2,                     OPAL_DUMP_INFO2);
@@ -247,3 +249,4 @@ OPAL_CALL(opal_set_param,                   OPAL_SET_PARAM);
 OPAL_CALL(opal_handle_hmi,                     OPAL_HANDLE_HMI);
 OPAL_CALL(opal_register_dump_region,           OPAL_REGISTER_DUMP_REGION);
 OPAL_CALL(opal_unregister_dump_region,         OPAL_UNREGISTER_DUMP_REGION);
+OPAL_CALL(opal_pci_set_phb_cxl_mode,           OPAL_PCI_SET_PHB_CXL_MODE);
index 4b005ae5dc4b6cfb4c9a73c94b3debd3b3edc08d..b642b0562f5aca3e8ecde20d3f86738e621f9092 100644 (file)
@@ -105,12 +105,12 @@ int __init early_init_dt_scan_opal(unsigned long node,
        if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) {
                powerpc_firmware_features |= FW_FEATURE_OPALv2;
                powerpc_firmware_features |= FW_FEATURE_OPALv3;
-               printk("OPAL V3 detected !\n");
+               pr_info("OPAL V3 detected !\n");
        } else if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) {
                powerpc_firmware_features |= FW_FEATURE_OPALv2;
-               printk("OPAL V2 detected !\n");
+               pr_info("OPAL V2 detected !\n");
        } else {
-               printk("OPAL V1 detected !\n");
+               pr_info("OPAL V1 detected !\n");
        }
 
        /* Reinit all cores with the right endian */
index df241b11d4f7d346f4a85c0632668677a9f3e07a..468a0f23c7f2b5f756c1b553315793c03492c0d6 100644 (file)
 #include <asm/xics.h>
 #include <asm/debug.h>
 #include <asm/firmware.h>
+#include <asm/pnv-pci.h>
+
+#include <misc/cxl.h>
 
 #include "powernv.h"
 #include "pci.h"
 
-#define define_pe_printk_level(func, kern_level)               \
-static int func(const struct pnv_ioda_pe *pe, const char *fmt, ...)    \
-{                                                              \
-       struct va_format vaf;                                   \
-       va_list args;                                           \
-       char pfix[32];                                          \
-       int r;                                                  \
-                                                               \
-       va_start(args, fmt);                                    \
-                                                               \
-       vaf.fmt = fmt;                                          \
-       vaf.va = &args;                                         \
-                                                               \
-       if (pe->pdev)                                           \
-               strlcpy(pfix, dev_name(&pe->pdev->dev),         \
-                       sizeof(pfix));                          \
-       else                                                    \
-               sprintf(pfix, "%04x:%02x     ",                 \
-                       pci_domain_nr(pe->pbus),                \
-                       pe->pbus->number);                      \
-       r = printk(kern_level "pci %s: [PE# %.3d] %pV",         \
-                  pfix, pe->pe_number, &vaf);                  \
-                                                               \
-       va_end(args);                                           \
-                                                               \
-       return r;                                               \
-}                                                              \
-
-define_pe_printk_level(pe_err, KERN_ERR);
-define_pe_printk_level(pe_warn, KERN_WARNING);
-define_pe_printk_level(pe_info, KERN_INFO);
+static void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level,
+                           const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+       char pfix[32];
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       if (pe->pdev)
+               strlcpy(pfix, dev_name(&pe->pdev->dev), sizeof(pfix));
+       else
+               sprintf(pfix, "%04x:%02x     ",
+                       pci_domain_nr(pe->pbus), pe->pbus->number);
+
+       printk("%spci %s: [PE# %.3d] %pV",
+              level, pfix, pe->pe_number, &vaf);
+
+       va_end(args);
+}
+
+#define pe_err(pe, fmt, ...)                                   \
+       pe_level_printk(pe, KERN_ERR, fmt, ##__VA_ARGS__)
+#define pe_warn(pe, fmt, ...)                                  \
+       pe_level_printk(pe, KERN_WARNING, fmt, ##__VA_ARGS__)
+#define pe_info(pe, fmt, ...)                                  \
+       pe_level_printk(pe, KERN_INFO, fmt, ##__VA_ARGS__)
 
 /*
  * stdcix is only supposed to be used in hypervisor real mode as per
@@ -385,7 +387,7 @@ static void pnv_ioda_freeze_pe(struct pnv_phb *phb, int pe_no)
        }
 }
 
-int pnv_ioda_unfreeze_pe(struct pnv_phb *phb, int pe_no, int opt)
+static int pnv_ioda_unfreeze_pe(struct pnv_phb *phb, int pe_no, int opt)
 {
        struct pnv_ioda_pe *pe, *slave;
        s64 rc;
@@ -890,6 +892,28 @@ static int pnv_pci_ioda_dma_set_mask(struct pnv_phb *phb,
        return 0;
 }
 
+static u64 pnv_pci_ioda_dma_get_required_mask(struct pnv_phb *phb,
+                                             struct pci_dev *pdev)
+{
+       struct pci_dn *pdn = pci_get_pdn(pdev);
+       struct pnv_ioda_pe *pe;
+       u64 end, mask;
+
+       if (WARN_ON(!pdn || pdn->pe_number == IODA_INVALID_PE))
+               return 0;
+
+       pe = &phb->ioda.pe_array[pdn->pe_number];
+       if (!pe->tce_bypass_enabled)
+               return __dma_get_required_mask(&pdev->dev);
+
+
+       end = pe->tce_bypass_base + memblock_end_of_DRAM();
+       mask = 1ULL << (fls64(end) - 1);
+       mask += mask - 1;
+
+       return mask;
+}
+
 static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe,
                                   struct pci_bus *bus,
                                   bool add_to_iommu_group)
@@ -1306,14 +1330,186 @@ static void pnv_ioda2_msi_eoi(struct irq_data *d)
        icp_native_eoi(d);
 }
 
+
+static void set_msi_irq_chip(struct pnv_phb *phb, unsigned int virq)
+{
+       struct irq_data *idata;
+       struct irq_chip *ichip;
+
+       if (phb->type != PNV_PHB_IODA2)
+               return;
+
+       if (!phb->ioda.irq_chip_init) {
+               /*
+                * First time we setup an MSI IRQ, we need to setup the
+                * corresponding IRQ chip to route correctly.
+                */
+               idata = irq_get_irq_data(virq);
+               ichip = irq_data_get_irq_chip(idata);
+               phb->ioda.irq_chip_init = 1;
+               phb->ioda.irq_chip = *ichip;
+               phb->ioda.irq_chip.irq_eoi = pnv_ioda2_msi_eoi;
+       }
+       irq_set_chip(virq, &phb->ioda.irq_chip);
+}
+
+#ifdef CONFIG_CXL_BASE
+
+struct device_node *pnv_pci_to_phb_node(struct pci_dev *dev)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+
+       return hose->dn;
+}
+EXPORT_SYMBOL(pnv_pci_to_phb_node);
+
+int pnv_phb_to_cxl(struct pci_dev *dev)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       struct pnv_phb *phb = hose->private_data;
+       struct pnv_ioda_pe *pe;
+       int rc;
+
+       pe = pnv_ioda_get_pe(dev);
+       if (!pe)
+               return -ENODEV;
+
+       pe_info(pe, "Switching PHB to CXL\n");
+
+       rc = opal_pci_set_phb_cxl_mode(phb->opal_id, 1, pe->pe_number);
+       if (rc)
+               dev_err(&dev->dev, "opal_pci_set_phb_cxl_mode failed: %i\n", rc);
+
+       return rc;
+}
+EXPORT_SYMBOL(pnv_phb_to_cxl);
+
+/* Find PHB for cxl dev and allocate MSI hwirqs?
+ * Returns the absolute hardware IRQ number
+ */
+int pnv_cxl_alloc_hwirqs(struct pci_dev *dev, int num)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       struct pnv_phb *phb = hose->private_data;
+       int hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, num);
+
+       if (hwirq < 0) {
+               dev_warn(&dev->dev, "Failed to find a free MSI\n");
+               return -ENOSPC;
+       }
+
+       return phb->msi_base + hwirq;
+}
+EXPORT_SYMBOL(pnv_cxl_alloc_hwirqs);
+
+void pnv_cxl_release_hwirqs(struct pci_dev *dev, int hwirq, int num)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       struct pnv_phb *phb = hose->private_data;
+
+       msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq - phb->msi_base, num);
+}
+EXPORT_SYMBOL(pnv_cxl_release_hwirqs);
+
+void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs,
+                                 struct pci_dev *dev)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       struct pnv_phb *phb = hose->private_data;
+       int i, hwirq;
+
+       for (i = 1; i < CXL_IRQ_RANGES; i++) {
+               if (!irqs->range[i])
+                       continue;
+               pr_devel("cxl release irq range 0x%x: offset: 0x%lx  limit: %ld\n",
+                        i, irqs->offset[i],
+                        irqs->range[i]);
+               hwirq = irqs->offset[i] - phb->msi_base;
+               msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq,
+                                      irqs->range[i]);
+       }
+}
+EXPORT_SYMBOL(pnv_cxl_release_hwirq_ranges);
+
+int pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs,
+                              struct pci_dev *dev, int num)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       struct pnv_phb *phb = hose->private_data;
+       int i, hwirq, try;
+
+       memset(irqs, 0, sizeof(struct cxl_irq_ranges));
+
+       /* 0 is reserved for the multiplexed PSL DSI interrupt */
+       for (i = 1; i < CXL_IRQ_RANGES && num; i++) {
+               try = num;
+               while (try) {
+                       hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, try);
+                       if (hwirq >= 0)
+                               break;
+                       try /= 2;
+               }
+               if (!try)
+                       goto fail;
+
+               irqs->offset[i] = phb->msi_base + hwirq;
+               irqs->range[i] = try;
+               pr_devel("cxl alloc irq range 0x%x: offset: 0x%lx  limit: %li\n",
+                        i, irqs->offset[i], irqs->range[i]);
+               num -= try;
+       }
+       if (num)
+               goto fail;
+
+       return 0;
+fail:
+       pnv_cxl_release_hwirq_ranges(irqs, dev);
+       return -ENOSPC;
+}
+EXPORT_SYMBOL(pnv_cxl_alloc_hwirq_ranges);
+
+int pnv_cxl_get_irq_count(struct pci_dev *dev)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       struct pnv_phb *phb = hose->private_data;
+
+       return phb->msi_bmp.irq_count;
+}
+EXPORT_SYMBOL(pnv_cxl_get_irq_count);
+
+int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq,
+                          unsigned int virq)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       struct pnv_phb *phb = hose->private_data;
+       unsigned int xive_num = hwirq - phb->msi_base;
+       struct pnv_ioda_pe *pe;
+       int rc;
+
+       if (!(pe = pnv_ioda_get_pe(dev)))
+               return -ENODEV;
+
+       /* Assign XIVE to PE */
+       rc = opal_pci_set_xive_pe(phb->opal_id, pe->pe_number, xive_num);
+       if (rc) {
+               pe_warn(pe, "%s: OPAL error %d setting msi_base 0x%x "
+                       "hwirq 0x%x XIVE 0x%x PE\n",
+                       pci_name(dev), rc, phb->msi_base, hwirq, xive_num);
+               return -EIO;
+       }
+       set_msi_irq_chip(phb, virq);
+
+       return 0;
+}
+EXPORT_SYMBOL(pnv_cxl_ioda_msi_setup);
+#endif
+
 static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
                                  unsigned int hwirq, unsigned int virq,
                                  unsigned int is_64, struct msi_msg *msg)
 {
        struct pnv_ioda_pe *pe = pnv_ioda_get_pe(dev);
        struct pci_dn *pdn = pci_get_pdn(dev);
-       struct irq_data *idata;
-       struct irq_chip *ichip;
        unsigned int xive_num = hwirq - phb->msi_base;
        __be32 data;
        int rc;
@@ -1365,22 +1561,7 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
        }
        msg->data = be32_to_cpu(data);
 
-       /*
-        * Change the IRQ chip for the MSI interrupts on PHB3.
-        * The corresponding IRQ chip should be populated for
-        * the first time.
-        */
-       if (phb->type == PNV_PHB_IODA2) {
-               if (!phb->ioda.irq_chip_init) {
-                       idata = irq_get_irq_data(virq);
-                       ichip = irq_data_get_irq_chip(idata);
-                       phb->ioda.irq_chip_init = 1;
-                       phb->ioda.irq_chip = *ichip;
-                       phb->ioda.irq_chip.irq_eoi = pnv_ioda2_msi_eoi;
-               }
-
-               irq_set_chip(virq, &phb->ioda.irq_chip);
-       }
+       set_msi_irq_chip(phb, virq);
 
        pr_devel("%s: %s-bit MSI on hwirq %x (xive #%d),"
                 " address=%x_%08x data=%x PE# %d\n",
@@ -1627,12 +1808,12 @@ static u32 pnv_ioda_bdfn_to_pe(struct pnv_phb *phb, struct pci_bus *bus,
 
 static void pnv_pci_ioda_shutdown(struct pnv_phb *phb)
 {
-       opal_pci_reset(phb->opal_id, OPAL_PCI_IODA_TABLE_RESET,
+       opal_pci_reset(phb->opal_id, OPAL_RESET_PCI_IODA_TABLE,
                       OPAL_ASSERT_RESET);
 }
 
-void __init pnv_pci_init_ioda_phb(struct device_node *np,
-                                 u64 hub_id, int ioda_type)
+static void __init pnv_pci_init_ioda_phb(struct device_node *np,
+                                        u64 hub_id, int ioda_type)
 {
        struct pci_controller *hose;
        struct pnv_phb *phb;
@@ -1782,6 +1963,7 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np,
        /* Setup TCEs */
        phb->dma_dev_setup = pnv_pci_ioda_dma_dev_setup;
        phb->dma_set_mask = pnv_pci_ioda_dma_set_mask;
+       phb->dma_get_required_mask = pnv_pci_ioda_dma_get_required_mask;
 
        /* Setup shutdown function for kexec */
        phb->shutdown = pnv_pci_ioda_shutdown;
@@ -1803,7 +1985,7 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np,
        pci_add_flags(PCI_REASSIGN_ALL_RSRC);
 
        /* Reset IODA tables to a clean state */
-       rc = opal_pci_reset(phb_id, OPAL_PCI_IODA_TABLE_RESET, OPAL_ASSERT_RESET);
+       rc = opal_pci_reset(phb_id, OPAL_RESET_PCI_IODA_TABLE, OPAL_ASSERT_RESET);
        if (rc)
                pr_warning("  OPAL Error %ld performing IODA table reset !\n", rc);
 
index b854b57ed5e1e26c95c9925db9465c6767a89567..b3ca77ddf36dfbd07de81feb5015b27bd675c009 100644 (file)
 //#define cfg_dbg(fmt...)      printk(fmt)
 
 #ifdef CONFIG_PCI_MSI
-static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type)
-{
-       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
-       struct pnv_phb *phb = hose->private_data;
-       struct pci_dn *pdn = pci_get_pdn(pdev);
-
-       if (pdn && pdn->force_32bit_msi && !phb->msi32_support)
-               return -ENODEV;
-
-       return (phb && phb->msi_bmp.bitmap) ? 0 : -ENODEV;
-}
-
 static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 {
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
        struct pnv_phb *phb = hose->private_data;
+       struct pci_dn *pdn = pci_get_pdn(pdev);
        struct msi_desc *entry;
        struct msi_msg msg;
        int hwirq;
        unsigned int virq;
        int rc;
 
-       if (WARN_ON(!phb))
+       if (WARN_ON(!phb) || !phb->msi_bmp.bitmap)
+               return -ENODEV;
+
+       if (pdn && pdn->force_32bit_msi && !phb->msi32_support)
                return -ENODEV;
 
        list_for_each_entry(entry, &pdev->msi_list, list) {
@@ -761,6 +753,17 @@ int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
        return __dma_set_mask(&pdev->dev, dma_mask);
 }
 
+u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev)
+{
+       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+       struct pnv_phb *phb = hose->private_data;
+
+       if (phb && phb->dma_get_required_mask)
+               return phb->dma_get_required_mask(phb, pdev);
+
+       return __dma_get_required_mask(&pdev->dev);
+}
+
 void pnv_pci_shutdown(void)
 {
        struct pci_controller *hose;
@@ -860,7 +863,6 @@ void __init pnv_pci_init(void)
 
        /* Configure MSIs */
 #ifdef CONFIG_PCI_MSI
-       ppc_md.msi_check_device = pnv_msi_check_device;
        ppc_md.setup_msi_irqs = pnv_setup_msi_irqs;
        ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs;
 #endif
index 48494d4b60581d43baac6a59a198166786ec4101..34d29eb2a4def4dc83044ad6cfaf5c42210a9e86 100644 (file)
@@ -85,6 +85,8 @@ struct pnv_eeh_ops {
        int (*get_log)(struct eeh_pe *pe, int severity,
                       char *drv_log, unsigned long len);
        int (*configure_bridge)(struct eeh_pe *pe);
+       int (*err_inject)(struct eeh_pe *pe, int type, int func,
+                         unsigned long addr, unsigned long mask);
        int (*next_error)(struct eeh_pe **pe);
 };
 #endif /* CONFIG_EEH */
@@ -122,6 +124,8 @@ struct pnv_phb {
        void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev);
        int (*dma_set_mask)(struct pnv_phb *phb, struct pci_dev *pdev,
                            u64 dma_mask);
+       u64 (*dma_get_required_mask)(struct pnv_phb *phb,
+                                    struct pci_dev *pdev);
        void (*fixup_phb)(struct pci_controller *hose);
        u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn);
        void (*shutdown)(struct pnv_phb *phb);
index 75501bfede7f9b2fdc51a3987fd03d09383477ee..6c8e2d188cd096330b7b1926da13c50de1e73f2e 100644 (file)
@@ -13,6 +13,7 @@ struct pci_dev;
 extern void pnv_pci_init(void);
 extern void pnv_pci_shutdown(void);
 extern int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask);
+extern u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev);
 #else
 static inline void pnv_pci_init(void) { }
 static inline void pnv_pci_shutdown(void) { }
@@ -21,6 +22,11 @@ static inline int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
 {
        return -ENODEV;
 }
+
+static inline u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev)
+{
+       return 0;
+}
 #endif
 
 extern void pnv_lpc_init(void);
index 5a0e2dc6de5f127abef1748090fb15e17dfb58fe..3f9546d8a51f907dda294d33c1d507bc01d6aa1b 100644 (file)
@@ -173,6 +173,14 @@ static int pnv_dma_set_mask(struct device *dev, u64 dma_mask)
        return __dma_set_mask(dev, dma_mask);
 }
 
+static u64 pnv_dma_get_required_mask(struct device *dev)
+{
+       if (dev_is_pci(dev))
+               return pnv_pci_dma_get_required_mask(to_pci_dev(dev));
+
+       return __dma_get_required_mask(dev);
+}
+
 static void pnv_shutdown(void)
 {
        /* Let the PCI code clear up IODA tables */
@@ -307,7 +315,7 @@ static int __init pnv_probe(void)
  * Returns the cpu frequency for 'cpu' in Hz. This is used by
  * /proc/cpuinfo
  */
-unsigned long pnv_get_proc_freq(unsigned int cpu)
+static unsigned long pnv_get_proc_freq(unsigned int cpu)
 {
        unsigned long ret_freq;
 
@@ -335,6 +343,7 @@ define_machine(powernv) {
        .power_save             = power7_idle,
        .calibrate_decr         = generic_calibrate_decr,
        .dma_set_mask           = pnv_dma_set_mask,
+       .dma_get_required_mask  = pnv_dma_get_required_mask,
 #ifdef CONFIG_KEXEC
        .kexec_cpu_down         = pnv_kexec_cpu_down,
 #endif
index 5fcfcf44e3a9b949e6536ebcf53038645286fde6..4753958cd509bdfeec1be8bf088eb69e32077569 100644 (file)
@@ -54,7 +54,7 @@ static void pnv_smp_setup_cpu(int cpu)
 #endif
 }
 
-int pnv_smp_kick_cpu(int nr)
+static int pnv_smp_kick_cpu(int nr)
 {
        unsigned int pcpu = get_hard_smp_processor_id(nr);
        unsigned long start_here =
@@ -168,9 +168,9 @@ static void pnv_smp_cpu_kill_self(void)
                power7_nap(1);
                ppc64_runlatch_on();
 
-               /* Reenable IRQs briefly to clear the IPI that woke us */
-               local_irq_enable();
-               local_irq_disable();
+               /* Clear the IPI that woke us up */
+               icp_native_flush_interrupt();
+               local_paca->irq_happened &= PACA_IRQ_HARD_DIS;
                mb();
 
                if (cpu_core_split_required())
index 894ecb3eb5963506be9e0efead851a48cf577b1e..c87f96b79d1a02ed5f5371a2d02fc509deb3f3a1 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/smp.h>
 
 #include "subcore.h"
+#include "powernv.h"
 
 
 /*
index 2d8bf15879fdd3e14da6c8c46f6bbeaf7ab3f138..fc44ad0475f845e6787e387511b2848698cb333e 100644 (file)
@@ -555,7 +555,6 @@ static int cmm_mem_going_offline(void *arg)
                                pa_last = pa_last->next;
                                free_page((unsigned long)cmm_page_list);
                                cmm_page_list = pa_last;
-                               continue;
                        }
                }
                pa_curr = pa_curr->next;
index a2450b8a50a5eaaf3bbab9fc402bbf8997c00ed4..fdf01b660d5930cd31c4f246c7d4f4659aad6690 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/slab.h>
 #include <linux/of.h>
 #include "offline_states.h"
+#include "pseries.h"
 
 #include <asm/prom.h>
 #include <asm/machdep.h>
@@ -363,7 +364,8 @@ static int dlpar_online_cpu(struct device_node *dn)
        int rc = 0;
        unsigned int cpu;
        int len, nthreads, i;
-       const u32 *intserv;
+       const __be32 *intserv;
+       u32 thread;
 
        intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
        if (!intserv)
@@ -373,8 +375,9 @@ static int dlpar_online_cpu(struct device_node *dn)
 
        cpu_maps_update_begin();
        for (i = 0; i < nthreads; i++) {
+               thread = be32_to_cpu(intserv[i]);
                for_each_present_cpu(cpu) {
-                       if (get_hard_smp_processor_id(cpu) != intserv[i])
+                       if (get_hard_smp_processor_id(cpu) != thread)
                                continue;
                        BUG_ON(get_cpu_current_state(cpu)
                                        != CPU_STATE_OFFLINE);
@@ -388,7 +391,7 @@ static int dlpar_online_cpu(struct device_node *dn)
                }
                if (cpu == num_possible_cpus())
                        printk(KERN_WARNING "Could not find cpu to online "
-                              "with physical id 0x%x\n", intserv[i]);
+                              "with physical id 0x%x\n", thread);
        }
        cpu_maps_update_done();
 
@@ -442,7 +445,8 @@ static int dlpar_offline_cpu(struct device_node *dn)
        int rc = 0;
        unsigned int cpu;
        int len, nthreads, i;
-       const u32 *intserv;
+       const __be32 *intserv;
+       u32 thread;
 
        intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
        if (!intserv)
@@ -452,8 +456,9 @@ static int dlpar_offline_cpu(struct device_node *dn)
 
        cpu_maps_update_begin();
        for (i = 0; i < nthreads; i++) {
+               thread = be32_to_cpu(intserv[i]);
                for_each_present_cpu(cpu) {
-                       if (get_hard_smp_processor_id(cpu) != intserv[i])
+                       if (get_hard_smp_processor_id(cpu) != thread)
                                continue;
 
                        if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE)
@@ -475,14 +480,14 @@ static int dlpar_offline_cpu(struct device_node *dn)
                         * Upgrade it's state to CPU_STATE_OFFLINE.
                         */
                        set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
-                       BUG_ON(plpar_hcall_norets(H_PROD, intserv[i])
+                       BUG_ON(plpar_hcall_norets(H_PROD, thread)
                                                                != H_SUCCESS);
                        __cpu_die(cpu);
                        break;
                }
                if (cpu == num_possible_cpus())
                        printk(KERN_WARNING "Could not find cpu to offline "
-                              "with physical id 0x%x\n", intserv[i]);
+                              "with physical id 0x%x\n", thread);
        }
        cpu_maps_update_done();
 
@@ -494,15 +499,15 @@ out:
 static ssize_t dlpar_cpu_release(const char *buf, size_t count)
 {
        struct device_node *dn;
-       const u32 *drc_index;
+       u32 drc_index;
        int rc;
 
        dn = of_find_node_by_path(buf);
        if (!dn)
                return -EINVAL;
 
-       drc_index = of_get_property(dn, "ibm,my-drc-index", NULL);
-       if (!drc_index) {
+       rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index);
+       if (rc) {
                of_node_put(dn);
                return -EINVAL;
        }
@@ -513,7 +518,7 @@ static ssize_t dlpar_cpu_release(const char *buf, size_t count)
                return -EINVAL;
        }
 
-       rc = dlpar_release_drc(*drc_index);
+       rc = dlpar_release_drc(drc_index);
        if (rc) {
                of_node_put(dn);
                return rc;
@@ -521,7 +526,7 @@ static ssize_t dlpar_cpu_release(const char *buf, size_t count)
 
        rc = dlpar_detach_node(dn);
        if (rc) {
-               dlpar_acquire_drc(*drc_index);
+               dlpar_acquire_drc(drc_index);
                return rc;
        }
 
index b08053819d99f1f9d2a7da36828faa268837e66e..a6c7e19f5eb365e2091a270929cf9a7025548e08 100644 (file)
@@ -88,29 +88,14 @@ static int pseries_eeh_init(void)
         * and its variant since the old firmware probably support address
         * of domain/bus/slot/function for EEH RTAS operations.
         */
-       if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE) {
-               pr_warn("%s: RTAS service <ibm,set-eeh-option> invalid\n",
-                       __func__);
-               return -EINVAL;
-       } else if (ibm_set_slot_reset == RTAS_UNKNOWN_SERVICE) {
-               pr_warn("%s: RTAS service <ibm,set-slot-reset> invalid\n",
-                       __func__);
-               return -EINVAL;
-       } else if (ibm_read_slot_reset_state2 == RTAS_UNKNOWN_SERVICE &&
-                  ibm_read_slot_reset_state == RTAS_UNKNOWN_SERVICE) {
-               pr_warn("%s: RTAS service <ibm,read-slot-reset-state2> and "
-                       "<ibm,read-slot-reset-state> invalid\n",
-                       __func__);
-               return -EINVAL;
-       } else if (ibm_slot_error_detail == RTAS_UNKNOWN_SERVICE) {
-               pr_warn("%s: RTAS service <ibm,slot-error-detail> invalid\n",
-                       __func__);
-               return -EINVAL;
-       } else if (ibm_configure_pe == RTAS_UNKNOWN_SERVICE &&
-                  ibm_configure_bridge == RTAS_UNKNOWN_SERVICE) {
-               pr_warn("%s: RTAS service <ibm,configure-pe> and "
-                       "<ibm,configure-bridge> invalid\n",
-                       __func__);
+       if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE          ||
+           ibm_set_slot_reset == RTAS_UNKNOWN_SERVICE          ||
+           (ibm_read_slot_reset_state2 == RTAS_UNKNOWN_SERVICE &&
+            ibm_read_slot_reset_state == RTAS_UNKNOWN_SERVICE) ||
+           ibm_slot_error_detail == RTAS_UNKNOWN_SERVICE       ||
+           (ibm_configure_pe == RTAS_UNKNOWN_SERVICE           &&
+            ibm_configure_bridge == RTAS_UNKNOWN_SERVICE)) {
+               pr_info("EEH functionality not supported\n");
                return -EINVAL;
        }
 
@@ -118,11 +103,11 @@ static int pseries_eeh_init(void)
        spin_lock_init(&slot_errbuf_lock);
        eeh_error_buf_size = rtas_token("rtas-error-log-max");
        if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) {
-               pr_warn("%s: unknown EEH error log size\n",
+               pr_info("%s: unknown EEH error log size\n",
                        __func__);
                eeh_error_buf_size = 1024;
        } else if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) {
-               pr_warn("%s: EEH error log size %d exceeds the maximal %d\n",
+               pr_info("%s: EEH error log size %d exceeds the maximal %d\n",
                        __func__, eeh_error_buf_size, RTAS_ERROR_LOG_MAX);
                eeh_error_buf_size = RTAS_ERROR_LOG_MAX;
        }
@@ -349,7 +334,9 @@ static int pseries_eeh_set_option(struct eeh_pe *pe, int option)
                if (pe->addr)
                        config_addr = pe->addr;
                break;
-
+       case EEH_OPT_FREEZE_PE:
+               /* Not support */
+               return 0;
        default:
                pr_err("%s: Invalid option %d\n",
                        __func__, option);
@@ -729,6 +716,7 @@ static struct eeh_ops pseries_eeh_ops = {
        .wait_state             = pseries_eeh_wait_state,
        .get_log                = pseries_eeh_get_log,
        .configure_bridge       = pseries_eeh_configure_bridge,
+       .err_inject             = NULL,
        .read_config            = pseries_eeh_read_config,
        .write_config           = pseries_eeh_write_config,
        .next_error             = NULL,
index 20d62975856fb7fa5795a566629bbb69aa7a3139..b174fa751d260bcebd20b0af83cf5e13ba28158a 100644 (file)
@@ -90,7 +90,7 @@ static void rtas_stop_self(void)
 {
        static struct rtas_args args = {
                .nargs = 0,
-               .nret = 1,
+               .nret = cpu_to_be32(1),
                .rets = &args.args[0],
        };
 
@@ -312,7 +312,8 @@ static void pseries_remove_processor(struct device_node *np)
 {
        unsigned int cpu;
        int len, nthreads, i;
-       const u32 *intserv;
+       const __be32 *intserv;
+       u32 thread;
 
        intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", &len);
        if (!intserv)
@@ -322,8 +323,9 @@ static void pseries_remove_processor(struct device_node *np)
 
        cpu_maps_update_begin();
        for (i = 0; i < nthreads; i++) {
+               thread = be32_to_cpu(intserv[i]);
                for_each_present_cpu(cpu) {
-                       if (get_hard_smp_processor_id(cpu) != intserv[i])
+                       if (get_hard_smp_processor_id(cpu) != thread)
                                continue;
                        BUG_ON(cpu_online(cpu));
                        set_cpu_present(cpu, false);
@@ -332,7 +334,7 @@ static void pseries_remove_processor(struct device_node *np)
                }
                if (cpu >= nr_cpu_ids)
                        printk(KERN_WARNING "Could not find cpu to remove "
-                              "with physical id 0x%x\n", intserv[i]);
+                              "with physical id 0x%x\n", thread);
        }
        cpu_maps_update_done();
 }
index 34064f50945ef22a1246c8201556c7dc75a7cc8e..3c4c0dcd90d3e03051b4b09a3c28caabfe6edd1c 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/machdep.h>
 #include <asm/prom.h>
 #include <asm/sparsemem.h>
+#include "pseries.h"
 
 unsigned long pseries_memory_block_size(void)
 {
index 4642d6a4d35641d5219a2378ce917409cea4c850..de1ec54a2a57924ac2f3c29a6d43153e773e8aee 100644 (file)
@@ -329,16 +329,16 @@ struct direct_window {
 
 /* Dynamic DMA Window support */
 struct ddw_query_response {
-       __be32 windows_available;
-       __be32 largest_available_block;
-       __be32 page_size;
-       __be32 migration_capable;
+       u32 windows_available;
+       u32 largest_available_block;
+       u32 page_size;
+       u32 migration_capable;
 };
 
 struct ddw_create_response {
-       __be32 liobn;
-       __be32 addr_hi;
-       __be32 addr_lo;
+       u32 liobn;
+       u32 addr_hi;
+       u32 addr_lo;
 };
 
 static LIST_HEAD(direct_window_list);
@@ -725,16 +725,18 @@ static void remove_ddw(struct device_node *np, bool remove_prop)
 {
        struct dynamic_dma_window_prop *dwp;
        struct property *win64;
-       const u32 *ddw_avail;
+       u32 ddw_avail[3];
        u64 liobn;
-       int len, ret = 0;
+       int ret = 0;
+
+       ret = of_property_read_u32_array(np, "ibm,ddw-applicable",
+                                        &ddw_avail[0], 3);
 
-       ddw_avail = of_get_property(np, "ibm,ddw-applicable", &len);
        win64 = of_find_property(np, DIRECT64_PROPNAME, NULL);
        if (!win64)
                return;
 
-       if (!ddw_avail || len < 3 * sizeof(u32) || win64->length < sizeof(*dwp))
+       if (ret || win64->length < sizeof(*dwp))
                goto delprop;
 
        dwp = win64->value;
@@ -872,8 +874,9 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail,
 
        do {
                /* extra outputs are LIOBN and dma-addr (hi, lo) */
-               ret = rtas_call(ddw_avail[1], 5, 4, (u32 *)create, cfg_addr,
-                               BUID_HI(buid), BUID_LO(buid), page_shift, window_shift);
+               ret = rtas_call(ddw_avail[1], 5, 4, (u32 *)create,
+                               cfg_addr, BUID_HI(buid), BUID_LO(buid),
+                               page_shift, window_shift);
        } while (rtas_busy_delay(ret));
        dev_info(&dev->dev,
                "ibm,create-pe-dma-window(%x) %x %x %x %x %x returned %d "
@@ -910,7 +913,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
        int page_shift;
        u64 dma_addr, max_addr;
        struct device_node *dn;
-       const u32 *uninitialized_var(ddw_avail);
+       u32 ddw_avail[3];
        struct direct_window *window;
        struct property *win64;
        struct dynamic_dma_window_prop *ddwprop;
@@ -942,8 +945,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
         * for the given node in that order.
         * the property is actually in the parent, not the PE
         */
-       ddw_avail = of_get_property(pdn, "ibm,ddw-applicable", &len);
-       if (!ddw_avail || len < 3 * sizeof(u32))
+       ret = of_property_read_u32_array(pdn, "ibm,ddw-applicable",
+                                        &ddw_avail[0], 3);
+       if (ret)
                goto out_failed;
 
        /*
@@ -966,11 +970,11 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
                dev_dbg(&dev->dev, "no free dynamic windows");
                goto out_failed;
        }
-       if (be32_to_cpu(query.page_size) & 4) {
+       if (query.page_size & 4) {
                page_shift = 24; /* 16MB */
-       } else if (be32_to_cpu(query.page_size) & 2) {
+       } else if (query.page_size & 2) {
                page_shift = 16; /* 64kB */
-       } else if (be32_to_cpu(query.page_size) & 1) {
+       } else if (query.page_size & 1) {
                page_shift = 12; /* 4kB */
        } else {
                dev_dbg(&dev->dev, "no supported direct page size in mask %x",
@@ -980,7 +984,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
        /* verify the window * number of ptes will map the partition */
        /* check largest block * page size > max memory hotplug addr */
        max_addr = memory_hotplug_max();
-       if (be32_to_cpu(query.largest_available_block) < (max_addr >> page_shift)) {
+       if (query.largest_available_block < (max_addr >> page_shift)) {
                dev_dbg(&dev->dev, "can't map partiton max 0x%llx with %u "
                          "%llu-sized pages\n", max_addr,  query.largest_available_block,
                          1ULL << page_shift);
@@ -1006,8 +1010,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
        if (ret != 0)
                goto out_free_prop;
 
-       ddwprop->liobn = create.liobn;
-       ddwprop->dma_base = cpu_to_be64(of_read_number(&create.addr_hi, 2));
+       ddwprop->liobn = cpu_to_be32(create.liobn);
+       ddwprop->dma_base = cpu_to_be64(((u64)create.addr_hi << 32) |
+                       create.addr_lo);
        ddwprop->tce_shift = cpu_to_be32(page_shift);
        ddwprop->window_shift = cpu_to_be32(len);
 
@@ -1039,7 +1044,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
        list_add(&window->list, &direct_window_list);
        spin_unlock(&direct_window_list_lock);
 
-       dma_addr = of_read_number(&create.addr_hi, 2);
+       dma_addr = be64_to_cpu(ddwprop->dma_base);
        goto out_unlock;
 
 out_free_window:
index 34e64237fff9a9ca4cf6d62751e99feff41a3f1e..8c509d5397c6e9a6f54c19ec569612e0005f96e2 100644 (file)
@@ -59,8 +59,6 @@ EXPORT_SYMBOL(plpar_hcall);
 EXPORT_SYMBOL(plpar_hcall9);
 EXPORT_SYMBOL(plpar_hcall_norets);
 
-extern void pSeries_find_serial_port(void);
-
 void vpa_init(int cpu)
 {
        int hwcpu = get_hard_smp_processor_id(cpu);
@@ -642,7 +640,7 @@ EXPORT_SYMBOL(arch_free_page);
 #endif
 
 #ifdef CONFIG_TRACEPOINTS
-#ifdef CONFIG_JUMP_LABEL
+#ifdef HAVE_JUMP_LABEL
 struct static_key hcall_tracepoint_key = STATIC_KEY_INIT;
 
 void hcall_tracepoint_regfunc(void)
index 18ff4626d74e0cc24de7ec6c1bab902f0f8e215a..8ab5add4ac824f43c6a6b299b24ed15bf0deafb2 100644 (file)
@@ -336,26 +336,6 @@ out:
        return request;
 }
 
-static int rtas_msi_check_device(struct pci_dev *pdev, int nvec, int type)
-{
-       int quota, rc;
-
-       if (type == PCI_CAP_ID_MSIX)
-               rc = check_req_msix(pdev, nvec);
-       else
-               rc = check_req_msi(pdev, nvec);
-
-       if (rc)
-               return rc;
-
-       quota = msi_quota_for_device(pdev, nvec);
-
-       if (quota && quota < nvec)
-               return quota;
-
-       return 0;
-}
-
 static int check_msix_entries(struct pci_dev *pdev)
 {
        struct msi_desc *entry;
@@ -397,15 +377,24 @@ static void rtas_hack_32bit_msi_gen2(struct pci_dev *pdev)
 static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
 {
        struct pci_dn *pdn;
-       int hwirq, virq, i, rc;
+       int hwirq, virq, i, quota, rc;
        struct msi_desc *entry;
        struct msi_msg msg;
        int nvec = nvec_in;
        int use_32bit_msi_hack = 0;
 
-       pdn = pci_get_pdn(pdev);
-       if (!pdn)
-               return -ENODEV;
+       if (type == PCI_CAP_ID_MSIX)
+               rc = check_req_msix(pdev, nvec);
+       else
+               rc = check_req_msi(pdev, nvec);
+
+       if (rc)
+               return rc;
+
+       quota = msi_quota_for_device(pdev, nvec);
+
+       if (quota && quota < nvec)
+               return quota;
 
        if (type == PCI_CAP_ID_MSIX && check_msix_entries(pdev))
                return -EINVAL;
@@ -416,12 +405,14 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
         */
        if (type == PCI_CAP_ID_MSIX) {
                int m = roundup_pow_of_two(nvec);
-               int quota = msi_quota_for_device(pdev, m);
+               quota = msi_quota_for_device(pdev, m);
 
                if (quota >= m)
                        nvec = m;
        }
 
+       pdn = pci_get_pdn(pdev);
+
        /*
         * Try the new more explicit firmware interface, if that fails fall
         * back to the old interface. The old interface is known to never
@@ -485,7 +476,7 @@ again:
                irq_set_msi_desc(virq, entry);
 
                /* Read config space back so we can restore after reset */
-               read_msi_msg(virq, &msg);
+               __read_msi_msg(entry, &msg);
                entry->msg = msg;
        }
 
@@ -526,7 +517,6 @@ static int rtas_msi_init(void)
        WARN_ON(ppc_md.setup_msi_irqs);
        ppc_md.setup_msi_irqs = rtas_setup_msi_irqs;
        ppc_md.teardown_msi_irqs = rtas_teardown_msi_irqs;
-       ppc_md.msi_check_device = rtas_msi_check_device;
 
        WARN_ON(ppc_md.pci_irq_fixup);
        ppc_md.pci_irq_fixup = rtas_msi_pci_irq_fixup;
index 0cc240b7f694ef3b35c2930d5356a4e4cb39d863..11a3b617ef5dbd4b7d6cbed7d045d9bc3c4c7d95 100644 (file)
@@ -276,8 +276,10 @@ static ssize_t pSeries_nvram_get_size(void)
  * sequence #: The unique sequence # for each event. (until it wraps)
  * error log: The error log from event_scan
  */
-int nvram_write_os_partition(struct nvram_os_partition *part, char * buff,
-               int length, unsigned int err_type, unsigned int error_log_cnt)
+static int nvram_write_os_partition(struct nvram_os_partition *part,
+                                   char *buff, int length,
+                                   unsigned int err_type,
+                                   unsigned int error_log_cnt)
 {
        int rc;
        loff_t tmp_index;
@@ -330,9 +332,9 @@ int nvram_write_error_log(char * buff, int length,
  *
  * Reads nvram partition for at most 'length'
  */
-int nvram_read_partition(struct nvram_os_partition *part, char *buff,
-                       int length, unsigned int *err_type,
-                       unsigned int *error_log_cnt)
+static int nvram_read_partition(struct nvram_os_partition *part, char *buff,
+                               int length, unsigned int *err_type,
+                               unsigned int *error_log_cnt)
 {
        int rc;
        loff_t tmp_index;
index c413ec158ff5a6587e7c611659f817dc33d9c7e4..67e48594040cb6fcceec3989b89ebb420f742037 100644 (file)
@@ -29,6 +29,7 @@
 #include <asm/pci-bridge.h>
 #include <asm/prom.h>
 #include <asm/ppc-pci.h>
+#include "pseries.h"
 
 #if 0
 void pcibios_name_device(struct pci_dev *dev)
index dff05b9eb94682267fed3a79c9ad72998ec7d053..5a4d0fc03b03f5666f9fe80e3e67d364cb46db9a 100644 (file)
@@ -126,7 +126,7 @@ struct epow_errorlog {
 #define EPOW_MAIN_ENCLOSURE            5
 #define EPOW_POWER_OFF                 7
 
-void rtas_parse_epow_errlog(struct rtas_error_log *log)
+static void rtas_parse_epow_errlog(struct rtas_error_log *log)
 {
        struct pseries_errorlog *pseries_log;
        struct epow_errorlog *epow_log;
index e724d3186e739999cc6daf726d073fe035d26739..125c589eeef52b9b8b2ee10bc8748f66c866ac12 100644 (file)
@@ -561,7 +561,7 @@ void pSeries_coalesce_init(void)
  * fw_cmo_feature_init - FW_FEATURE_CMO is not stored in ibm,hypertas-functions,
  * handle that here. (Stolen from parse_system_parameter_string)
  */
-void pSeries_cmo_feature_init(void)
+static void pSeries_cmo_feature_init(void)
 {
        char *ptr, *key, *value, *end;
        int call_status;
index 47b6b9f81d4305537b7d0e6290e178efb4253f4c..ad56edc39919fb223e21da9a9b75e8a9ba032b4e 100644 (file)
@@ -314,7 +314,7 @@ axon_ram_remove(struct platform_device *device)
        return 0;
 }
 
-static struct of_device_id axon_ram_device_id[] = {
+static const struct of_device_id axon_ram_device_id[] = {
        {
                .type   = "dma-memory"
        },
index e9056e4385756cf93a8e1d4ef7b54a9a1affa982..2d8a101b6b9edba7bebe70a8b03ebb4e2f0ba7ab 100644 (file)
@@ -230,5 +230,6 @@ EXPORT_SYMBOL_GPL(dcr_unmap_mmio);
 
 #ifdef CONFIG_PPC_DCR_NATIVE
 DEFINE_SPINLOCK(dcr_ind_lock);
+EXPORT_SYMBOL_GPL(dcr_ind_lock);
 #endif /* defined(CONFIG_PPC_DCR_NATIVE) */
 
index afc2dbf37011e67805d8ed1db68c801dca4073f0..90545ad1626ea2e68d1a1dc22baabc5731c0d2c3 100644 (file)
@@ -171,7 +171,7 @@ static int mpc85xx_l2ctlr_of_remove(struct platform_device *dev)
        return 0;
 }
 
-static struct of_device_id mpc85xx_l2ctlr_of_match[] = {
+static const struct of_device_id mpc85xx_l2ctlr_of_match[] = {
        {
                .compatible = "fsl,p2020-l2-cache-controller",
        },
index 77efbaec7b9cb84c4cdd0bac06f49d22d297a66b..de40b48b460e83a8aaec5168c8a4a1f025d8dd38 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/seq_file.h>
 #include <sysdev/fsl_soc.h>
 #include <asm/prom.h>
 #include <asm/hw_irq.h>
@@ -50,6 +52,7 @@ struct fsl_msi_feature {
 struct fsl_msi_cascade_data {
        struct fsl_msi *msi_data;
        int index;
+       int virq;
 };
 
 static inline u32 fsl_msi_read(u32 __iomem *base, unsigned int reg)
@@ -65,11 +68,24 @@ static void fsl_msi_end_irq(struct irq_data *d)
 {
 }
 
+static void fsl_msi_print_chip(struct irq_data *irqd, struct seq_file *p)
+{
+       struct fsl_msi *msi_data = irqd->domain->host_data;
+       irq_hw_number_t hwirq = irqd_to_hwirq(irqd);
+       int cascade_virq, srs;
+
+       srs = (hwirq >> msi_data->srs_shift) & MSI_SRS_MASK;
+       cascade_virq = msi_data->cascade_array[srs]->virq;
+
+       seq_printf(p, " fsl-msi-%d", cascade_virq);
+}
+
+
 static struct irq_chip fsl_msi_chip = {
        .irq_mask       = mask_msi_irq,
        .irq_unmask     = unmask_msi_irq,
        .irq_ack        = fsl_msi_end_irq,
-       .name           = "FSL-MSI",
+       .irq_print_chip = fsl_msi_print_chip,
 };
 
 static int fsl_msi_host_map(struct irq_domain *h, unsigned int virq,
@@ -109,14 +125,6 @@ static int fsl_msi_init_allocator(struct fsl_msi *msi_data)
        return 0;
 }
 
-static int fsl_msi_check_device(struct pci_dev *pdev, int nvec, int type)
-{
-       if (type == PCI_CAP_ID_MSIX)
-               pr_debug("fslmsi: MSI-X untested, trying anyway.\n");
-
-       return 0;
-}
-
 static void fsl_teardown_msi_irqs(struct pci_dev *pdev)
 {
        struct msi_desc *entry;
@@ -173,6 +181,9 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
        struct msi_msg msg;
        struct fsl_msi *msi_data;
 
+       if (type == PCI_CAP_ID_MSIX)
+               pr_debug("fslmsi: MSI-X untested, trying anyway.\n");
+
        /*
         * If the PCI node has an fsl,msi property, then we need to use it
         * to find the specific MSI.
@@ -180,7 +191,8 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
        np = of_parse_phandle(hose->dn, "fsl,msi", 0);
        if (np) {
                if (of_device_is_compatible(np, "fsl,mpic-msi") ||
-                   of_device_is_compatible(np, "fsl,vmpic-msi"))
+                   of_device_is_compatible(np, "fsl,vmpic-msi") ||
+                   of_device_is_compatible(np, "fsl,vmpic-msi-v4.3"))
                        phandle = np->phandle;
                else {
                        dev_err(&pdev->dev,
@@ -239,40 +251,24 @@ out_free:
        return rc;
 }
 
-static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc)
+static irqreturn_t fsl_msi_cascade(int irq, void *data)
 {
-       struct irq_chip *chip = irq_desc_get_chip(desc);
-       struct irq_data *idata = irq_desc_get_irq_data(desc);
        unsigned int cascade_irq;
        struct fsl_msi *msi_data;
        int msir_index = -1;
        u32 msir_value = 0;
        u32 intr_index;
        u32 have_shift = 0;
-       struct fsl_msi_cascade_data *cascade_data;
+       struct fsl_msi_cascade_data *cascade_data = data;
+       irqreturn_t ret = IRQ_NONE;
 
-       cascade_data = irq_get_handler_data(irq);
        msi_data = cascade_data->msi_data;
 
-       raw_spin_lock(&desc->lock);
-       if ((msi_data->feature &  FSL_PIC_IP_MASK) == FSL_PIC_IP_IPIC) {
-               if (chip->irq_mask_ack)
-                       chip->irq_mask_ack(idata);
-               else {
-                       chip->irq_mask(idata);
-                       chip->irq_ack(idata);
-               }
-       }
-
-       if (unlikely(irqd_irq_inprogress(idata)))
-               goto unlock;
-
        msir_index = cascade_data->index;
 
        if (msir_index >= NR_MSI_REG_MAX)
                cascade_irq = NO_IRQ;
 
-       irqd_set_chained_irq_inprogress(idata);
        switch (msi_data->feature & FSL_PIC_IP_MASK) {
        case FSL_PIC_IP_MPIC:
                msir_value = fsl_msi_read(msi_data->msi_regs,
@@ -301,40 +297,32 @@ static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc)
                cascade_irq = irq_linear_revmap(msi_data->irqhost,
                                msi_hwirq(msi_data, msir_index,
                                          intr_index + have_shift));
-               if (cascade_irq != NO_IRQ)
+               if (cascade_irq != NO_IRQ) {
                        generic_handle_irq(cascade_irq);
+                       ret = IRQ_HANDLED;
+               }
                have_shift += intr_index + 1;
                msir_value = msir_value >> (intr_index + 1);
        }
-       irqd_clr_chained_irq_inprogress(idata);
 
-       switch (msi_data->feature & FSL_PIC_IP_MASK) {
-       case FSL_PIC_IP_MPIC:
-       case FSL_PIC_IP_VMPIC:
-               chip->irq_eoi(idata);
-               break;
-       case FSL_PIC_IP_IPIC:
-               if (!irqd_irq_disabled(idata) && chip->irq_unmask)
-                       chip->irq_unmask(idata);
-               break;
-       }
-unlock:
-       raw_spin_unlock(&desc->lock);
+       return ret;
 }
 
 static int fsl_of_msi_remove(struct platform_device *ofdev)
 {
        struct fsl_msi *msi = platform_get_drvdata(ofdev);
        int virq, i;
-       struct fsl_msi_cascade_data *cascade_data;
 
        if (msi->list.prev != NULL)
                list_del(&msi->list);
        for (i = 0; i < NR_MSI_REG_MAX; i++) {
-               virq = msi->msi_virqs[i];
-               if (virq != NO_IRQ) {
-                       cascade_data = irq_get_handler_data(virq);
-                       kfree(cascade_data);
+               if (msi->cascade_array[i]) {
+                       virq = msi->cascade_array[i]->virq;
+
+                       BUG_ON(virq == NO_IRQ);
+
+                       free_irq(virq, msi->cascade_array[i]);
+                       kfree(msi->cascade_array[i]);
                        irq_dispose_mapping(virq);
                }
        }
@@ -353,7 +341,7 @@ static int fsl_msi_setup_hwirq(struct fsl_msi *msi, struct platform_device *dev,
                               int offset, int irq_index)
 {
        struct fsl_msi_cascade_data *cascade_data = NULL;
-       int virt_msir, i;
+       int virt_msir, i, ret;
 
        virt_msir = irq_of_parse_and_map(dev->dev.of_node, irq_index);
        if (virt_msir == NO_IRQ) {
@@ -368,11 +356,18 @@ static int fsl_msi_setup_hwirq(struct fsl_msi *msi, struct platform_device *dev,
                return -ENOMEM;
        }
        irq_set_lockdep_class(virt_msir, &fsl_msi_irq_class);
-       msi->msi_virqs[irq_index] = virt_msir;
        cascade_data->index = offset;
        cascade_data->msi_data = msi;
-       irq_set_handler_data(virt_msir, cascade_data);
-       irq_set_chained_handler(virt_msir, fsl_msi_cascade);
+       cascade_data->virq = virt_msir;
+       msi->cascade_array[irq_index] = cascade_data;
+
+       ret = request_irq(virt_msir, fsl_msi_cascade, 0,
+                         "fsl-msi-cascade", cascade_data);
+       if (ret) {
+               dev_err(&dev->dev, "failed to request_irq(%d), ret = %d\n",
+                       virt_msir, ret);
+               return ret;
+       }
 
        /* Release the hwirqs corresponding to this MSI register */
        for (i = 0; i < IRQS_PER_MSI_REG; i++)
@@ -466,7 +461,8 @@ static int fsl_of_msi_probe(struct platform_device *dev)
 
        p = of_get_property(dev->dev.of_node, "msi-available-ranges", &len);
 
-       if (of_device_is_compatible(dev->dev.of_node, "fsl,mpic-msi-v4.3")) {
+       if (of_device_is_compatible(dev->dev.of_node, "fsl,mpic-msi-v4.3") ||
+           of_device_is_compatible(dev->dev.of_node, "fsl,vmpic-msi-v4.3")) {
                msi->srs_shift = MSIIR1_SRS_SHIFT;
                msi->ibs_shift = MSIIR1_IBS_SHIFT;
                if (p)
@@ -527,7 +523,6 @@ static int fsl_of_msi_probe(struct platform_device *dev)
        if (!ppc_md.setup_msi_irqs) {
                ppc_md.setup_msi_irqs = fsl_setup_msi_irqs;
                ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs;
-               ppc_md.msi_check_device = fsl_msi_check_device;
        } else if (ppc_md.setup_msi_irqs != fsl_setup_msi_irqs) {
                dev_err(&dev->dev, "Different MSI driver already installed!\n");
                err = -ENODEV;
@@ -572,6 +567,10 @@ static const struct of_device_id fsl_of_msi_ids[] = {
                .compatible = "fsl,vmpic-msi",
                .data = &vmpic_msi_feature,
        },
+       {
+               .compatible = "fsl,vmpic-msi-v4.3",
+               .data = &vmpic_msi_feature,
+       },
 #endif
        {}
 };
index df9aa9fe0933b1f1f08ca8dcb270218b955e5b5b..420cfcbdac01c5c30a72fa64613a43373e17c8c5 100644 (file)
@@ -27,6 +27,8 @@
 #define FSL_PIC_IP_IPIC   0x00000002
 #define FSL_PIC_IP_VMPIC  0x00000003
 
+struct fsl_msi_cascade_data;
+
 struct fsl_msi {
        struct irq_domain *irqhost;
 
@@ -37,7 +39,7 @@ struct fsl_msi {
        u32 srs_shift; /* Shift of the shared interrupt register select */
        void __iomem *msi_regs;
        u32 feature;
-       int msi_virqs[NR_MSI_REG_MAX];
+       struct fsl_msi_cascade_data *cascade_array[NR_MSI_REG_MAX];
 
        struct msi_bitmap bitmap;
 
index c5077673bd9433633be839cc58c02bfc6137d29c..65d2ed4549e667ec981f272d88ff12fc234e9fd9 100644 (file)
@@ -522,7 +522,8 @@ int fsl_add_bridge(struct platform_device *pdev, int is_primary)
        } else {
                /* For PCI read PROG to identify controller mode */
                early_read_config_byte(hose, 0, 0, PCI_CLASS_PROG, &progif);
-               if ((progif & 1) == 1)
+               if ((progif & 1) &&
+                   !of_property_read_bool(dev, "fsl,pci-agent-force-enum"))
                        goto no_bridge;
        }
 
index be33c9768ea11b376669083eeb4eec605ae13e16..89cec0ed6a58acc9104ef2727d7313f5e9031451 100644 (file)
@@ -960,7 +960,7 @@ void mpic_set_vector(unsigned int virq, unsigned int vector)
        mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vecpri);
 }
 
-void mpic_set_destination(unsigned int virq, unsigned int cpuid)
+static void mpic_set_destination(unsigned int virq, unsigned int cpuid)
 {
        struct mpic *mpic = mpic_from_irq(virq);
        unsigned int src = virq_to_hw(virq);
index 38e62382070c9301135b0ff95b7181eeaab44bfd..15dccd35fa11a9d1fae5f1ca3fad180983693165 100644 (file)
@@ -63,14 +63,6 @@ static struct irq_chip mpic_pasemi_msi_chip = {
        .name                   = "PASEMI-MSI",
 };
 
-static int pasemi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
-{
-       if (type == PCI_CAP_ID_MSIX)
-               pr_debug("pasemi_msi: MSI-X untested, trying anyway\n");
-
-       return 0;
-}
-
 static void pasemi_msi_teardown_msi_irqs(struct pci_dev *pdev)
 {
        struct msi_desc *entry;
@@ -97,6 +89,8 @@ static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
        struct msi_msg msg;
        int hwirq;
 
+       if (type == PCI_CAP_ID_MSIX)
+               pr_debug("pasemi_msi: MSI-X untested, trying anyway\n");
        pr_debug("pasemi_msi_setup_msi_irqs, pdev %p nvec %d type %d\n",
                 pdev, nvec, type);
 
@@ -169,7 +163,6 @@ int mpic_pasemi_msi_init(struct mpic *mpic)
        WARN_ON(ppc_md.setup_msi_irqs);
        ppc_md.setup_msi_irqs = pasemi_msi_setup_msi_irqs;
        ppc_md.teardown_msi_irqs = pasemi_msi_teardown_msi_irqs;
-       ppc_md.msi_check_device = pasemi_msi_check_device;
 
        return 0;
 }
index 9a7aa0ed9c1c2bfcadf7edcc496ac8e9eb85a255..623d7fba15b4d3c22d9bba0299bc516a9d85a6be 100644 (file)
@@ -105,22 +105,6 @@ static u64 find_u4_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
        return 0;
 }
 
-static int u3msi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
-{
-       if (type == PCI_CAP_ID_MSIX)
-               pr_debug("u3msi: MSI-X untested, trying anyway.\n");
-
-       /* If we can't find a magic address then MSI ain't gonna work */
-       if (find_ht_magic_addr(pdev, 0) == 0 &&
-           find_u4_magic_addr(pdev, 0) == 0) {
-               pr_debug("u3msi: no magic address found for %s\n",
-                        pci_name(pdev));
-               return -ENXIO;
-       }
-
-       return 0;
-}
-
 static void u3msi_teardown_msi_irqs(struct pci_dev *pdev)
 {
        struct msi_desc *entry;
@@ -146,6 +130,17 @@ static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
        u64 addr;
        int hwirq;
 
+       if (type == PCI_CAP_ID_MSIX)
+               pr_debug("u3msi: MSI-X untested, trying anyway.\n");
+
+       /* If we can't find a magic address then MSI ain't gonna work */
+       if (find_ht_magic_addr(pdev, 0) == 0 &&
+           find_u4_magic_addr(pdev, 0) == 0) {
+               pr_debug("u3msi: no magic address found for %s\n",
+                        pci_name(pdev));
+               return -ENXIO;
+       }
+
        list_for_each_entry(entry, &pdev->msi_list, list) {
                hwirq = msi_bitmap_alloc_hwirqs(&msi_mpic->msi_bitmap, 1);
                if (hwirq < 0) {
@@ -202,7 +197,6 @@ int mpic_u3msi_init(struct mpic *mpic)
        WARN_ON(ppc_md.setup_msi_irqs);
        ppc_md.setup_msi_irqs = u3msi_setup_msi_irqs;
        ppc_md.teardown_msi_irqs = u3msi_teardown_msi_irqs;
-       ppc_md.msi_check_device = u3msi_msi_check_device;
 
        return 0;
 }
index 2ff630267e9efaef1eac6cf15634751e6de51b9e..0c75214b6f9220d433f8ea88776041694f744add 100644 (file)
@@ -20,32 +20,37 @@ int msi_bitmap_alloc_hwirqs(struct msi_bitmap *bmp, int num)
        int offset, order = get_count_order(num);
 
        spin_lock_irqsave(&bmp->lock, flags);
-       /*
-        * This is fast, but stricter than we need. We might want to add
-        * a fallback routine which does a linear search with no alignment.
-        */
-       offset = bitmap_find_free_region(bmp->bitmap, bmp->irq_count, order);
+
+       offset = bitmap_find_next_zero_area(bmp->bitmap, bmp->irq_count, 0,
+                                           num, (1 << order) - 1);
+       if (offset > bmp->irq_count)
+               goto err;
+
+       bitmap_set(bmp->bitmap, offset, num);
        spin_unlock_irqrestore(&bmp->lock, flags);
 
-       pr_debug("msi_bitmap: allocated 0x%x (2^%d) at offset 0x%x\n",
-                num, order, offset);
+       pr_debug("msi_bitmap: allocated 0x%x at offset 0x%x\n", num, offset);
 
        return offset;
+err:
+       spin_unlock_irqrestore(&bmp->lock, flags);
+       return -ENOMEM;
 }
+EXPORT_SYMBOL(msi_bitmap_alloc_hwirqs);
 
 void msi_bitmap_free_hwirqs(struct msi_bitmap *bmp, unsigned int offset,
                            unsigned int num)
 {
        unsigned long flags;
-       int order = get_count_order(num);
 
-       pr_debug("msi_bitmap: freeing 0x%x (2^%d) at offset 0x%x\n",
-                num, order, offset);
+       pr_debug("msi_bitmap: freeing 0x%x at offset 0x%x\n",
+                num, offset);
 
        spin_lock_irqsave(&bmp->lock, flags);
-       bitmap_release_region(bmp->bitmap, offset, order);
+       bitmap_clear(bmp->bitmap, offset, num);
        spin_unlock_irqrestore(&bmp->lock, flags);
 }
+EXPORT_SYMBOL(msi_bitmap_free_hwirqs);
 
 void msi_bitmap_reserve_hwirq(struct msi_bitmap *bmp, unsigned int hwirq)
 {
@@ -143,7 +148,7 @@ void msi_bitmap_free(struct msi_bitmap *bmp)
 #define check(x)       \
        if (!(x)) printk("msi_bitmap: test failed at line %d\n", __LINE__);
 
-void __init test_basics(void)
+static void __init test_basics(void)
 {
        struct msi_bitmap bmp;
        int i, size = 512;
@@ -180,6 +185,15 @@ void __init test_basics(void)
        msi_bitmap_free_hwirqs(&bmp, size / 2, 1);
        check(msi_bitmap_alloc_hwirqs(&bmp, 1) == size / 2);
 
+       /* Check we get a naturally aligned offset */
+       check(msi_bitmap_alloc_hwirqs(&bmp, 2) % 2 == 0);
+       check(msi_bitmap_alloc_hwirqs(&bmp, 4) % 4 == 0);
+       check(msi_bitmap_alloc_hwirqs(&bmp, 8) % 8 == 0);
+       check(msi_bitmap_alloc_hwirqs(&bmp, 9) % 16 == 0);
+       check(msi_bitmap_alloc_hwirqs(&bmp, 3) % 4 == 0);
+       check(msi_bitmap_alloc_hwirqs(&bmp, 7) % 8 == 0);
+       check(msi_bitmap_alloc_hwirqs(&bmp, 121) % 128 == 0);
+
        msi_bitmap_free(&bmp);
 
        /* Clients may check bitmap == NULL for "not-allocated" */
@@ -188,7 +202,7 @@ void __init test_basics(void)
        kfree(bmp.bitmap);
 }
 
-void __init test_of_node(void)
+static void __init test_of_node(void)
 {
        u32 prop_data[] = { 10, 10, 25, 3, 40, 1, 100, 100, 200, 20 };
        const char *expected_str = "0-9,20-24,28-39,41-99,220-255";
@@ -236,7 +250,7 @@ void __init test_of_node(void)
        kfree(bmp.bitmap);
 }
 
-int __init msi_bitmap_selftest(void)
+static int __init msi_bitmap_selftest(void)
 {
        printk(KERN_DEBUG "Running MSI bitmap self-tests ...\n");
 
index c2dba7db71ad533bf15b20739646f1369013033f..026bbc3b2c47d75f967d777bc797f446a615438c 100644 (file)
@@ -23,7 +23,7 @@
 
 /* These functions provide the necessary setup for the mv64x60 drivers. */
 
-static struct of_device_id __initdata of_mv64x60_devices[] = {
+static const struct of_device_id of_mv64x60_devices[] __initconst = {
        { .compatible = "marvell,mv64306-devctrl", },
        {}
 };
index 5aaf86c03893446474b538760d6d3fa169ec4e2b..13e67d93a7c111f785df07050c5e991104bb0407 100644 (file)
@@ -101,7 +101,7 @@ out:
 }
 
 
-static struct of_device_id pmi_match[] = {
+static const struct of_device_id pmi_match[] = {
        { .type = "ibm,pmi", .name = "ibm,pmi" },
        { .type = "ibm,pmi" },
        {},
index 11c888416f0a881665f4e8b6c230d93454bafbc2..a6a4dbda9078f59f93ea222493a8aeced3a5e22c 100644 (file)
@@ -44,6 +44,12 @@ static int hsta_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
        int irq, hwirq;
        u64 addr;
 
+       /* We don't support MSI-X */
+       if (type == PCI_CAP_ID_MSIX) {
+               pr_debug("%s: MSI-X not supported.\n", __func__);
+               return -EINVAL;
+       }
+
        list_for_each_entry(entry, &dev->msi_list, list) {
                irq = msi_bitmap_alloc_hwirqs(&ppc4xx_hsta_msi.bmp, 1);
                if (irq < 0) {
@@ -117,17 +123,6 @@ static void hsta_teardown_msi_irqs(struct pci_dev *dev)
        }
 }
 
-static int hsta_msi_check_device(struct pci_dev *pdev, int nvec, int type)
-{
-       /* We don't support MSI-X */
-       if (type == PCI_CAP_ID_MSIX) {
-               pr_debug("%s: MSI-X not supported.\n", __func__);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 static int hsta_msi_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -178,7 +173,6 @@ static int hsta_msi_probe(struct platform_device *pdev)
 
        ppc_md.setup_msi_irqs = hsta_setup_msi_irqs;
        ppc_md.teardown_msi_irqs = hsta_teardown_msi_irqs;
-       ppc_md.msi_check_device = hsta_msi_check_device;
        return 0;
 
 out2:
index 43948da837a799628aed3c33d9b89871ea4dc3b0..22b5200636e7f90299702d64d6b63ea1a5fe228f 100644 (file)
@@ -85,8 +85,12 @@ static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
        struct msi_desc *entry;
        struct ppc4xx_msi *msi_data = &ppc4xx_msi;
 
-       msi_data->msi_virqs = kmalloc((msi_irqs) * sizeof(int),
-                                           GFP_KERNEL);
+       dev_dbg(&dev->dev, "PCIE-MSI:%s called. vec %x type %d\n",
+               __func__, nvec, type);
+       if (type == PCI_CAP_ID_MSIX)
+               pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n");
+
+       msi_data->msi_virqs = kmalloc((msi_irqs) * sizeof(int), GFP_KERNEL);
        if (!msi_data->msi_virqs)
                return -ENOMEM;
 
@@ -134,16 +138,6 @@ void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
        }
 }
 
-static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type)
-{
-       dev_dbg(&pdev->dev, "PCIE-MSI:%s called. vec %x type %d\n",
-               __func__, nvec, type);
-       if (type == PCI_CAP_ID_MSIX)
-               pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n");
-
-       return 0;
-}
-
 static int ppc4xx_setup_pcieh_hw(struct platform_device *dev,
                                 struct resource res, struct ppc4xx_msi *msi)
 {
@@ -259,7 +253,6 @@ static int ppc4xx_msi_probe(struct platform_device *dev)
 
        ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
        ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
-       ppc_md.msi_check_device = ppc4xx_msi_check_device;
        return err;
 
 error_out:
index de8d9483bbe89136042d0685680c6bddc815849a..2fc4cf1b75575f9916e26fcf330cfce4959f39a5 100644 (file)
@@ -155,6 +155,31 @@ static void icp_native_cause_ipi(int cpu, unsigned long data)
                icp_native_set_qirr(cpu, IPI_PRIORITY);
 }
 
+/*
+ * Called when an interrupt is received on an off-line CPU to
+ * clear the interrupt, so that the CPU can go back to nap mode.
+ */
+void icp_native_flush_interrupt(void)
+{
+       unsigned int xirr = icp_native_get_xirr();
+       unsigned int vec = xirr & 0x00ffffff;
+
+       if (vec == XICS_IRQ_SPURIOUS)
+               return;
+       if (vec == XICS_IPI) {
+               /* Clear pending IPI */
+               int cpu = smp_processor_id();
+               kvmppc_set_host_ipi(cpu, 0);
+               icp_native_set_qirr(cpu, 0xff);
+       } else {
+               pr_err("XICS: hw interrupt 0x%x to offline cpu, disabling\n",
+                      vec);
+               xics_mask_unknown_vec(vec);
+       }
+       /* EOI the interrupt */
+       icp_native_set_xirr(xirr);
+}
+
 void xics_wake_cpu(int cpu)
 {
        icp_native_set_qirr(cpu, IPI_PRIORITY);
index 83f943a8e0db90ed0cf7a700c70f201861db8062..56f0524e47a6f84f723d26a3bf90a2c688b4d051 100644 (file)
@@ -265,7 +265,7 @@ static void __init xilinx_i8259_setup_cascade(void)
 static inline void xilinx_i8259_setup_cascade(void) { return; }
 #endif /* defined(CONFIG_PPC_I8259) */
 
-static struct of_device_id xilinx_intc_match[] __initconst = {
+static const struct of_device_id xilinx_intc_match[] __initconst = {
        { .compatible = "xlnx,opb-intc-1.00.c", },
        { .compatible = "xlnx,xps-intc-1.00.a", },
        {}
index 1453b0eed2202acf7facca6628db2f09b00ed9bf..fea5667699ed99f33795573254e994bebcb729de 100644 (file)
@@ -27,7 +27,7 @@
 
 #define PCI_HOST_ENABLE_CMD PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY
 
-static struct of_device_id xilinx_pci_match[] = {
+static const struct of_device_id xilinx_pci_match[] = {
        { .compatible = "xlnx,plbv46-pci-1.03.a", },
        {}
 };
index 05c78bb5f57024bf220e778af99888a1df6e6bbc..296391395b955bb3e4ba25dc33458a55982d5546 100644 (file)
@@ -496,8 +496,8 @@ config QDIO
 
 menuconfig PCI
        bool "PCI support"
-       default n
        depends on 64BIT
+       select HAVE_DMA_ATTRS
        select PCI_MSI
        help
          Enable PCI support.
@@ -544,9 +544,6 @@ config HAS_DMA
 config NEED_SG_DMA_LENGTH
        def_bool PCI
 
-config HAVE_DMA_ATTRS
-       def_bool PCI
-
 config NEED_DMA_MAP_STATE
        def_bool PCI
 
index b3fea0722ff1f80e71809f45f61ad48b66805ea1..773f866765882fc3de528298c3e97ff9095f5f72 100644 (file)
@@ -2,6 +2,7 @@
 
 generic-y += clkdev.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += preempt.h
 generic-y += scatterlist.h
index 3fbc67d9e19739edbf5be625a68c5f7e34153771..709955ddaa4deeb822715e24e3e24a149868ff10 100644 (file)
@@ -56,24 +56,35 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
        return dma_addr == DMA_ERROR_CODE;
 }
 
-static inline void *dma_alloc_coherent(struct device *dev, size_t size,
-                                      dma_addr_t *dma_handle, gfp_t flag)
+#define dma_alloc_coherent(d, s, h, f) dma_alloc_attrs(d, s, h, f, NULL)
+
+static inline void *dma_alloc_attrs(struct device *dev, size_t size,
+                                   dma_addr_t *dma_handle, gfp_t flags,
+                                   struct dma_attrs *attrs)
 {
        struct dma_map_ops *ops = get_dma_ops(dev);
-       void *ret;
+       void *cpu_addr;
+
+       BUG_ON(!ops);
 
-       ret = ops->alloc(dev, size, dma_handle, flag, NULL);
-       debug_dma_alloc_coherent(dev, size, *dma_handle, ret);
-       return ret;
+       cpu_addr = ops->alloc(dev, size, dma_handle, flags, attrs);
+       debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr);
+
+       return cpu_addr;
 }
 
-static inline void dma_free_coherent(struct device *dev, size_t size,
-                                    void *cpu_addr, dma_addr_t dma_handle)
+#define dma_free_coherent(d, s, c, h) dma_free_attrs(d, s, c, h, NULL)
+
+static inline void dma_free_attrs(struct device *dev, size_t size,
+                                 void *cpu_addr, dma_addr_t dma_handle,
+                                 struct dma_attrs *attrs)
 {
-       struct dma_map_ops *dma_ops = get_dma_ops(dev);
+       struct dma_map_ops *ops = get_dma_ops(dev);
+
+       BUG_ON(!ops);
 
        debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
-       dma_ops->free(dev, size, cpu_addr, dma_handle, NULL);
+       ops->free(dev, size, cpu_addr, dma_handle, attrs);
 }
 
 #endif /* _ASM_S390_DMA_MAPPING_H */
index a7a7537ce1e7e415460199098a609b8732de1175..1c4c5accd220b17e235599811948ff0a53fbd871 100644 (file)
 #include <asm/ipl.h>
 #include <asm/cio.h>
 #include <asm/pci.h>
+#include <asm/sections.h>
 #include "entry.h"
 
-/*
- * References to section boundaries
- */
-extern const void __nosave_begin, __nosave_end;
-
 /*
  * The restore of the saved pages in an hibernation image will set
  * the change and referenced bits in the storage key for each page.
index d26c48fc93c958827d6f7429d28b9752b8365358..46461c19f284fbeef42e2f4bdbd3b1381613cac8 100644 (file)
@@ -6,9 +6,11 @@ generic-y += barrier.h
 generic-y += clkdev.h
 generic-y += cputime.h
 generic-y += hash.h
+generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += preempt.h
 generic-y += scatterlist.h
+generic-y += sections.h
 generic-y += trace_clock.h
 generic-y += xor.h
 generic-y += serial.h
diff --git a/arch/score/include/asm/sections.h b/arch/score/include/asm/sections.h
deleted file mode 100644 (file)
index 9441d23..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_SCORE_SECTIONS_H
-#define _ASM_SCORE_SECTIONS_H
-
-#include <asm-generic/sections.h>
-
-#endif /* _ASM_SCORE_SECTIONS_H */
index 3ea65e9b56e80ed34e5aad1055ce31df6666acb3..f035a7ac6456213f94d0ddae409f8746d4b9b9f5 100644 (file)
@@ -128,10 +128,8 @@ int __init x3proto_gpio_setup(void)
        return 0;
 
 err_irq:
-       ret = gpiochip_remove(&x3proto_gpio_chip);
-       if (unlikely(ret))
-               pr_err("Failed deregistering GPIO\n");
-
+       gpiochip_remove(&x3proto_gpio_chip);
+       ret = 0;
 err_gpio:
        synchronize_irq(ilsel);
 
index c19e47dacb31bceec56abeac4c96ca32ad3a2977..5a6c9acff0d21eea4fcb802624eecbe854da9400 100644 (file)
@@ -12,6 +12,7 @@ generic-y += hash.h
 generic-y += ioctl.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kvm_para.h
 generic-y += local.h
 generic-y += local64.h
index 1b6199740e98ab7e554f1676d615983f451e56a7..7a99e6af637284d3061a96a274f375f2189f2eb2 100644 (file)
@@ -3,7 +3,6 @@
 
 #include <asm-generic/sections.h>
 
-extern long __nosave_begin, __nosave_end;
 extern long __machvec_start, __machvec_end;
 extern char __uncached_start, __uncached_end;
 extern char __start_eh_frame[], __stop_eh_frame[];
index a537816613f99d952bab734ca0e97134cda468ac..96ac69c5eba016a7b5c1ef93fd35baccf8e2f52b 100644 (file)
@@ -67,6 +67,7 @@ config SPARC64
        select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_CONTEXT_TRACKING
        select HAVE_DEBUG_KMEMLEAK
+       select SPARSE_IRQ
        select RTC_DRV_CMOS
        select RTC_DRV_BQ4802
        select RTC_DRV_SUN4V
index cdd1b447bb6cac1f10cc2a5239e1a477f7e6bb06..f5f94ce1692c42ddab2088c1c0a8d480a6294bfa 100644 (file)
@@ -8,6 +8,7 @@ generic-y += emergency-restart.h
 generic-y += exec.h
 generic-y += hash.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += linkage.h
 generic-y += local.h
 generic-y += local64.h
index 1ee02710b2dc61b47c8a9494995bcf1e21287cce..5b1b52a04ad6283fb67308d9bf84b08494870140 100644 (file)
@@ -20,10 +20,12 @@ extern struct bus_type pci_bus_type;
 
 static inline struct dma_map_ops *get_dma_ops(struct device *dev)
 {
-#if defined(CONFIG_SPARC32) && defined(CONFIG_PCI)
+#ifdef CONFIG_SPARC_LEON
        if (sparc_cpu_model == sparc_leon)
                return leon_dma_ops;
-       else if (dev->bus == &pci_bus_type)
+#endif
+#if defined(CONFIG_SPARC32) && defined(CONFIG_PCI)
+       if (dev->bus == &pci_bus_type)
                return &pci32_dma_ops;
 #endif
        return dma_ops;
index 94b39caea3ebabf262c2322cb9a3d8756400aa56..4f6725ff4c336878fd3600f840f81897fdc65099 100644 (file)
@@ -2947,6 +2947,16 @@ unsigned long sun4v_vt_set_perfreg(unsigned long reg_num,
                                   unsigned long reg_val);
 #endif
 
+#define        HV_FAST_T5_GET_PERFREG          0x1a8
+#define        HV_FAST_T5_SET_PERFREG          0x1a9
+
+#ifndef        __ASSEMBLY__
+unsigned long sun4v_t5_get_perfreg(unsigned long reg_num,
+                                  unsigned long *reg_val);
+unsigned long sun4v_t5_set_perfreg(unsigned long reg_num,
+                                  unsigned long reg_val);
+#endif
+
 /* Function numbers for HV_CORE_TRAP.  */
 #define HV_CORE_SET_VER                        0x00
 #define HV_CORE_PUTCHAR                        0x01
@@ -2978,6 +2988,7 @@ unsigned long sun4v_vt_set_perfreg(unsigned long reg_num,
 #define HV_GRP_VF_CPU                  0x0205
 #define HV_GRP_KT_CPU                  0x0209
 #define HV_GRP_VT_CPU                  0x020c
+#define HV_GRP_T5_CPU                  0x0211
 #define HV_GRP_DIAG                    0x0300
 
 #ifndef __ASSEMBLY__
index 91d2193813069eb8d7e4572dc48269a8d9d50812..3f70f900e834203f0974169ebe95380442486d1f 100644 (file)
@@ -37,7 +37,7 @@
  *
  * ino_bucket->irq allocation is made during {sun4v_,}build_irq().
  */
-#define NR_IRQS    255
+#define NR_IRQS                (2048)
 
 void irq_install_pre_handler(int irq,
                             void (*func)(unsigned int, void *, void *),
@@ -57,11 +57,8 @@ unsigned int sun4u_build_msi(u32 portid, unsigned int *irq_p,
                             unsigned long iclr_base);
 void sun4u_destroy_msi(unsigned int irq);
 
-unsigned char irq_alloc(unsigned int dev_handle,
-                       unsigned int dev_ino);
-#ifdef CONFIG_PCI_MSI
+unsigned int irq_alloc(unsigned int dev_handle, unsigned int dev_ino);
 void irq_free(unsigned int irq);
-#endif
 
 void __init init_IRQ(void);
 void fixup_irqs(void);
index c8c67f621f4f445f9842c4a392843475c909a7c3..58ab64de25d2ce7133c8af67882d37a478f51d78 100644 (file)
@@ -53,13 +53,14 @@ struct ldc_channel;
 /* Allocate state for a channel.  */
 struct ldc_channel *ldc_alloc(unsigned long id,
                              const struct ldc_channel_config *cfgp,
-                             void *event_arg);
+                             void *event_arg,
+                             const char *name);
 
 /* Shut down and free state for a channel.  */
 void ldc_free(struct ldc_channel *lp);
 
 /* Register TX and RX queues of the link with the hypervisor.  */
-int ldc_bind(struct ldc_channel *lp, const char *name);
+int ldc_bind(struct ldc_channel *lp);
 
 /* For non-RAW protocols we need to complete a handshake before
  * communication can proceed.  ldc_connect() does that, if the
index bf109984a03238be02980c92bdc70c55f523fb8f..8c2a8c937540ffebb206576d06334bb8ba1a2438 100644 (file)
@@ -57,18 +57,21 @@ void copy_user_page(void *to, void *from, unsigned long vaddr, struct page *topa
 typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long iopte; } iopte_t;
 typedef struct { unsigned long pmd; } pmd_t;
+typedef struct { unsigned long pud; } pud_t;
 typedef struct { unsigned long pgd; } pgd_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
 
 #define pte_val(x)     ((x).pte)
 #define iopte_val(x)   ((x).iopte)
 #define pmd_val(x)      ((x).pmd)
+#define pud_val(x)      ((x).pud)
 #define pgd_val(x)     ((x).pgd)
 #define pgprot_val(x)  ((x).pgprot)
 
 #define __pte(x)       ((pte_t) { (x) } )
 #define __iopte(x)     ((iopte_t) { (x) } )
 #define __pmd(x)        ((pmd_t) { (x) } )
+#define __pud(x)        ((pud_t) { (x) } )
 #define __pgd(x)       ((pgd_t) { (x) } )
 #define __pgprot(x)    ((pgprot_t) { (x) } )
 
@@ -77,18 +80,21 @@ typedef struct { unsigned long pgprot; } pgprot_t;
 typedef unsigned long pte_t;
 typedef unsigned long iopte_t;
 typedef unsigned long pmd_t;
+typedef unsigned long pud_t;
 typedef unsigned long pgd_t;
 typedef unsigned long pgprot_t;
 
 #define pte_val(x)     (x)
 #define iopte_val(x)   (x)
 #define pmd_val(x)      (x)
+#define pud_val(x)      (x)
 #define pgd_val(x)     (x)
 #define pgprot_val(x)  (x)
 
 #define __pte(x)       (x)
 #define __iopte(x)     (x)
 #define __pmd(x)        (x)
+#define __pud(x)        (x)
 #define __pgd(x)       (x)
 #define __pgprot(x)    (x)
 
@@ -96,21 +102,14 @@ typedef unsigned long pgprot_t;
 
 typedef pte_t *pgtable_t;
 
-/* These two values define the virtual address space range in which we
- * must forbid 64-bit user processes from making mappings.  It used to
- * represent precisely the virtual address space hole present in most
- * early sparc64 chips including UltraSPARC-I.  But now it also is
- * further constrained by the limits of our page tables, which is
- * 43-bits of virtual address.
- */
-#define SPARC64_VA_HOLE_TOP    _AC(0xfffffc0000000000,UL)
-#define SPARC64_VA_HOLE_BOTTOM _AC(0x0000040000000000,UL)
+extern unsigned long sparc64_va_hole_top;
+extern unsigned long sparc64_va_hole_bottom;
 
 /* The next two defines specify the actual exclusion region we
  * enforce, wherein we use a 4GB red zone on each side of the VA hole.
  */
-#define VA_EXCLUDE_START (SPARC64_VA_HOLE_BOTTOM - (1UL << 32UL))
-#define VA_EXCLUDE_END   (SPARC64_VA_HOLE_TOP + (1UL << 32UL))
+#define VA_EXCLUDE_START (sparc64_va_hole_bottom - (1UL << 32UL))
+#define VA_EXCLUDE_END   (sparc64_va_hole_top + (1UL << 32UL))
 
 #define TASK_UNMAPPED_BASE     (test_thread_flag(TIF_32BIT) ? \
                                 _AC(0x0000000070000000,UL) : \
@@ -118,20 +117,16 @@ typedef pte_t *pgtable_t;
 
 #include <asm-generic/memory_model.h>
 
-#define PAGE_OFFSET_BY_BITS(X) (-(_AC(1,UL) << (X)))
 extern unsigned long PAGE_OFFSET;
 
 #endif /* !(__ASSEMBLY__) */
 
-/* The maximum number of physical memory address bits we support, this
- * is used to size various tables used to manage kernel TLB misses and
- * also the sparsemem code.
+/* The maximum number of physical memory address bits we support.  The
+ * largest value we can support is whatever "KPGD_SHIFT + KPTE_BITS"
+ * evaluates to.
  */
-#define MAX_PHYS_ADDRESS_BITS  47
+#define MAX_PHYS_ADDRESS_BITS  53
 
-/* These two shift counts are used when indexing sparc64_valid_addr_bitmap
- * and kpte_linear_bitmap.
- */
 #define ILOG2_4MB              22
 #define ILOG2_256MB            28
 
index 39a7ac49b00c71353ef9e9f0a1a555187ab0457b..5e3187185b4a8b9c3f22d4a67ea538a12fdcd9e7 100644 (file)
 
 extern struct kmem_cache *pgtable_cache;
 
+static inline void __pgd_populate(pgd_t *pgd, pud_t *pud)
+{
+       pgd_set(pgd, pud);
+}
+
+#define pgd_populate(MM, PGD, PUD)     __pgd_populate(PGD, PUD)
+
 static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 {
        return kmem_cache_alloc(pgtable_cache, GFP_KERNEL);
@@ -25,7 +32,23 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
        kmem_cache_free(pgtable_cache, pgd);
 }
 
-#define pud_populate(MM, PUD, PMD)     pud_set(PUD, PMD)
+static inline void __pud_populate(pud_t *pud, pmd_t *pmd)
+{
+       pud_set(pud, pmd);
+}
+
+#define pud_populate(MM, PUD, PMD)     __pud_populate(PUD, PMD)
+
+static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr)
+{
+       return kmem_cache_alloc(pgtable_cache,
+                               GFP_KERNEL|__GFP_REPEAT);
+}
+
+static inline void pud_free(struct mm_struct *mm, pud_t *pud)
+{
+       kmem_cache_free(pgtable_cache, pud);
+}
 
 static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
 {
@@ -91,4 +114,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pte_t *pte,
 #define __pmd_free_tlb(tlb, pmd, addr)               \
        pgtable_free_tlb(tlb, pmd, false)
 
+#define __pud_free_tlb(tlb, pud, addr)               \
+       pgtable_free_tlb(tlb, pud, false)
+
 #endif /* _SPARC64_PGALLOC_H */
index 3770bf5c6e1b434feedd15150a23aaac0b15e51e..bfeb626085ac3964fb1f9c31a17092a98728a3b6 100644 (file)
@@ -20,8 +20,6 @@
 #include <asm/page.h>
 #include <asm/processor.h>
 
-#include <asm-generic/pgtable-nopud.h>
-
 /* The kernel image occupies 0x4000000 to 0x6000000 (4MB --> 96MB).
  * The page copy blockops can use 0x6000000 to 0x8000000.
  * The 8K TSB is mapped in the 0x8000000 to 0x8400000 range.
 #define LOW_OBP_ADDRESS                _AC(0x00000000f0000000,UL)
 #define HI_OBP_ADDRESS         _AC(0x0000000100000000,UL)
 #define VMALLOC_START          _AC(0x0000000100000000,UL)
-#define VMALLOC_END            _AC(0x0000010000000000,UL)
-#define VMEMMAP_BASE           _AC(0x0000010000000000,UL)
-
-#define vmemmap                        ((struct page *)VMEMMAP_BASE)
+#define VMEMMAP_BASE           VMALLOC_END
 
 /* PMD_SHIFT determines the size of the area a second-level page
  * table can map
 #define PMD_MASK       (~(PMD_SIZE-1))
 #define PMD_BITS       (PAGE_SHIFT - 3)
 
-/* PGDIR_SHIFT determines what a third-level page table entry can map */
-#define PGDIR_SHIFT    (PAGE_SHIFT + (PAGE_SHIFT-3) + PMD_BITS)
+/* PUD_SHIFT determines the size of the area a third-level page
+ * table can map
+ */
+#define PUD_SHIFT      (PMD_SHIFT + PMD_BITS)
+#define PUD_SIZE       (_AC(1,UL) << PUD_SHIFT)
+#define PUD_MASK       (~(PUD_SIZE-1))
+#define PUD_BITS       (PAGE_SHIFT - 3)
+
+/* PGDIR_SHIFT determines what a fourth-level page table entry can map */
+#define PGDIR_SHIFT    (PUD_SHIFT + PUD_BITS)
 #define PGDIR_SIZE     (_AC(1,UL) << PGDIR_SHIFT)
 #define PGDIR_MASK     (~(PGDIR_SIZE-1))
 #define PGDIR_BITS     (PAGE_SHIFT - 3)
 
-#if (PGDIR_SHIFT + PGDIR_BITS) != 43
+#if (MAX_PHYS_ADDRESS_BITS > PGDIR_SHIFT + PGDIR_BITS)
+#error MAX_PHYS_ADDRESS_BITS exceeds what kernel page tables can support
+#endif
+
+#if (PGDIR_SHIFT + PGDIR_BITS) != 53
 #error Page table parameters do not cover virtual address space properly.
 #endif
 
 
 #ifndef __ASSEMBLY__
 
-#include <linux/sched.h>
-
-extern unsigned long sparc64_valid_addr_bitmap[];
+extern unsigned long VMALLOC_END;
 
-/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
-static inline bool __kern_addr_valid(unsigned long paddr)
-{
-       if ((paddr >> MAX_PHYS_ADDRESS_BITS) != 0UL)
-               return false;
-       return test_bit(paddr >> ILOG2_4MB, sparc64_valid_addr_bitmap);
-}
+#define vmemmap                        ((struct page *)VMEMMAP_BASE)
 
-static inline bool kern_addr_valid(unsigned long addr)
-{
-       unsigned long paddr = __pa(addr);
+#include <linux/sched.h>
 
-       return __kern_addr_valid(paddr);
-}
+bool kern_addr_valid(unsigned long addr);
 
 /* Entries per page directory level. */
 #define PTRS_PER_PTE   (1UL << (PAGE_SHIFT-3))
 #define PTRS_PER_PMD   (1UL << PMD_BITS)
+#define PTRS_PER_PUD   (1UL << PUD_BITS)
 #define PTRS_PER_PGD   (1UL << PGDIR_BITS)
 
 /* Kernel has a separate 44bit address space. */
@@ -101,6 +98,9 @@ static inline bool kern_addr_valid(unsigned long addr)
 #define pmd_ERROR(e)                                                   \
        pr_err("%s:%d: bad pmd %p(%016lx) seen at (%pS)\n",             \
               __FILE__, __LINE__, &(e), pmd_val(e), __builtin_return_address(0))
+#define pud_ERROR(e)                                                   \
+       pr_err("%s:%d: bad pud %p(%016lx) seen at (%pS)\n",             \
+              __FILE__, __LINE__, &(e), pud_val(e), __builtin_return_address(0))
 #define pgd_ERROR(e)                                                   \
        pr_err("%s:%d: bad pgd %p(%016lx) seen at (%pS)\n",             \
               __FILE__, __LINE__, &(e), pgd_val(e), __builtin_return_address(0))
@@ -112,6 +112,7 @@ static inline bool kern_addr_valid(unsigned long addr)
 #define _PAGE_R                  _AC(0x8000000000000000,UL) /* Keep ref bit uptodate*/
 #define _PAGE_SPECIAL     _AC(0x0200000000000000,UL) /* Special page         */
 #define _PAGE_PMD_HUGE    _AC(0x0100000000000000,UL) /* Huge page            */
+#define _PAGE_PUD_HUGE    _PAGE_PMD_HUGE
 
 /* Advertise support for _PAGE_SPECIAL */
 #define __HAVE_ARCH_PTE_SPECIAL
@@ -658,26 +659,26 @@ static inline unsigned long pmd_large(pmd_t pmd)
        return pte_val(pte) & _PAGE_PMD_HUGE;
 }
 
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-static inline unsigned long pmd_young(pmd_t pmd)
+static inline unsigned long pmd_pfn(pmd_t pmd)
 {
        pte_t pte = __pte(pmd_val(pmd));
 
-       return pte_young(pte);
+       return pte_pfn(pte);
 }
 
-static inline unsigned long pmd_write(pmd_t pmd)
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+static inline unsigned long pmd_young(pmd_t pmd)
 {
        pte_t pte = __pte(pmd_val(pmd));
 
-       return pte_write(pte);
+       return pte_young(pte);
 }
 
-static inline unsigned long pmd_pfn(pmd_t pmd)
+static inline unsigned long pmd_write(pmd_t pmd)
 {
        pte_t pte = __pte(pmd_val(pmd));
 
-       return pte_pfn(pte);
+       return pte_write(pte);
 }
 
 static inline unsigned long pmd_trans_huge(pmd_t pmd)
@@ -771,13 +772,15 @@ static inline int pmd_present(pmd_t pmd)
  * the top bits outside of the range of any physical address size we
  * support are clear as well.  We also validate the physical itself.
  */
-#define pmd_bad(pmd)                   ((pmd_val(pmd) & ~PAGE_MASK) || \
-                                        !__kern_addr_valid(pmd_val(pmd)))
+#define pmd_bad(pmd)                   (pmd_val(pmd) & ~PAGE_MASK)
 
 #define pud_none(pud)                  (!pud_val(pud))
 
-#define pud_bad(pud)                   ((pud_val(pud) & ~PAGE_MASK) || \
-                                        !__kern_addr_valid(pud_val(pud)))
+#define pud_bad(pud)                   (pud_val(pud) & ~PAGE_MASK)
+
+#define pgd_none(pgd)                  (!pgd_val(pgd))
+
+#define pgd_bad(pgd)                   (pgd_val(pgd) & ~PAGE_MASK)
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 void set_pmd_at(struct mm_struct *mm, unsigned long addr,
@@ -815,10 +818,31 @@ static inline unsigned long __pmd_page(pmd_t pmd)
 #define pmd_clear(pmdp)                        (pmd_val(*(pmdp)) = 0UL)
 #define pud_present(pud)               (pud_val(pud) != 0U)
 #define pud_clear(pudp)                        (pud_val(*(pudp)) = 0UL)
+#define pgd_page_vaddr(pgd)            \
+       ((unsigned long) __va(pgd_val(pgd)))
+#define pgd_present(pgd)               (pgd_val(pgd) != 0U)
+#define pgd_clear(pgdp)                        (pgd_val(*(pgd)) = 0UL)
+
+static inline unsigned long pud_large(pud_t pud)
+{
+       pte_t pte = __pte(pud_val(pud));
+
+       return pte_val(pte) & _PAGE_PMD_HUGE;
+}
+
+static inline unsigned long pud_pfn(pud_t pud)
+{
+       pte_t pte = __pte(pud_val(pud));
+
+       return pte_pfn(pte);
+}
 
 /* Same in both SUN4V and SUN4U.  */
 #define pte_none(pte)                  (!pte_val(pte))
 
+#define pgd_set(pgdp, pudp)    \
+       (pgd_val(*(pgdp)) = (__pa((unsigned long) (pudp))))
+
 /* to find an entry in a page-table-directory. */
 #define pgd_index(address)     (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
 #define pgd_offset(mm, address)        ((mm)->pgd + pgd_index(address))
@@ -826,6 +850,11 @@ static inline unsigned long __pmd_page(pmd_t pmd)
 /* to find an entry in a kernel page-table-directory */
 #define pgd_offset_k(address) pgd_offset(&init_mm, address)
 
+/* Find an entry in the third-level page table.. */
+#define pud_index(address)     (((address) >> PUD_SHIFT) & (PTRS_PER_PUD - 1))
+#define pud_offset(pgdp, address)      \
+       ((pud_t *) pgd_page_vaddr(*(pgdp)) + pud_index(address))
+
 /* Find an entry in the second-level page table.. */
 #define pmd_offset(pudp, address)      \
        ((pmd_t *) pud_page_vaddr(*(pudp)) + \
@@ -898,7 +927,6 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
 #endif
 
 extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
-extern pmd_t swapper_low_pmd_dir[PTRS_PER_PMD];
 
 void paging_init(void);
 unsigned long find_ecache_flush_span(unsigned long size);
index 3fc58691dbd0d45311bacec9ddf61fb7e7d9e0c4..56f933816144d47bf96506d09d3249445bc0fe32 100644 (file)
@@ -45,6 +45,8 @@
 #define SUN4V_CHIP_NIAGARA3    0x03
 #define SUN4V_CHIP_NIAGARA4    0x04
 #define SUN4V_CHIP_NIAGARA5    0x05
+#define SUN4V_CHIP_SPARC_M6    0x06
+#define SUN4V_CHIP_SPARC_M7    0x07
 #define SUN4V_CHIP_SPARC64X    0x8a
 #define SUN4V_CHIP_UNKNOWN     0xff
 
index a5f01ac6d0f1a2619cdac6b639248cb5a7153dac..f85dc8512ab3f6fdea1f1430aa8141bdccec2d44 100644 (file)
@@ -102,6 +102,7 @@ struct thread_info {
 #define FAULT_CODE_ITLB                0x04    /* Miss happened in I-TLB          */
 #define FAULT_CODE_WINFIXUP    0x08    /* Miss happened during spill/fill */
 #define FAULT_CODE_BLKCOMMIT   0x10    /* Use blk-commit ASI in copy_page */
+#define        FAULT_CODE_BAD_RA       0x20    /* Bad RA for sun4v                */
 
 #if PAGE_SHIFT == 13
 #define THREAD_SIZE (2*PAGE_SIZE)
index 90916f955cac80153c59dc74a768c3fdef3fae56..ecb49cfa3be9fa274053fb15bc2d5f3c38c76e08 100644 (file)
@@ -133,9 +133,24 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
        sub     TSB, 0x8, TSB;   \
        TSB_STORE(TSB, TAG);
 
-       /* Do a kernel page table walk.  Leaves physical PTE pointer in
-        * REG1.  Jumps to FAIL_LABEL on early page table walk termination.
-        * VADDR will not be clobbered, but REG2 will.
+       /* Do a kernel page table walk.  Leaves valid PTE value in
+        * REG1.  Jumps to FAIL_LABEL on early page table walk
+        * termination.  VADDR will not be clobbered, but REG2 will.
+        *
+        * There are two masks we must apply to propagate bits from
+        * the virtual address into the PTE physical address field
+        * when dealing with huge pages.  This is because the page
+        * table boundaries do not match the huge page size(s) the
+        * hardware supports.
+        *
+        * In these cases we propagate the bits that are below the
+        * page table level where we saw the huge page mapping, but
+        * are still within the relevant physical bits for the huge
+        * page size in question.  So for PMD mappings (which fall on
+        * bit 23, for 8MB per PMD) we must propagate bit 22 for a
+        * 4MB huge page.  For huge PUDs (which fall on bit 33, for
+        * 8GB per PUD), we have to accomodate 256MB and 2GB huge
+        * pages.  So for those we propagate bits 32 to 28.
         */
 #define KERN_PGTABLE_WALK(VADDR, REG1, REG2, FAIL_LABEL)       \
        sethi           %hi(swapper_pg_dir), REG1; \
@@ -145,15 +160,40 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
        andn            REG2, 0x7, REG2; \
        ldx             [REG1 + REG2], REG1; \
        brz,pn          REG1, FAIL_LABEL; \
-        sllx           VADDR, 64 - (PMD_SHIFT + PMD_BITS), REG2; \
+        sllx           VADDR, 64 - (PUD_SHIFT + PUD_BITS), REG2; \
        srlx            REG2, 64 - PAGE_SHIFT, REG2; \
        andn            REG2, 0x7, REG2; \
        ldxa            [REG1 + REG2] ASI_PHYS_USE_EC, REG1; \
        brz,pn          REG1, FAIL_LABEL; \
-        sllx           VADDR, 64 - PMD_SHIFT, REG2; \
+       sethi           %uhi(_PAGE_PUD_HUGE), REG2; \
+       brz,pn          REG1, FAIL_LABEL; \
+        sllx           REG2, 32, REG2; \
+       andcc           REG1, REG2, %g0; \
+       sethi           %hi(0xf8000000), REG2; \
+       bne,pt          %xcc, 697f; \
+        sllx           REG2, 1, REG2; \
+       sllx            VADDR, 64 - (PMD_SHIFT + PMD_BITS), REG2; \
        srlx            REG2, 64 - PAGE_SHIFT, REG2; \
        andn            REG2, 0x7, REG2; \
-       add             REG1, REG2, REG1;
+       ldxa            [REG1 + REG2] ASI_PHYS_USE_EC, REG1; \
+       sethi           %uhi(_PAGE_PMD_HUGE), REG2; \
+       brz,pn          REG1, FAIL_LABEL; \
+        sllx           REG2, 32, REG2; \
+       andcc           REG1, REG2, %g0; \
+       be,pn           %xcc, 698f; \
+        sethi          %hi(0x400000), REG2; \
+697:   brgez,pn        REG1, FAIL_LABEL; \
+        andn           REG1, REG2, REG1; \
+       and             VADDR, REG2, REG2; \
+       ba,pt           %xcc, 699f; \
+        or             REG1, REG2, REG1; \
+698:   sllx            VADDR, 64 - PMD_SHIFT, REG2; \
+       srlx            REG2, 64 - PAGE_SHIFT, REG2; \
+       andn            REG2, 0x7, REG2; \
+       ldxa            [REG1 + REG2] ASI_PHYS_USE_EC, REG1; \
+       brgez,pn        REG1, FAIL_LABEL; \
+        nop; \
+699:
 
        /* PMD has been loaded into REG1, interpret the value, seeing
         * if it is a HUGE PMD or a normal one.  If it is not valid
@@ -197,6 +237,11 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
        srlx            REG2, 64 - PAGE_SHIFT, REG2; \
        andn            REG2, 0x7, REG2; \
        ldxa            [PHYS_PGD + REG2] ASI_PHYS_USE_EC, REG1; \
+       brz,pn          REG1, FAIL_LABEL; \
+        sllx           VADDR, 64 - (PUD_SHIFT + PUD_BITS), REG2; \
+       srlx            REG2, 64 - PAGE_SHIFT, REG2; \
+       andn            REG2, 0x7, REG2; \
+       ldxa            [REG1 + REG2] ASI_PHYS_USE_EC, REG1; \
        brz,pn          REG1, FAIL_LABEL; \
         sllx           VADDR, 64 - (PMD_SHIFT + PMD_BITS), REG2; \
        srlx            REG2, 64 - PAGE_SHIFT, REG2; \
@@ -246,8 +291,6 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
        (KERNEL_TSB_SIZE_BYTES / 16)
 #define KERNEL_TSB4M_NENTRIES  4096
 
-#define KTSB_PHYS_SHIFT                15
-
        /* Do a kernel TSB lookup at tl>0 on VADDR+TAG, branch to OK_LABEL
         * on TSB hit.  REG1, REG2, REG3, and REG4 are used as temporaries
         * and the found TTE will be left in REG1.  REG3 and REG4 must
@@ -256,17 +299,15 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
         * VADDR and TAG will be preserved and not clobbered by this macro.
         */
 #define KERN_TSB_LOOKUP_TL1(VADDR, TAG, REG1, REG2, REG3, REG4, OK_LABEL) \
-661:   sethi           %hi(swapper_tsb), REG1;                 \
-       or              REG1, %lo(swapper_tsb), REG1; \
+661:   sethi           %uhi(swapper_tsb), REG1; \
+       sethi           %hi(swapper_tsb), REG2; \
+       or              REG1, %ulo(swapper_tsb), REG1; \
+       or              REG2, %lo(swapper_tsb), REG2; \
        .section        .swapper_tsb_phys_patch, "ax"; \
        .word           661b; \
        .previous; \
-661:   nop; \
-       .section        .tsb_ldquad_phys_patch, "ax"; \
-       .word           661b; \
-       sllx            REG1, KTSB_PHYS_SHIFT, REG1; \
-       sllx            REG1, KTSB_PHYS_SHIFT, REG1; \
-       .previous; \
+       sllx            REG1, 32, REG1; \
+       or              REG1, REG2, REG1; \
        srlx            VADDR, PAGE_SHIFT, REG2; \
        and             REG2, (KERNEL_TSB_NENTRIES - 1), REG2; \
        sllx            REG2, 4, REG2; \
@@ -281,17 +322,15 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
         * we can make use of that for the index computation.
         */
 #define KERN_TSB4M_LOOKUP_TL1(TAG, REG1, REG2, REG3, REG4, OK_LABEL) \
-661:   sethi           %hi(swapper_4m_tsb), REG1;           \
-       or              REG1, %lo(swapper_4m_tsb), REG1; \
+661:   sethi           %uhi(swapper_4m_tsb), REG1; \
+       sethi           %hi(swapper_4m_tsb), REG2; \
+       or              REG1, %ulo(swapper_4m_tsb), REG1; \
+       or              REG2, %lo(swapper_4m_tsb), REG2; \
        .section        .swapper_4m_tsb_phys_patch, "ax"; \
        .word           661b; \
        .previous; \
-661:   nop; \
-       .section        .tsb_ldquad_phys_patch, "ax"; \
-       .word           661b; \
-       sllx            REG1, KTSB_PHYS_SHIFT, REG1; \
-       sllx            REG1, KTSB_PHYS_SHIFT, REG1; \
-       .previous; \
+       sllx            REG1, 32, REG1; \
+       or              REG1, REG2, REG1; \
        and             TAG, (KERNEL_TSB4M_NENTRIES - 1), REG2; \
        sllx            REG2, 4, REG2; \
        add             REG1, REG2, REG2; \
index 6b135a8ab07bfdca9d8c2aa066d104a92bbb68e6..d758c8d8f47d90b9cefc18fc5ba973692b122a41 100644 (file)
@@ -121,12 +121,18 @@ struct vio_disk_attr_info {
        u8                      vdisk_type;
 #define VD_DISK_TYPE_SLICE     0x01 /* Slice in block device   */
 #define VD_DISK_TYPE_DISK      0x02 /* Entire block device     */
-       u16                     resv1;
+       u8                      vdisk_mtype;            /* v1.1 */
+#define VD_MEDIA_TYPE_FIXED    0x01 /* Fixed device */
+#define VD_MEDIA_TYPE_CD       0x02 /* CD Device    */
+#define VD_MEDIA_TYPE_DVD      0x03 /* DVD Device   */
+       u8                      resv1;
        u32                     vdisk_block_size;
        u64                     operations;
-       u64                     vdisk_size;
+       u64                     vdisk_size;             /* v1.1 */
        u64                     max_xfer_size;
-       u64                     resv2[2];
+       u32                     phys_block_size;        /* v1.2 */
+       u32                     resv2;
+       u64                     resv3[1];
 };
 
 struct vio_disk_desc {
@@ -272,7 +278,7 @@ static inline u32 vio_dring_avail(struct vio_dring_state *dr,
                                  unsigned int ring_size)
 {
        return (dr->pending -
-               ((dr->prod - dr->cons) & (ring_size - 1)));
+               ((dr->prod - dr->cons) & (ring_size - 1)) - 1);
 }
 
 #define VIO_MAX_TYPE_LEN       32
@@ -292,6 +298,7 @@ struct vio_dev {
 
        unsigned int            tx_irq;
        unsigned int            rx_irq;
+       u64                     rx_ino;
 
        struct device           dev;
 };
@@ -447,5 +454,6 @@ int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev,
                    char *name);
 
 void vio_port_up(struct vio_driver_state *vio);
+int vio_set_intr(unsigned long dev_ino, int state);
 
 #endif /* _SPARC64_VIO_H */
index 82a3a71c451e437248534d939834ae933dc2bdf4..dfad8b1aea9fb042a40290c766a3bb7ede4e0480 100644 (file)
@@ -494,6 +494,18 @@ static void __init sun4v_cpu_probe(void)
                sparc_pmu_type = "niagara5";
                break;
 
+       case SUN4V_CHIP_SPARC_M6:
+               sparc_cpu_type = "SPARC-M6";
+               sparc_fpu_type = "SPARC-M6 integrated FPU";
+               sparc_pmu_type = "sparc-m6";
+               break;
+
+       case SUN4V_CHIP_SPARC_M7:
+               sparc_cpu_type = "SPARC-M7";
+               sparc_fpu_type = "SPARC-M7 integrated FPU";
+               sparc_pmu_type = "sparc-m7";
+               break;
+
        case SUN4V_CHIP_SPARC64X:
                sparc_cpu_type = "SPARC64-X";
                sparc_fpu_type = "SPARC64-X integrated FPU";
index de1c844dfabc02569cfe71c91b3e52a9ea705ef4..e69ec0e3f15527705b3ce28fc89ff5c0341f03fb 100644 (file)
@@ -326,6 +326,8 @@ static int iterate_cpu(struct cpuinfo_tree *t, unsigned int root_index)
        case SUN4V_CHIP_NIAGARA3:
        case SUN4V_CHIP_NIAGARA4:
        case SUN4V_CHIP_NIAGARA5:
+       case SUN4V_CHIP_SPARC_M6:
+       case SUN4V_CHIP_SPARC_M7:
        case SUN4V_CHIP_SPARC64X:
                rover_inc_table = niagara_iterate_method;
                break;
index dff60abbea012f30a8b55221cc8d7a3a60a762ea..f87a55d7709469c63d8234fa9d3455513025dc55 100644 (file)
@@ -1200,14 +1200,14 @@ static int ds_probe(struct vio_dev *vdev, const struct vio_device_id *id)
        ds_cfg.tx_irq = vdev->tx_irq;
        ds_cfg.rx_irq = vdev->rx_irq;
 
-       lp = ldc_alloc(vdev->channel_id, &ds_cfg, dp);
+       lp = ldc_alloc(vdev->channel_id, &ds_cfg, dp, "DS");
        if (IS_ERR(lp)) {
                err = PTR_ERR(lp);
                goto out_free_ds_states;
        }
        dp->lp = lp;
 
-       err = ldc_bind(lp, "DS");
+       err = ldc_bind(lp);
        if (err)
                goto out_free_ldc;
 
index 452f04fe8da698bb8b4620abd40ac6d4fbcd8393..4fdeb8040d4dd4ae0326909f6b77231dcbc3a89d 100644 (file)
@@ -427,6 +427,12 @@ sun4v_chip_type:
        cmp     %g2, '5'
        be,pt   %xcc, 5f
         mov    SUN4V_CHIP_NIAGARA5, %g4
+       cmp     %g2, '6'
+       be,pt   %xcc, 5f
+        mov    SUN4V_CHIP_SPARC_M6, %g4
+       cmp     %g2, '7'
+       be,pt   %xcc, 5f
+        mov    SUN4V_CHIP_SPARC_M7, %g4
        ba,pt   %xcc, 49f
         nop
 
@@ -583,6 +589,12 @@ niagara_tlb_fixup:
        be,pt   %xcc, niagara4_patch
         nop
        cmp     %g1, SUN4V_CHIP_NIAGARA5
+       be,pt   %xcc, niagara4_patch
+        nop
+       cmp     %g1, SUN4V_CHIP_SPARC_M6
+       be,pt   %xcc, niagara4_patch
+        nop
+       cmp     %g1, SUN4V_CHIP_SPARC_M7
        be,pt   %xcc, niagara4_patch
         nop
 
index c0a2de0fd62424914754e19885f3ec1abff1c44e..5c55145bfbf02d616ec606feb94539e26cbe1831 100644 (file)
@@ -46,6 +46,7 @@ static struct api_info api_table[] = {
        { .group = HV_GRP_VF_CPU,                               },
        { .group = HV_GRP_KT_CPU,                               },
        { .group = HV_GRP_VT_CPU,                               },
+       { .group = HV_GRP_T5_CPU,                               },
        { .group = HV_GRP_DIAG,         .flags = FLAG_PRE_API   },
 };
 
index f3ab509b76a8daf32123ec47bbbf0ebb5b360bde..caedf8320416e1fe8bfc4cb76369ce1efd8ce729 100644 (file)
@@ -821,3 +821,19 @@ ENTRY(sun4v_vt_set_perfreg)
        retl
         nop
 ENDPROC(sun4v_vt_set_perfreg)
+
+ENTRY(sun4v_t5_get_perfreg)
+       mov     %o1, %o4
+       mov     HV_FAST_T5_GET_PERFREG, %o5
+       ta      HV_FAST_TRAP
+       stx     %o1, [%o4]
+       retl
+        nop
+ENDPROC(sun4v_t5_get_perfreg)
+
+ENTRY(sun4v_t5_set_perfreg)
+       mov     HV_FAST_T5_SET_PERFREG, %o5
+       ta      HV_FAST_TRAP
+       retl
+        nop
+ENDPROC(sun4v_t5_set_perfreg)
index 7f08ec8a7c682338dec59ff51c15740ee59cdc45..28fed53b13a0d3f1c69be7aec07734b39a2fac92 100644 (file)
@@ -278,7 +278,8 @@ static void *sbus_alloc_coherent(struct device *dev, size_t len,
        }
 
        order = get_order(len_total);
-       if ((va = __get_free_pages(GFP_KERNEL|__GFP_COMP, order)) == 0)
+       va = __get_free_pages(gfp, order);
+       if (va == 0)
                goto err_nopages;
 
        if ((res = kzalloc(sizeof(struct resource), GFP_KERNEL)) == NULL)
@@ -443,7 +444,7 @@ static void *pci32_alloc_coherent(struct device *dev, size_t len,
        }
 
        order = get_order(len_total);
-       va = (void *) __get_free_pages(GFP_KERNEL, order);
+       va = (void *) __get_free_pages(gfp, order);
        if (va == NULL) {
                printk("pci_alloc_consistent: no %ld pages\n", len_total>>PAGE_SHIFT);
                goto err_nopages;
index 666193f4e8bb414fcf64e45a15f89ebf0ab2f628..4033c23bdfa6cc76a9ef4b34ad671da39b6b5077 100644 (file)
@@ -47,8 +47,6 @@
 #include "cpumap.h"
 #include "kstack.h"
 
-#define NUM_IVECS      (IMAP_INR + 1)
-
 struct ino_bucket *ivector_table;
 unsigned long ivector_table_pa;
 
@@ -107,55 +105,196 @@ static void bucket_set_irq(unsigned long bucket_pa, unsigned int irq)
 
 #define irq_work_pa(__cpu)     &(trap_block[(__cpu)].irq_worklist_pa)
 
-static struct {
-       unsigned int dev_handle;
-       unsigned int dev_ino;
-       unsigned int in_use;
-} irq_table[NR_IRQS];
-static DEFINE_SPINLOCK(irq_alloc_lock);
+static unsigned long hvirq_major __initdata;
+static int __init early_hvirq_major(char *p)
+{
+       int rc = kstrtoul(p, 10, &hvirq_major);
+
+       return rc;
+}
+early_param("hvirq", early_hvirq_major);
+
+static int hv_irq_version;
+
+/* Major version 2.0 of HV_GRP_INTR added support for the VIRQ cookie
+ * based interfaces, but:
+ *
+ * 1) Several OSs, Solaris and Linux included, use them even when only
+ *    negotiating version 1.0 (or failing to negotiate at all).  So the
+ *    hypervisor has a workaround that provides the VIRQ interfaces even
+ *    when only verion 1.0 of the API is in use.
+ *
+ * 2) Second, and more importantly, with major version 2.0 these VIRQ
+ *    interfaces only were actually hooked up for LDC interrupts, even
+ *    though the Hypervisor specification clearly stated:
+ *
+ *     The new interrupt API functions will be available to a guest
+ *     when it negotiates version 2.0 in the interrupt API group 0x2. When
+ *     a guest negotiates version 2.0, all interrupt sources will only
+ *     support using the cookie interface, and any attempt to use the
+ *     version 1.0 interrupt APIs numbered 0xa0 to 0xa6 will result in the
+ *     ENOTSUPPORTED error being returned.
+ *
+ *   with an emphasis on "all interrupt sources".
+ *
+ * To correct this, major version 3.0 was created which does actually
+ * support VIRQs for all interrupt sources (not just LDC devices).  So
+ * if we want to move completely over the cookie based VIRQs we must
+ * negotiate major version 3.0 or later of HV_GRP_INTR.
+ */
+static bool sun4v_cookie_only_virqs(void)
+{
+       if (hv_irq_version >= 3)
+               return true;
+       return false;
+}
 
-unsigned char irq_alloc(unsigned int dev_handle, unsigned int dev_ino)
+static void __init irq_init_hv(void)
 {
-       unsigned long flags;
-       unsigned char ent;
+       unsigned long hv_error, major, minor = 0;
+
+       if (tlb_type != hypervisor)
+               return;
 
-       BUILD_BUG_ON(NR_IRQS >= 256);
+       if (hvirq_major)
+               major = hvirq_major;
+       else
+               major = 3;
 
-       spin_lock_irqsave(&irq_alloc_lock, flags);
+       hv_error = sun4v_hvapi_register(HV_GRP_INTR, major, &minor);
+       if (!hv_error)
+               hv_irq_version = major;
+       else
+               hv_irq_version = 1;
 
-       for (ent = 1; ent < NR_IRQS; ent++) {
-               if (!irq_table[ent].in_use)
+       pr_info("SUN4V: Using IRQ API major %d, cookie only virqs %s\n",
+               hv_irq_version,
+               sun4v_cookie_only_virqs() ? "enabled" : "disabled");
+}
+
+/* This function is for the timer interrupt.*/
+int __init arch_probe_nr_irqs(void)
+{
+       return 1;
+}
+
+#define DEFAULT_NUM_IVECS      (0xfffU)
+static unsigned int nr_ivec = DEFAULT_NUM_IVECS;
+#define NUM_IVECS (nr_ivec)
+
+static unsigned int __init size_nr_ivec(void)
+{
+       if (tlb_type == hypervisor) {
+               switch (sun4v_chip_type) {
+               /* Athena's devhandle|devino is large.*/
+               case SUN4V_CHIP_SPARC64X:
+                       nr_ivec = 0xffff;
                        break;
+               }
        }
-       if (ent >= NR_IRQS) {
-               printk(KERN_ERR "IRQ: Out of virtual IRQs.\n");
-               ent = 0;
-       } else {
-               irq_table[ent].dev_handle = dev_handle;
-               irq_table[ent].dev_ino = dev_ino;
-               irq_table[ent].in_use = 1;
-       }
+       return nr_ivec;
+}
+
+struct irq_handler_data {
+       union {
+               struct {
+                       unsigned int dev_handle;
+                       unsigned int dev_ino;
+               };
+               unsigned long sysino;
+       };
+       struct ino_bucket bucket;
+       unsigned long   iclr;
+       unsigned long   imap;
+};
+
+static inline unsigned int irq_data_to_handle(struct irq_data *data)
+{
+       struct irq_handler_data *ihd = data->handler_data;
+
+       return ihd->dev_handle;
+}
+
+static inline unsigned int irq_data_to_ino(struct irq_data *data)
+{
+       struct irq_handler_data *ihd = data->handler_data;
 
-       spin_unlock_irqrestore(&irq_alloc_lock, flags);
+       return ihd->dev_ino;
+}
+
+static inline unsigned long irq_data_to_sysino(struct irq_data *data)
+{
+       struct irq_handler_data *ihd = data->handler_data;
 
-       return ent;
+       return ihd->sysino;
 }
 
-#ifdef CONFIG_PCI_MSI
 void irq_free(unsigned int irq)
 {
-       unsigned long flags;
+       void *data = irq_get_handler_data(irq);
 
-       if (irq >= NR_IRQS)
-               return;
+       kfree(data);
+       irq_set_handler_data(irq, NULL);
+       irq_free_descs(irq, 1);
+}
 
-       spin_lock_irqsave(&irq_alloc_lock, flags);
+unsigned int irq_alloc(unsigned int dev_handle, unsigned int dev_ino)
+{
+       int irq;
 
-       irq_table[irq].in_use = 0;
+       irq = __irq_alloc_descs(-1, 1, 1, numa_node_id(), NULL);
+       if (irq <= 0)
+               goto out;
 
-       spin_unlock_irqrestore(&irq_alloc_lock, flags);
+       return irq;
+out:
+       return 0;
+}
+
+static unsigned int cookie_exists(u32 devhandle, unsigned int devino)
+{
+       unsigned long hv_err, cookie;
+       struct ino_bucket *bucket;
+       unsigned int irq = 0U;
+
+       hv_err = sun4v_vintr_get_cookie(devhandle, devino, &cookie);
+       if (hv_err) {
+               pr_err("HV get cookie failed hv_err = %ld\n", hv_err);
+               goto out;
+       }
+
+       if (cookie & ((1UL << 63UL))) {
+               cookie = ~cookie;
+               bucket = (struct ino_bucket *) __va(cookie);
+               irq = bucket->__irq;
+       }
+out:
+       return irq;
+}
+
+static unsigned int sysino_exists(u32 devhandle, unsigned int devino)
+{
+       unsigned long sysino = sun4v_devino_to_sysino(devhandle, devino);
+       struct ino_bucket *bucket;
+       unsigned int irq;
+
+       bucket = &ivector_table[sysino];
+       irq = bucket_get_irq(__pa(bucket));
+
+       return irq;
+}
+
+void ack_bad_irq(unsigned int irq)
+{
+       pr_crit("BAD IRQ ack %d\n", irq);
+}
+
+void irq_install_pre_handler(int irq,
+                            void (*func)(unsigned int, void *, void *),
+                            void *arg1, void *arg2)
+{
+       pr_warn("IRQ pre handler NOT supported.\n");
 }
-#endif
 
 /*
  * /proc/interrupts printing:
@@ -206,15 +345,6 @@ static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid)
        return tid;
 }
 
-struct irq_handler_data {
-       unsigned long   iclr;
-       unsigned long   imap;
-
-       void            (*pre_handler)(unsigned int, void *, void *);
-       void            *arg1;
-       void            *arg2;
-};
-
 #ifdef CONFIG_SMP
 static int irq_choose_cpu(unsigned int irq, const struct cpumask *affinity)
 {
@@ -316,8 +446,8 @@ static void sun4u_irq_eoi(struct irq_data *data)
 
 static void sun4v_irq_enable(struct irq_data *data)
 {
-       unsigned int ino = irq_table[data->irq].dev_ino;
        unsigned long cpuid = irq_choose_cpu(data->irq, data->affinity);
+       unsigned int ino = irq_data_to_sysino(data);
        int err;
 
        err = sun4v_intr_settarget(ino, cpuid);
@@ -337,8 +467,8 @@ static void sun4v_irq_enable(struct irq_data *data)
 static int sun4v_set_affinity(struct irq_data *data,
                               const struct cpumask *mask, bool force)
 {
-       unsigned int ino = irq_table[data->irq].dev_ino;
        unsigned long cpuid = irq_choose_cpu(data->irq, mask);
+       unsigned int ino = irq_data_to_sysino(data);
        int err;
 
        err = sun4v_intr_settarget(ino, cpuid);
@@ -351,7 +481,7 @@ static int sun4v_set_affinity(struct irq_data *data,
 
 static void sun4v_irq_disable(struct irq_data *data)
 {
-       unsigned int ino = irq_table[data->irq].dev_ino;
+       unsigned int ino = irq_data_to_sysino(data);
        int err;
 
        err = sun4v_intr_setenabled(ino, HV_INTR_DISABLED);
@@ -362,7 +492,7 @@ static void sun4v_irq_disable(struct irq_data *data)
 
 static void sun4v_irq_eoi(struct irq_data *data)
 {
-       unsigned int ino = irq_table[data->irq].dev_ino;
+       unsigned int ino = irq_data_to_sysino(data);
        int err;
 
        err = sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE);
@@ -373,14 +503,13 @@ static void sun4v_irq_eoi(struct irq_data *data)
 
 static void sun4v_virq_enable(struct irq_data *data)
 {
-       unsigned long cpuid, dev_handle, dev_ino;
+       unsigned long dev_handle = irq_data_to_handle(data);
+       unsigned long dev_ino = irq_data_to_ino(data);
+       unsigned long cpuid;
        int err;
 
        cpuid = irq_choose_cpu(data->irq, data->affinity);
 
-       dev_handle = irq_table[data->irq].dev_handle;
-       dev_ino = irq_table[data->irq].dev_ino;
-
        err = sun4v_vintr_set_target(dev_handle, dev_ino, cpuid);
        if (err != HV_EOK)
                printk(KERN_ERR "sun4v_vintr_set_target(%lx,%lx,%lu): "
@@ -403,14 +532,13 @@ static void sun4v_virq_enable(struct irq_data *data)
 static int sun4v_virt_set_affinity(struct irq_data *data,
                                    const struct cpumask *mask, bool force)
 {
-       unsigned long cpuid, dev_handle, dev_ino;
+       unsigned long dev_handle = irq_data_to_handle(data);
+       unsigned long dev_ino = irq_data_to_ino(data);
+       unsigned long cpuid;
        int err;
 
        cpuid = irq_choose_cpu(data->irq, mask);
 
-       dev_handle = irq_table[data->irq].dev_handle;
-       dev_ino = irq_table[data->irq].dev_ino;
-
        err = sun4v_vintr_set_target(dev_handle, dev_ino, cpuid);
        if (err != HV_EOK)
                printk(KERN_ERR "sun4v_vintr_set_target(%lx,%lx,%lu): "
@@ -422,11 +550,10 @@ static int sun4v_virt_set_affinity(struct irq_data *data,
 
 static void sun4v_virq_disable(struct irq_data *data)
 {
-       unsigned long dev_handle, dev_ino;
+       unsigned long dev_handle = irq_data_to_handle(data);
+       unsigned long dev_ino = irq_data_to_ino(data);
        int err;
 
-       dev_handle = irq_table[data->irq].dev_handle;
-       dev_ino = irq_table[data->irq].dev_ino;
 
        err = sun4v_vintr_set_valid(dev_handle, dev_ino,
                                    HV_INTR_DISABLED);
@@ -438,12 +565,10 @@ static void sun4v_virq_disable(struct irq_data *data)
 
 static void sun4v_virq_eoi(struct irq_data *data)
 {
-       unsigned long dev_handle, dev_ino;
+       unsigned long dev_handle = irq_data_to_handle(data);
+       unsigned long dev_ino = irq_data_to_ino(data);
        int err;
 
-       dev_handle = irq_table[data->irq].dev_handle;
-       dev_ino = irq_table[data->irq].dev_ino;
-
        err = sun4v_vintr_set_state(dev_handle, dev_ino,
                                    HV_INTR_STATE_IDLE);
        if (err != HV_EOK)
@@ -479,31 +604,10 @@ static struct irq_chip sun4v_virq = {
        .flags                  = IRQCHIP_EOI_IF_HANDLED,
 };
 
-static void pre_flow_handler(struct irq_data *d)
-{
-       struct irq_handler_data *handler_data = irq_data_get_irq_handler_data(d);
-       unsigned int ino = irq_table[d->irq].dev_ino;
-
-       handler_data->pre_handler(ino, handler_data->arg1, handler_data->arg2);
-}
-
-void irq_install_pre_handler(int irq,
-                            void (*func)(unsigned int, void *, void *),
-                            void *arg1, void *arg2)
-{
-       struct irq_handler_data *handler_data = irq_get_handler_data(irq);
-
-       handler_data->pre_handler = func;
-       handler_data->arg1 = arg1;
-       handler_data->arg2 = arg2;
-
-       __irq_set_preflow_handler(irq, pre_flow_handler);
-}
-
 unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
 {
-       struct ino_bucket *bucket;
        struct irq_handler_data *handler_data;
+       struct ino_bucket *bucket;
        unsigned int irq;
        int ino;
 
@@ -537,119 +641,166 @@ out:
        return irq;
 }
 
-static unsigned int sun4v_build_common(unsigned long sysino,
-                                      struct irq_chip *chip)
+static unsigned int sun4v_build_common(u32 devhandle, unsigned int devino,
+               void (*handler_data_init)(struct irq_handler_data *data,
+               u32 devhandle, unsigned int devino),
+               struct irq_chip *chip)
 {
-       struct ino_bucket *bucket;
-       struct irq_handler_data *handler_data;
+       struct irq_handler_data *data;
        unsigned int irq;
 
-       BUG_ON(tlb_type != hypervisor);
+       irq = irq_alloc(devhandle, devino);
+       if (!irq)
+               goto out;
 
-       bucket = &ivector_table[sysino];
-       irq = bucket_get_irq(__pa(bucket));
-       if (!irq) {
-               irq = irq_alloc(0, sysino);
-               bucket_set_irq(__pa(bucket), irq);
-               irq_set_chip_and_handler_name(irq, chip, handle_fasteoi_irq,
-                                             "IVEC");
+       data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
+       if (unlikely(!data)) {
+               pr_err("IRQ handler data allocation failed.\n");
+               irq_free(irq);
+               irq = 0;
+               goto out;
        }
 
-       handler_data = irq_get_handler_data(irq);
-       if (unlikely(handler_data))
-               goto out;
+       irq_set_handler_data(irq, data);
+       handler_data_init(data, devhandle, devino);
+       irq_set_chip_and_handler_name(irq, chip, handle_fasteoi_irq, "IVEC");
+       data->imap = ~0UL;
+       data->iclr = ~0UL;
+out:
+       return irq;
+}
 
-       handler_data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
-       if (unlikely(!handler_data)) {
-               prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
-               prom_halt();
-       }
-       irq_set_handler_data(irq, handler_data);
+static unsigned long cookie_assign(unsigned int irq, u32 devhandle,
+               unsigned int devino)
+{
+       struct irq_handler_data *ihd = irq_get_handler_data(irq);
+       unsigned long hv_error, cookie;
 
-       /* Catch accidental accesses to these things.  IMAP/ICLR handling
-        * is done by hypervisor calls on sun4v platforms, not by direct
-        * register accesses.
+       /* handler_irq needs to find the irq. cookie is seen signed in
+        * sun4v_dev_mondo and treated as a non ivector_table delivery.
         */
-       handler_data->imap = ~0UL;
-       handler_data->iclr = ~0UL;
+       ihd->bucket.__irq = irq;
+       cookie = ~__pa(&ihd->bucket);
 
-out:
-       return irq;
+       hv_error = sun4v_vintr_set_cookie(devhandle, devino, cookie);
+       if (hv_error)
+               pr_err("HV vintr set cookie failed = %ld\n", hv_error);
+
+       return hv_error;
 }
 
-unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino)
+static void cookie_handler_data(struct irq_handler_data *data,
+                               u32 devhandle, unsigned int devino)
 {
-       unsigned long sysino = sun4v_devino_to_sysino(devhandle, devino);
+       data->dev_handle = devhandle;
+       data->dev_ino = devino;
+}
 
-       return sun4v_build_common(sysino, &sun4v_irq);
+static unsigned int cookie_build_irq(u32 devhandle, unsigned int devino,
+                                    struct irq_chip *chip)
+{
+       unsigned long hv_error;
+       unsigned int irq;
+
+       irq = sun4v_build_common(devhandle, devino, cookie_handler_data, chip);
+
+       hv_error = cookie_assign(irq, devhandle, devino);
+       if (hv_error) {
+               irq_free(irq);
+               irq = 0;
+       }
+
+       return irq;
 }
 
-unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino)
+static unsigned int sun4v_build_cookie(u32 devhandle, unsigned int devino)
 {
-       struct irq_handler_data *handler_data;
-       unsigned long hv_err, cookie;
-       struct ino_bucket *bucket;
        unsigned int irq;
 
-       bucket = kzalloc(sizeof(struct ino_bucket), GFP_ATOMIC);
-       if (unlikely(!bucket))
-               return 0;
+       irq = cookie_exists(devhandle, devino);
+       if (irq)
+               goto out;
 
-       /* The only reference we store to the IRQ bucket is
-        * by physical address which kmemleak can't see, tell
-        * it that this object explicitly is not a leak and
-        * should be scanned.
-        */
-       kmemleak_not_leak(bucket);
+       irq = cookie_build_irq(devhandle, devino, &sun4v_virq);
 
-       __flush_dcache_range((unsigned long) bucket,
-                            ((unsigned long) bucket +
-                             sizeof(struct ino_bucket)));
+out:
+       return irq;
+}
 
-       irq = irq_alloc(devhandle, devino);
+static void sysino_set_bucket(unsigned int irq)
+{
+       struct irq_handler_data *ihd = irq_get_handler_data(irq);
+       struct ino_bucket *bucket;
+       unsigned long sysino;
+
+       sysino = sun4v_devino_to_sysino(ihd->dev_handle, ihd->dev_ino);
+       BUG_ON(sysino >= nr_ivec);
+       bucket = &ivector_table[sysino];
        bucket_set_irq(__pa(bucket), irq);
+}
 
-       irq_set_chip_and_handler_name(irq, &sun4v_virq, handle_fasteoi_irq,
-                                     "IVEC");
+static void sysino_handler_data(struct irq_handler_data *data,
+                               u32 devhandle, unsigned int devino)
+{
+       unsigned long sysino;
 
-       handler_data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
-       if (unlikely(!handler_data))
-               return 0;
+       sysino = sun4v_devino_to_sysino(devhandle, devino);
+       data->sysino = sysino;
+}
 
-       /* In order to make the LDC channel startup sequence easier,
-        * especially wrt. locking, we do not let request_irq() enable
-        * the interrupt.
-        */
-       irq_set_status_flags(irq, IRQ_NOAUTOEN);
-       irq_set_handler_data(irq, handler_data);
+static unsigned int sysino_build_irq(u32 devhandle, unsigned int devino,
+                                    struct irq_chip *chip)
+{
+       unsigned int irq;
 
-       /* Catch accidental accesses to these things.  IMAP/ICLR handling
-        * is done by hypervisor calls on sun4v platforms, not by direct
-        * register accesses.
-        */
-       handler_data->imap = ~0UL;
-       handler_data->iclr = ~0UL;
+       irq = sun4v_build_common(devhandle, devino, sysino_handler_data, chip);
+       if (!irq)
+               goto out;
 
-       cookie = ~__pa(bucket);
-       hv_err = sun4v_vintr_set_cookie(devhandle, devino, cookie);
-       if (hv_err) {
-               prom_printf("IRQ: Fatal, cannot set cookie for [%x:%x] "
-                           "err=%lu\n", devhandle, devino, hv_err);
-               prom_halt();
-       }
+       sysino_set_bucket(irq);
+out:
+       return irq;
+}
 
+static int sun4v_build_sysino(u32 devhandle, unsigned int devino)
+{
+       int irq;
+
+       irq = sysino_exists(devhandle, devino);
+       if (irq)
+               goto out;
+
+       irq = sysino_build_irq(devhandle, devino, &sun4v_irq);
+out:
        return irq;
 }
 
-void ack_bad_irq(unsigned int irq)
+unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino)
 {
-       unsigned int ino = irq_table[irq].dev_ino;
+       unsigned int irq;
 
-       if (!ino)
-               ino = 0xdeadbeef;
+       if (sun4v_cookie_only_virqs())
+               irq = sun4v_build_cookie(devhandle, devino);
+       else
+               irq = sun4v_build_sysino(devhandle, devino);
 
-       printk(KERN_CRIT "Unexpected IRQ from ino[%x] irq[%u]\n",
-              ino, irq);
+       return irq;
+}
+
+unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino)
+{
+       int irq;
+
+       irq = cookie_build_irq(devhandle, devino, &sun4v_virq);
+       if (!irq)
+               goto out;
+
+       /* This is borrowed from the original function.
+        */
+       irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+out:
+       return irq;
 }
 
 void *hardirq_stack[NR_CPUS];
@@ -720,9 +871,12 @@ void fixup_irqs(void)
 
        for (irq = 0; irq < NR_IRQS; irq++) {
                struct irq_desc *desc = irq_to_desc(irq);
-               struct irq_data *data = irq_desc_get_irq_data(desc);
+               struct irq_data *data;
                unsigned long flags;
 
+               if (!desc)
+                       continue;
+               data = irq_desc_get_irq_data(desc);
                raw_spin_lock_irqsave(&desc->lock, flags);
                if (desc->action && !irqd_is_per_cpu(data)) {
                        if (data->chip->irq_set_affinity)
@@ -922,16 +1076,22 @@ static struct irqaction timer_irq_action = {
        .name = "timer",
 };
 
-/* Only invoked on boot processor. */
-void __init init_IRQ(void)
+static void __init irq_ivector_init(void)
 {
-       unsigned long size;
+       unsigned long size, order;
+       unsigned int ivecs;
 
-       map_prom_timers();
-       kill_prom_timer();
+       /* If we are doing cookie only VIRQs then we do not need the ivector
+        * table to process interrupts.
+        */
+       if (sun4v_cookie_only_virqs())
+               return;
 
-       size = sizeof(struct ino_bucket) * NUM_IVECS;
-       ivector_table = kzalloc(size, GFP_KERNEL);
+       ivecs = size_nr_ivec();
+       size = sizeof(struct ino_bucket) * ivecs;
+       order = get_order(size);
+       ivector_table = (struct ino_bucket *)
+               __get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
        if (!ivector_table) {
                prom_printf("Fatal error, cannot allocate ivector_table\n");
                prom_halt();
@@ -940,6 +1100,15 @@ void __init init_IRQ(void)
                             ((unsigned long) ivector_table) + size);
 
        ivector_table_pa = __pa(ivector_table);
+}
+
+/* Only invoked on boot processor.*/
+void __init init_IRQ(void)
+{
+       irq_init_hv();
+       irq_ivector_init();
+       map_prom_timers();
+       kill_prom_timer();
 
        if (tlb_type == hypervisor)
                sun4v_init_mondo_queues();
index 605d49204580585356a7fda6dede8657641fb7e1..ef0d8e9e1210e6dffbc184a8ce426c708a968062 100644 (file)
@@ -47,14 +47,6 @@ kvmap_itlb_vmalloc_addr:
        KERN_PGTABLE_WALK(%g4, %g5, %g2, kvmap_itlb_longpath)
 
        TSB_LOCK_TAG(%g1, %g2, %g7)
-
-       /* Load and check PTE.  */
-       ldxa            [%g5] ASI_PHYS_USE_EC, %g5
-       mov             1, %g7
-       sllx            %g7, TSB_TAG_INVALID_BIT, %g7
-       brgez,a,pn      %g5, kvmap_itlb_longpath
-        TSB_STORE(%g1, %g7)
-
        TSB_WRITE(%g1, %g5, %g6)
 
        /* fallthrough to TLB load */
@@ -118,6 +110,12 @@ kvmap_dtlb_obp:
        ba,pt           %xcc, kvmap_dtlb_load
         nop
 
+kvmap_linear_early:
+       sethi           %hi(kern_linear_pte_xor), %g7
+       ldx             [%g7 + %lo(kern_linear_pte_xor)], %g2
+       ba,pt           %xcc, kvmap_dtlb_tsb4m_load
+        xor            %g2, %g4, %g5
+
        .align          32
 kvmap_dtlb_tsb4m_load:
        TSB_LOCK_TAG(%g1, %g2, %g7)
@@ -146,105 +144,17 @@ kvmap_dtlb_4v:
        /* Correct TAG_TARGET is already in %g6, check 4mb TSB.  */
        KERN_TSB4M_LOOKUP_TL1(%g6, %g5, %g1, %g2, %g3, kvmap_dtlb_load)
 #endif
-       /* TSB entry address left in %g1, lookup linear PTE.
-        * Must preserve %g1 and %g6 (TAG).
-        */
-kvmap_dtlb_tsb4m_miss:
-       /* Clear the PAGE_OFFSET top virtual bits, shift
-        * down to get PFN, and make sure PFN is in range.
-        */
-661:   sllx            %g4, 0, %g5
-       .section        .page_offset_shift_patch, "ax"
-       .word           661b
-       .previous
-
-       /* Check to see if we know about valid memory at the 4MB
-        * chunk this physical address will reside within.
+       /* Linear mapping TSB lookup failed.  Fallthrough to kernel
+        * page table based lookup.
         */
-661:   srlx            %g5, MAX_PHYS_ADDRESS_BITS, %g2
-       .section        .page_offset_shift_patch, "ax"
-       .word           661b
-       .previous
-
-       brnz,pn         %g2, kvmap_dtlb_longpath
-        nop
-
-       /* This unconditional branch and delay-slot nop gets patched
-        * by the sethi sequence once the bitmap is properly setup.
-        */
-       .globl          valid_addr_bitmap_insn
-valid_addr_bitmap_insn:
-       ba,pt           %xcc, 2f
-        nop
-       .subsection     2
-       .globl          valid_addr_bitmap_patch
-valid_addr_bitmap_patch:
-       sethi           %hi(sparc64_valid_addr_bitmap), %g7
-       or              %g7, %lo(sparc64_valid_addr_bitmap), %g7
-       .previous
-
-661:   srlx            %g5, ILOG2_4MB, %g2
-       .section        .page_offset_shift_patch, "ax"
-       .word           661b
-       .previous
-
-       srlx            %g2, 6, %g5
-       and             %g2, 63, %g2
-       sllx            %g5, 3, %g5
-       ldx             [%g7 + %g5], %g5
-       mov             1, %g7
-       sllx            %g7, %g2, %g7
-       andcc           %g5, %g7, %g0
-       be,pn           %xcc, kvmap_dtlb_longpath
-
-2:      sethi          %hi(kpte_linear_bitmap), %g2
-
-       /* Get the 256MB physical address index. */
-661:   sllx            %g4, 0, %g5
-       .section        .page_offset_shift_patch, "ax"
-       .word           661b
-       .previous
-
-       or              %g2, %lo(kpte_linear_bitmap), %g2
-
-661:   srlx            %g5, ILOG2_256MB, %g5
-       .section        .page_offset_shift_patch, "ax"
-       .word           661b
-       .previous
-
-       and             %g5, (32 - 1), %g7
-
-       /* Divide by 32 to get the offset into the bitmask.  */
-       srlx            %g5, 5, %g5
-       add             %g7, %g7, %g7
-       sllx            %g5, 3, %g5
-
-       /* kern_linear_pte_xor[(mask >> shift) & 3)] */
-       ldx             [%g2 + %g5], %g2
-       srlx            %g2, %g7, %g7
-       sethi           %hi(kern_linear_pte_xor), %g5
-       and             %g7, 3, %g7
-       or              %g5, %lo(kern_linear_pte_xor), %g5
-       sllx            %g7, 3, %g7
-       ldx             [%g5 + %g7], %g2
-
        .globl          kvmap_linear_patch
 kvmap_linear_patch:
-       ba,pt           %xcc, kvmap_dtlb_tsb4m_load
-        xor            %g2, %g4, %g5
+       ba,a,pt         %xcc, kvmap_linear_early
 
 kvmap_dtlb_vmalloc_addr:
        KERN_PGTABLE_WALK(%g4, %g5, %g2, kvmap_dtlb_longpath)
 
        TSB_LOCK_TAG(%g1, %g2, %g7)
-
-       /* Load and check PTE.  */
-       ldxa            [%g5] ASI_PHYS_USE_EC, %g5
-       mov             1, %g7
-       sllx            %g7, TSB_TAG_INVALID_BIT, %g7
-       brgez,a,pn      %g5, kvmap_dtlb_longpath
-        TSB_STORE(%g1, %g7)
-
        TSB_WRITE(%g1, %g5, %g6)
 
        /* fallthrough to TLB load */
@@ -276,13 +186,8 @@ kvmap_dtlb_load:
 
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
 kvmap_vmemmap:
-       sub             %g4, %g5, %g5
-       srlx            %g5, ILOG2_4MB, %g5
-       sethi           %hi(vmemmap_table), %g1
-       sllx            %g5, 3, %g5
-       or              %g1, %lo(vmemmap_table), %g1
-       ba,pt           %xcc, kvmap_dtlb_load
-        ldx            [%g1 + %g5], %g5
+       KERN_PGTABLE_WALK(%g4, %g5, %g2, kvmap_dtlb_longpath)
+       ba,a,pt         %xcc, kvmap_dtlb_load
 #endif
 
 kvmap_dtlb_nonlinear:
@@ -294,8 +199,8 @@ kvmap_dtlb_nonlinear:
 
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
        /* Do not use the TSB for vmemmap.  */
-       mov             (VMEMMAP_BASE >> 40), %g5
-       sllx            %g5, 40, %g5
+       sethi           %hi(VMEMMAP_BASE), %g5
+       ldx             [%g5 + %lo(VMEMMAP_BASE)], %g5
        cmp             %g4,%g5
        bgeu,pn         %xcc, kvmap_vmemmap
         nop
@@ -307,8 +212,8 @@ kvmap_dtlb_tsbmiss:
        sethi           %hi(MODULES_VADDR), %g5
        cmp             %g4, %g5
        blu,pn          %xcc, kvmap_dtlb_longpath
-        mov            (VMALLOC_END >> 40), %g5
-       sllx            %g5, 40, %g5
+        sethi          %hi(VMALLOC_END), %g5
+       ldx             [%g5 + %lo(VMALLOC_END)], %g5
        cmp             %g4, %g5
        bgeu,pn         %xcc, kvmap_dtlb_longpath
         nop
index 0af28b9846954593612cf512cf85afb167eacbfc..4310332872d4cf90727b1e91b900dcbe601d296f 100644 (file)
@@ -1078,7 +1078,8 @@ static void ldc_iommu_release(struct ldc_channel *lp)
 
 struct ldc_channel *ldc_alloc(unsigned long id,
                              const struct ldc_channel_config *cfgp,
-                             void *event_arg)
+                             void *event_arg,
+                             const char *name)
 {
        struct ldc_channel *lp;
        const struct ldc_mode_ops *mops;
@@ -1093,6 +1094,8 @@ struct ldc_channel *ldc_alloc(unsigned long id,
        err = -EINVAL;
        if (!cfgp)
                goto out_err;
+       if (!name)
+               goto out_err;
 
        switch (cfgp->mode) {
        case LDC_MODE_RAW:
@@ -1185,6 +1188,21 @@ struct ldc_channel *ldc_alloc(unsigned long id,
 
        INIT_HLIST_HEAD(&lp->mh_list);
 
+       snprintf(lp->rx_irq_name, LDC_IRQ_NAME_MAX, "%s RX", name);
+       snprintf(lp->tx_irq_name, LDC_IRQ_NAME_MAX, "%s TX", name);
+
+       err = request_irq(lp->cfg.rx_irq, ldc_rx, 0,
+                         lp->rx_irq_name, lp);
+       if (err)
+               goto out_free_txq;
+
+       err = request_irq(lp->cfg.tx_irq, ldc_tx, 0,
+                         lp->tx_irq_name, lp);
+       if (err) {
+               free_irq(lp->cfg.rx_irq, lp);
+               goto out_free_txq;
+       }
+
        return lp;
 
 out_free_txq:
@@ -1237,31 +1255,14 @@ EXPORT_SYMBOL(ldc_free);
  * state.  This does not initiate a handshake, ldc_connect() does
  * that.
  */
-int ldc_bind(struct ldc_channel *lp, const char *name)
+int ldc_bind(struct ldc_channel *lp)
 {
        unsigned long hv_err, flags;
        int err = -EINVAL;
 
-       if (!name ||
-           (lp->state != LDC_STATE_INIT))
+       if (lp->state != LDC_STATE_INIT)
                return -EINVAL;
 
-       snprintf(lp->rx_irq_name, LDC_IRQ_NAME_MAX, "%s RX", name);
-       snprintf(lp->tx_irq_name, LDC_IRQ_NAME_MAX, "%s TX", name);
-
-       err = request_irq(lp->cfg.rx_irq, ldc_rx, 0,
-                         lp->rx_irq_name, lp);
-       if (err)
-               return err;
-
-       err = request_irq(lp->cfg.tx_irq, ldc_tx, 0,
-                         lp->tx_irq_name, lp);
-       if (err) {
-               free_irq(lp->cfg.rx_irq, lp);
-               return err;
-       }
-
-
        spin_lock_irqsave(&lp->lock, flags);
 
        enable_irq(lp->cfg.rx_irq);
index 683c4af999de5214d31dcfd1ff9438c32d0a5d59..9bbb8f2bbfcce1d0925be62ce3af8e525cfc5082 100644 (file)
@@ -37,6 +37,7 @@ unsigned long amba_system_id;
 static DEFINE_SPINLOCK(leon_irq_lock);
 
 static unsigned long leon3_gptimer_idx; /* Timer Index (0..6) within Timer Core */
+static unsigned long leon3_gptimer_ackmask; /* For clearing pending bit */
 unsigned long leon3_gptimer_irq; /* interrupt controller irq number */
 unsigned int sparc_leon_eirq;
 #define LEON_IMASK(cpu) (&leon3_irqctrl_regs->mask[cpu])
@@ -260,11 +261,19 @@ void leon_update_virq_handling(unsigned int virq,
 
 static u32 leon_cycles_offset(void)
 {
-       u32 rld, val, off;
+       u32 rld, val, ctrl, off;
+
        rld = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld);
        val = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val);
-       off = rld - val;
-       return rld - val;
+       ctrl = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl);
+       if (LEON3_GPTIMER_CTRL_ISPENDING(ctrl)) {
+               val = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val);
+               off = 2 * rld - val;
+       } else {
+               off = rld - val;
+       }
+
+       return off;
 }
 
 #ifdef CONFIG_SMP
@@ -302,6 +311,7 @@ void __init leon_init_timers(void)
        int ampopts;
        int err;
        u32 config;
+       u32 ctrl;
 
        sparc_config.get_cycles_offset = leon_cycles_offset;
        sparc_config.cs_period = 1000000 / HZ;
@@ -374,6 +384,16 @@ void __init leon_init_timers(void)
        if (!(leon3_gptimer_regs && leon3_irqctrl_regs && leon3_gptimer_irq))
                goto bad;
 
+       ctrl = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl);
+       LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
+                             ctrl | LEON3_GPTIMER_CTRL_PENDING);
+       ctrl = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl);
+
+       if ((ctrl & LEON3_GPTIMER_CTRL_PENDING) != 0)
+               leon3_gptimer_ackmask = ~LEON3_GPTIMER_CTRL_PENDING;
+       else
+               leon3_gptimer_ackmask = ~0;
+
        LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val, 0);
        LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld,
                                (((1000000 / HZ) - 1)));
@@ -452,6 +472,11 @@ bad:
 
 static void leon_clear_clock_irq(void)
 {
+       u32 ctrl;
+
+       ctrl = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl);
+       LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
+                             ctrl & leon3_gptimer_ackmask);
 }
 
 static void leon_load_profile_irq(int cpu, unsigned int limit)
index 269af58497aa8b0844ea6e26d083e1c60eb60bca..7e967c8018c8cf19ba1886b39b087d9eb0638b0c 100644 (file)
@@ -191,12 +191,41 @@ static const struct pcr_ops n4_pcr_ops = {
        .pcr_nmi_disable        = PCR_N4_PICNPT,
 };
 
+static u64 n5_pcr_read(unsigned long reg_num)
+{
+       unsigned long val;
+
+       (void) sun4v_t5_get_perfreg(reg_num, &val);
+
+       return val;
+}
+
+static void n5_pcr_write(unsigned long reg_num, u64 val)
+{
+       (void) sun4v_t5_set_perfreg(reg_num, val);
+}
+
+static const struct pcr_ops n5_pcr_ops = {
+       .read_pcr               = n5_pcr_read,
+       .write_pcr              = n5_pcr_write,
+       .read_pic               = n4_pic_read,
+       .write_pic              = n4_pic_write,
+       .nmi_picl_value         = n4_picl_value,
+       .pcr_nmi_enable         = (PCR_N4_PICNPT | PCR_N4_STRACE |
+                                  PCR_N4_UTRACE | PCR_N4_TOE |
+                                  (26 << PCR_N4_SL_SHIFT)),
+       .pcr_nmi_disable        = PCR_N4_PICNPT,
+};
+
+
 static unsigned long perf_hsvc_group;
 static unsigned long perf_hsvc_major;
 static unsigned long perf_hsvc_minor;
 
 static int __init register_perf_hsvc(void)
 {
+       unsigned long hverror;
+
        if (tlb_type == hypervisor) {
                switch (sun4v_chip_type) {
                case SUN4V_CHIP_NIAGARA1:
@@ -215,6 +244,10 @@ static int __init register_perf_hsvc(void)
                        perf_hsvc_group = HV_GRP_VT_CPU;
                        break;
 
+               case SUN4V_CHIP_NIAGARA5:
+                       perf_hsvc_group = HV_GRP_T5_CPU;
+                       break;
+
                default:
                        return -ENODEV;
                }
@@ -222,10 +255,12 @@ static int __init register_perf_hsvc(void)
 
                perf_hsvc_major = 1;
                perf_hsvc_minor = 0;
-               if (sun4v_hvapi_register(perf_hsvc_group,
-                                        perf_hsvc_major,
-                                        &perf_hsvc_minor)) {
-                       printk("perfmon: Could not register hvapi.\n");
+               hverror = sun4v_hvapi_register(perf_hsvc_group,
+                                              perf_hsvc_major,
+                                              &perf_hsvc_minor);
+               if (hverror) {
+                       pr_err("perfmon: Could not register hvapi(0x%lx).\n",
+                              hverror);
                        return -ENODEV;
                }
        }
@@ -254,6 +289,10 @@ static int __init setup_sun4v_pcr_ops(void)
                pcr_ops = &n4_pcr_ops;
                break;
 
+       case SUN4V_CHIP_NIAGARA5:
+               pcr_ops = &n5_pcr_ops;
+               break;
+
        default:
                ret = -ENODEV;
                break;
index d35c490a91cb2952e6fa047591a5760087c966fe..c9759ad3f34af8230104309eec4168bae2980708 100644 (file)
@@ -1662,7 +1662,8 @@ static bool __init supported_pmu(void)
                sparc_pmu = &niagara2_pmu;
                return true;
        }
-       if (!strcmp(sparc_pmu_type, "niagara4")) {
+       if (!strcmp(sparc_pmu_type, "niagara4") ||
+           !strcmp(sparc_pmu_type, "niagara5")) {
                sparc_pmu = &niagara4_pmu;
                return true;
        }
index 3fdb455e3318fab7626763ba6fb9b907e02203d8..e629b83775879496a50b3db46684bb1c71fab0c4 100644 (file)
@@ -141,21 +141,9 @@ static void __init boot_flags_init(char *commands)
                                process_switch(*commands++);
                        continue;
                }
-               if (!strncmp(commands, "mem=", 4)) {
-                       /*
-                        * "mem=XXX[kKmM]" overrides the PROM-reported
-                        * memory size.
-                        */
-                       cmdline_memory_size = simple_strtoul(commands + 4,
-                                                            &commands, 0);
-                       if (*commands == 'K' || *commands == 'k') {
-                               cmdline_memory_size <<= 10;
-                               commands++;
-                       } else if (*commands=='M' || *commands=='m') {
-                               cmdline_memory_size <<= 20;
-                               commands++;
-                       }
-               }
+               if (!strncmp(commands, "mem=", 4))
+                       cmdline_memory_size = memparse(commands + 4, &commands);
+
                while (*commands && *commands != ' ')
                        commands++;
        }
@@ -500,12 +488,16 @@ static void __init init_sparc64_elf_hwcap(void)
                    sun4v_chip_type == SUN4V_CHIP_NIAGARA3 ||
                    sun4v_chip_type == SUN4V_CHIP_NIAGARA4 ||
                    sun4v_chip_type == SUN4V_CHIP_NIAGARA5 ||
+                   sun4v_chip_type == SUN4V_CHIP_SPARC_M6 ||
+                   sun4v_chip_type == SUN4V_CHIP_SPARC_M7 ||
                    sun4v_chip_type == SUN4V_CHIP_SPARC64X)
                        cap |= HWCAP_SPARC_BLKINIT;
                if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
                    sun4v_chip_type == SUN4V_CHIP_NIAGARA3 ||
                    sun4v_chip_type == SUN4V_CHIP_NIAGARA4 ||
                    sun4v_chip_type == SUN4V_CHIP_NIAGARA5 ||
+                   sun4v_chip_type == SUN4V_CHIP_SPARC_M6 ||
+                   sun4v_chip_type == SUN4V_CHIP_SPARC_M7 ||
                    sun4v_chip_type == SUN4V_CHIP_SPARC64X)
                        cap |= HWCAP_SPARC_N2;
        }
@@ -533,6 +525,8 @@ static void __init init_sparc64_elf_hwcap(void)
                            sun4v_chip_type == SUN4V_CHIP_NIAGARA3 ||
                            sun4v_chip_type == SUN4V_CHIP_NIAGARA4 ||
                            sun4v_chip_type == SUN4V_CHIP_NIAGARA5 ||
+                           sun4v_chip_type == SUN4V_CHIP_SPARC_M6 ||
+                           sun4v_chip_type == SUN4V_CHIP_SPARC_M7 ||
                            sun4v_chip_type == SUN4V_CHIP_SPARC64X)
                                cap |= (AV_SPARC_VIS | AV_SPARC_VIS2 |
                                        AV_SPARC_ASI_BLK_INIT |
@@ -540,6 +534,8 @@ static void __init init_sparc64_elf_hwcap(void)
                        if (sun4v_chip_type == SUN4V_CHIP_NIAGARA3 ||
                            sun4v_chip_type == SUN4V_CHIP_NIAGARA4 ||
                            sun4v_chip_type == SUN4V_CHIP_NIAGARA5 ||
+                           sun4v_chip_type == SUN4V_CHIP_SPARC_M6 ||
+                           sun4v_chip_type == SUN4V_CHIP_SPARC_M7 ||
                            sun4v_chip_type == SUN4V_CHIP_SPARC64X)
                                cap |= (AV_SPARC_VIS3 | AV_SPARC_HPC |
                                        AV_SPARC_FMAF);
index f7ba87543e5ff092e723a14cf6df8403e343ac4c..c9300bfaee5ae37789baf4269536a60ede94a9c6 100644 (file)
@@ -1467,6 +1467,13 @@ static void __init pcpu_populate_pte(unsigned long addr)
        pud_t *pud;
        pmd_t *pmd;
 
+       if (pgd_none(*pgd)) {
+               pud_t *new;
+
+               new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
+               pgd_populate(&init_mm, pgd, new);
+       }
+
        pud = pud_offset(pgd, addr);
        if (pud_none(*pud)) {
                pmd_t *new;
index e0c09bf85610117e1632a86cfd87ef828d286709..6179e19bc9b98ea4542b59bb4953c1f9f2718330 100644 (file)
@@ -195,6 +195,11 @@ sun4v_tsb_miss_common:
         ldx    [%g2 + TRAP_PER_CPU_PGD_PADDR], %g7
 
 sun4v_itlb_error:
+       rdpr    %tl, %g1
+       cmp     %g1, 1
+       ble,pt  %icc, sun4v_bad_ra
+        or     %g0, FAULT_CODE_BAD_RA | FAULT_CODE_ITLB, %g1
+
        sethi   %hi(sun4v_err_itlb_vaddr), %g1
        stx     %g4, [%g1 + %lo(sun4v_err_itlb_vaddr)]
        sethi   %hi(sun4v_err_itlb_ctx), %g1
@@ -206,15 +211,10 @@ sun4v_itlb_error:
        sethi   %hi(sun4v_err_itlb_error), %g1
        stx     %o0, [%g1 + %lo(sun4v_err_itlb_error)]
 
+       sethi   %hi(1f), %g7
        rdpr    %tl, %g4
-       cmp     %g4, 1
-       ble,pt  %icc, 1f
-        sethi  %hi(2f), %g7
        ba,pt   %xcc, etraptl1
-        or     %g7, %lo(2f), %g7
-
-1:     ba,pt   %xcc, etrap
-2:      or     %g7, %lo(2b), %g7
+1:      or     %g7, %lo(1f), %g7
        mov     %l4, %o1
        call    sun4v_itlb_error_report
         add    %sp, PTREGS_OFF, %o0
@@ -222,6 +222,11 @@ sun4v_itlb_error:
        /* NOTREACHED */
 
 sun4v_dtlb_error:
+       rdpr    %tl, %g1
+       cmp     %g1, 1
+       ble,pt  %icc, sun4v_bad_ra
+        or     %g0, FAULT_CODE_BAD_RA | FAULT_CODE_DTLB, %g1
+
        sethi   %hi(sun4v_err_dtlb_vaddr), %g1
        stx     %g4, [%g1 + %lo(sun4v_err_dtlb_vaddr)]
        sethi   %hi(sun4v_err_dtlb_ctx), %g1
@@ -233,21 +238,23 @@ sun4v_dtlb_error:
        sethi   %hi(sun4v_err_dtlb_error), %g1
        stx     %o0, [%g1 + %lo(sun4v_err_dtlb_error)]
 
+       sethi   %hi(1f), %g7
        rdpr    %tl, %g4
-       cmp     %g4, 1
-       ble,pt  %icc, 1f
-        sethi  %hi(2f), %g7
        ba,pt   %xcc, etraptl1
-        or     %g7, %lo(2f), %g7
-
-1:     ba,pt   %xcc, etrap
-2:      or     %g7, %lo(2b), %g7
+1:      or     %g7, %lo(1f), %g7
        mov     %l4, %o1
        call    sun4v_dtlb_error_report
         add    %sp, PTREGS_OFF, %o0
 
        /* NOTREACHED */
 
+sun4v_bad_ra:
+       or      %g0, %g4, %g5
+       ba,pt   %xcc, sparc64_realfault_common
+        or     %g1, %g0, %g4
+
+       /* NOTREACHED */
+
        /* Instruction Access Exception, tl0. */
 sun4v_iacc:
        ldxa    [%g0] ASI_SCRATCHPAD, %g2
index fb6640ec8557a97281bf06dc0134d0feb0471d2c..981a769b955802573c00d184ed76581f68d88777 100644 (file)
@@ -2104,6 +2104,11 @@ void sun4v_nonresum_overflow(struct pt_regs *regs)
        atomic_inc(&sun4v_nonresum_oflow_cnt);
 }
 
+static void sun4v_tlb_error(struct pt_regs *regs)
+{
+       die_if_kernel("TLB/TSB error", regs);
+}
+
 unsigned long sun4v_err_itlb_vaddr;
 unsigned long sun4v_err_itlb_ctx;
 unsigned long sun4v_err_itlb_pte;
@@ -2111,8 +2116,7 @@ unsigned long sun4v_err_itlb_error;
 
 void sun4v_itlb_error_report(struct pt_regs *regs, int tl)
 {
-       if (tl > 1)
-               dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+       dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
 
        printk(KERN_EMERG "SUN4V-ITLB: Error at TPC[%lx], tl %d\n",
               regs->tpc, tl);
@@ -2125,7 +2129,7 @@ void sun4v_itlb_error_report(struct pt_regs *regs, int tl)
               sun4v_err_itlb_vaddr, sun4v_err_itlb_ctx,
               sun4v_err_itlb_pte, sun4v_err_itlb_error);
 
-       prom_halt();
+       sun4v_tlb_error(regs);
 }
 
 unsigned long sun4v_err_dtlb_vaddr;
@@ -2135,8 +2139,7 @@ unsigned long sun4v_err_dtlb_error;
 
 void sun4v_dtlb_error_report(struct pt_regs *regs, int tl)
 {
-       if (tl > 1)
-               dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+       dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
 
        printk(KERN_EMERG "SUN4V-DTLB: Error at TPC[%lx], tl %d\n",
               regs->tpc, tl);
@@ -2149,7 +2152,7 @@ void sun4v_dtlb_error_report(struct pt_regs *regs, int tl)
               sun4v_err_dtlb_vaddr, sun4v_err_dtlb_ctx,
               sun4v_err_dtlb_pte, sun4v_err_dtlb_error);
 
-       prom_halt();
+       sun4v_tlb_error(regs);
 }
 
 void hypervisor_tlbop_error(unsigned long err, unsigned long op)
index 8647fcc5ca6c5d5fad0f2b56f8865b8dcee876be..cb5789c9f9613ed692733d50dcf0e2c39784b1f7 100644 (file)
@@ -180,8 +180,10 @@ static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
                        vdev->tx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);
 
                irq = mdesc_get_property(hp, target, "rx-ino", NULL);
-               if (irq)
+               if (irq) {
                        vdev->rx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);
+                       vdev->rx_ino = *irq;
+               }
 
                chan_id = mdesc_get_property(hp, target, "id", NULL);
                if (chan_id)
@@ -189,6 +191,15 @@ static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
        }
 }
 
+int vio_set_intr(unsigned long dev_ino, int state)
+{
+       int err;
+
+       err = sun4v_vintr_set_valid(cdev_cfg_handle, dev_ino, state);
+       return err;
+}
+EXPORT_SYMBOL(vio_set_intr);
+
 static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
                                      struct device *parent)
 {
index 7ef081a185b124cfd24757e9c7f206191efccfaa..526fcb5d8ce95d54c7afa7f5ea7c9c3a652dce3a 100644 (file)
@@ -724,7 +724,7 @@ int vio_ldc_alloc(struct vio_driver_state *vio,
        cfg.tx_irq = vio->vdev->tx_irq;
        cfg.rx_irq = vio->vdev->rx_irq;
 
-       lp = ldc_alloc(vio->vdev->channel_id, &cfg, event_arg);
+       lp = ldc_alloc(vio->vdev->channel_id, &cfg, event_arg, vio->name);
        if (IS_ERR(lp))
                return PTR_ERR(lp);
 
@@ -756,7 +756,7 @@ void vio_port_up(struct vio_driver_state *vio)
 
        err = 0;
        if (state == LDC_STATE_INIT) {
-               err = ldc_bind(vio->lp, vio->name);
+               err = ldc_bind(vio->lp);
                if (err)
                        printk(KERN_WARNING "%s: Port %lu bind failed, "
                               "err=%d\n",
index 932ff90fd7602b3f44aeac291f3e56936a197af2..09243057cb0b48f7fd1679129db63eb4094a8be6 100644 (file)
@@ -35,8 +35,9 @@ jiffies = jiffies_64;
 
 SECTIONS
 {
-       /* swapper_low_pmd_dir is sparc64 only */
-       swapper_low_pmd_dir = 0x0000000000402000;
+#ifdef CONFIG_SPARC64
+       swapper_pg_dir = 0x0000000000402000;
+#endif
        . = INITIAL_ADDRESS;
        .text TEXTSTART :
        {
@@ -122,11 +123,6 @@ SECTIONS
                *(.swapper_4m_tsb_phys_patch)
                __swapper_4m_tsb_phys_patch_end = .;
        }
-       .page_offset_shift_patch : {
-               __page_offset_shift_patch = .;
-               *(.page_offset_shift_patch)
-               __page_offset_shift_patch_end = .;
-       }
        .popc_3insn_patch : {
                __popc_3insn_patch = .;
                *(.popc_3insn_patch)
index 99c017be8719609bbc6e99ada83610e2d38763a1..f75e6906df146aae9a99cc83c6750903f9853783 100644 (file)
@@ -3,8 +3,9 @@
  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  *
- * Returns 0, if ok, and number of bytes not yet set if exception
- * occurs and we were called as clear_user.
+ * Calls to memset returns initial %o0. Calls to bzero returns 0, if ok, and
+ * number of bytes not yet set if exception occurs and we were called as
+ * clear_user.
  */
 
 #include <asm/ptrace.h>
@@ -65,6 +66,8 @@ __bzero_begin:
        .globl  __memset_start, __memset_end
 __memset_start:
 memset:
+       mov     %o0, %g1
+       mov     1, %g4
        and     %o1, 0xff, %g3
        sll     %g3, 8, %g2
        or      %g3, %g2, %g3
@@ -89,6 +92,7 @@ memset:
         sub    %o0, %o2, %o0
 
 __bzero:
+       clr     %g4
        mov     %g0, %g3
 1:
        cmp     %o1, 7
@@ -151,8 +155,8 @@ __bzero:
        bne,a   8f
         EX(stb %g3, [%o0], and %o1, 1)
 8:
-       retl
-        clr    %o0
+       b       0f
+        nop
 7:
        be      13b
         orcc   %o1, 0, %g0
@@ -164,6 +168,12 @@ __bzero:
        bne     8b
         EX(stb %g3, [%o0 - 1], add %o1, 1)
 0:
+       andcc   %g4, 1, %g0
+       be      5f
+        nop
+       retl
+        mov    %g1, %o0
+5:
        retl
         clr    %o0
 __memset_end:
index 587cd056512850f4b47f59fed47355fe07b41375..18fcd71670959291f8ef4933e37d5bc394e98f51 100644 (file)
@@ -346,6 +346,9 @@ retry:
                down_read(&mm->mmap_sem);
        }
 
+       if (fault_code & FAULT_CODE_BAD_RA)
+               goto do_sigbus;
+
        vma = find_vma(mm, address);
        if (!vma)
                goto bad_area;
index 98ac8e80adae07a841ade82c0ec3d5896f3aa3dd..2d91c62f7f5f156524b7bef4ff7737701c08db85 100644 (file)
@@ -75,7 +75,6 @@ unsigned long kern_linear_pte_xor[4] __read_mostly;
  * 'cpu' properties, but we need to have this table setup before the
  * MDESC is initialized.
  */
-unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
 
 #ifndef CONFIG_DEBUG_PAGEALLOC
 /* A special kernel TSB for 4MB, 256MB, 2GB and 16GB linear mappings.
@@ -84,10 +83,11 @@ unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
  */
 extern struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];
 #endif
+extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
 
 static unsigned long cpu_pgsz_mask;
 
-#define MAX_BANKS      32
+#define MAX_BANKS      1024
 
 static struct linux_prom64_registers pavail[MAX_BANKS];
 static int pavail_ents;
@@ -165,10 +165,6 @@ static void __init read_obp_memory(const char *property,
             cmp_p64, NULL);
 }
 
-unsigned long sparc64_valid_addr_bitmap[VALID_ADDR_BITMAP_BYTES /
-                                       sizeof(unsigned long)];
-EXPORT_SYMBOL(sparc64_valid_addr_bitmap);
-
 /* Kernel physical address base and size in bytes.  */
 unsigned long kern_base __read_mostly;
 unsigned long kern_size __read_mostly;
@@ -840,7 +836,10 @@ static int find_node(unsigned long addr)
                if ((addr & p->mask) == p->val)
                        return i;
        }
-       return -1;
+       /* The following condition has been observed on LDOM guests.*/
+       WARN_ONCE(1, "find_node: A physical address doesn't match a NUMA node"
+               " rule. Some physical memory will be owned by node 0.");
+       return 0;
 }
 
 static u64 memblock_nid_range(u64 start, u64 end, int *nid)
@@ -1366,9 +1365,144 @@ static unsigned long __init bootmem_init(unsigned long phys_base)
 static struct linux_prom64_registers pall[MAX_BANKS] __initdata;
 static int pall_ents __initdata;
 
-#ifdef CONFIG_DEBUG_PAGEALLOC
+static unsigned long max_phys_bits = 40;
+
+bool kern_addr_valid(unsigned long addr)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       pte_t *pte;
+
+       if ((long)addr < 0L) {
+               unsigned long pa = __pa(addr);
+
+               if ((addr >> max_phys_bits) != 0UL)
+                       return false;
+
+               return pfn_valid(pa >> PAGE_SHIFT);
+       }
+
+       if (addr >= (unsigned long) KERNBASE &&
+           addr < (unsigned long)&_end)
+               return true;
+
+       pgd = pgd_offset_k(addr);
+       if (pgd_none(*pgd))
+               return 0;
+
+       pud = pud_offset(pgd, addr);
+       if (pud_none(*pud))
+               return 0;
+
+       if (pud_large(*pud))
+               return pfn_valid(pud_pfn(*pud));
+
+       pmd = pmd_offset(pud, addr);
+       if (pmd_none(*pmd))
+               return 0;
+
+       if (pmd_large(*pmd))
+               return pfn_valid(pmd_pfn(*pmd));
+
+       pte = pte_offset_kernel(pmd, addr);
+       if (pte_none(*pte))
+               return 0;
+
+       return pfn_valid(pte_pfn(*pte));
+}
+EXPORT_SYMBOL(kern_addr_valid);
+
+static unsigned long __ref kernel_map_hugepud(unsigned long vstart,
+                                             unsigned long vend,
+                                             pud_t *pud)
+{
+       const unsigned long mask16gb = (1UL << 34) - 1UL;
+       u64 pte_val = vstart;
+
+       /* Each PUD is 8GB */
+       if ((vstart & mask16gb) ||
+           (vend - vstart <= mask16gb)) {
+               pte_val ^= kern_linear_pte_xor[2];
+               pud_val(*pud) = pte_val | _PAGE_PUD_HUGE;
+
+               return vstart + PUD_SIZE;
+       }
+
+       pte_val ^= kern_linear_pte_xor[3];
+       pte_val |= _PAGE_PUD_HUGE;
+
+       vend = vstart + mask16gb + 1UL;
+       while (vstart < vend) {
+               pud_val(*pud) = pte_val;
+
+               pte_val += PUD_SIZE;
+               vstart += PUD_SIZE;
+               pud++;
+       }
+       return vstart;
+}
+
+static bool kernel_can_map_hugepud(unsigned long vstart, unsigned long vend,
+                                  bool guard)
+{
+       if (guard && !(vstart & ~PUD_MASK) && (vend - vstart) >= PUD_SIZE)
+               return true;
+
+       return false;
+}
+
+static unsigned long __ref kernel_map_hugepmd(unsigned long vstart,
+                                             unsigned long vend,
+                                             pmd_t *pmd)
+{
+       const unsigned long mask256mb = (1UL << 28) - 1UL;
+       const unsigned long mask2gb = (1UL << 31) - 1UL;
+       u64 pte_val = vstart;
+
+       /* Each PMD is 8MB */
+       if ((vstart & mask256mb) ||
+           (vend - vstart <= mask256mb)) {
+               pte_val ^= kern_linear_pte_xor[0];
+               pmd_val(*pmd) = pte_val | _PAGE_PMD_HUGE;
+
+               return vstart + PMD_SIZE;
+       }
+
+       if ((vstart & mask2gb) ||
+           (vend - vstart <= mask2gb)) {
+               pte_val ^= kern_linear_pte_xor[1];
+               pte_val |= _PAGE_PMD_HUGE;
+               vend = vstart + mask256mb + 1UL;
+       } else {
+               pte_val ^= kern_linear_pte_xor[2];
+               pte_val |= _PAGE_PMD_HUGE;
+               vend = vstart + mask2gb + 1UL;
+       }
+
+       while (vstart < vend) {
+               pmd_val(*pmd) = pte_val;
+
+               pte_val += PMD_SIZE;
+               vstart += PMD_SIZE;
+               pmd++;
+       }
+
+       return vstart;
+}
+
+static bool kernel_can_map_hugepmd(unsigned long vstart, unsigned long vend,
+                                  bool guard)
+{
+       if (guard && !(vstart & ~PMD_MASK) && (vend - vstart) >= PMD_SIZE)
+               return true;
+
+       return false;
+}
+
 static unsigned long __ref kernel_map_range(unsigned long pstart,
-                                           unsigned long pend, pgprot_t prot)
+                                           unsigned long pend, pgprot_t prot,
+                                           bool use_huge)
 {
        unsigned long vstart = PAGE_OFFSET + pstart;
        unsigned long vend = PAGE_OFFSET + pend;
@@ -1387,19 +1521,34 @@ static unsigned long __ref kernel_map_range(unsigned long pstart,
                pmd_t *pmd;
                pte_t *pte;
 
+               if (pgd_none(*pgd)) {
+                       pud_t *new;
+
+                       new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
+                       alloc_bytes += PAGE_SIZE;
+                       pgd_populate(&init_mm, pgd, new);
+               }
                pud = pud_offset(pgd, vstart);
                if (pud_none(*pud)) {
                        pmd_t *new;
 
+                       if (kernel_can_map_hugepud(vstart, vend, use_huge)) {
+                               vstart = kernel_map_hugepud(vstart, vend, pud);
+                               continue;
+                       }
                        new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
                        alloc_bytes += PAGE_SIZE;
                        pud_populate(&init_mm, pud, new);
                }
 
                pmd = pmd_offset(pud, vstart);
-               if (!pmd_present(*pmd)) {
+               if (pmd_none(*pmd)) {
                        pte_t *new;
 
+                       if (kernel_can_map_hugepmd(vstart, vend, use_huge)) {
+                               vstart = kernel_map_hugepmd(vstart, vend, pmd);
+                               continue;
+                       }
                        new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
                        alloc_bytes += PAGE_SIZE;
                        pmd_populate_kernel(&init_mm, pmd, new);
@@ -1422,100 +1571,34 @@ static unsigned long __ref kernel_map_range(unsigned long pstart,
        return alloc_bytes;
 }
 
-extern unsigned int kvmap_linear_patch[1];
-#endif /* CONFIG_DEBUG_PAGEALLOC */
-
-static void __init kpte_set_val(unsigned long index, unsigned long val)
-{
-       unsigned long *ptr = kpte_linear_bitmap;
-
-       val <<= ((index % (BITS_PER_LONG / 2)) * 2);
-       ptr += (index / (BITS_PER_LONG / 2));
-
-       *ptr |= val;
-}
-
-static const unsigned long kpte_shift_min = 28; /* 256MB */
-static const unsigned long kpte_shift_max = 34; /* 16GB */
-static const unsigned long kpte_shift_incr = 3;
-
-static unsigned long kpte_mark_using_shift(unsigned long start, unsigned long end,
-                                          unsigned long shift)
+static void __init flush_all_kernel_tsbs(void)
 {
-       unsigned long size = (1UL << shift);
-       unsigned long mask = (size - 1UL);
-       unsigned long remains = end - start;
-       unsigned long val;
-
-       if (remains < size || (start & mask))
-               return start;
-
-       /* VAL maps:
-        *
-        *      shift 28 --> kern_linear_pte_xor index 1
-        *      shift 31 --> kern_linear_pte_xor index 2
-        *      shift 34 --> kern_linear_pte_xor index 3
-        */
-       val = ((shift - kpte_shift_min) / kpte_shift_incr) + 1;
-
-       remains &= ~mask;
-       if (shift != kpte_shift_max)
-               remains = size;
-
-       while (remains) {
-               unsigned long index = start >> kpte_shift_min;
+       int i;
 
-               kpte_set_val(index, val);
+       for (i = 0; i < KERNEL_TSB_NENTRIES; i++) {
+               struct tsb *ent = &swapper_tsb[i];
 
-               start += 1UL << kpte_shift_min;
-               remains -= 1UL << kpte_shift_min;
+               ent->tag = (1UL << TSB_TAG_INVALID_BIT);
        }
+#ifndef CONFIG_DEBUG_PAGEALLOC
+       for (i = 0; i < KERNEL_TSB4M_NENTRIES; i++) {
+               struct tsb *ent = &swapper_4m_tsb[i];
 
-       return start;
-}
-
-static void __init mark_kpte_bitmap(unsigned long start, unsigned long end)
-{
-       unsigned long smallest_size, smallest_mask;
-       unsigned long s;
-
-       smallest_size = (1UL << kpte_shift_min);
-       smallest_mask = (smallest_size - 1UL);
-
-       while (start < end) {
-               unsigned long orig_start = start;
-
-               for (s = kpte_shift_max; s >= kpte_shift_min; s -= kpte_shift_incr) {
-                       start = kpte_mark_using_shift(start, end, s);
-
-                       if (start != orig_start)
-                               break;
-               }
-
-               if (start == orig_start)
-                       start = (start + smallest_size) & ~smallest_mask;
+               ent->tag = (1UL << TSB_TAG_INVALID_BIT);
        }
+#endif
 }
 
-static void __init init_kpte_bitmap(void)
-{
-       unsigned long i;
-
-       for (i = 0; i < pall_ents; i++) {
-               unsigned long phys_start, phys_end;
-
-               phys_start = pall[i].phys_addr;
-               phys_end = phys_start + pall[i].reg_size;
-
-               mark_kpte_bitmap(phys_start, phys_end);
-       }
-}
+extern unsigned int kvmap_linear_patch[1];
 
 static void __init kernel_physical_mapping_init(void)
 {
-#ifdef CONFIG_DEBUG_PAGEALLOC
        unsigned long i, mem_alloced = 0UL;
+       bool use_huge = true;
 
+#ifdef CONFIG_DEBUG_PAGEALLOC
+       use_huge = false;
+#endif
        for (i = 0; i < pall_ents; i++) {
                unsigned long phys_start, phys_end;
 
@@ -1523,7 +1606,7 @@ static void __init kernel_physical_mapping_init(void)
                phys_end = phys_start + pall[i].reg_size;
 
                mem_alloced += kernel_map_range(phys_start, phys_end,
-                                               PAGE_KERNEL);
+                                               PAGE_KERNEL, use_huge);
        }
 
        printk("Allocated %ld bytes for kernel page tables.\n",
@@ -1532,8 +1615,9 @@ static void __init kernel_physical_mapping_init(void)
        kvmap_linear_patch[0] = 0x01000000; /* nop */
        flushi(&kvmap_linear_patch[0]);
 
+       flush_all_kernel_tsbs();
+
        __flush_tlb_all();
-#endif
 }
 
 #ifdef CONFIG_DEBUG_PAGEALLOC
@@ -1543,7 +1627,7 @@ void kernel_map_pages(struct page *page, int numpages, int enable)
        unsigned long phys_end = phys_start + (numpages * PAGE_SIZE);
 
        kernel_map_range(phys_start, phys_end,
-                        (enable ? PAGE_KERNEL : __pgprot(0)));
+                        (enable ? PAGE_KERNEL : __pgprot(0)), false);
 
        flush_tsb_kernel_range(PAGE_OFFSET + phys_start,
                               PAGE_OFFSET + phys_end);
@@ -1571,76 +1655,56 @@ unsigned long __init find_ecache_flush_span(unsigned long size)
 unsigned long PAGE_OFFSET;
 EXPORT_SYMBOL(PAGE_OFFSET);
 
-static void __init page_offset_shift_patch_one(unsigned int *insn, unsigned long phys_bits)
-{
-       unsigned long final_shift;
-       unsigned int val = *insn;
-       unsigned int cnt;
-
-       /* We are patching in ilog2(max_supported_phys_address), and
-        * we are doing so in a manner similar to a relocation addend.
-        * That is, we are adding the shift value to whatever value
-        * is in the shift instruction count field already.
-        */
-       cnt = (val & 0x3f);
-       val &= ~0x3f;
-
-       /* If we are trying to shift >= 64 bits, clear the destination
-        * register.  This can happen when phys_bits ends up being equal
-        * to MAX_PHYS_ADDRESS_BITS.
-        */
-       final_shift = (cnt + (64 - phys_bits));
-       if (final_shift >= 64) {
-               unsigned int rd = (val >> 25) & 0x1f;
-
-               val = 0x80100000 | (rd << 25);
-       } else {
-               val |= final_shift;
-       }
-       *insn = val;
-
-       __asm__ __volatile__("flush     %0"
-                            : /* no outputs */
-                            : "r" (insn));
-}
-
-static void __init page_offset_shift_patch(unsigned long phys_bits)
-{
-       extern unsigned int __page_offset_shift_patch;
-       extern unsigned int __page_offset_shift_patch_end;
-       unsigned int *p;
-
-       p = &__page_offset_shift_patch;
-       while (p < &__page_offset_shift_patch_end) {
-               unsigned int *insn = (unsigned int *)(unsigned long)*p;
+unsigned long VMALLOC_END   = 0x0000010000000000UL;
+EXPORT_SYMBOL(VMALLOC_END);
 
-               page_offset_shift_patch_one(insn, phys_bits);
-
-               p++;
-       }
-}
+unsigned long sparc64_va_hole_top =    0xfffff80000000000UL;
+unsigned long sparc64_va_hole_bottom = 0x0000080000000000UL;
 
 static void __init setup_page_offset(void)
 {
-       unsigned long max_phys_bits = 40;
-
        if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+               /* Cheetah/Panther support a full 64-bit virtual
+                * address, so we can use all that our page tables
+                * support.
+                */
+               sparc64_va_hole_top =    0xfff0000000000000UL;
+               sparc64_va_hole_bottom = 0x0010000000000000UL;
+
                max_phys_bits = 42;
        } else if (tlb_type == hypervisor) {
                switch (sun4v_chip_type) {
                case SUN4V_CHIP_NIAGARA1:
                case SUN4V_CHIP_NIAGARA2:
+                       /* T1 and T2 support 48-bit virtual addresses.  */
+                       sparc64_va_hole_top =    0xffff800000000000UL;
+                       sparc64_va_hole_bottom = 0x0000800000000000UL;
+
                        max_phys_bits = 39;
                        break;
                case SUN4V_CHIP_NIAGARA3:
+                       /* T3 supports 48-bit virtual addresses.  */
+                       sparc64_va_hole_top =    0xffff800000000000UL;
+                       sparc64_va_hole_bottom = 0x0000800000000000UL;
+
                        max_phys_bits = 43;
                        break;
                case SUN4V_CHIP_NIAGARA4:
                case SUN4V_CHIP_NIAGARA5:
                case SUN4V_CHIP_SPARC64X:
-               default:
+               case SUN4V_CHIP_SPARC_M6:
+                       /* T4 and later support 52-bit virtual addresses.  */
+                       sparc64_va_hole_top =    0xfff8000000000000UL;
+                       sparc64_va_hole_bottom = 0x0008000000000000UL;
                        max_phys_bits = 47;
                        break;
+               case SUN4V_CHIP_SPARC_M7:
+               default:
+                       /* M7 and later support 52-bit virtual addresses.  */
+                       sparc64_va_hole_top =    0xfff8000000000000UL;
+                       sparc64_va_hole_bottom = 0x0008000000000000UL;
+                       max_phys_bits = 49;
+                       break;
                }
        }
 
@@ -1650,12 +1714,16 @@ static void __init setup_page_offset(void)
                prom_halt();
        }
 
-       PAGE_OFFSET = PAGE_OFFSET_BY_BITS(max_phys_bits);
+       PAGE_OFFSET = sparc64_va_hole_top;
+       VMALLOC_END = ((sparc64_va_hole_bottom >> 1) +
+                      (sparc64_va_hole_bottom >> 2));
 
-       pr_info("PAGE_OFFSET is 0x%016lx (max_phys_bits == %lu)\n",
+       pr_info("MM: PAGE_OFFSET is 0x%016lx (max_phys_bits == %lu)\n",
                PAGE_OFFSET, max_phys_bits);
-
-       page_offset_shift_patch(max_phys_bits);
+       pr_info("MM: VMALLOC [0x%016lx --> 0x%016lx]\n",
+               VMALLOC_START, VMALLOC_END);
+       pr_info("MM: VMEMMAP [0x%016lx --> 0x%016lx]\n",
+               VMEMMAP_BASE, VMEMMAP_BASE << 1);
 }
 
 static void __init tsb_phys_patch(void)
@@ -1700,21 +1768,42 @@ static void __init tsb_phys_patch(void)
 #define NUM_KTSB_DESCR 1
 #endif
 static struct hv_tsb_descr ktsb_descr[NUM_KTSB_DESCR];
-extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
+
+/* The swapper TSBs are loaded with a base sequence of:
+ *
+ *     sethi   %uhi(SYMBOL), REG1
+ *     sethi   %hi(SYMBOL), REG2
+ *     or      REG1, %ulo(SYMBOL), REG1
+ *     or      REG2, %lo(SYMBOL), REG2
+ *     sllx    REG1, 32, REG1
+ *     or      REG1, REG2, REG1
+ *
+ * When we use physical addressing for the TSB accesses, we patch the
+ * first four instructions in the above sequence.
+ */
 
 static void patch_one_ktsb_phys(unsigned int *start, unsigned int *end, unsigned long pa)
 {
-       pa >>= KTSB_PHYS_SHIFT;
+       unsigned long high_bits, low_bits;
+
+       high_bits = (pa >> 32) & 0xffffffff;
+       low_bits = (pa >> 0) & 0xffffffff;
 
        while (start < end) {
                unsigned int *ia = (unsigned int *)(unsigned long)*start;
 
-               ia[0] = (ia[0] & ~0x3fffff) | (pa >> 10);
+               ia[0] = (ia[0] & ~0x3fffff) | (high_bits >> 10);
                __asm__ __volatile__("flush     %0" : : "r" (ia));
 
-               ia[1] = (ia[1] & ~0x3ff) | (pa & 0x3ff);
+               ia[1] = (ia[1] & ~0x3fffff) | (low_bits >> 10);
                __asm__ __volatile__("flush     %0" : : "r" (ia + 1));
 
+               ia[2] = (ia[2] & ~0x1fff) | (high_bits & 0x3ff);
+               __asm__ __volatile__("flush     %0" : : "r" (ia + 2));
+
+               ia[3] = (ia[3] & ~0x1fff) | (low_bits & 0x3ff);
+               __asm__ __volatile__("flush     %0" : : "r" (ia + 3));
+
                start++;
        }
 }
@@ -1853,11 +1942,56 @@ static void __init sun4v_linear_pte_xor_finalize(void)
 /* paging_init() sets up the page tables */
 
 static unsigned long last_valid_pfn;
-pgd_t swapper_pg_dir[PTRS_PER_PGD];
 
 static void sun4u_pgprot_init(void);
 static void sun4v_pgprot_init(void);
 
+static phys_addr_t __init available_memory(void)
+{
+       phys_addr_t available = 0ULL;
+       phys_addr_t pa_start, pa_end;
+       u64 i;
+
+       for_each_free_mem_range(i, NUMA_NO_NODE, &pa_start, &pa_end, NULL)
+               available = available + (pa_end  - pa_start);
+
+       return available;
+}
+
+/* We need to exclude reserved regions. This exclusion will include
+ * vmlinux and initrd. To be more precise the initrd size could be used to
+ * compute a new lower limit because it is freed later during initialization.
+ */
+static void __init reduce_memory(phys_addr_t limit_ram)
+{
+       phys_addr_t avail_ram = available_memory();
+       phys_addr_t pa_start, pa_end;
+       u64 i;
+
+       if (limit_ram >= avail_ram)
+               return;
+
+       for_each_free_mem_range(i, NUMA_NO_NODE, &pa_start, &pa_end, NULL) {
+               phys_addr_t region_size = pa_end - pa_start;
+               phys_addr_t clip_start = pa_start;
+
+               avail_ram = avail_ram - region_size;
+               /* Are we consuming too much? */
+               if (avail_ram < limit_ram) {
+                       phys_addr_t give_back = limit_ram - avail_ram;
+
+                       region_size = region_size - give_back;
+                       clip_start = clip_start + give_back;
+               }
+
+               memblock_remove(clip_start, region_size);
+
+               if (avail_ram <= limit_ram)
+                       break;
+               i = 0UL;
+       }
+}
+
 void __init paging_init(void)
 {
        unsigned long end_pfn, shift, phys_base;
@@ -1937,7 +2071,8 @@ void __init paging_init(void)
 
        find_ramdisk(phys_base);
 
-       memblock_enforce_memory_limit(cmdline_memory_size);
+       if (cmdline_memory_size)
+               reduce_memory(cmdline_memory_size);
 
        memblock_allow_resize();
        memblock_dump_all();
@@ -1956,16 +2091,10 @@ void __init paging_init(void)
         */
        init_mm.pgd += ((shift) / (sizeof(pgd_t)));
        
-       memset(swapper_low_pmd_dir, 0, sizeof(swapper_low_pmd_dir));
+       memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir));
 
-       /* Now can init the kernel/bad page tables. */
-       pud_set(pud_offset(&swapper_pg_dir[0], 0),
-               swapper_low_pmd_dir + (shift / sizeof(pgd_t)));
-       
        inherit_prom_mappings();
        
-       init_kpte_bitmap();
-
        /* Ok, we can use our TLB miss and window trap handlers safely.  */
        setup_tba();
 
@@ -2072,70 +2201,6 @@ int page_in_phys_avail(unsigned long paddr)
        return 0;
 }
 
-static struct linux_prom64_registers pavail_rescan[MAX_BANKS] __initdata;
-static int pavail_rescan_ents __initdata;
-
-/* Certain OBP calls, such as fetching "available" properties, can
- * claim physical memory.  So, along with initializing the valid
- * address bitmap, what we do here is refetch the physical available
- * memory list again, and make sure it provides at least as much
- * memory as 'pavail' does.
- */
-static void __init setup_valid_addr_bitmap_from_pavail(unsigned long *bitmap)
-{
-       int i;
-
-       read_obp_memory("available", &pavail_rescan[0], &pavail_rescan_ents);
-
-       for (i = 0; i < pavail_ents; i++) {
-               unsigned long old_start, old_end;
-
-               old_start = pavail[i].phys_addr;
-               old_end = old_start + pavail[i].reg_size;
-               while (old_start < old_end) {
-                       int n;
-
-                       for (n = 0; n < pavail_rescan_ents; n++) {
-                               unsigned long new_start, new_end;
-
-                               new_start = pavail_rescan[n].phys_addr;
-                               new_end = new_start +
-                                       pavail_rescan[n].reg_size;
-
-                               if (new_start <= old_start &&
-                                   new_end >= (old_start + PAGE_SIZE)) {
-                                       set_bit(old_start >> ILOG2_4MB, bitmap);
-                                       goto do_next_page;
-                               }
-                       }
-
-                       prom_printf("mem_init: Lost memory in pavail\n");
-                       prom_printf("mem_init: OLD start[%lx] size[%lx]\n",
-                                   pavail[i].phys_addr,
-                                   pavail[i].reg_size);
-                       prom_printf("mem_init: NEW start[%lx] size[%lx]\n",
-                                   pavail_rescan[i].phys_addr,
-                                   pavail_rescan[i].reg_size);
-                       prom_printf("mem_init: Cannot continue, aborting.\n");
-                       prom_halt();
-
-               do_next_page:
-                       old_start += PAGE_SIZE;
-               }
-       }
-}
-
-static void __init patch_tlb_miss_handler_bitmap(void)
-{
-       extern unsigned int valid_addr_bitmap_insn[];
-       extern unsigned int valid_addr_bitmap_patch[];
-
-       valid_addr_bitmap_insn[1] = valid_addr_bitmap_patch[1];
-       mb();
-       valid_addr_bitmap_insn[0] = valid_addr_bitmap_patch[0];
-       flushi(&valid_addr_bitmap_insn[0]);
-}
-
 static void __init register_page_bootmem_info(void)
 {
 #ifdef CONFIG_NEED_MULTIPLE_NODES
@@ -2148,18 +2213,6 @@ static void __init register_page_bootmem_info(void)
 }
 void __init mem_init(void)
 {
-       unsigned long addr, last;
-
-       addr = PAGE_OFFSET + kern_base;
-       last = PAGE_ALIGN(kern_size) + addr;
-       while (addr < last) {
-               set_bit(__pa(addr) >> ILOG2_4MB, sparc64_valid_addr_bitmap);
-               addr += PAGE_SIZE;
-       }
-
-       setup_valid_addr_bitmap_from_pavail(sparc64_valid_addr_bitmap);
-       patch_tlb_miss_handler_bitmap();
-
        high_memory = __va(last_valid_pfn << PAGE_SHIFT);
 
        register_page_bootmem_info();
@@ -2249,18 +2302,9 @@ unsigned long _PAGE_CACHE __read_mostly;
 EXPORT_SYMBOL(_PAGE_CACHE);
 
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
-unsigned long vmemmap_table[VMEMMAP_SIZE];
-
-static long __meminitdata addr_start, addr_end;
-static int __meminitdata node_start;
-
 int __meminit vmemmap_populate(unsigned long vstart, unsigned long vend,
                               int node)
 {
-       unsigned long phys_start = (vstart - VMEMMAP_BASE);
-       unsigned long phys_end = (vend - VMEMMAP_BASE);
-       unsigned long addr = phys_start & VMEMMAP_CHUNK_MASK;
-       unsigned long end = VMEMMAP_ALIGN(phys_end);
        unsigned long pte_base;
 
        pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4U |
@@ -2271,47 +2315,52 @@ int __meminit vmemmap_populate(unsigned long vstart, unsigned long vend,
                            _PAGE_CP_4V | _PAGE_CV_4V |
                            _PAGE_P_4V | _PAGE_W_4V);
 
-       for (; addr < end; addr += VMEMMAP_CHUNK) {
-               unsigned long *vmem_pp =
-                       vmemmap_table + (addr >> VMEMMAP_CHUNK_SHIFT);
-               void *block;
+       pte_base |= _PAGE_PMD_HUGE;
 
-               if (!(*vmem_pp & _PAGE_VALID)) {
-                       block = vmemmap_alloc_block(1UL << ILOG2_4MB, node);
-                       if (!block)
+       vstart = vstart & PMD_MASK;
+       vend = ALIGN(vend, PMD_SIZE);
+       for (; vstart < vend; vstart += PMD_SIZE) {
+               pgd_t *pgd = pgd_offset_k(vstart);
+               unsigned long pte;
+               pud_t *pud;
+               pmd_t *pmd;
+
+               if (pgd_none(*pgd)) {
+                       pud_t *new = vmemmap_alloc_block(PAGE_SIZE, node);
+
+                       if (!new)
                                return -ENOMEM;
+                       pgd_populate(&init_mm, pgd, new);
+               }
 
-                       *vmem_pp = pte_base | __pa(block);
+               pud = pud_offset(pgd, vstart);
+               if (pud_none(*pud)) {
+                       pmd_t *new = vmemmap_alloc_block(PAGE_SIZE, node);
 
-                       /* check to see if we have contiguous blocks */
-                       if (addr_end != addr || node_start != node) {
-                               if (addr_start)
-                                       printk(KERN_DEBUG " [%lx-%lx] on node %d\n",
-                                              addr_start, addr_end-1, node_start);
-                               addr_start = addr;
-                               node_start = node;
-                       }
-                       addr_end = addr + VMEMMAP_CHUNK;
+                       if (!new)
+                               return -ENOMEM;
+                       pud_populate(&init_mm, pud, new);
                }
-       }
-       return 0;
-}
 
-void __meminit vmemmap_populate_print_last(void)
-{
-       if (addr_start) {
-               printk(KERN_DEBUG " [%lx-%lx] on node %d\n",
-                      addr_start, addr_end-1, node_start);
-               addr_start = 0;
-               addr_end = 0;
-               node_start = 0;
+               pmd = pmd_offset(pud, vstart);
+
+               pte = pmd_val(*pmd);
+               if (!(pte & _PAGE_VALID)) {
+                       void *block = vmemmap_alloc_block(PMD_SIZE, node);
+
+                       if (!block)
+                               return -ENOMEM;
+
+                       pmd_val(*pmd) = pte_base | __pa(block);
+               }
        }
+
+       return 0;
 }
 
 void vmemmap_free(unsigned long start, unsigned long end)
 {
 }
-
 #endif /* CONFIG_SPARSEMEM_VMEMMAP */
 
 static void prot_init_common(unsigned long page_none,
@@ -2787,8 +2836,8 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
                        do_flush_tlb_kernel_range(start, LOW_OBP_ADDRESS);
                }
                if (end > HI_OBP_ADDRESS) {
-                       flush_tsb_kernel_range(end, HI_OBP_ADDRESS);
-                       do_flush_tlb_kernel_range(end, HI_OBP_ADDRESS);
+                       flush_tsb_kernel_range(HI_OBP_ADDRESS, end);
+                       do_flush_tlb_kernel_range(HI_OBP_ADDRESS, end);
                }
        } else {
                flush_tsb_kernel_range(start, end);
index 0668b364f44ddb93ccac1849677529b206d5500f..a4c09603b05c09d8f1478d68004398e4ef4dbc1d 100644 (file)
@@ -8,15 +8,8 @@
  */
 
 #define MAX_PHYS_ADDRESS       (1UL << MAX_PHYS_ADDRESS_BITS)
-#define KPTE_BITMAP_CHUNK_SZ           (256UL * 1024UL * 1024UL)
-#define KPTE_BITMAP_BYTES      \
-       ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 4)
-#define VALID_ADDR_BITMAP_CHUNK_SZ     (4UL * 1024UL * 1024UL)
-#define VALID_ADDR_BITMAP_BYTES        \
-       ((MAX_PHYS_ADDRESS / VALID_ADDR_BITMAP_CHUNK_SZ) / 8)
 
 extern unsigned long kern_linear_pte_xor[4];
-extern unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
 extern unsigned int sparc64_highest_unlocked_tlb_ent;
 extern unsigned long sparc64_kern_pri_context;
 extern unsigned long sparc64_kern_pri_nuc_bits;
@@ -38,15 +31,4 @@ extern unsigned long kern_locked_tte_data;
 
 void prom_world(int enter);
 
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-#define VMEMMAP_CHUNK_SHIFT    22
-#define VMEMMAP_CHUNK          (1UL << VMEMMAP_CHUNK_SHIFT)
-#define VMEMMAP_CHUNK_MASK     ~(VMEMMAP_CHUNK - 1UL)
-#define VMEMMAP_ALIGN(x)       (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK)
-
-#define VMEMMAP_SIZE   ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \
-                         sizeof(struct page)) >> VMEMMAP_CHUNK_SHIFT)
-extern unsigned long vmemmap_table[VMEMMAP_SIZE];
-#endif
-
 #endif /* _SPARC64_MM_INIT_H */
index 42b0b8ce699a94b295e9ec311de1141531275a14..17bd2e167e07edd934dfe9957c43712b21401c55 100644 (file)
@@ -9,11 +9,9 @@
 #include <asm/hibernate.h>
 #include <asm/visasm.h>
 #include <asm/page.h>
+#include <asm/sections.h>
 #include <asm/tlb.h>
 
-/* References to section boundaries */
-extern const void __nosave_begin, __nosave_end;
-
 struct saved_context saved_context;
 
 /*
index 79942166df841eeef137d25f9c8330ff7b27e7ac..d7d9017dcb15b23b8c98b4de8e14f1cc62749e34 100644 (file)
@@ -54,8 +54,8 @@ ENTRY(swsusp_arch_resume)
         nop
 
        /* Write PAGE_OFFSET to %g7 */
-       sethi   %uhi(PAGE_OFFSET), %g7
-       sllx    %g7, 32, %g7
+       sethi   %hi(PAGE_OFFSET), %g7
+       ldx     [%g7 + %lo(PAGE_OFFSET)], %g7
 
        setuw   (PAGE_SIZE-8), %g3
 
index ab9ccc63b3880f5de18eb427869574b872a1ceb2..7149e77714a4a45b3ffb96e1388adca25577cca6 100644 (file)
  *          the .bss section or it will break things.
  */
 
-#define BARG_LEN  256
+/* We limit BARG_LEN to 1024 because this is the size of the
+ * 'barg_out' command line buffer in the SILO bootloader.
+ */
+#define BARG_LEN 1024
 struct {
        int bootstr_len;
        int bootstr_valid;
index e58b817263199f9d45d28aed5f735b38eb94904b..b2340f008ae06a27614f2fc1c78e3b45b36946b0 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/smp.h>
 #include <linux/string.h>
 #include <linux/spinlock.h>
+#include <linux/irqflags.h>
 
 #include <asm/openprom.h>
 #include <asm/oplib.h>
@@ -36,8 +37,8 @@ void p1275_cmd_direct(unsigned long *args)
 {
        unsigned long flags;
 
-       raw_local_save_flags(flags);
-       raw_local_irq_restore((unsigned long)PIL_NMI);
+       local_save_flags(flags);
+       local_irq_restore((unsigned long)PIL_NMI);
        raw_spin_lock(&prom_entry_lock);
 
        prom_world(1);
@@ -45,7 +46,7 @@ void p1275_cmd_direct(unsigned long *args)
        prom_world(0);
 
        raw_spin_unlock(&prom_entry_lock);
-       raw_local_irq_restore(flags);
+       local_irq_restore(flags);
 }
 
 void prom_cif_init(void *cif_handler, void *cif_stack)
index 0aa5675e7025e1f791da57ed9bc52133dd4121da..e6462b8a62842534f8e8ffe9eb628e586aaf8ec3 100644 (file)
@@ -17,6 +17,7 @@ generic-y += ioctl.h
 generic-y += ioctls.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += local.h
 generic-y += local64.h
 generic-y += mcs_spinlock.h
index 7bd64aa2e94a40599211460fc615fe0dd12c7680..244b12c8cb391b9e7fec48c2846a818a9e4db93e 100644 (file)
@@ -14,6 +14,7 @@ generic-y += hash.h
 generic-y += hw_irq.h
 generic-y += io.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += mcs_spinlock.h
 generic-y += mutex.h
index 1e5fb872a4aa60d3292705b8bc34a69a9038d85a..5a2bb53faa42137607feafad23e3896e9249d078 100644 (file)
@@ -22,6 +22,7 @@ generic-y += ioctl.h
 generic-y += ioctls.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += kmap_types.h
 generic-y += local.h
index 4dcd34ae194cdc54b772a246706ffa5d7460b3f0..77b522694e744b07c4d49b807ebd4c3551ab0bdd 100644 (file)
@@ -36,8 +36,5 @@ extern int puv3_pm_enter(suspend_state_t state);
 /* Defined in hibernate_asm.S */
 extern int restore_image(pgd_t *resume_pg_dir, struct pbe *restore_pblist);
 
-/* References to section boundaries */
-extern const void __nosave_begin, __nosave_end;
-
 extern struct pbe *restore_pblist;
 #endif
index d75ef8b6cb5618f28cb2ed0db4192910fcf056e2..9969ec374abb345abcad9ffd5742f155fae36d37 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
+#include <asm/sections.h>
 #include <asm/suspend.h>
 
 #include "mach/pm.h"
index e4b1f431c7ed3abe5e100c9d64a97aa9278b6e75..3eb8a41509b3d8ab531da89df39b20389793b23a 100644 (file)
@@ -30,7 +30,6 @@ config X86
        select HAVE_UNSTABLE_SCHED_CLOCK
        select ARCH_SUPPORTS_NUMA_BALANCING if X86_64
        select ARCH_SUPPORTS_INT128 if X86_64
-       select ARCH_WANTS_PROT_NUMA_PROT_NONE
        select HAVE_IDE
        select HAVE_OPROFILE
        select HAVE_PCSPKR_PLATFORM
diff --git a/arch/x86/include/asm/irq_work.h b/arch/x86/include/asm/irq_work.h
new file mode 100644 (file)
index 0000000..78162f8
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _ASM_IRQ_WORK_H
+#define _ASM_IRQ_WORK_H
+
+#include <asm/processor.h>
+
+static inline bool arch_irq_work_has_interrupt(void)
+{
+       return cpu_has_apic;
+}
+
+#endif /* _ASM_IRQ_WORK_H */
index f216963760e5195f18c03dd8477e02d5cc729c87..07789647bf333458840685a5d35e91d49ed2e299 100644 (file)
@@ -23,7 +23,6 @@
 #define _PAGE_BIT_SPECIAL      _PAGE_BIT_SOFTW1
 #define _PAGE_BIT_CPA_TEST     _PAGE_BIT_SOFTW1
 #define _PAGE_BIT_SPLITTING    _PAGE_BIT_SOFTW2 /* only valid on a PSE pmd */
-#define _PAGE_BIT_IOMAP                _PAGE_BIT_SOFTW2 /* flag used to indicate IO mapping */
 #define _PAGE_BIT_HIDDEN       _PAGE_BIT_SOFTW3 /* hidden by kmemcheck */
 #define _PAGE_BIT_SOFT_DIRTY   _PAGE_BIT_SOFTW3 /* software dirty tracking */
 #define _PAGE_BIT_NX           63       /* No execute: only valid after cpuid check */
@@ -52,7 +51,7 @@
 #define _PAGE_PSE      (_AT(pteval_t, 1) << _PAGE_BIT_PSE)
 #define _PAGE_GLOBAL   (_AT(pteval_t, 1) << _PAGE_BIT_GLOBAL)
 #define _PAGE_SOFTW1   (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW1)
-#define _PAGE_IOMAP    (_AT(pteval_t, 1) << _PAGE_BIT_IOMAP)
+#define _PAGE_SOFTW2   (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW2)
 #define _PAGE_PAT      (_AT(pteval_t, 1) << _PAGE_BIT_PAT)
 #define _PAGE_PAT_LARGE (_AT(pteval_t, 1) << _PAGE_BIT_PAT_LARGE)
 #define _PAGE_SPECIAL  (_AT(pteval_t, 1) << _PAGE_BIT_SPECIAL)
 #define __PAGE_KERNEL_LARGE_NOCACHE    (__PAGE_KERNEL | _PAGE_CACHE_UC | _PAGE_PSE)
 #define __PAGE_KERNEL_LARGE_EXEC       (__PAGE_KERNEL_EXEC | _PAGE_PSE)
 
-#define __PAGE_KERNEL_IO               (__PAGE_KERNEL | _PAGE_IOMAP)
-#define __PAGE_KERNEL_IO_NOCACHE       (__PAGE_KERNEL_NOCACHE | _PAGE_IOMAP)
-#define __PAGE_KERNEL_IO_UC_MINUS      (__PAGE_KERNEL_UC_MINUS | _PAGE_IOMAP)
-#define __PAGE_KERNEL_IO_WC            (__PAGE_KERNEL_WC | _PAGE_IOMAP)
+#define __PAGE_KERNEL_IO               (__PAGE_KERNEL)
+#define __PAGE_KERNEL_IO_NOCACHE       (__PAGE_KERNEL_NOCACHE)
+#define __PAGE_KERNEL_IO_UC_MINUS      (__PAGE_KERNEL_UC_MINUS)
+#define __PAGE_KERNEL_IO_WC            (__PAGE_KERNEL_WC)
 
 #define PAGE_KERNEL                    __pgprot(__PAGE_KERNEL)
 #define PAGE_KERNEL_RO                 __pgprot(__PAGE_KERNEL_RO)
@@ -325,6 +324,20 @@ static inline pteval_t pte_flags(pte_t pte)
        return native_pte_val(pte) & PTE_FLAGS_MASK;
 }
 
+#ifdef CONFIG_NUMA_BALANCING
+/* Set of bits that distinguishes present, prot_none and numa ptes */
+#define _PAGE_NUMA_MASK (_PAGE_NUMA|_PAGE_PROTNONE|_PAGE_PRESENT)
+static inline pteval_t ptenuma_flags(pte_t pte)
+{
+       return pte_flags(pte) & _PAGE_NUMA_MASK;
+}
+
+static inline pmdval_t pmdnuma_flags(pmd_t pmd)
+{
+       return pmd_flags(pmd) & _PAGE_NUMA_MASK;
+}
+#endif /* CONFIG_NUMA_BALANCING */
+
 #define pgprot_val(x)  ((x).pgprot)
 #define __pgprot(x)    ((pgprot_t) { (x) } )
 
index 337ce5a9b15c86bb7e9ea7747749fed1aee0d2d7..1183d545da1e95a56233f39baf8047a38c77fe22 100644 (file)
@@ -2623,6 +2623,7 @@ static struct irq_chip ioapic_chip __read_mostly = {
        .irq_eoi                = ack_apic_level,
        .irq_set_affinity       = native_ioapic_set_affinity,
        .irq_retrigger          = ioapic_retrigger_irq,
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
 };
 
 static inline void init_IO_APIC_traps(void)
@@ -3173,6 +3174,7 @@ static struct irq_chip msi_chip = {
        .irq_ack                = ack_apic_edge,
        .irq_set_affinity       = msi_set_affinity,
        .irq_retrigger          = ioapic_retrigger_irq,
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
 };
 
 int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
@@ -3271,6 +3273,7 @@ static struct irq_chip dmar_msi_type = {
        .irq_ack                = ack_apic_edge,
        .irq_set_affinity       = dmar_msi_set_affinity,
        .irq_retrigger          = ioapic_retrigger_irq,
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
 };
 
 int arch_setup_dmar_msi(unsigned int irq)
@@ -3321,6 +3324,7 @@ static struct irq_chip hpet_msi_type = {
        .irq_ack = ack_apic_edge,
        .irq_set_affinity = hpet_msi_set_affinity,
        .irq_retrigger = ioapic_retrigger_irq,
+       .flags = IRQCHIP_SKIP_SET_WAKE,
 };
 
 int default_setup_hpet_msi(unsigned int irq, unsigned int id)
@@ -3384,6 +3388,7 @@ static struct irq_chip ht_irq_chip = {
        .irq_ack                = ack_apic_edge,
        .irq_set_affinity       = ht_set_affinity,
        .irq_retrigger          = ioapic_retrigger_irq,
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
 };
 
 int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
index 1de84e3ab4e0737df845648aad772e815908c2f8..15d741ddfeeb7c4497e28052e1ad7ea08e68c252 100644 (file)
@@ -41,7 +41,7 @@ __visible void smp_trace_irq_work_interrupt(struct pt_regs *regs)
 void arch_irq_work_raise(void)
 {
 #ifdef CONFIG_X86_LOCAL_APIC
-       if (!cpu_has_apic)
+       if (!arch_irq_work_has_interrupt())
                return;
 
        apic->send_IPI_self(IRQ_WORK_VECTOR);
index 3201e93ebd07ca336e7e15c046c8596b73fdcbc7..ac1c4de3a48491d9b0cf939897e9af57238b3f71 100644 (file)
@@ -4549,7 +4549,7 @@ int kvm_mmu_module_init(void)
        if (!mmu_page_header_cache)
                goto nomem;
 
-       if (percpu_counter_init(&kvm_total_used_mmu_pages, 0))
+       if (percpu_counter_init(&kvm_total_used_mmu_pages, 0, GFP_KERNEL))
                goto nomem;
 
        register_shrinker(&mmu_shrinker);
index a241946815131904498ae6ecd95d87c4e28bf2f9..83bb03bfa259bc54ae69c8131e0dbc3730512fc9 100644 (file)
@@ -933,8 +933,17 @@ static int spurious_fault_check(unsigned long error_code, pte_t *pte)
  * cross-processor TLB flush, even if no stale TLB entries exist
  * on other processors.
  *
+ * Spurious faults may only occur if the TLB contains an entry with
+ * fewer permission than the page table entry.  Non-present (P = 0)
+ * and reserved bit (R = 1) faults are never spurious.
+ *
  * There are no security implications to leaving a stale TLB when
  * increasing the permissions on a page.
+ *
+ * Returns non-zero if a spurious fault was handled, zero otherwise.
+ *
+ * See Intel Developer's Manual Vol 3 Section 4.10.4.3, bullet 3
+ * (Optional Invalidation).
  */
 static noinline int
 spurious_fault(unsigned long error_code, unsigned long address)
@@ -945,8 +954,17 @@ spurious_fault(unsigned long error_code, unsigned long address)
        pte_t *pte;
        int ret;
 
-       /* Reserved-bit violation or user access to kernel space? */
-       if (error_code & (PF_USER | PF_RSVD))
+       /*
+        * Only writes to RO or instruction fetches from NX may cause
+        * spurious faults.
+        *
+        * These could be from user or supervisor accesses but the TLB
+        * is only lazily flushed after a kernel mapping protection
+        * change, so user accesses are not expected to cause spurious
+        * faults.
+        */
+       if (error_code != (PF_WRITE | PF_PROT)
+           && error_code != (PF_INSTR | PF_PROT))
                return 0;
 
        pgd = init_mm.pgd + pgd_index(address);
index 7d05565ba7813047cfc4f4d96d339f9af0e5c3d3..c8140e12816a51f77702b273289222071ecb2350 100644 (file)
@@ -537,7 +537,7 @@ static void __init pagetable_init(void)
        permanent_kmaps_init(pgd_base);
 }
 
-pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL | _PAGE_IOMAP);
+pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL);
 EXPORT_SYMBOL_GPL(__supported_pte_mask);
 
 /* user-defined highmem size */
index 5621c47d7a1a0e7274c7fd49b6aa7602d952bd74..5d984769cbd8c7ab706925ecfad585489420869a 100644 (file)
@@ -151,7 +151,7 @@ early_param("gbpages", parse_direct_gbpages_on);
  * around without checking the pgd every time.
  */
 
-pteval_t __supported_pte_mask __read_mostly = ~_PAGE_IOMAP;
+pteval_t __supported_pte_mask __read_mostly = ~0;
 EXPORT_SYMBOL_GPL(__supported_pte_mask);
 
 int force_personality32;
index 059a76c29739a60f7665e0f582c317d038893918..7b20bccf3648dfb0fcb534f0290c023e12a44f9a 100644 (file)
@@ -81,14 +81,14 @@ struct pci_ops pci_root_ops = {
  */
 DEFINE_RAW_SPINLOCK(pci_config_lock);
 
-static int can_skip_ioresource_align(const struct dmi_system_id *d)
+static int __init can_skip_ioresource_align(const struct dmi_system_id *d)
 {
        pci_probe |= PCI_CAN_SKIP_ISA_ALIGN;
        printk(KERN_INFO "PCI: %s detected, can skip ISA alignment\n", d->ident);
        return 0;
 }
 
-static const struct dmi_system_id can_skip_pciprobe_dmi_table[] = {
+static const struct dmi_system_id can_skip_pciprobe_dmi_table[] __initconst = {
 /*
  * Systems where PCI IO resource ISA alignment can be skipped
  * when the ISA enable bit in the bridge control is not set
@@ -186,7 +186,7 @@ void pcibios_remove_bus(struct pci_bus *bus)
  * on the kernel command line (which was parsed earlier).
  */
 
-static int set_bf_sort(const struct dmi_system_id *d)
+static int __init set_bf_sort(const struct dmi_system_id *d)
 {
        if (pci_bf_sort == pci_bf_sort_default) {
                pci_bf_sort = pci_dmi_bf;
@@ -195,8 +195,8 @@ static int set_bf_sort(const struct dmi_system_id *d)
        return 0;
 }
 
-static void read_dmi_type_b1(const struct dmi_header *dm,
-                                      void *private_data)
+static void __init read_dmi_type_b1(const struct dmi_header *dm,
+                                   void *private_data)
 {
        u8 *d = (u8 *)dm + 4;
 
@@ -217,7 +217,7 @@ static void read_dmi_type_b1(const struct dmi_header *dm,
        }
 }
 
-static int find_sort_method(const struct dmi_system_id *d)
+static int __init find_sort_method(const struct dmi_system_id *d)
 {
        dmi_walk(read_dmi_type_b1, NULL);
 
@@ -232,7 +232,7 @@ static int find_sort_method(const struct dmi_system_id *d)
  * Enable renumbering of PCI bus# ranges to reach all PCI busses (Cardbus)
  */
 #ifdef __i386__
-static int assign_all_busses(const struct dmi_system_id *d)
+static int __init assign_all_busses(const struct dmi_system_id *d)
 {
        pci_probe |= PCI_ASSIGN_ALL_BUSSES;
        printk(KERN_INFO "%s detected: enabling PCI bus# renumbering"
@@ -241,7 +241,7 @@ static int assign_all_busses(const struct dmi_system_id *d)
 }
 #endif
 
-static int set_scan_all(const struct dmi_system_id *d)
+static int __init set_scan_all(const struct dmi_system_id *d)
 {
        printk(KERN_INFO "PCI: %s detected, enabling pci=pcie_scan_all\n",
               d->ident);
@@ -249,7 +249,7 @@ static int set_scan_all(const struct dmi_system_id *d)
        return 0;
 }
 
-static const struct dmi_system_id pciprobe_dmi_table[] = {
+static const struct dmi_system_id pciprobe_dmi_table[] __initconst = {
 #ifdef __i386__
 /*
  * Laptops which need pci=assign-busses to see Cardbus cards
@@ -512,7 +512,7 @@ int __init pcibios_init(void)
        return 0;
 }
 
-char * __init pcibios_setup(char *str)
+char *__init pcibios_setup(char *str)
 {
        if (!strcmp(str, "off")) {
                pci_probe = 0;
index 2ae525e0d8ba641c1a58d9892d8c8d6e754ee74e..37c1435889cea24c66dd68e8e396a2fe5503d10a 100644 (file)
@@ -442,8 +442,6 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
                 */
                prot |= _PAGE_CACHE_UC_MINUS;
 
-       prot |= _PAGE_IOMAP;    /* creating a mapping for IO */
-
        vma->vm_page_prot = __pgprot(prot);
 
        if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
index 248642f4bab7140c6ee7e80862b46b0c76fdc42f..326198a4434e241cd7b50446586f90653931e7dd 100644 (file)
@@ -31,7 +31,7 @@ static DEFINE_MUTEX(pci_mmcfg_lock);
 
 LIST_HEAD(pci_mmcfg_list);
 
-static __init void pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
+static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
 {
        if (cfg->res.parent)
                release_resource(&cfg->res);
@@ -39,7 +39,7 @@ static __init void pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
        kfree(cfg);
 }
 
-static __init void free_all_mmcfg(void)
+static void __init free_all_mmcfg(void)
 {
        struct pci_mmcfg_region *cfg, *tmp;
 
@@ -93,7 +93,7 @@ static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
        return new;
 }
 
-static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
                                                        int end, u64 addr)
 {
        struct pci_mmcfg_region *new;
@@ -125,7 +125,7 @@ struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
        return NULL;
 }
 
-static const char __init *pci_mmcfg_e7520(void)
+static const char *__init pci_mmcfg_e7520(void)
 {
        u32 win;
        raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win);
@@ -140,7 +140,7 @@ static const char __init *pci_mmcfg_e7520(void)
        return "Intel Corporation E7520 Memory Controller Hub";
 }
 
-static const char __init *pci_mmcfg_intel_945(void)
+static const char *__init pci_mmcfg_intel_945(void)
 {
        u32 pciexbar, mask = 0, len = 0;
 
@@ -184,7 +184,7 @@ static const char __init *pci_mmcfg_intel_945(void)
        return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
 }
 
-static const char __init *pci_mmcfg_amd_fam10h(void)
+static const char *__init pci_mmcfg_amd_fam10h(void)
 {
        u32 low, high, address;
        u64 base, msr;
@@ -235,21 +235,25 @@ static const char __init *pci_mmcfg_amd_fam10h(void)
 }
 
 static bool __initdata mcp55_checked;
-static const char __init *pci_mmcfg_nvidia_mcp55(void)
+static const char *__init pci_mmcfg_nvidia_mcp55(void)
 {
        int bus;
        int mcp55_mmconf_found = 0;
 
-       static const u32 extcfg_regnum          = 0x90;
-       static const u32 extcfg_regsize         = 4;
-       static const u32 extcfg_enable_mask     = 1<<31;
-       static const u32 extcfg_start_mask      = 0xff<<16;
-       static const int extcfg_start_shift     = 16;
-       static const u32 extcfg_size_mask       = 0x3<<28;
-       static const int extcfg_size_shift      = 28;
-       static const int extcfg_sizebus[]       = {0x100, 0x80, 0x40, 0x20};
-       static const u32 extcfg_base_mask[]     = {0x7ff8, 0x7ffc, 0x7ffe, 0x7fff};
-       static const int extcfg_base_lshift     = 25;
+       static const u32 extcfg_regnum __initconst      = 0x90;
+       static const u32 extcfg_regsize __initconst     = 4;
+       static const u32 extcfg_enable_mask __initconst = 1 << 31;
+       static const u32 extcfg_start_mask __initconst  = 0xff << 16;
+       static const int extcfg_start_shift __initconst = 16;
+       static const u32 extcfg_size_mask __initconst   = 0x3 << 28;
+       static const int extcfg_size_shift __initconst  = 28;
+       static const int extcfg_sizebus[] __initconst   = {
+               0x100, 0x80, 0x40, 0x20
+       };
+       static const u32 extcfg_base_mask[] __initconst = {
+               0x7ff8, 0x7ffc, 0x7ffe, 0x7fff
+       };
+       static const int extcfg_base_lshift __initconst = 25;
 
        /*
         * do check if amd fam10h already took over
@@ -302,7 +306,7 @@ struct pci_mmcfg_hostbridge_probe {
        const char *(*probe)(void);
 };
 
-static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = {
+static const struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initconst = {
        { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
          PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 },
        { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
index c77b24a8b2daf499e07527be6f27d5b8c2df20d0..9b83b9051ae7bb78daf009e1d6c95927c74f2877 100644 (file)
@@ -79,13 +79,13 @@ union bios32 {
 static struct {
        unsigned long address;
        unsigned short segment;
-} bios32_indirect = { 0, __KERNEL_CS };
+} bios32_indirect __initdata = { 0, __KERNEL_CS };
 
 /*
  * Returns the entry point for the given service, NULL on error
  */
 
-static unsigned long bios32_service(unsigned long service)
+static unsigned long __init bios32_service(unsigned long service)
 {
        unsigned char return_code;      /* %al */
        unsigned long address;          /* %ebx */
@@ -124,7 +124,7 @@ static struct {
 
 static int pci_bios_present;
 
-static int check_pcibios(void)
+static int __init check_pcibios(void)
 {
        u32 signature, eax, ebx, ecx;
        u8 status, major_ver, minor_ver, hw_mech;
@@ -312,7 +312,7 @@ static const struct pci_raw_ops pci_bios_access = {
  * Try to find PCI BIOS.
  */
 
-static const struct pci_raw_ops *pci_find_bios(void)
+static const struct pci_raw_ops *__init pci_find_bios(void)
 {
        union bios32 *check;
        unsigned char sum;
index 7d28c885d2385b85c133add5b51176eea8b52af7..291226b952a997f55d3a0be723f15cb9b9b1ab92 100644 (file)
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/mmzone.h>
+#include <asm/sections.h>
 
 /* Defined in hibernate_asm_32.S */
 extern int restore_image(void);
 
-/* References to section boundaries */
-extern const void __nosave_begin, __nosave_end;
-
 /* Pointer to the temporary resume page tables */
 pgd_t *resume_pg_dir;
 
index 35e2bb6c0f372d1ff6f91a6078efa339b25efd78..009947d419a61ae2fc41f58ad9d85ece1555fec2 100644 (file)
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/mtrr.h>
+#include <asm/sections.h>
 #include <asm/suspend.h>
 
-/* References to section boundaries */
-extern __visible const void __nosave_begin, __nosave_end;
-
 /* Defined in hibernate_asm_64.S */
 extern asmlinkage __visible int restore_image(void);
 
index a02e09e18f57b160c8c5f036ac64d5e97a72a4c1..be14cc3e48d5fd8e0b5440396478a5c6a8932025 100644 (file)
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/bitops.h>
 #include <linux/efi.h>
 #include <linux/init.h>
 #include <linux/string.h>
 
 #include <xen/xen-ops.h>
 
+#include <asm/page.h>
 #include <asm/setup.h>
 
 void __init xen_efi_init(void)
index c0cb11fb500895f60b91e8ad1af679d878d40903..acb0effd80773f347ef8a7e6591d6a447c894423 100644 (file)
@@ -1463,6 +1463,7 @@ static void __ref xen_setup_gdt(int cpu)
        pv_cpu_ops.load_gdt = xen_load_gdt;
 }
 
+#ifdef CONFIG_XEN_PVH
 /*
  * A PV guest starts with default flags that are not set for PVH, set them
  * here asap.
@@ -1508,17 +1509,21 @@ static void __init xen_pvh_early_guest_init(void)
                return;
 
        xen_have_vector_callback = 1;
+
+       xen_pvh_early_cpu_init(0, false);
        xen_pvh_set_cr_flags(0);
 
 #ifdef CONFIG_X86_32
        BUG(); /* PVH: Implement proper support. */
 #endif
 }
+#endif    /* CONFIG_XEN_PVH */
 
 /* First C function to be called on Xen boot */
 asmlinkage __visible void __init xen_start_kernel(void)
 {
        struct physdev_set_iopl set_iopl;
+       unsigned long initrd_start = 0;
        int rc;
 
        if (!xen_start_info)
@@ -1527,7 +1532,9 @@ asmlinkage __visible void __init xen_start_kernel(void)
        xen_domain_type = XEN_PV_DOMAIN;
 
        xen_setup_features();
+#ifdef CONFIG_XEN_PVH
        xen_pvh_early_guest_init();
+#endif
        xen_setup_machphys_mapping();
 
        /* Install Xen paravirt ops */
@@ -1559,8 +1566,6 @@ asmlinkage __visible void __init xen_start_kernel(void)
 #endif
                __supported_pte_mask &= ~(_PAGE_PWT | _PAGE_PCD);
 
-       __supported_pte_mask |= _PAGE_IOMAP;
-
        /*
         * Prevent page tables from being allocated in highmem, even
         * if CONFIG_HIGHPTE is enabled.
@@ -1667,10 +1672,16 @@ asmlinkage __visible void __init xen_start_kernel(void)
        new_cpu_data.x86_capability[0] = cpuid_edx(1);
 #endif
 
+       if (xen_start_info->mod_start) {
+           if (xen_start_info->flags & SIF_MOD_START_PFN)
+               initrd_start = PFN_PHYS(xen_start_info->mod_start);
+           else
+               initrd_start = __pa(xen_start_info->mod_start);
+       }
+
        /* Poke various useful things into boot_params */
        boot_params.hdr.type_of_loader = (9 << 4) | 0;
-       boot_params.hdr.ramdisk_image = xen_start_info->mod_start
-               ? __pa(xen_start_info->mod_start) : 0;
+       boot_params.hdr.ramdisk_image = initrd_start;
        boot_params.hdr.ramdisk_size = xen_start_info->mod_len;
        boot_params.hdr.cmd_line_ptr = __pa(xen_start_info->cmd_line);
 
index 16fb0099b7f295ed40c206e25478037f0b2ff651..f62af7647ec9879055f433a540166cb4162a1f0d 100644 (file)
@@ -399,38 +399,14 @@ static pteval_t pte_pfn_to_mfn(pteval_t val)
                if (unlikely(mfn == INVALID_P2M_ENTRY)) {
                        mfn = 0;
                        flags = 0;
-               } else {
-                       /*
-                        * Paramount to do this test _after_ the
-                        * INVALID_P2M_ENTRY as INVALID_P2M_ENTRY &
-                        * IDENTITY_FRAME_BIT resolves to true.
-                        */
-                       mfn &= ~FOREIGN_FRAME_BIT;
-                       if (mfn & IDENTITY_FRAME_BIT) {
-                               mfn &= ~IDENTITY_FRAME_BIT;
-                               flags |= _PAGE_IOMAP;
-                       }
-               }
+               } else
+                       mfn &= ~(FOREIGN_FRAME_BIT | IDENTITY_FRAME_BIT);
                val = ((pteval_t)mfn << PAGE_SHIFT) | flags;
        }
 
        return val;
 }
 
-static pteval_t iomap_pte(pteval_t val)
-{
-       if (val & _PAGE_PRESENT) {
-               unsigned long pfn = (val & PTE_PFN_MASK) >> PAGE_SHIFT;
-               pteval_t flags = val & PTE_FLAGS_MASK;
-
-               /* We assume the pte frame number is a MFN, so
-                  just use it as-is. */
-               val = ((pteval_t)pfn << PAGE_SHIFT) | flags;
-       }
-
-       return val;
-}
-
 __visible pteval_t xen_pte_val(pte_t pte)
 {
        pteval_t pteval = pte.pte;
@@ -441,9 +417,6 @@ __visible pteval_t xen_pte_val(pte_t pte)
                pteval = (pteval & ~_PAGE_PAT) | _PAGE_PWT;
        }
 #endif
-       if (xen_initial_domain() && (pteval & _PAGE_IOMAP))
-               return pteval;
-
        return pte_mfn_to_pfn(pteval);
 }
 PV_CALLEE_SAVE_REGS_THUNK(xen_pte_val);
@@ -481,7 +454,6 @@ void xen_set_pat(u64 pat)
 
 __visible pte_t xen_make_pte(pteval_t pte)
 {
-       phys_addr_t addr = (pte & PTE_PFN_MASK);
 #if 0
        /* If Linux is trying to set a WC pte, then map to the Xen WC.
         * If _PAGE_PAT is set, then it probably means it is really
@@ -496,19 +468,7 @@ __visible pte_t xen_make_pte(pteval_t pte)
                        pte = (pte & ~(_PAGE_PCD | _PAGE_PWT)) | _PAGE_PAT;
        }
 #endif
-       /*
-        * Unprivileged domains are allowed to do IOMAPpings for
-        * PCI passthrough, but not map ISA space.  The ISA
-        * mappings are just dummy local mappings to keep other
-        * parts of the kernel happy.
-        */
-       if (unlikely(pte & _PAGE_IOMAP) &&
-           (xen_initial_domain() || addr >= ISA_END_ADDRESS)) {
-               pte = iomap_pte(pte);
-       } else {
-               pte &= ~_PAGE_IOMAP;
-               pte = pte_pfn_to_mfn(pte);
-       }
+       pte = pte_pfn_to_mfn(pte);
 
        return native_make_pte(pte);
 }
@@ -2091,7 +2051,7 @@ static void xen_set_fixmap(unsigned idx, phys_addr_t phys, pgprot_t prot)
 
        default:
                /* By default, set_fixmap is used for hardware mappings */
-               pte = mfn_pte(phys, __pgprot(pgprot_val(prot) | _PAGE_IOMAP));
+               pte = mfn_pte(phys, prot);
                break;
        }
 
index 3172692381aec4bb5dade9851fa7fa7715d58e46..9f5983b01ed91ebe7080a0e5928e3f549d0c1242 100644 (file)
 #include <xen/balloon.h>
 #include <xen/grant_table.h>
 
+#include "p2m.h"
 #include "multicalls.h"
 #include "xen-ops.h"
 
@@ -180,12 +181,6 @@ static void __init m2p_override_init(void);
 
 unsigned long xen_max_p2m_pfn __read_mostly;
 
-#define P2M_PER_PAGE           (PAGE_SIZE / sizeof(unsigned long))
-#define P2M_MID_PER_PAGE       (PAGE_SIZE / sizeof(unsigned long *))
-#define P2M_TOP_PER_PAGE       (PAGE_SIZE / sizeof(unsigned long **))
-
-#define MAX_P2M_PFN            (P2M_TOP_PER_PAGE * P2M_MID_PER_PAGE * P2M_PER_PAGE)
-
 /* Placeholders for holes in the address space */
 static RESERVE_BRK_ARRAY(unsigned long, p2m_missing, P2M_PER_PAGE);
 static RESERVE_BRK_ARRAY(unsigned long *, p2m_mid_missing, P2M_MID_PER_PAGE);
@@ -202,16 +197,12 @@ static RESERVE_BRK_ARRAY(unsigned long, p2m_mid_identity_mfn, P2M_MID_PER_PAGE);
 RESERVE_BRK(p2m_mid, PAGE_SIZE * (MAX_DOMAIN_PAGES / (P2M_PER_PAGE * P2M_MID_PER_PAGE)));
 RESERVE_BRK(p2m_mid_mfn, PAGE_SIZE * (MAX_DOMAIN_PAGES / (P2M_PER_PAGE * P2M_MID_PER_PAGE)));
 
-/* We might hit two boundary violations at the start and end, at max each
- * boundary violation will require three middle nodes. */
-RESERVE_BRK(p2m_mid_extra, PAGE_SIZE * 2 * 3);
-
-/* When we populate back during bootup, the amount of pages can vary. The
- * max we have is seen is 395979, but that does not mean it can't be more.
- * Some machines can have 3GB I/O holes even. With early_can_reuse_p2m_middle
- * it can re-use Xen provided mfn_list array, so we only need to allocate at
- * most three P2M top nodes. */
-RESERVE_BRK(p2m_populated, PAGE_SIZE * 3);
+/* For each I/O range remapped we may lose up to two leaf pages for the boundary
+ * violations and three mid pages to cover up to 3GB. With
+ * early_can_reuse_p2m_middle() most of the leaf pages will be reused by the
+ * remapped region.
+ */
+RESERVE_BRK(p2m_identity_remap, PAGE_SIZE * 2 * 3 * MAX_REMAP_RANGES);
 
 static inline unsigned p2m_top_index(unsigned long pfn)
 {
diff --git a/arch/x86/xen/p2m.h b/arch/x86/xen/p2m.h
new file mode 100644 (file)
index 0000000..ad8aee2
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _XEN_P2M_H
+#define _XEN_P2M_H
+
+#define P2M_PER_PAGE        (PAGE_SIZE / sizeof(unsigned long))
+#define P2M_MID_PER_PAGE    (PAGE_SIZE / sizeof(unsigned long *))
+#define P2M_TOP_PER_PAGE    (PAGE_SIZE / sizeof(unsigned long **))
+
+#define MAX_P2M_PFN         (P2M_TOP_PER_PAGE * P2M_MID_PER_PAGE * P2M_PER_PAGE)
+
+#define MAX_REMAP_RANGES    10
+
+extern unsigned long __init set_phys_range_identity(unsigned long pfn_s,
+                                      unsigned long pfn_e);
+
+#endif  /* _XEN_P2M_H */
index 2e555163c2fe4f0b44f37289887539ab274adc1c..af7216128d93ac4949e574b52d45f91079f6898f 100644 (file)
@@ -29,6 +29,7 @@
 #include <xen/features.h>
 #include "xen-ops.h"
 #include "vdso.h"
+#include "p2m.h"
 
 /* These are code, but not functions.  Defined in entry.S */
 extern const char xen_hypervisor_callback[];
@@ -46,6 +47,9 @@ struct xen_memory_region xen_extra_mem[XEN_EXTRA_MEM_MAX_REGIONS] __initdata;
 /* Number of pages released from the initial allocation. */
 unsigned long xen_released_pages;
 
+/* Buffer used to remap identity mapped pages */
+unsigned long xen_remap_buf[P2M_PER_PAGE] __initdata;
+
 /* 
  * The maximum amount of extra memory compared to the base size.  The
  * main scaling factor is the size of struct page.  At extreme ratios
@@ -151,107 +155,325 @@ static unsigned long __init xen_do_chunk(unsigned long start,
        return len;
 }
 
-static unsigned long __init xen_release_chunk(unsigned long start,
-                                             unsigned long end)
-{
-       return xen_do_chunk(start, end, true);
-}
-
-static unsigned long __init xen_populate_chunk(
+/*
+ * Finds the next RAM pfn available in the E820 map after min_pfn.
+ * This function updates min_pfn with the pfn found and returns
+ * the size of that range or zero if not found.
+ */
+static unsigned long __init xen_find_pfn_range(
        const struct e820entry *list, size_t map_size,
-       unsigned long max_pfn, unsigned long *last_pfn,
-       unsigned long credits_left)
+       unsigned long *min_pfn)
 {
        const struct e820entry *entry;
        unsigned int i;
        unsigned long done = 0;
-       unsigned long dest_pfn;
 
        for (i = 0, entry = list; i < map_size; i++, entry++) {
                unsigned long s_pfn;
                unsigned long e_pfn;
-               unsigned long pfns;
-               long capacity;
-
-               if (credits_left <= 0)
-                       break;
 
                if (entry->type != E820_RAM)
                        continue;
 
                e_pfn = PFN_DOWN(entry->addr + entry->size);
 
-               /* We only care about E820 after the xen_start_info->nr_pages */
-               if (e_pfn <= max_pfn)
+               /* We only care about E820 after this */
+               if (e_pfn < *min_pfn)
                        continue;
 
                s_pfn = PFN_UP(entry->addr);
-               /* If the E820 falls within the nr_pages, we want to start
-                * at the nr_pages PFN.
-                * If that would mean going past the E820 entry, skip it
+
+               /* If min_pfn falls within the E820 entry, we want to start
+                * at the min_pfn PFN.
                 */
-               if (s_pfn <= max_pfn) {
-                       capacity = e_pfn - max_pfn;
-                       dest_pfn = max_pfn;
+               if (s_pfn <= *min_pfn) {
+                       done = e_pfn - *min_pfn;
                } else {
-                       capacity = e_pfn - s_pfn;
-                       dest_pfn = s_pfn;
+                       done = e_pfn - s_pfn;
+                       *min_pfn = s_pfn;
                }
+               break;
+       }
 
-               if (credits_left < capacity)
-                       capacity = credits_left;
+       return done;
+}
 
-               pfns = xen_do_chunk(dest_pfn, dest_pfn + capacity, false);
-               done += pfns;
-               *last_pfn = (dest_pfn + pfns);
-               if (pfns < capacity)
-                       break;
-               credits_left -= pfns;
+/*
+ * This releases a chunk of memory and then does the identity map. It's used as
+ * as a fallback if the remapping fails.
+ */
+static void __init xen_set_identity_and_release_chunk(unsigned long start_pfn,
+       unsigned long end_pfn, unsigned long nr_pages, unsigned long *identity,
+       unsigned long *released)
+{
+       WARN_ON(start_pfn > end_pfn);
+
+       /* Need to release pages first */
+       *released += xen_do_chunk(start_pfn, min(end_pfn, nr_pages), true);
+       *identity += set_phys_range_identity(start_pfn, end_pfn);
+}
+
+/*
+ * Helper function to update both the p2m and m2p tables.
+ */
+static unsigned long __init xen_update_mem_tables(unsigned long pfn,
+                                                 unsigned long mfn)
+{
+       struct mmu_update update = {
+               .ptr = ((unsigned long long)mfn << PAGE_SHIFT) | MMU_MACHPHYS_UPDATE,
+               .val = pfn
+       };
+
+       /* Update p2m */
+       if (!early_set_phys_to_machine(pfn, mfn)) {
+               WARN(1, "Failed to set p2m mapping for pfn=%ld mfn=%ld\n",
+                    pfn, mfn);
+               return false;
        }
-       return done;
+
+       /* Update m2p */
+       if (HYPERVISOR_mmu_update(&update, 1, NULL, DOMID_SELF) < 0) {
+               WARN(1, "Failed to set m2p mapping for mfn=%ld pfn=%ld\n",
+                    mfn, pfn);
+               return false;
+       }
+
+       return true;
 }
 
-static void __init xen_set_identity_and_release_chunk(
-       unsigned long start_pfn, unsigned long end_pfn, unsigned long nr_pages,
-       unsigned long *released, unsigned long *identity)
+/*
+ * This function updates the p2m and m2p tables with an identity map from
+ * start_pfn to start_pfn+size and remaps the underlying RAM of the original
+ * allocation at remap_pfn. It must do so carefully in P2M_PER_PAGE sized blocks
+ * to not exhaust the reserved brk space. Doing it in properly aligned blocks
+ * ensures we only allocate the minimum required leaf pages in the p2m table. It
+ * copies the existing mfns from the p2m table under the 1:1 map, overwrites
+ * them with the identity map and then updates the p2m and m2p tables with the
+ * remapped memory.
+ */
+static unsigned long __init xen_do_set_identity_and_remap_chunk(
+        unsigned long start_pfn, unsigned long size, unsigned long remap_pfn)
 {
-       unsigned long pfn;
+       unsigned long ident_pfn_iter, remap_pfn_iter;
+       unsigned long ident_start_pfn_align, remap_start_pfn_align;
+       unsigned long ident_end_pfn_align, remap_end_pfn_align;
+       unsigned long ident_boundary_pfn, remap_boundary_pfn;
+       unsigned long ident_cnt = 0;
+       unsigned long remap_cnt = 0;
+       unsigned long left = size;
+       unsigned long mod;
+       int i;
+
+       WARN_ON(size == 0);
+
+       BUG_ON(xen_feature(XENFEAT_auto_translated_physmap));
 
        /*
-        * If the PFNs are currently mapped, clear the mappings
-        * (except for the ISA region which must be 1:1 mapped) to
-        * release the refcounts (in Xen) on the original frames.
+        * Determine the proper alignment to remap memory in P2M_PER_PAGE sized
+        * blocks. We need to keep track of both the existing pfn mapping and
+        * the new pfn remapping.
         */
-       for (pfn = start_pfn; pfn <= max_pfn_mapped && pfn < end_pfn; pfn++) {
-               pte_t pte = __pte_ma(0);
+       mod = start_pfn % P2M_PER_PAGE;
+       ident_start_pfn_align =
+               mod ? (start_pfn - mod + P2M_PER_PAGE) : start_pfn;
+       mod = remap_pfn % P2M_PER_PAGE;
+       remap_start_pfn_align =
+               mod ? (remap_pfn - mod + P2M_PER_PAGE) : remap_pfn;
+       mod = (start_pfn + size) % P2M_PER_PAGE;
+       ident_end_pfn_align = start_pfn + size - mod;
+       mod = (remap_pfn + size) % P2M_PER_PAGE;
+       remap_end_pfn_align = remap_pfn + size - mod;
+
+       /* Iterate over each p2m leaf node in each range */
+       for (ident_pfn_iter = ident_start_pfn_align, remap_pfn_iter = remap_start_pfn_align;
+            ident_pfn_iter < ident_end_pfn_align && remap_pfn_iter < remap_end_pfn_align;
+            ident_pfn_iter += P2M_PER_PAGE, remap_pfn_iter += P2M_PER_PAGE) {
+               /* Check we aren't past the end */
+               BUG_ON(ident_pfn_iter + P2M_PER_PAGE > start_pfn + size);
+               BUG_ON(remap_pfn_iter + P2M_PER_PAGE > remap_pfn + size);
+
+               /* Save p2m mappings */
+               for (i = 0; i < P2M_PER_PAGE; i++)
+                       xen_remap_buf[i] = pfn_to_mfn(ident_pfn_iter + i);
+
+               /* Set identity map which will free a p2m leaf */
+               ident_cnt += set_phys_range_identity(ident_pfn_iter,
+                       ident_pfn_iter + P2M_PER_PAGE);
+
+#ifdef DEBUG
+               /* Helps verify a p2m leaf has been freed */
+               for (i = 0; i < P2M_PER_PAGE; i++) {
+                       unsigned int pfn = ident_pfn_iter + i;
+                       BUG_ON(pfn_to_mfn(pfn) != pfn);
+               }
+#endif
+               /* Now remap memory */
+               for (i = 0; i < P2M_PER_PAGE; i++) {
+                       unsigned long mfn = xen_remap_buf[i];
+
+                       /* This will use the p2m leaf freed above */
+                       if (!xen_update_mem_tables(remap_pfn_iter + i, mfn)) {
+                               WARN(1, "Failed to update mem mapping for pfn=%ld mfn=%ld\n",
+                                       remap_pfn_iter + i, mfn);
+                               return 0;
+                       }
+
+                       remap_cnt++;
+               }
 
-               if (pfn < PFN_UP(ISA_END_ADDRESS))
-                       pte = mfn_pte(pfn, PAGE_KERNEL_IO);
+               left -= P2M_PER_PAGE;
+       }
 
-               (void)HYPERVISOR_update_va_mapping(
-                       (unsigned long)__va(pfn << PAGE_SHIFT), pte, 0);
+       /* Max boundary space possible */
+       BUG_ON(left > (P2M_PER_PAGE - 1) * 2);
+
+       /* Now handle the boundary conditions */
+       ident_boundary_pfn = start_pfn;
+       remap_boundary_pfn = remap_pfn;
+       for (i = 0; i < left; i++) {
+               unsigned long mfn;
+
+               /* These two checks move from the start to end boundaries */
+               if (ident_boundary_pfn == ident_start_pfn_align)
+                       ident_boundary_pfn = ident_pfn_iter;
+               if (remap_boundary_pfn == remap_start_pfn_align)
+                       remap_boundary_pfn = remap_pfn_iter;
+
+               /* Check we aren't past the end */
+               BUG_ON(ident_boundary_pfn >= start_pfn + size);
+               BUG_ON(remap_boundary_pfn >= remap_pfn + size);
+
+               mfn = pfn_to_mfn(ident_boundary_pfn);
+
+               if (!xen_update_mem_tables(remap_boundary_pfn, mfn)) {
+                       WARN(1, "Failed to update mem mapping for pfn=%ld mfn=%ld\n",
+                               remap_pfn_iter + i, mfn);
+                       return 0;
+               }
+               remap_cnt++;
+
+               ident_boundary_pfn++;
+               remap_boundary_pfn++;
        }
 
-       if (start_pfn < nr_pages)
-               *released += xen_release_chunk(
-                       start_pfn, min(end_pfn, nr_pages));
+       /* Finish up the identity map */
+       if (ident_start_pfn_align >= ident_end_pfn_align) {
+               /*
+                 * In this case we have an identity range which does not span an
+                 * aligned block so everything needs to be identity mapped here.
+                 * If we didn't check this we might remap too many pages since
+                 * the align boundaries are not meaningful in this case.
+                */
+               ident_cnt += set_phys_range_identity(start_pfn,
+                       start_pfn + size);
+       } else {
+               /* Remapped above so check each end of the chunk */
+               if (start_pfn < ident_start_pfn_align)
+                       ident_cnt += set_phys_range_identity(start_pfn,
+                               ident_start_pfn_align);
+               if (start_pfn + size > ident_pfn_iter)
+                       ident_cnt += set_phys_range_identity(ident_pfn_iter,
+                               start_pfn + size);
+       }
 
-       *identity += set_phys_range_identity(start_pfn, end_pfn);
+       BUG_ON(ident_cnt != size);
+       BUG_ON(remap_cnt != size);
+
+       return size;
 }
 
-static unsigned long __init xen_set_identity_and_release(
-       const struct e820entry *list, size_t map_size, unsigned long nr_pages)
+/*
+ * This function takes a contiguous pfn range that needs to be identity mapped
+ * and:
+ *
+ *  1) Finds a new range of pfns to use to remap based on E820 and remap_pfn.
+ *  2) Calls the do_ function to actually do the mapping/remapping work.
+ *
+ * The goal is to not allocate additional memory but to remap the existing
+ * pages. In the case of an error the underlying memory is simply released back
+ * to Xen and not remapped.
+ */
+static unsigned long __init xen_set_identity_and_remap_chunk(
+        const struct e820entry *list, size_t map_size, unsigned long start_pfn,
+       unsigned long end_pfn, unsigned long nr_pages, unsigned long remap_pfn,
+       unsigned long *identity, unsigned long *remapped,
+       unsigned long *released)
+{
+       unsigned long pfn;
+       unsigned long i = 0;
+       unsigned long n = end_pfn - start_pfn;
+
+       while (i < n) {
+               unsigned long cur_pfn = start_pfn + i;
+               unsigned long left = n - i;
+               unsigned long size = left;
+               unsigned long remap_range_size;
+
+               /* Do not remap pages beyond the current allocation */
+               if (cur_pfn >= nr_pages) {
+                       /* Identity map remaining pages */
+                       *identity += set_phys_range_identity(cur_pfn,
+                               cur_pfn + size);
+                       break;
+               }
+               if (cur_pfn + size > nr_pages)
+                       size = nr_pages - cur_pfn;
+
+               remap_range_size = xen_find_pfn_range(list, map_size,
+                                                     &remap_pfn);
+               if (!remap_range_size) {
+                       pr_warning("Unable to find available pfn range, not remapping identity pages\n");
+                       xen_set_identity_and_release_chunk(cur_pfn,
+                               cur_pfn + left, nr_pages, identity, released);
+                       break;
+               }
+               /* Adjust size to fit in current e820 RAM region */
+               if (size > remap_range_size)
+                       size = remap_range_size;
+
+               if (!xen_do_set_identity_and_remap_chunk(cur_pfn, size, remap_pfn)) {
+                       WARN(1, "Failed to remap 1:1 memory cur_pfn=%ld size=%ld remap_pfn=%ld\n",
+                               cur_pfn, size, remap_pfn);
+                       xen_set_identity_and_release_chunk(cur_pfn,
+                               cur_pfn + left, nr_pages, identity, released);
+                       break;
+               }
+
+               /* Update variables to reflect new mappings. */
+               i += size;
+               remap_pfn += size;
+               *identity += size;
+               *remapped += size;
+       }
+
+       /*
+        * If the PFNs are currently mapped, the VA mapping also needs
+        * to be updated to be 1:1.
+        */
+       for (pfn = start_pfn; pfn <= max_pfn_mapped && pfn < end_pfn; pfn++)
+               (void)HYPERVISOR_update_va_mapping(
+                       (unsigned long)__va(pfn << PAGE_SHIFT),
+                       mfn_pte(pfn, PAGE_KERNEL_IO), 0);
+
+       return remap_pfn;
+}
+
+static unsigned long __init xen_set_identity_and_remap(
+       const struct e820entry *list, size_t map_size, unsigned long nr_pages,
+       unsigned long *released)
 {
        phys_addr_t start = 0;
-       unsigned long released = 0;
        unsigned long identity = 0;
+       unsigned long remapped = 0;
+       unsigned long last_pfn = nr_pages;
        const struct e820entry *entry;
+       unsigned long num_released = 0;
        int i;
 
        /*
         * Combine non-RAM regions and gaps until a RAM region (or the
         * end of the map) is reached, then set the 1:1 map and
-        * release the pages (if available) in those non-RAM regions.
+        * remap the memory in those non-RAM regions.
         *
         * The combined non-RAM regions are rounded to a whole number
         * of pages so any partial pages are accessible via the 1:1
@@ -269,22 +491,24 @@ static unsigned long __init xen_set_identity_and_release(
                                end_pfn = PFN_UP(entry->addr);
 
                        if (start_pfn < end_pfn)
-                               xen_set_identity_and_release_chunk(
-                                       start_pfn, end_pfn, nr_pages,
-                                       &released, &identity);
-
+                               last_pfn = xen_set_identity_and_remap_chunk(
+                                               list, map_size, start_pfn,
+                                               end_pfn, nr_pages, last_pfn,
+                                               &identity, &remapped,
+                                               &num_released);
                        start = end;
                }
        }
 
-       if (released)
-               printk(KERN_INFO "Released %lu pages of unused memory\n", released);
-       if (identity)
-               printk(KERN_INFO "Set %ld page(s) to 1-1 mapping\n", identity);
+       *released = num_released;
 
-       return released;
-}
+       pr_info("Set %ld page(s) to 1-1 mapping\n", identity);
+       pr_info("Remapped %ld page(s), last_pfn=%ld\n", remapped,
+               last_pfn);
+       pr_info("Released %ld page(s)\n", num_released);
 
+       return last_pfn;
+}
 static unsigned long __init xen_get_max_pages(void)
 {
        unsigned long max_pages = MAX_DOMAIN_PAGES;
@@ -347,7 +571,6 @@ char * __init xen_memory_setup(void)
        unsigned long max_pages;
        unsigned long last_pfn = 0;
        unsigned long extra_pages = 0;
-       unsigned long populated;
        int i;
        int op;
 
@@ -392,20 +615,11 @@ char * __init xen_memory_setup(void)
                extra_pages += max_pages - max_pfn;
 
        /*
-        * Set P2M for all non-RAM pages and E820 gaps to be identity
-        * type PFNs.  Any RAM pages that would be made inaccesible by
-        * this are first released.
+        * Set identity map on non-RAM pages and remap the underlying RAM.
         */
-       xen_released_pages = xen_set_identity_and_release(
-               map, memmap.nr_entries, max_pfn);
-
-       /*
-        * Populate back the non-RAM pages and E820 gaps that had been
-        * released. */
-       populated = xen_populate_chunk(map, memmap.nr_entries,
-                       max_pfn, &last_pfn, xen_released_pages);
+       last_pfn = xen_set_identity_and_remap(map, memmap.nr_entries, max_pfn,
+                                             &xen_released_pages);
 
-       xen_released_pages -= populated;
        extra_pages += xen_released_pages;
 
        if (last_pfn > max_pfn) {
index 7005974c3ff308a2504def1cb035d393ed82693f..c670d7518cf4a21e4b8e3238d2825a34b67b5a03 100644 (file)
@@ -37,6 +37,7 @@
 #include <xen/hvc-console.h>
 #include "xen-ops.h"
 #include "mmu.h"
+#include "smp.h"
 
 cpumask_var_t xen_cpu_initialized_map;
 
@@ -99,10 +100,14 @@ static void cpu_bringup(void)
        wmb();                  /* make sure everything is out */
 }
 
-/* Note: cpu parameter is only relevant for PVH */
-static void cpu_bringup_and_idle(int cpu)
+/*
+ * Note: cpu parameter is only relevant for PVH. The reason for passing it
+ * is we can't do smp_processor_id until the percpu segments are loaded, for
+ * which we need the cpu number! So we pass it in rdi as first parameter.
+ */
+asmlinkage __visible void cpu_bringup_and_idle(int cpu)
 {
-#ifdef CONFIG_X86_64
+#ifdef CONFIG_XEN_PVH
        if (xen_feature(XENFEAT_auto_translated_physmap) &&
            xen_feature(XENFEAT_supervisor_mode_kernel))
                xen_pvh_secondary_vcpu_init(cpu);
@@ -374,11 +379,10 @@ cpu_initialize_context(unsigned int cpu, struct task_struct *idle)
        ctxt->user_regs.fs = __KERNEL_PERCPU;
        ctxt->user_regs.gs = __KERNEL_STACK_CANARY;
 #endif
-       ctxt->user_regs.eip = (unsigned long)cpu_bringup_and_idle;
-
        memset(&ctxt->fpu_ctxt, 0, sizeof(ctxt->fpu_ctxt));
 
        if (!xen_feature(XENFEAT_auto_translated_physmap)) {
+               ctxt->user_regs.eip = (unsigned long)cpu_bringup_and_idle;
                ctxt->flags = VGCF_IN_KERNEL;
                ctxt->user_regs.eflags = 0x1000; /* IOPL_RING1 */
                ctxt->user_regs.ds = __USER_DS;
@@ -413,15 +417,18 @@ cpu_initialize_context(unsigned int cpu, struct task_struct *idle)
                                        (unsigned long)xen_failsafe_callback;
                ctxt->user_regs.cs = __KERNEL_CS;
                per_cpu(xen_cr3, cpu) = __pa(swapper_pg_dir);
-#ifdef CONFIG_X86_32
        }
-#else
-       } else
-               /* N.B. The user_regs.eip (cpu_bringup_and_idle) is called with
-                * %rdi having the cpu number - which means are passing in
-                * as the first parameter the cpu. Subtle!
+#ifdef CONFIG_XEN_PVH
+       else {
+               /*
+                * The vcpu comes on kernel page tables which have the NX pte
+                * bit set. This means before DS/SS is touched, NX in
+                * EFER must be set. Hence the following assembly glue code.
                 */
+               ctxt->user_regs.eip = (unsigned long)xen_pvh_early_cpu_init;
                ctxt->user_regs.rdi = cpu;
+               ctxt->user_regs.rsi = true;  /* entry == true */
+       }
 #endif
        ctxt->user_regs.esp = idle->thread.sp0 - sizeof(struct pt_regs);
        ctxt->ctrlreg[3] = xen_pfn_to_cr3(virt_to_mfn(swapper_pg_dir));
index c7c2d89efd76ac3c7627168b0a3d2f3964627f66..963d62a35c824f69400675b51c2f4dc95e947826 100644 (file)
@@ -8,4 +8,12 @@ extern void xen_send_IPI_allbutself(int vector);
 extern void xen_send_IPI_all(int vector);
 extern void xen_send_IPI_self(int vector);
 
+#ifdef CONFIG_XEN_PVH
+extern void xen_pvh_early_cpu_init(int cpu, bool entry);
+#else
+static inline void xen_pvh_early_cpu_init(int cpu, bool entry)
+{
+}
+#endif
+
 #endif
index 485b69585540dc4fd67eada10d3a4c028aa81394..674b222544b78dde6c3b737a767500e5f19627b3 100644 (file)
@@ -47,6 +47,41 @@ ENTRY(startup_xen)
 
        __FINIT
 
+#ifdef CONFIG_XEN_PVH
+/*
+ * xen_pvh_early_cpu_init() - early PVH VCPU initialization
+ * @cpu:   this cpu number (%rdi)
+ * @entry: true if this is a secondary vcpu coming up on this entry
+ *         point, false if this is the boot CPU being initialized for
+ *         the first time (%rsi)
+ *
+ * Note: This is called as a function on the boot CPU, and is the entry point
+ *       on the secondary CPU.
+ */
+ENTRY(xen_pvh_early_cpu_init)
+       mov     %rsi, %r11
+
+       /* Gather features to see if NX implemented. */
+       mov     $0x80000001, %eax
+       cpuid
+       mov     %edx, %esi
+
+       mov     $MSR_EFER, %ecx
+       rdmsr
+       bts     $_EFER_SCE, %eax
+
+       bt      $20, %esi
+       jnc     1f              /* No NX, skip setting it */
+       bts     $_EFER_NX, %eax
+1:     wrmsr
+#ifdef CONFIG_SMP
+       cmp     $0, %r11b
+       jne     cpu_bringup_and_idle
+#endif
+       ret
+
+#endif /* CONFIG_XEN_PVH */
+
 .pushsection .text
        .balign PAGE_SIZE
 ENTRY(hypercall_page)
@@ -124,6 +159,7 @@ NEXT_HYPERCALL(arch_6)
        ELFNOTE(Xen, XEN_ELFNOTE_L1_MFN_VALID,
                .quad _PAGE_PRESENT; .quad _PAGE_PRESENT)
        ELFNOTE(Xen, XEN_ELFNOTE_SUSPEND_CANCEL, .long 1)
+       ELFNOTE(Xen, XEN_ELFNOTE_MOD_START_PFN,  .long 1)
        ELFNOTE(Xen, XEN_ELFNOTE_HV_START_LOW,   _ASM_PTR __HYPERVISOR_VIRT_START)
        ELFNOTE(Xen, XEN_ELFNOTE_PADDR_OFFSET,   _ASM_PTR 0)
 
index c3d20ba6eb86d6f7d42102f076f6085bcbe096c8..105d38922c442624fd4bfadb5a5e475857adf041 100644 (file)
@@ -12,6 +12,7 @@ generic-y += hardirq.h
 generic-y += hash.h
 generic-y += ioctl.h
 generic-y += irq_regs.h
+generic-y += irq_work.h
 generic-y += kdebug.h
 generic-y += kmap_types.h
 generic-y += kvm_para.h
index ed521786755570989dad96a14613be5ae80e7d3f..371d8800b48a631c9807bce20f1e973a4c3a35b8 100644 (file)
@@ -402,6 +402,12 @@ static void blk_mq_sysfs_init(struct request_queue *q)
        }
 }
 
+/* see blk_register_queue() */
+void blk_mq_finish_init(struct request_queue *q)
+{
+       percpu_ref_switch_to_percpu(&q->mq_usage_counter);
+}
+
 int blk_mq_register_disk(struct gendisk *disk)
 {
        struct device *dev = disk_to_dev(disk);
index df8e1e09dd172d9ab67dbd0a91e519c5c01919ef..38f4a165640dd0aba321bb3196d8b1d1996635c1 100644 (file)
@@ -119,16 +119,7 @@ void blk_mq_freeze_queue(struct request_queue *q)
        spin_unlock_irq(q->queue_lock);
 
        if (freeze) {
-               /*
-                * XXX: Temporary kludge to work around SCSI blk-mq stall.
-                * SCSI synchronously creates and destroys many queues
-                * back-to-back during probe leading to lengthy stalls.
-                * This will be fixed by keeping ->mq_usage_counter in
-                * atomic mode until genhd registration, but, for now,
-                * let's work around using expedited synchronization.
-                */
-               __percpu_ref_kill_expedited(&q->mq_usage_counter);
-
+               percpu_ref_kill(&q->mq_usage_counter);
                blk_mq_run_queues(q, false);
        }
        wait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter));
@@ -1804,7 +1795,12 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set)
        if (!q)
                goto err_hctxs;
 
-       if (percpu_ref_init(&q->mq_usage_counter, blk_mq_usage_counter_release))
+       /*
+        * Init percpu_ref in atomic mode so that it's faster to shutdown.
+        * See blk_register_queue() for details.
+        */
+       if (percpu_ref_init(&q->mq_usage_counter, blk_mq_usage_counter_release,
+                           PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
                goto err_map;
 
        setup_timer(&q->timeout, blk_mq_rq_timer, (unsigned long) q);
index 17f5c84ce7bfb588a5a34bc244a7ac38067b4391..521ae9089c50c704af07d643cd7cf3729e259327 100644 (file)
@@ -551,12 +551,19 @@ int blk_register_queue(struct gendisk *disk)
                return -ENXIO;
 
        /*
-        * Initialization must be complete by now.  Finish the initial
-        * bypass from queue allocation.
+        * SCSI probing may synchronously create and destroy a lot of
+        * request_queues for non-existent devices.  Shutting down a fully
+        * functional queue takes measureable wallclock time as RCU grace
+        * periods are involved.  To avoid excessive latency in these
+        * cases, a request_queue starts out in a degraded mode which is
+        * faster to shut down and is made fully functional here as
+        * request_queues for non-existent devices never get registered.
         */
        if (!blk_queue_init_done(q)) {
                queue_flag_set_unlocked(QUEUE_FLAG_INIT_DONE, q);
                blk_queue_bypass_end(q);
+               if (q->mq_ops)
+                       blk_mq_finish_init(q);
        }
 
        ret = blk_trace_init_sysfs(dev);
index b0ea767c86968ceac331294a6f74694c486f39c2..93d160661f4c94391534a1581e8f4e74edd5e608 100644 (file)
@@ -54,55 +54,58 @@ ACPI_MODULE_NAME("acpi_lpss");
 
 #define LPSS_PRV_REG_COUNT             9
 
-struct lpss_shared_clock {
-       const char *name;
-       unsigned long rate;
-       struct clk *clk;
-};
+/* LPSS Flags */
+#define LPSS_CLK                       BIT(0)
+#define LPSS_CLK_GATE                  BIT(1)
+#define LPSS_CLK_DIVIDER               BIT(2)
+#define LPSS_LTR                       BIT(3)
+#define LPSS_SAVE_CTX                  BIT(4)
 
 struct lpss_private_data;
 
 struct lpss_device_desc {
-       bool clk_required;
-       const char *clkdev_name;
-       bool ltr_required;
+       unsigned int flags;
        unsigned int prv_offset;
        size_t prv_size_override;
-       bool clk_divider;
-       bool clk_gate;
-       bool save_ctx;
-       struct lpss_shared_clock *shared_clock;
        void (*setup)(struct lpss_private_data *pdata);
 };
 
 static struct lpss_device_desc lpss_dma_desc = {
-       .clk_required = true,
-       .clkdev_name = "hclk",
+       .flags = LPSS_CLK,
 };
 
 struct lpss_private_data {
        void __iomem *mmio_base;
        resource_size_t mmio_size;
+       unsigned int fixed_clk_rate;
        struct clk *clk;
        const struct lpss_device_desc *dev_desc;
        u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
 };
 
+/* UART Component Parameter Register */
+#define LPSS_UART_CPR                  0xF4
+#define LPSS_UART_CPR_AFCE             BIT(4)
+
 static void lpss_uart_setup(struct lpss_private_data *pdata)
 {
        unsigned int offset;
-       u32 reg;
+       u32 val;
 
        offset = pdata->dev_desc->prv_offset + LPSS_TX_INT;
-       reg = readl(pdata->mmio_base + offset);
-       writel(reg | LPSS_TX_INT_MASK, pdata->mmio_base + offset);
-
-       offset = pdata->dev_desc->prv_offset + LPSS_GENERAL;
-       reg = readl(pdata->mmio_base + offset);
-       writel(reg | LPSS_GENERAL_UART_RTS_OVRD, pdata->mmio_base + offset);
+       val = readl(pdata->mmio_base + offset);
+       writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset);
+
+       val = readl(pdata->mmio_base + LPSS_UART_CPR);
+       if (!(val & LPSS_UART_CPR_AFCE)) {
+               offset = pdata->dev_desc->prv_offset + LPSS_GENERAL;
+               val = readl(pdata->mmio_base + offset);
+               val |= LPSS_GENERAL_UART_RTS_OVRD;
+               writel(val, pdata->mmio_base + offset);
+       }
 }
 
-static void lpss_i2c_setup(struct lpss_private_data *pdata)
+static void byt_i2c_setup(struct lpss_private_data *pdata)
 {
        unsigned int offset;
        u32 val;
@@ -111,100 +114,56 @@ static void lpss_i2c_setup(struct lpss_private_data *pdata)
        val = readl(pdata->mmio_base + offset);
        val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC;
        writel(val, pdata->mmio_base + offset);
-}
 
-static struct lpss_device_desc wpt_dev_desc = {
-       .clk_required = true,
-       .prv_offset = 0x800,
-       .ltr_required = true,
-       .clk_divider = true,
-       .clk_gate = true,
-};
+       if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset))
+               pdata->fixed_clk_rate = 133000000;
+}
 
 static struct lpss_device_desc lpt_dev_desc = {
-       .clk_required = true,
+       .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
        .prv_offset = 0x800,
-       .ltr_required = true,
-       .clk_divider = true,
-       .clk_gate = true,
 };
 
 static struct lpss_device_desc lpt_i2c_dev_desc = {
-       .clk_required = true,
+       .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR,
        .prv_offset = 0x800,
-       .ltr_required = true,
-       .clk_gate = true,
 };
 
 static struct lpss_device_desc lpt_uart_dev_desc = {
-       .clk_required = true,
+       .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
        .prv_offset = 0x800,
-       .ltr_required = true,
-       .clk_divider = true,
-       .clk_gate = true,
        .setup = lpss_uart_setup,
 };
 
 static struct lpss_device_desc lpt_sdio_dev_desc = {
+       .flags = LPSS_LTR,
        .prv_offset = 0x1000,
        .prv_size_override = 0x1018,
-       .ltr_required = true,
-};
-
-static struct lpss_shared_clock pwm_clock = {
-       .name = "pwm_clk",
-       .rate = 25000000,
 };
 
 static struct lpss_device_desc byt_pwm_dev_desc = {
-       .clk_required = true,
-       .save_ctx = true,
-       .shared_clock = &pwm_clock,
+       .flags = LPSS_SAVE_CTX,
 };
 
 static struct lpss_device_desc byt_uart_dev_desc = {
-       .clk_required = true,
+       .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
        .prv_offset = 0x800,
-       .clk_divider = true,
-       .clk_gate = true,
-       .save_ctx = true,
        .setup = lpss_uart_setup,
 };
 
 static struct lpss_device_desc byt_spi_dev_desc = {
-       .clk_required = true,
+       .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
        .prv_offset = 0x400,
-       .clk_divider = true,
-       .clk_gate = true,
-       .save_ctx = true,
 };
 
 static struct lpss_device_desc byt_sdio_dev_desc = {
-       .clk_required = true,
-};
-
-static struct lpss_shared_clock i2c_clock = {
-       .name = "i2c_clk",
-       .rate = 100000000,
+       .flags = LPSS_CLK,
 };
 
 static struct lpss_device_desc byt_i2c_dev_desc = {
-       .clk_required = true,
+       .flags = LPSS_CLK | LPSS_SAVE_CTX,
        .prv_offset = 0x800,
-       .save_ctx = true,
-       .shared_clock = &i2c_clock,
-       .setup = lpss_i2c_setup,
-};
-
-static struct lpss_shared_clock bsw_pwm_clock = {
-       .name = "pwm_clk",
-       .rate = 19200000,
-};
-
-static struct lpss_device_desc bsw_pwm_dev_desc = {
-       .clk_required = true,
-       .save_ctx = true,
-       .shared_clock = &bsw_pwm_clock,
+       .setup = byt_i2c_setup,
 };
 
 #else
@@ -237,7 +196,7 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
        { "INT33FC", },
 
        /* Braswell LPSS devices */
-       { "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
+       { "80862288", LPSS_ADDR(byt_pwm_dev_desc) },
        { "8086228A", LPSS_ADDR(byt_uart_dev_desc) },
        { "8086228E", LPSS_ADDR(byt_spi_dev_desc) },
        { "808622C1", LPSS_ADDR(byt_i2c_dev_desc) },
@@ -251,7 +210,8 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
        { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) },
        { "INT3437", },
 
-       { "INT3438", LPSS_ADDR(wpt_dev_desc) },
+       /* Wildcat Point LPSS devices */
+       { "INT3438", LPSS_ADDR(lpt_dev_desc) },
 
        { }
 };
@@ -276,7 +236,6 @@ static int register_device_clock(struct acpi_device *adev,
                                 struct lpss_private_data *pdata)
 {
        const struct lpss_device_desc *dev_desc = pdata->dev_desc;
-       struct lpss_shared_clock *shared_clock = dev_desc->shared_clock;
        const char *devname = dev_name(&adev->dev);
        struct clk *clk = ERR_PTR(-ENODEV);
        struct lpss_clk_data *clk_data;
@@ -289,12 +248,7 @@ static int register_device_clock(struct acpi_device *adev,
        clk_data = platform_get_drvdata(lpss_clk_dev);
        if (!clk_data)
                return -ENODEV;
-
-       if (dev_desc->clkdev_name) {
-               clk_register_clkdev(clk_data->clk, dev_desc->clkdev_name,
-                                   devname);
-               return 0;
-       }
+       clk = clk_data->clk;
 
        if (!pdata->mmio_base
            || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
@@ -303,24 +257,19 @@ static int register_device_clock(struct acpi_device *adev,
        parent = clk_data->name;
        prv_base = pdata->mmio_base + dev_desc->prv_offset;
 
-       if (shared_clock) {
-               clk = shared_clock->clk;
-               if (!clk) {
-                       clk = clk_register_fixed_rate(NULL, shared_clock->name,
-                                                     "lpss_clk", 0,
-                                                     shared_clock->rate);
-                       shared_clock->clk = clk;
-               }
-               parent = shared_clock->name;
+       if (pdata->fixed_clk_rate) {
+               clk = clk_register_fixed_rate(NULL, devname, parent, 0,
+                                             pdata->fixed_clk_rate);
+               goto out;
        }
 
-       if (dev_desc->clk_gate) {
+       if (dev_desc->flags & LPSS_CLK_GATE) {
                clk = clk_register_gate(NULL, devname, parent, 0,
                                        prv_base, 0, 0, NULL);
                parent = devname;
        }
 
-       if (dev_desc->clk_divider) {
+       if (dev_desc->flags & LPSS_CLK_DIVIDER) {
                /* Prevent division by zero */
                if (!readl(prv_base))
                        writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
@@ -344,7 +293,7 @@ static int register_device_clock(struct acpi_device *adev,
                kfree(parent);
                kfree(clk_name);
        }
-
+out:
        if (IS_ERR(clk))
                return PTR_ERR(clk);
 
@@ -392,7 +341,10 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
 
        pdata->dev_desc = dev_desc;
 
-       if (dev_desc->clk_required) {
+       if (dev_desc->setup)
+               dev_desc->setup(pdata);
+
+       if (dev_desc->flags & LPSS_CLK) {
                ret = register_device_clock(adev, pdata);
                if (ret) {
                        /* Skip the device, but continue the namespace scan. */
@@ -413,9 +365,6 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
                goto err_out;
        }
 
-       if (dev_desc->setup)
-               dev_desc->setup(pdata);
-
        adev->driver_data = pdata;
        pdev = acpi_create_platform_device(adev);
        if (!IS_ERR_OR_NULL(pdev)) {
@@ -692,19 +641,19 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
 
        switch (action) {
        case BUS_NOTIFY_BOUND_DRIVER:
-               if (pdata->dev_desc->save_ctx)
+               if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
                        pdev->dev.pm_domain = &acpi_lpss_pm_domain;
                break;
        case BUS_NOTIFY_UNBOUND_DRIVER:
-               if (pdata->dev_desc->save_ctx)
+               if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
                        pdev->dev.pm_domain = NULL;
                break;
        case BUS_NOTIFY_ADD_DEVICE:
-               if (pdata->dev_desc->ltr_required)
+               if (pdata->dev_desc->flags & LPSS_LTR)
                        return sysfs_create_group(&pdev->dev.kobj,
                                                  &lpss_attr_group);
        case BUS_NOTIFY_DEL_DEVICE:
-               if (pdata->dev_desc->ltr_required)
+               if (pdata->dev_desc->flags & LPSS_LTR)
                        sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
        default:
                break;
@@ -721,7 +670,7 @@ static void acpi_lpss_bind(struct device *dev)
 {
        struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
 
-       if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
+       if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR))
                return;
 
        if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
index 1f8b20496f32698a76bf7958ded72ae9961e34d4..b193f842599902445015a219687310cdf3bfe9c9 100644 (file)
@@ -130,10 +130,6 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = {
        {"PNP0401"},            /* ECP Printer Port */
        /* apple-gmux */
        {"APP000B"},
-       /* fujitsu-laptop.c */
-       {"FUJ02bf"},
-       {"FUJ02B1"},
-       {"FUJ02E3"},
        /* system */
        {"PNP0c02"},            /* General ID for reserving resources */
        {"PNP0c01"},            /* memory controller */
index 0cf159cc6e6d79085fa03ef15dcc91d9e53ec409..56710a03c9b0785229ac56470d82cd449f0478d8 100644 (file)
@@ -596,6 +596,38 @@ acpi_status acpi_enable_all_runtime_gpes(void)
 
 ACPI_EXPORT_SYMBOL(acpi_enable_all_runtime_gpes)
 
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_enable_all_wakeup_gpes
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Enable all "wakeup" GPEs and disable all of the other GPEs, in
+ *              all GPE blocks.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_enable_all_wakeup_gpes(void)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_enable_all_wakeup_gpes);
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       status = acpi_hw_enable_all_wakeup_gpes();
+       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+       return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enable_all_wakeup_gpes)
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_install_gpe_block
index 2e6caabba07a1852b766164231da39527a5e5f44..ea62d40fd161c75c9a598ef1e317ecb3c81a4ede 100644 (file)
@@ -396,11 +396,11 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
        /* Examine each GPE Register within the block */
 
        for (i = 0; i < gpe_block->register_count; i++) {
-               if (!gpe_block->register_info[i].enable_for_wake) {
-                       continue;
-               }
 
-               /* Enable all "wake" GPEs in this register */
+               /*
+                * Enable all "wake" GPEs in this register and disable the
+                * remaining ones.
+                */
 
                status =
                    acpi_hw_write(gpe_block->register_info[i].enable_for_wake,
index 14cb6c0c8be2b67df5a2693681c2028245b2363f..5cd017c7ac0ea58af1449742e53686f51bd2c8e9 100644 (file)
@@ -87,7 +87,9 @@ const char *acpi_gbl_io_decode[] = {
 
 const char *acpi_gbl_ll_decode[] = {
        "ActiveHigh",
-       "ActiveLow"
+       "ActiveLow",
+       "ActiveBoth",
+       "Reserved"
 };
 
 const char *acpi_gbl_max_decode[] = {
index 5fdfe65fe165d0a95b72e258a6c4584682f7019a..8ec8a89a20ab9d734b6fca390d60f3501dc591a3 100644 (file)
@@ -695,7 +695,7 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
        if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
                const char *s;
                s = dmi_get_system_info(DMI_PRODUCT_VERSION);
-               if (s && !strnicmp(s, "ThinkPad", 8)) {
+               if (s && !strncasecmp(s, "ThinkPad", 8)) {
                        dmi_walk(find_battery, battery);
                        if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
                                     &battery->flags) &&
index 36eb42e3b0bb80688a52d4748d19d762d777aec9..ed122e17636e32298129a8e28429ef86cb27a4fa 100644 (file)
@@ -247,8 +247,8 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
        },
 
        /*
-        * These machines will power on immediately after shutdown when
-        * reporting the Windows 2012 OSI.
+        * The wireless hotkey does not work on those machines when
+        * returning true for _OSI("Windows 2012")
         */
        {
        .callback = dmi_disable_osi_win8,
@@ -258,6 +258,38 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
                    DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
                },
        },
+       {
+       .callback = dmi_disable_osi_win8,
+       .ident = "Dell Inspiron 7537",
+       .matches = {
+                   DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                   DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
+               },
+       },
+       {
+       .callback = dmi_disable_osi_win8,
+       .ident = "Dell Inspiron 5437",
+       .matches = {
+                   DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                   DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"),
+               },
+       },
+       {
+       .callback = dmi_disable_osi_win8,
+       .ident = "Dell Inspiron 3437",
+       .matches = {
+                   DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                   DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"),
+               },
+       },
+       {
+       .callback = dmi_disable_osi_win8,
+       .ident = "Dell Vostro 3446",
+       .matches = {
+                   DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                   DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
+               },
+       },
 
        /*
         * BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
index 67075f800e34cb69b3a28d852b9bc15b00ffa702..bea6896be1229b12d5983bb7a738db2c256fd3d1 100644 (file)
@@ -1040,6 +1040,40 @@ static struct dev_pm_domain acpi_general_pm_domain = {
        },
 };
 
+/**
+ * acpi_dev_pm_detach - Remove ACPI power management from the device.
+ * @dev: Device to take care of.
+ * @power_off: Whether or not to try to remove power from the device.
+ *
+ * Remove the device from the general ACPI PM domain and remove its wakeup
+ * notifier.  If @power_off is set, additionally remove power from the device if
+ * possible.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ */
+static void acpi_dev_pm_detach(struct device *dev, bool power_off)
+{
+       struct acpi_device *adev = ACPI_COMPANION(dev);
+
+       if (adev && dev->pm_domain == &acpi_general_pm_domain) {
+               dev->pm_domain = NULL;
+               acpi_remove_pm_notifier(adev);
+               if (power_off) {
+                       /*
+                        * If the device's PM QoS resume latency limit or flags
+                        * have been exposed to user space, they have to be
+                        * hidden at this point, so that they don't affect the
+                        * choice of the low-power state to put the device into.
+                        */
+                       dev_pm_qos_hide_latency_limit(dev);
+                       dev_pm_qos_hide_flags(dev);
+                       acpi_device_wakeup(adev, ACPI_STATE_S0, false);
+                       acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
+               }
+       }
+}
+
 /**
  * acpi_dev_pm_attach - Prepare device for ACPI power management.
  * @dev: Device to prepare.
@@ -1072,42 +1106,9 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
                acpi_dev_pm_full_power(adev);
                acpi_device_wakeup(adev, ACPI_STATE_S0, false);
        }
+
+       dev->pm_domain->detach = acpi_dev_pm_detach;
        return 0;
 }
 EXPORT_SYMBOL_GPL(acpi_dev_pm_attach);
-
-/**
- * acpi_dev_pm_detach - Remove ACPI power management from the device.
- * @dev: Device to take care of.
- * @power_off: Whether or not to try to remove power from the device.
- *
- * Remove the device from the general ACPI PM domain and remove its wakeup
- * notifier.  If @power_off is set, additionally remove power from the device if
- * possible.
- *
- * Callers must ensure proper synchronization of this function with power
- * management callbacks.
- */
-void acpi_dev_pm_detach(struct device *dev, bool power_off)
-{
-       struct acpi_device *adev = ACPI_COMPANION(dev);
-
-       if (adev && dev->pm_domain == &acpi_general_pm_domain) {
-               dev->pm_domain = NULL;
-               acpi_remove_pm_notifier(adev);
-               if (power_off) {
-                       /*
-                        * If the device's PM QoS resume latency limit or flags
-                        * have been exposed to user space, they have to be
-                        * hidden at this point, so that they don't affect the
-                        * choice of the low-power state to put the device into.
-                        */
-                       dev_pm_qos_hide_latency_limit(dev);
-                       dev_pm_qos_hide_flags(dev);
-                       acpi_device_wakeup(adev, ACPI_STATE_S0, false);
-                       acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
-               }
-       }
-}
-EXPORT_SYMBOL_GPL(acpi_dev_pm_detach);
 #endif /* CONFIG_PM */
index 8acf53e6296605a013a6359299c3e9cf29bcbf67..5328b1090e08681dc4905585470fcd43b2b514f1 100644 (file)
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #include <linux/thermal.h>
 #include <linux/acpi.h>
 
-#define PREFIX "ACPI: "
-
 #define ACPI_FAN_CLASS                 "fan"
 #define ACPI_FAN_FILE_STATE            "state"
 
@@ -127,8 +125,9 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = {
 };
 
 /* --------------------------------------------------------------------------
-                                 Driver Interface
-   -------------------------------------------------------------------------- */
+ *                               Driver Interface
+ * --------------------------------------------------------------------------
+*/
 
 static int acpi_fan_add(struct acpi_device *device)
 {
@@ -143,7 +142,7 @@ static int acpi_fan_add(struct acpi_device *device)
 
        result = acpi_bus_update_power(device->handle, NULL);
        if (result) {
-               printk(KERN_ERR PREFIX "Setting initial power state\n");
+               dev_err(&device->dev, "Setting initial power state\n");
                goto end;
        }
 
@@ -168,10 +167,9 @@ static int acpi_fan_add(struct acpi_device *device)
                                   &device->dev.kobj,
                                   "device");
        if (result)
-               dev_err(&device->dev, "Failed to create sysfs link "
-                       "'device'\n");
+               dev_err(&device->dev, "Failed to create sysfs link 'device'\n");
 
-       printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
+       dev_info(&device->dev, "ACPI: %s [%s] (%s)\n",
               acpi_device_name(device), acpi_device_bid(device),
               !device->power.state ? "on" : "off");
 
@@ -217,7 +215,7 @@ static int acpi_fan_resume(struct device *dev)
 
        result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL);
        if (result)
-               printk(KERN_ERR PREFIX "Error updating fan power state\n");
+               dev_err(dev, "Error updating fan power state\n");
 
        return result;
 }
index 3abe9b223ba717a644ecfd0a4edf401004f24807..9964f70be98de5f26df212a415bcf472aabd0c95 100644 (file)
@@ -152,6 +152,16 @@ static u32 acpi_osi_handler(acpi_string interface, u32 supported)
                        osi_linux.dmi ? " via DMI" : "");
        }
 
+       if (!strcmp("Darwin", interface)) {
+               /*
+                * Apple firmware will behave poorly if it receives positive
+                * answers to "Darwin" and any other OS. Respond positively
+                * to Darwin and then disable all other vendor strings.
+                */
+               acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS);
+               supported = ACPI_UINT32_MAX;
+       }
+
        return supported;
 }
 
@@ -825,7 +835,7 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
 
        acpi_irq_handler = handler;
        acpi_irq_context = context;
-       if (request_irq(irq, acpi_irq, IRQF_SHARED | IRQF_NO_SUSPEND, "acpi", acpi_irq)) {
+       if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) {
                printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq);
                acpi_irq_handler = NULL;
                return AE_NOT_ACQUIRED;
index e6ae603ed1a18594b2d4766371d748d5a0cfedc3..cd4de7e038ea5e0460a896232a99c89554436b52 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/pci-aspm.h>
 #include <linux/acpi.h>
 #include <linux/slab.h>
+#include <linux/dmi.h>
 #include <acpi/apei.h> /* for acpi_hest_init() */
 
 #include "internal.h"
@@ -429,6 +430,19 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm,
        struct acpi_device *device = root->device;
        acpi_handle handle = device->handle;
 
+       /*
+        * Apple always return failure on _OSC calls when _OSI("Darwin") has
+        * been called successfully. We know the feature set supported by the
+        * platform, so avoid calling _OSC at all
+        */
+
+       if (dmi_match(DMI_SYS_VENDOR, "Apple Inc.")) {
+               root->osc_control_set = ~OSC_PCI_EXPRESS_PME_CONTROL;
+               decode_osc_control(root, "OS assumes control of",
+                                  root->osc_control_set);
+               return;
+       }
+
        /*
         * All supported architectures that use ACPI have support for
         * PCI domains, so we indicate this in _OSC support capabilities.
index e32321ce9a5cfbc4a80e7101b2784506ff057fc6..ef58f46c844287e4f64ff44a16ff63b1dd884537 100644 (file)
@@ -16,7 +16,7 @@ static int map_lapic_id(struct acpi_subtable_header *entry,
                 u32 acpi_id, int *apic_id)
 {
        struct acpi_madt_local_apic *lapic =
-               (struct acpi_madt_local_apic *)entry;
+               container_of(entry, struct acpi_madt_local_apic, header);
 
        if (!(lapic->lapic_flags & ACPI_MADT_ENABLED))
                return -ENODEV;
@@ -32,7 +32,7 @@ static int map_x2apic_id(struct acpi_subtable_header *entry,
                         int device_declaration, u32 acpi_id, int *apic_id)
 {
        struct acpi_madt_local_x2apic *apic =
-               (struct acpi_madt_local_x2apic *)entry;
+               container_of(entry, struct acpi_madt_local_x2apic, header);
 
        if (!(apic->lapic_flags & ACPI_MADT_ENABLED))
                return -ENODEV;
@@ -49,7 +49,7 @@ static int map_lsapic_id(struct acpi_subtable_header *entry,
                int device_declaration, u32 acpi_id, int *apic_id)
 {
        struct acpi_madt_local_sapic *lsapic =
-               (struct acpi_madt_local_sapic *)entry;
+               container_of(entry, struct acpi_madt_local_sapic, header);
 
        if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED))
                return -ENODEV;
index 366ca40a6f703433efc1016a658eaac9e28c5c48..a7a3edd28beb8d5f881ef891075e0f650e0482de 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/jiffies.h>
 #include <linux/delay.h>
 #include <linux/power_supply.h>
+#include <linux/dmi.h>
 
 #include "sbshc.h"
 #include "battery.h"
@@ -61,6 +62,8 @@ static unsigned int cache_time = 1000;
 module_param(cache_time, uint, 0644);
 MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
 
+static bool sbs_manager_broken;
+
 #define MAX_SBS_BAT                    4
 #define ACPI_SBS_BLOCK_MAX             32
 
@@ -109,6 +112,7 @@ struct acpi_sbs {
        u8 batteries_supported:4;
        u8 manager_present:1;
        u8 charger_present:1;
+       u8 charger_exists:1;
 };
 
 #define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger)
@@ -429,9 +433,19 @@ static int acpi_ac_get_present(struct acpi_sbs *sbs)
 
        result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_CHARGER,
                                 0x13, (u8 *) & status);
-       if (!result)
-               sbs->charger_present = (status >> 15) & 0x1;
-       return result;
+
+       if (result)
+               return result;
+
+       /*
+        * The spec requires that bit 4 always be 1. If it's not set, assume
+        * that the implementation doesn't support an SBS charger
+        */
+       if (!((status >> 4) & 0x1))
+               return -ENODEV;
+
+       sbs->charger_present = (status >> 15) & 0x1;
+       return 0;
 }
 
 static ssize_t acpi_battery_alarm_show(struct device *dev,
@@ -483,16 +497,21 @@ static int acpi_battery_read(struct acpi_battery *battery)
                                  ACPI_SBS_MANAGER, 0x01, (u8 *)&state, 2);
        } else if (battery->id == 0)
                battery->present = 1;
+
        if (result || !battery->present)
                return result;
 
        if (saved_present != battery->present) {
                battery->update_time = 0;
                result = acpi_battery_get_info(battery);
-               if (result)
+               if (result) {
+                       battery->present = 0;
                        return result;
+               }
        }
        result = acpi_battery_get_state(battery);
+       if (result)
+               battery->present = 0;
        return result;
 }
 
@@ -524,6 +543,7 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
        result = power_supply_register(&sbs->device->dev, &battery->bat);
        if (result)
                goto end;
+
        result = device_create_file(battery->bat.dev, &alarm_attr);
        if (result)
                goto end;
@@ -554,6 +574,7 @@ static int acpi_charger_add(struct acpi_sbs *sbs)
        if (result)
                goto end;
 
+       sbs->charger_exists = 1;
        sbs->charger.name = "sbs-charger";
        sbs->charger.type = POWER_SUPPLY_TYPE_MAINS;
        sbs->charger.properties = sbs_ac_props;
@@ -580,9 +601,12 @@ static void acpi_sbs_callback(void *context)
        struct acpi_battery *bat;
        u8 saved_charger_state = sbs->charger_present;
        u8 saved_battery_state;
-       acpi_ac_get_present(sbs);
-       if (sbs->charger_present != saved_charger_state)
-               kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE);
+
+       if (sbs->charger_exists) {
+               acpi_ac_get_present(sbs);
+               if (sbs->charger_present != saved_charger_state)
+                       kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE);
+       }
 
        if (sbs->manager_present) {
                for (id = 0; id < MAX_SBS_BAT; ++id) {
@@ -598,12 +622,31 @@ static void acpi_sbs_callback(void *context)
        }
 }
 
+static int disable_sbs_manager(const struct dmi_system_id *d)
+{
+       sbs_manager_broken = true;
+       return 0;
+}
+
+static struct dmi_system_id acpi_sbs_dmi_table[] = {
+       {
+               .callback = disable_sbs_manager,
+               .ident = "Apple",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc.")
+               },
+       },
+       { },
+};
+
 static int acpi_sbs_add(struct acpi_device *device)
 {
        struct acpi_sbs *sbs;
        int result = 0;
        int id;
 
+       dmi_check_system(acpi_sbs_dmi_table);
+
        sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
        if (!sbs) {
                result = -ENOMEM;
@@ -619,17 +662,24 @@ static int acpi_sbs_add(struct acpi_device *device)
        device->driver_data = sbs;
 
        result = acpi_charger_add(sbs);
-       if (result)
+       if (result && result != -ENODEV)
                goto end;
 
-       result = acpi_manager_get_info(sbs);
-       if (!result) {
-               sbs->manager_present = 1;
-               for (id = 0; id < MAX_SBS_BAT; ++id)
-                       if ((sbs->batteries_supported & (1 << id)))
-                               acpi_battery_add(sbs, id);
-       } else
+       result = 0;
+
+       if (!sbs_manager_broken) {
+               result = acpi_manager_get_info(sbs);
+               if (!result) {
+                       sbs->manager_present = 0;
+                       for (id = 0; id < MAX_SBS_BAT; ++id)
+                               if ((sbs->batteries_supported & (1 << id)))
+                                       acpi_battery_add(sbs, id);
+               }
+       }
+
+       if (!sbs->manager_present)
                acpi_battery_add(sbs, 0);
+
        acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs);
       end:
        if (result)
index 54da4a3fe65e65d4b6334d93b82244f95c245b1f..05a31b573fc327b2a843c314294eff18d6b288ba 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/irq.h>
 #include <linux/dmi.h>
 #include <linux/device.h>
+#include <linux/interrupt.h>
 #include <linux/suspend.h>
 #include <linux/reboot.h>
 #include <linux/acpi.h>
@@ -626,6 +627,19 @@ static int acpi_freeze_begin(void)
        return 0;
 }
 
+static int acpi_freeze_prepare(void)
+{
+       acpi_enable_all_wakeup_gpes();
+       enable_irq_wake(acpi_gbl_FADT.sci_interrupt);
+       return 0;
+}
+
+static void acpi_freeze_restore(void)
+{
+       disable_irq_wake(acpi_gbl_FADT.sci_interrupt);
+       acpi_enable_all_runtime_gpes();
+}
+
 static void acpi_freeze_end(void)
 {
        acpi_scan_lock_release();
@@ -633,6 +647,8 @@ static void acpi_freeze_end(void)
 
 static const struct platform_freeze_ops acpi_freeze_ops = {
        .begin = acpi_freeze_begin,
+       .prepare = acpi_freeze_prepare,
+       .restore = acpi_freeze_restore,
        .end = acpi_freeze_end,
 };
 
index 07c8c5a5ee95cfec11ae94114a22398cd99273f6..834f35c4bf8d50061e1cae9f0b581cdad9467369 100644 (file)
@@ -661,7 +661,6 @@ EXPORT_SYMBOL(acpi_evaluate_dsm);
  * @uuid: UUID of requested functions, should be 16 bytes at least
  * @rev: revision number of requested functions
  * @funcs: bitmap of requested functions
- * @exclude: excluding special value, used to support i915 and nouveau
  *
  * Evaluate device's _DSM method to check whether it supports requested
  * functions. Currently only support 64 functions at maximum, should be
index 8e7e18567ae626fefd9039943662b84a627c8a58..807a88a0f394f8a639cbc3f6e2b78073078986cc 100644 (file)
@@ -411,12 +411,6 @@ static int __init video_set_bqc_offset(const struct dmi_system_id *d)
        return 0;
 }
 
-static int __init video_set_use_native_backlight(const struct dmi_system_id *d)
-{
-       use_native_backlight_dmi = true;
-       return 0;
-}
-
 static int __init video_disable_native_backlight(const struct dmi_system_id *d)
 {
        use_native_backlight_dmi = false;
@@ -467,265 +461,6 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
                },
        },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "ThinkPad X230",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X230"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "ThinkPad T430 and T430s",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T430"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "ThinkPad T430",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "2349D15"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "ThinkPad T431s",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "20AACTO1WW"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "ThinkPad Edge E530",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "3259A2G"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "ThinkPad Edge E530",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "3259CTO"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "ThinkPad Edge E530",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "3259HJG"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "ThinkPad W530",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W530"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-       .ident = "ThinkPad X1 Carbon",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X1 Carbon"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Lenovo Yoga 13",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Yoga 13"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Lenovo Yoga 2 11",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "Thinkpad Helix",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Dell Inspiron 7520",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Acer Aspire 5733Z",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5733Z"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Acer Aspire 5742G",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5742G"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Acer Aspire V5-171",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "V5-171"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Acer Aspire V5-431",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-431"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Acer Aspire V5-471G",
-        .matches = {
-               DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-471G"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Acer TravelMate B113",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate B113"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Acer Aspire V5-572G",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Acer Aspire"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "V5-572G/Dazzle_CX"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "Acer Aspire V5-573G",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Acer Aspire"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "V5-573G/Dazzle_HW"),
-               },
-       },
-       {
-        .callback = video_set_use_native_backlight,
-        .ident = "ASUS Zenbook Prime UX31A",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "UX31A"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP ProBook 4340s",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "HP ProBook 4340s"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP ProBook 4540s",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "HP ProBook 4540s"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP ProBook 2013 models",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook "),
-               DMI_MATCH(DMI_PRODUCT_NAME, " G1"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP EliteBook 2013 models",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook "),
-               DMI_MATCH(DMI_PRODUCT_NAME, " G1"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP EliteBook 2014 models",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook "),
-               DMI_MATCH(DMI_PRODUCT_NAME, " G2"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP ZBook 14",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 14"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP ZBook 15",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 15"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP ZBook 17",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 17"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP EliteBook 8470p",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8470p"),
-               },
-       },
-       {
-       .callback = video_set_use_native_backlight,
-       .ident = "HP EliteBook 8780w",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8780w"),
-               },
-       },
 
        /*
         * These models have a working acpi_video backlight control, and using
@@ -1419,6 +1154,23 @@ acpi_video_device_bind(struct acpi_video_bus *video,
        }
 }
 
+static bool acpi_video_device_in_dod(struct acpi_video_device *device)
+{
+       struct acpi_video_bus *video = device->video;
+       int i;
+
+       /* If we have a broken _DOD, no need to test */
+       if (!video->attached_count)
+               return true;
+
+       for (i = 0; i < video->attached_count; i++) {
+               if (video->attached_array[i].bind_info == device)
+                       return true;
+       }
+
+       return false;
+}
+
 /*
  *  Arg:
  *     video   : video bus device
@@ -1858,6 +1610,15 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
        static int count;
        char *name;
 
+       /*
+        * Do not create backlight device for video output
+        * device that is not in the enumerated list.
+        */
+       if (!acpi_video_device_in_dod(device)) {
+               dev_dbg(&device->dev->dev, "not in _DOD list, ignore\n");
+               return;
+       }
+
        result = acpi_video_init_brightness(device);
        if (result)
                return;
index c42feb2bacd0eb783bc94f0e10187d08ea907eea..27c43499977a2a6236e1c3675bf2bb42a314e42f 100644 (file)
@@ -174,6 +174,14 @@ static struct dmi_system_id video_detect_dmi_table[] = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5737"),
                },
        },
+       {
+       .callback = video_detect_force_vendor,
+       .ident = "Lenovo IdeaPad Z570",
+       .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"),
+               },
+       },
        { },
 };
 
index 3cf61a127ee54623f5dc4e99e5e5a0ad46868797..47bbdc1b5be327ddf913f920d8b78cfc4f253a0c 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/io.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
 #include <linux/amba/bus.h>
 #include <linux/sizes.h>
 
@@ -182,9 +183,15 @@ static int amba_probe(struct device *dev)
        int ret;
 
        do {
+               ret = dev_pm_domain_attach(dev, true);
+               if (ret == -EPROBE_DEFER)
+                       break;
+
                ret = amba_get_enable_pclk(pcdev);
-               if (ret)
+               if (ret) {
+                       dev_pm_domain_detach(dev, true);
                        break;
+               }
 
                pm_runtime_get_noresume(dev);
                pm_runtime_set_active(dev);
@@ -199,6 +206,7 @@ static int amba_probe(struct device *dev)
                pm_runtime_put_noidle(dev);
 
                amba_put_disable_pclk(pcdev);
+               dev_pm_domain_detach(dev, true);
        } while (0);
 
        return ret;
@@ -220,6 +228,7 @@ static int amba_remove(struct device *dev)
        pm_runtime_put_noidle(dev);
 
        amba_put_disable_pclk(pcdev);
+       dev_pm_domain_detach(dev, true);
 
        return ret;
 }
index 25d0ac32e7215ce85b6776405ef32c24eb32a191..c962886d7e7149fdde2c7ecda9ecf68ab04379ce 100644 (file)
@@ -498,8 +498,7 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id
        acard_ahci_pci_print_info(host);
 
        pci_set_master(pdev);
-       return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
-                                &acard_ahci_sht);
+       return ahci_host_activate(host, pdev->irq, &acard_ahci_sht);
 }
 
 module_pci_driver(acard_ahci_pci_driver);
index a0cc0edafc78e27a9b1cee2031d1a74e523425d2..5f039f1910677ff45d59cb4f7c0812f84f1dac23 100644 (file)
@@ -790,7 +790,7 @@ static void ahci_pci_print_info(struct ata_host *host)
  */
 static void ahci_p5wdh_workaround(struct ata_host *host)
 {
-       static struct dmi_system_id sysids[] = {
+       static const struct dmi_system_id sysids[] = {
                {
                        .ident = "P5W DH Deluxe",
                        .matches = {
@@ -1221,6 +1221,9 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
                goto single_msi;
        }
 
+       if (nvec > 1)
+               hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
+
        return nvec;
 
 single_msi:
@@ -1233,71 +1236,6 @@ intx:
        return 0;
 }
 
-/**
- *     ahci_host_activate - start AHCI host, request IRQs and register it
- *     @host: target ATA host
- *     @irq: base IRQ number to request
- *     @n_msis: number of MSIs allocated for this host
- *     @irq_handler: irq_handler used when requesting IRQs
- *     @irq_flags: irq_flags used when requesting IRQs
- *
- *     Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
- *     when multiple MSIs were allocated. That is one MSI per port, starting
- *     from @irq.
- *
- *     LOCKING:
- *     Inherited from calling layer (may sleep).
- *
- *     RETURNS:
- *     0 on success, -errno otherwise.
- */
-int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis)
-{
-       int i, rc;
-
-       /* Sharing Last Message among several ports is not supported */
-       if (n_msis < host->n_ports)
-               return -EINVAL;
-
-       rc = ata_host_start(host);
-       if (rc)
-               return rc;
-
-       for (i = 0; i < host->n_ports; i++) {
-               struct ahci_port_priv *pp = host->ports[i]->private_data;
-
-               /* Do not receive interrupts sent by dummy ports */
-               if (!pp) {
-                       disable_irq(irq + i);
-                       continue;
-               }
-
-               rc = devm_request_threaded_irq(host->dev, irq + i,
-                                              ahci_hw_interrupt,
-                                              ahci_thread_fn, IRQF_SHARED,
-                                              pp->irq_desc, host->ports[i]);
-               if (rc)
-                       goto out_free_irqs;
-       }
-
-       for (i = 0; i < host->n_ports; i++)
-               ata_port_desc(host->ports[i], "irq %d", irq + i);
-
-       rc = ata_host_register(host, &ahci_sht);
-       if (rc)
-               goto out_free_all_irqs;
-
-       return 0;
-
-out_free_all_irqs:
-       i = host->n_ports;
-out_free_irqs:
-       for (i--; i >= 0; i--)
-               devm_free_irq(host->dev, irq + i, host->ports[i]);
-
-       return rc;
-}
-
 static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        unsigned int board_id = ent->driver_data;
@@ -1306,7 +1244,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct device *dev = &pdev->dev;
        struct ahci_host_priv *hpriv;
        struct ata_host *host;
-       int n_ports, n_msis, i, rc;
+       int n_ports, i, rc;
        int ahci_pci_bar = AHCI_PCI_BAR_STANDARD;
 
        VPRINTK("ENTER\n");
@@ -1459,9 +1397,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
         */
        n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
 
-       n_msis = ahci_init_interrupts(pdev, n_ports, hpriv);
-       if (n_msis > 1)
-               hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
+       ahci_init_interrupts(pdev, n_ports, hpriv);
 
        host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
        if (!host)
@@ -1513,11 +1449,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_set_master(pdev);
 
-       if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
-               return ahci_host_activate(host, pdev->irq, n_msis);
-
-       return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
-                                &ahci_sht);
+       return ahci_host_activate(host, pdev->irq, &ahci_sht);
 }
 
 module_pci_driver(ahci_pci_driver);
index 59ae0ee001491ea7f9b8c1bdd61cb305e0cd648c..40f0e34f17af38e84167f79c162bf3b5922004ed 100644 (file)
@@ -53,7 +53,7 @@
 
 enum {
        AHCI_MAX_PORTS          = 32,
-       AHCI_MAX_CLKS           = 4,
+       AHCI_MAX_CLKS           = 5,
        AHCI_MAX_SG             = 168, /* hardware max is 64K */
        AHCI_DMA_BOUNDARY       = 0xffffffff,
        AHCI_MAX_CMDS           = 32,
@@ -304,7 +304,7 @@ struct ahci_port_priv {
        unsigned int            ncq_saw_d2h:1;
        unsigned int            ncq_saw_dmas:1;
        unsigned int            ncq_saw_sdb:1;
-       u32                     intr_status;    /* interrupts to handle */
+       atomic_t                intr_status;    /* interrupts to handle */
        spinlock_t              lock;           /* protects parent ata_port */
        u32                     intr_mask;      /* interrupts to enable */
        bool                    fbs_supported;  /* set iff FBS is supported */
@@ -388,11 +388,9 @@ int ahci_port_resume(struct ata_port *ap);
 void ahci_set_em_messages(struct ahci_host_priv *hpriv,
                          struct ata_port_info *pi);
 int ahci_reset_em(struct ata_host *host);
-irqreturn_t ahci_interrupt(int irq, void *dev_instance);
-irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance);
-irqreturn_t ahci_thread_fn(int irq, void *dev_instance);
 void ahci_print_info(struct ata_host *host, const char *scc_s);
-int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis);
+int ahci_host_activate(struct ata_host *host, int irq,
+                      struct scsi_host_template *sht);
 void ahci_error_handler(struct ata_port *ap);
 
 static inline void __iomem *__ahci_port_base(struct ata_host *host,
index f61ddb9146d6ff82971f60d18bc0cb291f6eb740..06f1d59fa678fe8da1da0b90d730025aca689796 100644 (file)
@@ -32,7 +32,6 @@ static const struct ata_port_info ahci_port_info = {
 static int ahci_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct ahci_platform_data *pdata = dev_get_platdata(dev);
        struct ahci_host_priv *hpriv;
        int rc;
 
@@ -44,29 +43,14 @@ static int ahci_probe(struct platform_device *pdev)
        if (rc)
                return rc;
 
-       /*
-        * Some platforms might need to prepare for mmio region access,
-        * which could be done in the following init call. So, the mmio
-        * region shouldn't be accessed before init (if provided) has
-        * returned successfully.
-        */
-       if (pdata && pdata->init) {
-               rc = pdata->init(dev, hpriv->mmio);
-               if (rc)
-                       goto disable_resources;
-       }
-
        if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci"))
                hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ;
 
        rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info);
        if (rc)
-               goto pdata_exit;
+               goto disable_resources;
 
        return 0;
-pdata_exit:
-       if (pdata && pdata->exit)
-               pdata->exit(dev);
 disable_resources:
        ahci_platform_disable_resources(hpriv);
        return rc;
index f03aab187f4da1cf16d3f2cb34642f4fcaad7434..0f8538f238b63f60fb323e075f6f721c5e22c16a 100644 (file)
@@ -434,7 +434,7 @@ static int xgene_ahci_mux_select(struct xgene_ahci_context *ctx)
        u32 val;
 
        /* Check for optional MUX resource */
-       if (IS_ERR(ctx->csr_mux))
+       if (!ctx->csr_mux)
                return 0;
 
        val = readl(ctx->csr_mux + SATA_ENET_CONFIG_REG);
@@ -484,7 +484,13 @@ static int xgene_ahci_probe(struct platform_device *pdev)
 
        /* Retrieve the optional IP mux resource */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
-       ctx->csr_mux = devm_ioremap_resource(dev, res);
+       if (res) {
+               void __iomem *csr = devm_ioremap_resource(dev, res);
+               if (IS_ERR(csr))
+                       return PTR_ERR(csr);
+
+               ctx->csr_mux = csr;
+       }
 
        dev_dbg(dev, "VAddr 0x%p Mmio VAddr 0x%p\n", ctx->csr_core,
                hpriv->mmio);
index b784e9de426a227943a681b1dba6cbdf885e3f11..5eb61c9e63da95cbd24a54ee0604262288c8a9f6 100644 (file)
@@ -1778,30 +1778,28 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
        }
 }
 
-static void ahci_port_intr(struct ata_port *ap)
+static void ahci_update_intr_status(struct ata_port *ap)
 {
        void __iomem *port_mmio = ahci_port_base(ap);
+       struct ahci_port_priv *pp = ap->private_data;
        u32 status;
 
        status = readl(port_mmio + PORT_IRQ_STAT);
        writel(status, port_mmio + PORT_IRQ_STAT);
 
-       ahci_handle_port_interrupt(ap, port_mmio, status);
+       atomic_or(status, &pp->intr_status);
 }
 
-irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
+static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
 {
        struct ata_port *ap = dev_instance;
        struct ahci_port_priv *pp = ap->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
-       unsigned long flags;
        u32 status;
 
-       spin_lock_irqsave(&ap->host->lock, flags);
-       status = pp->intr_status;
-       if (status)
-               pp->intr_status = 0;
-       spin_unlock_irqrestore(&ap->host->lock, flags);
+       status = atomic_xchg(&pp->intr_status, 0);
+       if (!status)
+               return IRQ_NONE;
 
        spin_lock_bh(ap->lock);
        ahci_handle_port_interrupt(ap, port_mmio, status);
@@ -1809,47 +1807,13 @@ irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
 
        return IRQ_HANDLED;
 }
-EXPORT_SYMBOL_GPL(ahci_thread_fn);
-
-static void ahci_hw_port_interrupt(struct ata_port *ap)
-{
-       void __iomem *port_mmio = ahci_port_base(ap);
-       struct ahci_port_priv *pp = ap->private_data;
-       u32 status;
-
-       status = readl(port_mmio + PORT_IRQ_STAT);
-       writel(status, port_mmio + PORT_IRQ_STAT);
 
-       pp->intr_status |= status;
-}
-
-irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)
+irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
 {
-       struct ata_port *ap_this = dev_instance;
-       struct ahci_port_priv *pp = ap_this->private_data;
-       struct ata_host *host = ap_this->host;
+       struct ata_host *host = dev_instance;
        struct ahci_host_priv *hpriv = host->private_data;
-       void __iomem *mmio = hpriv->mmio;
+       u32 irq_masked = hpriv->port_map;
        unsigned int i;
-       u32 irq_stat, irq_masked;
-
-       VPRINTK("ENTER\n");
-
-       spin_lock(&host->lock);
-
-       irq_stat = readl(mmio + HOST_IRQ_STAT);
-
-       if (!irq_stat) {
-               u32 status = pp->intr_status;
-
-               spin_unlock(&host->lock);
-
-               VPRINTK("EXIT\n");
-
-               return status ? IRQ_WAKE_THREAD : IRQ_NONE;
-       }
-
-       irq_masked = irq_stat & hpriv->port_map;
 
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap;
@@ -1859,7 +1823,7 @@ irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)
 
                ap = host->ports[i];
                if (ap) {
-                       ahci_hw_port_interrupt(ap);
+                       ahci_port_thread_fn(irq, ap);
                        VPRINTK("port %u\n", i);
                } else {
                        VPRINTK("port %u (no irq)\n", i);
@@ -1869,17 +1833,29 @@ irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)
                }
        }
 
-       writel(irq_stat, mmio + HOST_IRQ_STAT);
+       return IRQ_HANDLED;
+}
 
-       spin_unlock(&host->lock);
+static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
+{
+       struct ata_port *ap = dev_instance;
+       void __iomem *port_mmio = ahci_port_base(ap);
+       struct ahci_port_priv *pp = ap->private_data;
+       u32 status;
+
+       VPRINTK("ENTER\n");
+
+       status = readl(port_mmio + PORT_IRQ_STAT);
+       writel(status, port_mmio + PORT_IRQ_STAT);
+
+       atomic_or(status, &pp->intr_status);
 
        VPRINTK("EXIT\n");
 
        return IRQ_WAKE_THREAD;
 }
-EXPORT_SYMBOL_GPL(ahci_hw_interrupt);
 
-irqreturn_t ahci_interrupt(int irq, void *dev_instance)
+static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
 {
        struct ata_host *host = dev_instance;
        struct ahci_host_priv *hpriv;
@@ -1899,8 +1875,6 @@ irqreturn_t ahci_interrupt(int irq, void *dev_instance)
 
        irq_masked = irq_stat & hpriv->port_map;
 
-       spin_lock(&host->lock);
-
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap;
 
@@ -1909,7 +1883,7 @@ irqreturn_t ahci_interrupt(int irq, void *dev_instance)
 
                ap = host->ports[i];
                if (ap) {
-                       ahci_port_intr(ap);
+                       ahci_update_intr_status(ap);
                        VPRINTK("port %u\n", i);
                } else {
                        VPRINTK("port %u (no irq)\n", i);
@@ -1932,13 +1906,10 @@ irqreturn_t ahci_interrupt(int irq, void *dev_instance)
         */
        writel(irq_stat, mmio + HOST_IRQ_STAT);
 
-       spin_unlock(&host->lock);
-
        VPRINTK("EXIT\n");
 
-       return IRQ_RETVAL(handled);
+       return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
 }
-EXPORT_SYMBOL_GPL(ahci_interrupt);
 
 unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
 {
@@ -2349,13 +2320,8 @@ static int ahci_port_start(struct ata_port *ap)
         */
        pp->intr_mask = DEF_PORT_IRQ;
 
-       /*
-        * Switch to per-port locking in case each port has its own MSI vector.
-        */
-       if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
-               spin_lock_init(&pp->lock);
-               ap->lock = &pp->lock;
-       }
+       spin_lock_init(&pp->lock);
+       ap->lock = &pp->lock;
 
        ap->private_data = pp;
 
@@ -2472,6 +2438,105 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
 }
 EXPORT_SYMBOL_GPL(ahci_set_em_messages);
 
+static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
+                                        struct scsi_host_template *sht)
+{
+       int i, rc;
+
+       rc = ata_host_start(host);
+       if (rc)
+               return rc;
+
+       for (i = 0; i < host->n_ports; i++) {
+               struct ahci_port_priv *pp = host->ports[i]->private_data;
+
+               /* Do not receive interrupts sent by dummy ports */
+               if (!pp) {
+                       disable_irq(irq + i);
+                       continue;
+               }
+
+               rc = devm_request_threaded_irq(host->dev, irq + i,
+                                              ahci_multi_irqs_intr,
+                                              ahci_port_thread_fn, IRQF_SHARED,
+                                              pp->irq_desc, host->ports[i]);
+               if (rc)
+                       goto out_free_irqs;
+       }
+
+       for (i = 0; i < host->n_ports; i++)
+               ata_port_desc(host->ports[i], "irq %d", irq + i);
+
+       rc = ata_host_register(host, sht);
+       if (rc)
+               goto out_free_all_irqs;
+
+       return 0;
+
+out_free_all_irqs:
+       i = host->n_ports;
+out_free_irqs:
+       for (i--; i >= 0; i--)
+               devm_free_irq(host->dev, irq + i, host->ports[i]);
+
+       return rc;
+}
+
+static int ahci_host_activate_single_irq(struct ata_host *host, int irq,
+                                        struct scsi_host_template *sht)
+{
+       int i, rc;
+
+       rc = ata_host_start(host);
+       if (rc)
+               return rc;
+
+       rc = devm_request_threaded_irq(host->dev, irq, ahci_single_irq_intr,
+                                      ahci_thread_fn, IRQF_SHARED,
+                                      dev_driver_string(host->dev), host);
+       if (rc)
+               return rc;
+
+       for (i = 0; i < host->n_ports; i++)
+               ata_port_desc(host->ports[i], "irq %d", irq);
+
+       rc = ata_host_register(host, sht);
+       if (rc)
+               devm_free_irq(host->dev, irq, host);
+
+       return rc;
+}
+
+/**
+ *     ahci_host_activate - start AHCI host, request IRQs and register it
+ *     @host: target ATA host
+ *     @irq: base IRQ number to request
+ *     @sht: scsi_host_template to use when registering the host
+ *
+ *     Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
+ *     when multiple MSIs were allocated. That is one MSI per port, starting
+ *     from @irq.
+ *
+ *     LOCKING:
+ *     Inherited from calling layer (may sleep).
+ *
+ *     RETURNS:
+ *     0 on success, -errno otherwise.
+ */
+int ahci_host_activate(struct ata_host *host, int irq,
+                      struct scsi_host_template *sht)
+{
+       struct ahci_host_priv *hpriv = host->private_data;
+       int rc;
+
+       if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
+               rc = ahci_host_activate_multi_irqs(host, irq, sht);
+       else
+               rc = ahci_host_activate_single_irq(host, irq, sht);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(ahci_host_activate);
+
 MODULE_AUTHOR("Jeff Garzik");
 MODULE_DESCRIPTION("Common AHCI SATA low-level routines");
 MODULE_LICENSE("GPL");
index 5b92c290e6c6e99dccb24a45cb76d81bfd23e6a8..0b03f90566924182e4c0411a798c66837d4530ac 100644 (file)
@@ -49,7 +49,7 @@ static struct scsi_host_template ahci_platform_sht = {
  * RETURNS:
  * 0 on success otherwise a negative error code
  */
-int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
+static int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
 {
        int rc, i;
 
@@ -77,7 +77,6 @@ disable_phys:
        }
        return rc;
 }
-EXPORT_SYMBOL_GPL(ahci_platform_enable_phys);
 
 /**
  * ahci_platform_disable_phys - Disable PHYs
@@ -85,7 +84,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_phys);
  *
  * This function disables all PHYs found in hpriv->phys.
  */
-void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
+static void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
 {
        int i;
 
@@ -97,7 +96,6 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
                phy_exit(hpriv->phys[i]);
        }
 }
-EXPORT_SYMBOL_GPL(ahci_platform_disable_phys);
 
 /**
  * ahci_platform_enable_clks - Enable platform clocks
@@ -495,20 +493,14 @@ int ahci_platform_init_host(struct platform_device *pdev,
        ahci_init_controller(host);
        ahci_print_info(host, "platform");
 
-       return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
-                                &ahci_platform_sht);
+       return ahci_host_activate(host, irq, &ahci_platform_sht);
 }
 EXPORT_SYMBOL_GPL(ahci_platform_init_host);
 
 static void ahci_host_stop(struct ata_host *host)
 {
-       struct device *dev = host->dev;
-       struct ahci_platform_data *pdata = dev_get_platdata(dev);
        struct ahci_host_priv *hpriv = host->private_data;
 
-       if (pdata && pdata->exit)
-               pdata->exit(dev);
-
        ahci_platform_disable_resources(hpriv);
 }
 
@@ -592,7 +584,6 @@ EXPORT_SYMBOL_GPL(ahci_platform_resume_host);
  */
 int ahci_platform_suspend(struct device *dev)
 {
-       struct ahci_platform_data *pdata = dev_get_platdata(dev);
        struct ata_host *host = dev_get_drvdata(dev);
        struct ahci_host_priv *hpriv = host->private_data;
        int rc;
@@ -601,19 +592,9 @@ int ahci_platform_suspend(struct device *dev)
        if (rc)
                return rc;
 
-       if (pdata && pdata->suspend) {
-               rc = pdata->suspend(dev);
-               if (rc)
-                       goto resume_host;
-       }
-
        ahci_platform_disable_resources(hpriv);
 
        return 0;
-
-resume_host:
-       ahci_platform_resume_host(dev);
-       return rc;
 }
 EXPORT_SYMBOL_GPL(ahci_platform_suspend);
 
@@ -629,7 +610,6 @@ EXPORT_SYMBOL_GPL(ahci_platform_suspend);
  */
 int ahci_platform_resume(struct device *dev)
 {
-       struct ahci_platform_data *pdata = dev_get_platdata(dev);
        struct ata_host *host = dev_get_drvdata(dev);
        struct ahci_host_priv *hpriv = host->private_data;
        int rc;
@@ -638,12 +618,6 @@ int ahci_platform_resume(struct device *dev)
        if (rc)
                return rc;
 
-       if (pdata && pdata->resume) {
-               rc = pdata->resume(dev);
-               if (rc)
-                       goto disable_resources;
-       }
-
        rc = ahci_platform_resume_host(dev);
        if (rc)
                goto disable_resources;
index f3e7b9f894cd131daea921fbd0e149fcaccadaeb..c5ba15af87d3eccd8553f0daab21a4f5237e8d91 100644 (file)
@@ -4261,10 +4261,10 @@ static unsigned long ata_dev_blacklisted(const struct ata_device *dev)
        ata_id_c_string(dev->id, model_rev, ATA_ID_FW_REV, sizeof(model_rev));
 
        while (ad->model_num) {
-               if (glob_match(model_num, ad->model_num)) {
+               if (glob_match(ad->model_num, model_num)) {
                        if (ad->model_rev == NULL)
                                return ad->horkage;
-                       if (glob_match(model_rev, ad->model_rev))
+                       if (glob_match(ad->model_rev, model_rev))
                                return ad->horkage;
                }
                ad++;
@@ -6227,7 +6227,7 @@ int ata_host_activate(struct ata_host *host, int irq,
        }
 
        rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags,
-                             dev_driver_string(host->dev), host);
+                             dev_name(host->dev), host);
        if (rc)
                return rc;
 
@@ -6772,32 +6772,28 @@ const struct ata_port_info ata_dummy_port_info = {
 /*
  * Utility print functions
  */
-int ata_port_printk(const struct ata_port *ap, const char *level,
-                   const char *fmt, ...)
+void ata_port_printk(const struct ata_port *ap, const char *level,
+                    const char *fmt, ...)
 {
        struct va_format vaf;
        va_list args;
-       int r;
 
        va_start(args, fmt);
 
        vaf.fmt = fmt;
        vaf.va = &args;
 
-       r = printk("%sata%u: %pV", level, ap->print_id, &vaf);
+       printk("%sata%u: %pV", level, ap->print_id, &vaf);
 
        va_end(args);
-
-       return r;
 }
 EXPORT_SYMBOL(ata_port_printk);
 
-int ata_link_printk(const struct ata_link *link, const char *level,
-                   const char *fmt, ...)
+void ata_link_printk(const struct ata_link *link, const char *level,
+                    const char *fmt, ...)
 {
        struct va_format vaf;
        va_list args;
-       int r;
 
        va_start(args, fmt);
 
@@ -6805,37 +6801,32 @@ int ata_link_printk(const struct ata_link *link, const char *level,
        vaf.va = &args;
 
        if (sata_pmp_attached(link->ap) || link->ap->slave_link)
-               r = printk("%sata%u.%02u: %pV",
-                          level, link->ap->print_id, link->pmp, &vaf);
+               printk("%sata%u.%02u: %pV",
+                      level, link->ap->print_id, link->pmp, &vaf);
        else
-               r = printk("%sata%u: %pV",
-                          level, link->ap->print_id, &vaf);
+               printk("%sata%u: %pV",
+                      level, link->ap->print_id, &vaf);
 
        va_end(args);
-
-       return r;
 }
 EXPORT_SYMBOL(ata_link_printk);
 
-int ata_dev_printk(const struct ata_device *dev, const char *level,
+void ata_dev_printk(const struct ata_device *dev, const char *level,
                    const char *fmt, ...)
 {
        struct va_format vaf;
        va_list args;
-       int r;
 
        va_start(args, fmt);
 
        vaf.fmt = fmt;
        vaf.va = &args;
 
-       r = printk("%sata%u.%02u: %pV",
-                  level, dev->link->ap->print_id, dev->link->pmp + dev->devno,
-                  &vaf);
+       printk("%sata%u.%02u: %pV",
+              level, dev->link->ap->print_id, dev->link->pmp + dev->devno,
+              &vaf);
 
        va_end(args);
-
-       return r;
 }
 EXPORT_SYMBOL(ata_dev_printk);
 
index 1121153f1ecde7e08502c2a0a33bc501abc6a9bd..db90aa35cb71e9456d176dcdd1214b760165cc4c 100644 (file)
@@ -2008,13 +2008,15 @@ static int ata_bus_softreset(struct ata_port *ap, unsigned int devmask,
 
        DPRINTK("ata%u: bus reset via SRST\n", ap->print_id);
 
-       /* software reset.  causes dev0 to be selected */
-       iowrite8(ap->ctl, ioaddr->ctl_addr);
-       udelay(20);     /* FIXME: flush */
-       iowrite8(ap->ctl | ATA_SRST, ioaddr->ctl_addr);
-       udelay(20);     /* FIXME: flush */
-       iowrite8(ap->ctl, ioaddr->ctl_addr);
-       ap->last_ctl = ap->ctl;
+       if (ap->ioaddr.ctl_addr) {
+               /* software reset.  causes dev0 to be selected */
+               iowrite8(ap->ctl, ioaddr->ctl_addr);
+               udelay(20);     /* FIXME: flush */
+               iowrite8(ap->ctl | ATA_SRST, ioaddr->ctl_addr);
+               udelay(20);     /* FIXME: flush */
+               iowrite8(ap->ctl, ioaddr->ctl_addr);
+               ap->last_ctl = ap->ctl;
+       }
 
        /* wait the port to become ready */
        return ata_sff_wait_after_reset(&ap->link, devmask, deadline);
@@ -2215,10 +2217,6 @@ void ata_sff_error_handler(struct ata_port *ap)
 
        spin_unlock_irqrestore(ap->lock, flags);
 
-       /* ignore ata_sff_softreset if ctl isn't accessible */
-       if (softreset == ata_sff_softreset && !ap->ioaddr.ctl_addr)
-               softreset = NULL;
-
        /* ignore built-in hardresets if SCR access is not available */
        if ((hardreset == sata_std_hardreset ||
             hardreset == sata_sff_hardreset) && !sata_scr_valid(&ap->link))
index af424573c2ff1700777415f1c95c1b43582d2a2b..989ff5ac69ecc8a583271003518037d121e561d1 100644 (file)
@@ -221,13 +221,10 @@ static int pata_imx_resume(struct device *dev)
 
        return 0;
 }
-
-static const struct dev_pm_ops pata_imx_pm_ops = {
-       .suspend        = pata_imx_suspend,
-       .resume         = pata_imx_resume,
-};
 #endif
 
+static SIMPLE_DEV_PM_OPS(pata_imx_pm_ops, pata_imx_suspend, pata_imx_resume);
+
 static const struct of_device_id imx_pata_dt_ids[] = {
        {
                .compatible = "fsl,imx27-pata",
@@ -244,9 +241,7 @@ static struct platform_driver pata_imx_driver = {
                .name           = DRV_NAME,
                .of_match_table = imx_pata_dt_ids,
                .owner          = THIS_MODULE,
-#ifdef CONFIG_PM_SLEEP
                .pm             = &pata_imx_pm_ops,
-#endif
        },
 };
 
index a7e95a54c7827582c5a47344cb3dcc313e53ce22..64965398914a7b28cf78b11cffd2e129c7de5890 100644 (file)
@@ -35,25 +35,14 @@ static int pata_of_platform_probe(struct platform_device *ofdev)
                return -EINVAL;
        }
 
-       if (of_device_is_compatible(dn, "electra-ide")) {
-               /* Altstatus is really at offset 0x3f6 from the primary window
-                * on electra-ide. Adjust ctl_res and io_res accordingly.
-                */
-               ctl_res = io_res;
-               ctl_res.start = ctl_res.start+0x3f6;
-               io_res.end = ctl_res.start-1;
-       } else {
-               ret = of_address_to_resource(dn, 1, &ctl_res);
-               if (ret) {
-                       dev_err(&ofdev->dev, "can't get CTL address from "
-                               "device tree\n");
-                       return -EINVAL;
-               }
+       ret = of_address_to_resource(dn, 1, &ctl_res);
+       if (ret) {
+               dev_err(&ofdev->dev, "can't get CTL address from "
+                       "device tree\n");
+               return -EINVAL;
        }
 
        irq_res = platform_get_resource(ofdev, IORESOURCE_IRQ, 0);
-       if (irq_res)
-               irq_res->flags = 0;
 
        prop = of_get_property(dn, "reg-shift", NULL);
        if (prop)
@@ -79,8 +68,7 @@ static int pata_of_platform_probe(struct platform_device *ofdev)
 
 static struct of_device_id pata_of_platform_match[] = {
        { .compatible = "ata-generic", },
-       { .compatible = "electra-ide", },
-       {},
+       { },
 };
 MODULE_DEVICE_TABLE(of, pata_of_platform_match);
 
index a5579b55e3329ee395cfbddd30eb034711e0aab1..f8cff3e247c525c339110c3b452d9fb3bc03bd7e 100644 (file)
@@ -118,7 +118,7 @@ int __pata_platform_probe(struct device *dev, struct resource *io_res,
         */
        if (irq_res && irq_res->start > 0) {
                irq = irq_res->start;
-               irq_flags = irq_res->flags;
+               irq_flags = irq_res->flags & IRQF_TRIGGER_MASK;
        }
 
        /*
@@ -213,8 +213,6 @@ static int pata_platform_probe(struct platform_device *pdev)
         * And the IRQ
         */
        irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (irq_res)
-               irq_res->flags = pp_info ? pp_info->irq_flags : 0;
 
        return __pata_platform_probe(&pdev->dev, io_res, ctl_res, irq_res,
                                     pp_info ? pp_info->ioport_shift : 0,
index fc5f31d4828e4ab5c27ec4463a1dc4423da53678..57de02123c4cde74a69314f0d8d408140f459377 100644 (file)
@@ -251,12 +251,18 @@ static void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev
        pci_write_config_byte(pdev, 0x54, ultra_cfg);
 }
 
-static struct scsi_host_template serverworks_sht = {
+static struct scsi_host_template serverworks_osb4_sht = {
+       ATA_BMDMA_SHT(DRV_NAME),
+       .sg_tablesize   = LIBATA_DUMB_MAX_PRD,
+};
+
+static struct scsi_host_template serverworks_csb_sht = {
        ATA_BMDMA_SHT(DRV_NAME),
 };
 
 static struct ata_port_operations serverworks_osb4_port_ops = {
        .inherits       = &ata_bmdma_port_ops,
+       .qc_prep        = ata_bmdma_dumb_qc_prep,
        .cable_detect   = serverworks_cable_detect,
        .mode_filter    = serverworks_osb4_filter,
        .set_piomode    = serverworks_set_piomode,
@@ -265,6 +271,7 @@ static struct ata_port_operations serverworks_osb4_port_ops = {
 
 static struct ata_port_operations serverworks_csb_port_ops = {
        .inherits       = &serverworks_osb4_port_ops,
+       .qc_prep        = ata_bmdma_qc_prep,
        .mode_filter    = serverworks_csb_filter,
 };
 
@@ -404,6 +411,7 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id
                }
        };
        const struct ata_port_info *ppi[] = { &info[id->driver_data], NULL };
+       struct scsi_host_template *sht = &serverworks_csb_sht;
        int rc;
 
        rc = pcim_enable_device(pdev);
@@ -417,6 +425,7 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id
                /* Select non UDMA capable OSB4 if we can't do fixups */
                if (rc < 0)
                        ppi[0] = &info[1];
+               sht = &serverworks_osb4_sht;
        }
        /* setup CSB5/CSB6 : South Bridge and IDE option RAID */
        else if ((pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) ||
@@ -433,7 +442,7 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id
                        ppi[1] = &ata_dummy_port_info;
        }
 
-       return ata_pci_bmdma_init_one(pdev, ppi, &serverworks_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, sht, NULL, 0);
 }
 
 #ifdef CONFIG_PM_SLEEP
index da3bc2709c6318671c0d485751416f48fe973b68..ce2b99a1ed70211bfe54818e3510df8b8b71a4de 100644 (file)
@@ -568,8 +568,7 @@ static int ahci_highbank_probe(struct platform_device *pdev)
        ahci_init_controller(host);
        ahci_print_info(host, "platform");
 
-       rc = ata_host_activate(host, irq, ahci_interrupt, 0,
-                                       &ahci_highbank_platform_sht);
+       rc = ahci_host_activate(host, irq, &ahci_highbank_platform_sht);
        if (rc)
                goto err0;
 
index 134f763d90fdaf36668198cf6ac3d0406c77efdc..61a33f4ba608cdc2fe01f691f46f92b2aba1d3e7 100644 (file)
@@ -252,6 +252,9 @@ config DMA_CMA
          to allocate big physically-contiguous blocks of memory for use with
          hardware components that do not support I/O map nor scatter-gather.
 
+         You can disable CMA by specifying "cma=0" on the kernel's command
+         line.
+
          For more information see <include/linux/dma-contiguous.h>.
          If unsure, say "n".
 
index 6cd08e145bfa042b3073c10a993c9734741faa4e..9e8bbdd470ca8e8842ba7ef56fdd120f7e31dc13 100644 (file)
@@ -10,6 +10,8 @@
 #include <linux/dma-mapping.h>
 #include <linux/export.h>
 #include <linux/gfp.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
 #include <asm-generic/dma-coherent.h>
 
 /*
@@ -267,3 +269,73 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
        return ret;
 }
 EXPORT_SYMBOL(dma_common_mmap);
+
+#ifdef CONFIG_MMU
+/*
+ * remaps an array of PAGE_SIZE pages into another vm_area
+ * Cannot be used in non-sleeping contexts
+ */
+void *dma_common_pages_remap(struct page **pages, size_t size,
+                       unsigned long vm_flags, pgprot_t prot,
+                       const void *caller)
+{
+       struct vm_struct *area;
+
+       area = get_vm_area_caller(size, vm_flags, caller);
+       if (!area)
+               return NULL;
+
+       area->pages = pages;
+
+       if (map_vm_area(area, prot, pages)) {
+               vunmap(area->addr);
+               return NULL;
+       }
+
+       return area->addr;
+}
+
+/*
+ * remaps an allocated contiguous region into another vm_area.
+ * Cannot be used in non-sleeping contexts
+ */
+
+void *dma_common_contiguous_remap(struct page *page, size_t size,
+                       unsigned long vm_flags,
+                       pgprot_t prot, const void *caller)
+{
+       int i;
+       struct page **pages;
+       void *ptr;
+       unsigned long pfn;
+
+       pages = kmalloc(sizeof(struct page *) << get_order(size), GFP_KERNEL);
+       if (!pages)
+               return NULL;
+
+       for (i = 0, pfn = page_to_pfn(page); i < (size >> PAGE_SHIFT); i++)
+               pages[i] = pfn_to_page(pfn + i);
+
+       ptr = dma_common_pages_remap(pages, size, vm_flags, prot, caller);
+
+       kfree(pages);
+
+       return ptr;
+}
+
+/*
+ * unmaps a range previously mapped by dma_common_*_remap
+ */
+void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags)
+{
+       struct vm_struct *area = find_vm_area(cpu_addr);
+
+       if (!area || (area->flags & vm_flags) != vm_flags) {
+               WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
+               return;
+       }
+
+       unmap_kernel_range((unsigned long)cpu_addr, size);
+       vunmap(cpu_addr);
+}
+#endif
index a2e13e250bba2f54eea93e72e9692340d3298d66..7c5d87191b286a1275f43ef380d7cc0d9db6b43b 100644 (file)
@@ -373,6 +373,45 @@ static ssize_t show_phys_device(struct device *dev,
        return sprintf(buf, "%d\n", mem->phys_device);
 }
 
+#ifdef CONFIG_MEMORY_HOTREMOVE
+static ssize_t show_valid_zones(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct memory_block *mem = to_memory_block(dev);
+       unsigned long start_pfn, end_pfn;
+       unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
+       struct page *first_page;
+       struct zone *zone;
+
+       start_pfn = section_nr_to_pfn(mem->start_section_nr);
+       end_pfn = start_pfn + nr_pages;
+       first_page = pfn_to_page(start_pfn);
+
+       /* The block contains more than one zone can not be offlined. */
+       if (!test_pages_in_a_zone(start_pfn, end_pfn))
+               return sprintf(buf, "none\n");
+
+       zone = page_zone(first_page);
+
+       if (zone_idx(zone) == ZONE_MOVABLE - 1) {
+               /*The mem block is the last memoryblock of this zone.*/
+               if (end_pfn == zone_end_pfn(zone))
+                       return sprintf(buf, "%s %s\n",
+                                       zone->name, (zone + 1)->name);
+       }
+
+       if (zone_idx(zone) == ZONE_MOVABLE) {
+               /*The mem block is the first memoryblock of ZONE_MOVABLE.*/
+               if (start_pfn == zone->zone_start_pfn)
+                       return sprintf(buf, "%s %s\n",
+                                       zone->name, (zone - 1)->name);
+       }
+
+       return sprintf(buf, "%s\n", zone->name);
+}
+static DEVICE_ATTR(valid_zones, 0444, show_valid_zones, NULL);
+#endif
+
 static DEVICE_ATTR(phys_index, 0444, show_mem_start_phys_index, NULL);
 static DEVICE_ATTR(state, 0644, show_mem_state, store_mem_state);
 static DEVICE_ATTR(phys_device, 0444, show_phys_device, NULL);
@@ -523,6 +562,9 @@ static struct attribute *memory_memblk_attrs[] = {
        &dev_attr_state.attr,
        &dev_attr_phys_device.attr,
        &dev_attr_removable.attr,
+#ifdef CONFIG_MEMORY_HOTREMOVE
+       &dev_attr_valid_zones.attr,
+#endif
        NULL
 };
 
index d51c49c9bafac65ce4ca0b064fc4c825674dc484..472168cd0c9787a99e7d7d70d4a22cb8bf8680ab 100644 (file)
@@ -289,8 +289,6 @@ static int register_node(struct node *node, int num, struct node *parent)
                device_create_file(&node->dev, &dev_attr_distance);
                device_create_file(&node->dev, &dev_attr_vmstat);
 
-               scan_unevictable_register_node(node);
-
                hugetlb_register_node(node);
 
                compaction_register_node(node);
@@ -314,7 +312,6 @@ void unregister_node(struct node *node)
        device_remove_file(&node->dev, &dev_attr_distance);
        device_remove_file(&node->dev, &dev_attr_vmstat);
 
-       scan_unevictable_unregister_node(node);
        hugetlb_unregister_node(node);          /* no-op, if memoryless node */
 
        device_unregister(&node->dev);
index ab4f4ce02722d0520e53f30baaeefd00ada8aea6..b2afc29403f9e887554d441a0c36629ccb49bce6 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
 #include <linux/idr.h>
 #include <linux/acpi.h>
 #include <linux/clk/clk-conf.h>
@@ -506,11 +507,12 @@ static int platform_drv_probe(struct device *_dev)
        if (ret < 0)
                return ret;
 
-       acpi_dev_pm_attach(_dev, true);
-
-       ret = drv->probe(dev);
-       if (ret)
-               acpi_dev_pm_detach(_dev, true);
+       ret = dev_pm_domain_attach(_dev, true);
+       if (ret != -EPROBE_DEFER) {
+               ret = drv->probe(dev);
+               if (ret)
+                       dev_pm_domain_detach(_dev, true);
+       }
 
        if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
                dev_warn(_dev, "probe deferral not supported\n");
@@ -532,7 +534,7 @@ static int platform_drv_remove(struct device *_dev)
        int ret;
 
        ret = drv->remove(dev);
-       acpi_dev_pm_detach(_dev, true);
+       dev_pm_domain_detach(_dev, true);
 
        return ret;
 }
@@ -543,7 +545,7 @@ static void platform_drv_shutdown(struct device *_dev)
        struct platform_device *dev = to_platform_device(_dev);
 
        drv->shutdown(dev);
-       acpi_dev_pm_detach(_dev, true);
+       dev_pm_domain_detach(_dev, true);
 }
 
 /**
index b99e6c06ee678ecb5bcc6e206d3954976832eb38..78369305e0698109a42b172af8b925c05209701d 100644 (file)
@@ -368,8 +368,13 @@ int pm_clk_suspend(struct device *dev)
 
        spin_lock_irqsave(&psd->lock, flags);
 
-       list_for_each_entry_reverse(ce, &psd->clock_list, node)
-               clk_disable(ce->clk);
+       list_for_each_entry_reverse(ce, &psd->clock_list, node) {
+               if (ce->status < PCE_STATUS_ERROR) {
+                       if (ce->status == PCE_STATUS_ENABLED)
+                               clk_disable(ce->clk);
+                       ce->status = PCE_STATUS_ACQUIRED;
+               }
+       }
 
        spin_unlock_irqrestore(&psd->lock, flags);
 
@@ -385,6 +390,7 @@ int pm_clk_resume(struct device *dev)
        struct pm_subsys_data *psd = dev_to_psd(dev);
        struct pm_clock_entry *ce;
        unsigned long flags;
+       int ret;
 
        dev_dbg(dev, "%s()\n", __func__);
 
@@ -394,8 +400,13 @@ int pm_clk_resume(struct device *dev)
 
        spin_lock_irqsave(&psd->lock, flags);
 
-       list_for_each_entry(ce, &psd->clock_list, node)
-               __pm_clk_enable(dev, ce->clk);
+       list_for_each_entry(ce, &psd->clock_list, node) {
+               if (ce->status < PCE_STATUS_ERROR) {
+                       ret = __pm_clk_enable(dev, ce->clk);
+                       if (!ret)
+                               ce->status = PCE_STATUS_ENABLED;
+               }
+       }
 
        spin_unlock_irqrestore(&psd->lock, flags);
 
index df2e5eeaeb05570757ec7872c2022484c1824c87..b0f138806bbc4cb67bc782c075a9228acc500f12 100644 (file)
@@ -11,6 +11,8 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/pm_clock.h>
+#include <linux/acpi.h>
+#include <linux/pm_domain.h>
 
 /**
  * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
@@ -82,3 +84,53 @@ int dev_pm_put_subsys_data(struct device *dev)
        return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
+
+/**
+ * dev_pm_domain_attach - Attach a device to its PM domain.
+ * @dev: Device to attach.
+ * @power_on: Used to indicate whether we should power on the device.
+ *
+ * The @dev may only be attached to a single PM domain. By iterating through
+ * the available alternatives we try to find a valid PM domain for the device.
+ * As attachment succeeds, the ->detach() callback in the struct dev_pm_domain
+ * should be assigned by the corresponding attach function.
+ *
+ * This function should typically be invoked from subsystem level code during
+ * the probe phase. Especially for those that holds devices which requires
+ * power management through PM domains.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ *
+ * Returns 0 on successfully attached PM domain or negative error code.
+ */
+int dev_pm_domain_attach(struct device *dev, bool power_on)
+{
+       int ret;
+
+       ret = acpi_dev_pm_attach(dev, power_on);
+       if (ret)
+               ret = genpd_dev_pm_attach(dev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_attach);
+
+/**
+ * dev_pm_domain_detach - Detach a device from its PM domain.
+ * @dev: Device to attach.
+ * @power_off: Used to indicate whether we should power off the device.
+ *
+ * This functions will reverse the actions from dev_pm_domain_attach() and thus
+ * try to detach the @dev from its PM domain. Typically it should be invoked
+ * from subsystem level code during the remove phase.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ */
+void dev_pm_domain_detach(struct device *dev, bool power_off)
+{
+       if (dev->pm_domain && dev->pm_domain->detach)
+               dev->pm_domain->detach(dev, power_off);
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_detach);
index eee55c1e5fde49310779c9fc4e15aa8ce5415114..40bc2f4072cc28ea4138ae36b3b08cb96f2ed158 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/kernel.h>
 #include <linux/io.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_domain.h>
 #include <linux/pm_qos.h>
        __routine = genpd->dev_ops.callback;                    \
        if (__routine) {                                        \
                __ret = __routine(dev);                         \
-       } else {                                                \
-               __routine = dev_gpd_data(dev)->ops.callback;    \
-               if (__routine)                                  \
-                       __ret = __routine(dev);                 \
        }                                                       \
        __ret;                                                  \
 })
@@ -70,8 +67,6 @@ static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
        return genpd;
 }
 
-#ifdef CONFIG_PM
-
 struct generic_pm_domain *dev_to_genpd(struct device *dev)
 {
        if (IS_ERR_OR_NULL(dev->pm_domain))
@@ -147,13 +142,13 @@ static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd)
 {
        s64 usecs64;
 
-       if (!genpd->cpu_data)
+       if (!genpd->cpuidle_data)
                return;
 
        usecs64 = genpd->power_on_latency_ns;
        do_div(usecs64, NSEC_PER_USEC);
-       usecs64 += genpd->cpu_data->saved_exit_latency;
-       genpd->cpu_data->idle_state->exit_latency = usecs64;
+       usecs64 += genpd->cpuidle_data->saved_exit_latency;
+       genpd->cpuidle_data->idle_state->exit_latency = usecs64;
 }
 
 /**
@@ -193,9 +188,9 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd)
                return 0;
        }
 
-       if (genpd->cpu_data) {
+       if (genpd->cpuidle_data) {
                cpuidle_pause_and_lock();
-               genpd->cpu_data->idle_state->disabled = true;
+               genpd->cpuidle_data->idle_state->disabled = true;
                cpuidle_resume_and_unlock();
                goto out;
        }
@@ -285,8 +280,6 @@ int pm_genpd_name_poweron(const char *domain_name)
        return genpd ? pm_genpd_poweron(genpd) : -EINVAL;
 }
 
-#endif /* CONFIG_PM */
-
 #ifdef CONFIG_PM_RUNTIME
 
 static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd,
@@ -430,7 +423,7 @@ static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
  * Queue up the execution of pm_genpd_poweroff() unless it's already been done
  * before.
  */
-void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
+static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
 {
        queue_work(pm_wq, &genpd->power_off_work);
 }
@@ -520,17 +513,17 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
                }
        }
 
-       if (genpd->cpu_data) {
+       if (genpd->cpuidle_data) {
                /*
-                * If cpu_data is set, cpuidle should turn the domain off when
-                * the CPU in it is idle.  In that case we don't decrement the
-                * subdomain counts of the master domains, so that power is not
-                * removed from the current domain prematurely as a result of
-                * cutting off the masters' power.
+                * If cpuidle_data is set, cpuidle should turn the domain off
+                * when the CPU in it is idle.  In that case we don't decrement
+                * the subdomain counts of the master domains, so that power is
+                * not removed from the current domain prematurely as a result
+                * of cutting off the masters' power.
                 */
                genpd->status = GPD_STATE_POWER_OFF;
                cpuidle_pause_and_lock();
-               genpd->cpu_data->idle_state->disabled = false;
+               genpd->cpuidle_data->idle_state->disabled = false;
                cpuidle_resume_and_unlock();
                goto out;
        }
@@ -619,8 +612,6 @@ static int pm_genpd_runtime_suspend(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       might_sleep_if(!genpd->dev_irq_safe);
-
        stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
        if (stop_ok && !stop_ok(dev))
                return -EBUSY;
@@ -665,8 +656,6 @@ static int pm_genpd_runtime_resume(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       might_sleep_if(!genpd->dev_irq_safe);
-
        /* If power.irq_safe, the PM domain is never powered off. */
        if (dev->power.irq_safe)
                return genpd_start_dev_no_timing(genpd, dev);
@@ -733,6 +722,13 @@ void pm_genpd_poweroff_unused(void)
        mutex_unlock(&gpd_list_lock);
 }
 
+static int __init genpd_poweroff_unused(void)
+{
+       pm_genpd_poweroff_unused();
+       return 0;
+}
+late_initcall(genpd_poweroff_unused);
+
 #else
 
 static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
@@ -741,6 +737,9 @@ static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
        return NOTIFY_DONE;
 }
 
+static inline void
+genpd_queue_power_off_work(struct generic_pm_domain *genpd) {}
+
 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
 
 #define pm_genpd_runtime_suspend       NULL
@@ -774,46 +773,6 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
        return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
 }
 
-static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev)
-{
-       return GENPD_DEV_CALLBACK(genpd, int, suspend, dev);
-}
-
-static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev)
-{
-       return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev);
-}
-
-static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev)
-{
-       return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev);
-}
-
-static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev)
-{
-       return GENPD_DEV_CALLBACK(genpd, int, resume, dev);
-}
-
-static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev)
-{
-       return GENPD_DEV_CALLBACK(genpd, int, freeze, dev);
-}
-
-static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev)
-{
-       return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev);
-}
-
-static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev)
-{
-       return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev);
-}
-
-static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
-{
-       return GENPD_DEV_CALLBACK(genpd, int, thaw, dev);
-}
-
 /**
  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
  * @genpd: PM domain to power off, if possible.
@@ -995,7 +954,7 @@ static int pm_genpd_suspend(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev);
+       return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
 }
 
 /**
@@ -1016,7 +975,7 @@ static int pm_genpd_suspend_late(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev);
+       return genpd->suspend_power_off ? 0 : pm_generic_suspend_late(dev);
 }
 
 /**
@@ -1103,7 +1062,7 @@ static int pm_genpd_resume_early(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev);
+       return genpd->suspend_power_off ? 0 : pm_generic_resume_early(dev);
 }
 
 /**
@@ -1124,7 +1083,7 @@ static int pm_genpd_resume(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
+       return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
 }
 
 /**
@@ -1145,7 +1104,7 @@ static int pm_genpd_freeze(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
+       return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
 }
 
 /**
@@ -1167,7 +1126,7 @@ static int pm_genpd_freeze_late(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev);
+       return genpd->suspend_power_off ? 0 : pm_generic_freeze_late(dev);
 }
 
 /**
@@ -1231,7 +1190,7 @@ static int pm_genpd_thaw_early(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev);
+       return genpd->suspend_power_off ? 0 : pm_generic_thaw_early(dev);
 }
 
 /**
@@ -1252,7 +1211,7 @@ static int pm_genpd_thaw(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
+       return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
 }
 
 /**
@@ -1344,13 +1303,13 @@ static void pm_genpd_complete(struct device *dev)
 }
 
 /**
- * pm_genpd_syscore_switch - Switch power during system core suspend or resume.
+ * genpd_syscore_switch - Switch power during system core suspend or resume.
  * @dev: Device that normally is marked as "always on" to switch power for.
  *
  * This routine may only be called during the system core (syscore) suspend or
  * resume phase for devices whose "always on" flags are set.
  */
-void pm_genpd_syscore_switch(struct device *dev, bool suspend)
+static void genpd_syscore_switch(struct device *dev, bool suspend)
 {
        struct generic_pm_domain *genpd;
 
@@ -1366,7 +1325,18 @@ void pm_genpd_syscore_switch(struct device *dev, bool suspend)
                genpd->suspended_count--;
        }
 }
-EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch);
+
+void pm_genpd_syscore_poweroff(struct device *dev)
+{
+       genpd_syscore_switch(dev, true);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff);
+
+void pm_genpd_syscore_poweron(struct device *dev)
+{
+       genpd_syscore_switch(dev, false);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron);
 
 #else
 
@@ -1466,6 +1436,9 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
 
        spin_unlock_irq(&dev->power.lock);
 
+       if (genpd->attach_dev)
+               genpd->attach_dev(dev);
+
        mutex_lock(&gpd_data->lock);
        gpd_data->base.dev = dev;
        list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
@@ -1483,39 +1456,6 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
        return ret;
 }
 
-/**
- * __pm_genpd_of_add_device - Add a device to an I/O PM domain.
- * @genpd_node: Device tree node pointer representing a PM domain to which the
- *   the device is added to.
- * @dev: Device to be added.
- * @td: Set of PM QoS timing parameters to attach to the device.
- */
-int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
-                            struct gpd_timing_data *td)
-{
-       struct generic_pm_domain *genpd = NULL, *gpd;
-
-       dev_dbg(dev, "%s()\n", __func__);
-
-       if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
-               return -EINVAL;
-
-       mutex_lock(&gpd_list_lock);
-       list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
-               if (gpd->of_node == genpd_node) {
-                       genpd = gpd;
-                       break;
-               }
-       }
-       mutex_unlock(&gpd_list_lock);
-
-       if (!genpd)
-               return -EINVAL;
-
-       return __pm_genpd_add_device(genpd, dev, td);
-}
-
-
 /**
  * __pm_genpd_name_add_device - Find I/O PM domain and add a device to it.
  * @domain_name: Name of the PM domain to add the device to.
@@ -1558,6 +1498,9 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
        genpd->device_count--;
        genpd->max_off_time_changed = true;
 
+       if (genpd->detach_dev)
+               genpd->detach_dev(dev);
+
        spin_lock_irq(&dev->power.lock);
 
        dev->pm_domain = NULL;
@@ -1743,112 +1686,6 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
        return ret;
 }
 
-/**
- * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
- * @dev: Device to add the callbacks to.
- * @ops: Set of callbacks to add.
- * @td: Timing data to add to the device along with the callbacks (optional).
- *
- * Every call to this routine should be balanced with a call to
- * __pm_genpd_remove_callbacks() and they must not be nested.
- */
-int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
-                          struct gpd_timing_data *td)
-{
-       struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
-       int ret = 0;
-
-       if (!(dev && ops))
-               return -EINVAL;
-
-       gpd_data_new = __pm_genpd_alloc_dev_data(dev);
-       if (!gpd_data_new)
-               return -ENOMEM;
-
-       pm_runtime_disable(dev);
-       device_pm_lock();
-
-       ret = dev_pm_get_subsys_data(dev);
-       if (ret)
-               goto out;
-
-       spin_lock_irq(&dev->power.lock);
-
-       if (dev->power.subsys_data->domain_data) {
-               gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
-       } else {
-               gpd_data = gpd_data_new;
-               dev->power.subsys_data->domain_data = &gpd_data->base;
-       }
-       gpd_data->refcount++;
-       gpd_data->ops = *ops;
-       if (td)
-               gpd_data->td = *td;
-
-       spin_unlock_irq(&dev->power.lock);
-
- out:
-       device_pm_unlock();
-       pm_runtime_enable(dev);
-
-       if (gpd_data != gpd_data_new)
-               __pm_genpd_free_dev_data(dev, gpd_data_new);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
-
-/**
- * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
- * @dev: Device to remove the callbacks from.
- * @clear_td: If set, clear the device's timing data too.
- *
- * This routine can only be called after pm_genpd_add_callbacks().
- */
-int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
-{
-       struct generic_pm_domain_data *gpd_data = NULL;
-       bool remove = false;
-       int ret = 0;
-
-       if (!(dev && dev->power.subsys_data))
-               return -EINVAL;
-
-       pm_runtime_disable(dev);
-       device_pm_lock();
-
-       spin_lock_irq(&dev->power.lock);
-
-       if (dev->power.subsys_data->domain_data) {
-               gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
-               gpd_data->ops = (struct gpd_dev_ops){ NULL };
-               if (clear_td)
-                       gpd_data->td = (struct gpd_timing_data){ 0 };
-
-               if (--gpd_data->refcount == 0) {
-                       dev->power.subsys_data->domain_data = NULL;
-                       remove = true;
-               }
-       } else {
-               ret = -EINVAL;
-       }
-
-       spin_unlock_irq(&dev->power.lock);
-
-       device_pm_unlock();
-       pm_runtime_enable(dev);
-
-       if (ret)
-               return ret;
-
-       dev_pm_put_subsys_data(dev);
-       if (remove)
-               __pm_genpd_free_dev_data(dev, gpd_data);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
-
 /**
  * pm_genpd_attach_cpuidle - Connect the given PM domain with cpuidle.
  * @genpd: PM domain to be connected with cpuidle.
@@ -1861,7 +1698,7 @@ EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
 int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)
 {
        struct cpuidle_driver *cpuidle_drv;
-       struct gpd_cpu_data *cpu_data;
+       struct gpd_cpuidle_data *cpuidle_data;
        struct cpuidle_state *idle_state;
        int ret = 0;
 
@@ -1870,12 +1707,12 @@ int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)
 
        genpd_acquire_lock(genpd);
 
-       if (genpd->cpu_data) {
+       if (genpd->cpuidle_data) {
                ret = -EEXIST;
                goto out;
        }
-       cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL);
-       if (!cpu_data) {
+       cpuidle_data = kzalloc(sizeof(*cpuidle_data), GFP_KERNEL);
+       if (!cpuidle_data) {
                ret = -ENOMEM;
                goto out;
        }
@@ -1893,9 +1730,9 @@ int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)
                ret = -EAGAIN;
                goto err;
        }
-       cpu_data->idle_state = idle_state;
-       cpu_data->saved_exit_latency = idle_state->exit_latency;
-       genpd->cpu_data = cpu_data;
+       cpuidle_data->idle_state = idle_state;
+       cpuidle_data->saved_exit_latency = idle_state->exit_latency;
+       genpd->cpuidle_data = cpuidle_data;
        genpd_recalc_cpu_exit_latency(genpd);
 
  out:
@@ -1906,7 +1743,7 @@ int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)
        cpuidle_driver_unref();
 
  err_drv:
-       kfree(cpu_data);
+       kfree(cpuidle_data);
        goto out;
 }
 
@@ -1929,7 +1766,7 @@ int pm_genpd_name_attach_cpuidle(const char *name, int state)
  */
 int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd)
 {
-       struct gpd_cpu_data *cpu_data;
+       struct gpd_cpuidle_data *cpuidle_data;
        struct cpuidle_state *idle_state;
        int ret = 0;
 
@@ -1938,20 +1775,20 @@ int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd)
 
        genpd_acquire_lock(genpd);
 
-       cpu_data = genpd->cpu_data;
-       if (!cpu_data) {
+       cpuidle_data = genpd->cpuidle_data;
+       if (!cpuidle_data) {
                ret = -ENODEV;
                goto out;
        }
-       idle_state = cpu_data->idle_state;
+       idle_state = cpuidle_data->idle_state;
        if (!idle_state->disabled) {
                ret = -EAGAIN;
                goto out;
        }
-       idle_state->exit_latency = cpu_data->saved_exit_latency;
+       idle_state->exit_latency = cpuidle_data->saved_exit_latency;
        cpuidle_driver_unref();
-       genpd->cpu_data = NULL;
-       kfree(cpu_data);
+       genpd->cpuidle_data = NULL;
+       kfree(cpuidle_data);
 
  out:
        genpd_release_lock(genpd);
@@ -1970,17 +1807,13 @@ int pm_genpd_name_detach_cpuidle(const char *name)
 /* Default device callbacks for generic PM domains. */
 
 /**
- * pm_genpd_default_save_state - Default "save device state" for PM domians.
+ * pm_genpd_default_save_state - Default "save device state" for PM domains.
  * @dev: Device to handle.
  */
 static int pm_genpd_default_save_state(struct device *dev)
 {
        int (*cb)(struct device *__dev);
 
-       cb = dev_gpd_data(dev)->ops.save_state;
-       if (cb)
-               return cb(dev);
-
        if (dev->type && dev->type->pm)
                cb = dev->type->pm->runtime_suspend;
        else if (dev->class && dev->class->pm)
@@ -1997,17 +1830,13 @@ static int pm_genpd_default_save_state(struct device *dev)
 }
 
 /**
- * pm_genpd_default_restore_state - Default PM domians "restore device state".
+ * pm_genpd_default_restore_state - Default PM domains "restore device state".
  * @dev: Device to handle.
  */
 static int pm_genpd_default_restore_state(struct device *dev)
 {
        int (*cb)(struct device *__dev);
 
-       cb = dev_gpd_data(dev)->ops.restore_state;
-       if (cb)
-               return cb(dev);
-
        if (dev->type && dev->type->pm)
                cb = dev->type->pm->runtime_resume;
        else if (dev->class && dev->class->pm)
@@ -2023,109 +1852,6 @@ static int pm_genpd_default_restore_state(struct device *dev)
        return cb ? cb(dev) : 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-
-/**
- * pm_genpd_default_suspend - Default "device suspend" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_suspend(struct device *dev)
-{
-       int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
-
-       return cb ? cb(dev) : pm_generic_suspend(dev);
-}
-
-/**
- * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_suspend_late(struct device *dev)
-{
-       int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
-
-       return cb ? cb(dev) : pm_generic_suspend_late(dev);
-}
-
-/**
- * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_resume_early(struct device *dev)
-{
-       int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
-
-       return cb ? cb(dev) : pm_generic_resume_early(dev);
-}
-
-/**
- * pm_genpd_default_resume - Default "device resume" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_resume(struct device *dev)
-{
-       int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
-
-       return cb ? cb(dev) : pm_generic_resume(dev);
-}
-
-/**
- * pm_genpd_default_freeze - Default "device freeze" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_freeze(struct device *dev)
-{
-       int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
-
-       return cb ? cb(dev) : pm_generic_freeze(dev);
-}
-
-/**
- * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_freeze_late(struct device *dev)
-{
-       int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
-
-       return cb ? cb(dev) : pm_generic_freeze_late(dev);
-}
-
-/**
- * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_thaw_early(struct device *dev)
-{
-       int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
-
-       return cb ? cb(dev) : pm_generic_thaw_early(dev);
-}
-
-/**
- * pm_genpd_default_thaw - Default "device thaw" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_thaw(struct device *dev)
-{
-       int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
-
-       return cb ? cb(dev) : pm_generic_thaw(dev);
-}
-
-#else /* !CONFIG_PM_SLEEP */
-
-#define pm_genpd_default_suspend       NULL
-#define pm_genpd_default_suspend_late  NULL
-#define pm_genpd_default_resume_early  NULL
-#define pm_genpd_default_resume                NULL
-#define pm_genpd_default_freeze                NULL
-#define pm_genpd_default_freeze_late   NULL
-#define pm_genpd_default_thaw_early    NULL
-#define pm_genpd_default_thaw          NULL
-
-#endif /* !CONFIG_PM_SLEEP */
-
 /**
  * pm_genpd_init - Initialize a generic I/O PM domain object.
  * @genpd: PM domain object to initialize.
@@ -2177,15 +1903,452 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
        genpd->domain.ops.complete = pm_genpd_complete;
        genpd->dev_ops.save_state = pm_genpd_default_save_state;
        genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
-       genpd->dev_ops.suspend = pm_genpd_default_suspend;
-       genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
-       genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
-       genpd->dev_ops.resume = pm_genpd_default_resume;
-       genpd->dev_ops.freeze = pm_genpd_default_freeze;
-       genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
-       genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
-       genpd->dev_ops.thaw = pm_genpd_default_thaw;
        mutex_lock(&gpd_list_lock);
        list_add(&genpd->gpd_list_node, &gpd_list);
        mutex_unlock(&gpd_list_lock);
 }
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
+/*
+ * Device Tree based PM domain providers.
+ *
+ * The code below implements generic device tree based PM domain providers that
+ * bind device tree nodes with generic PM domains registered in the system.
+ *
+ * Any driver that registers generic PM domains and needs to support binding of
+ * devices to these domains is supposed to register a PM domain provider, which
+ * maps a PM domain specifier retrieved from the device tree to a PM domain.
+ *
+ * Two simple mapping functions have been provided for convenience:
+ *  - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping.
+ *  - __of_genpd_xlate_onecell() for mapping of multiple PM domains per node by
+ *    index.
+ */
+
+/**
+ * struct of_genpd_provider - PM domain provider registration structure
+ * @link: Entry in global list of PM domain providers
+ * @node: Pointer to device tree node of PM domain provider
+ * @xlate: Provider-specific xlate callback mapping a set of specifier cells
+ *         into a PM domain.
+ * @data: context pointer to be passed into @xlate callback
+ */
+struct of_genpd_provider {
+       struct list_head link;
+       struct device_node *node;
+       genpd_xlate_t xlate;
+       void *data;
+};
+
+/* List of registered PM domain providers. */
+static LIST_HEAD(of_genpd_providers);
+/* Mutex to protect the list above. */
+static DEFINE_MUTEX(of_genpd_mutex);
+
+/**
+ * __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping
+ * @genpdspec: OF phandle args to map into a PM domain
+ * @data: xlate function private data - pointer to struct generic_pm_domain
+ *
+ * This is a generic xlate function that can be used to model PM domains that
+ * have their own device tree nodes. The private data of xlate function needs
+ * to be a valid pointer to struct generic_pm_domain.
+ */
+struct generic_pm_domain *__of_genpd_xlate_simple(
+                                       struct of_phandle_args *genpdspec,
+                                       void *data)
+{
+       if (genpdspec->args_count != 0)
+               return ERR_PTR(-EINVAL);
+       return data;
+}
+EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple);
+
+/**
+ * __of_genpd_xlate_onecell() - Xlate function using a single index.
+ * @genpdspec: OF phandle args to map into a PM domain
+ * @data: xlate function private data - pointer to struct genpd_onecell_data
+ *
+ * This is a generic xlate function that can be used to model simple PM domain
+ * controllers that have one device tree node and provide multiple PM domains.
+ * A single cell is used as an index into an array of PM domains specified in
+ * the genpd_onecell_data struct when registering the provider.
+ */
+struct generic_pm_domain *__of_genpd_xlate_onecell(
+                                       struct of_phandle_args *genpdspec,
+                                       void *data)
+{
+       struct genpd_onecell_data *genpd_data = data;
+       unsigned int idx = genpdspec->args[0];
+
+       if (genpdspec->args_count != 1)
+               return ERR_PTR(-EINVAL);
+
+       if (idx >= genpd_data->num_domains) {
+               pr_err("%s: invalid domain index %u\n", __func__, idx);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (!genpd_data->domains[idx])
+               return ERR_PTR(-ENOENT);
+
+       return genpd_data->domains[idx];
+}
+EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell);
+
+/**
+ * __of_genpd_add_provider() - Register a PM domain provider for a node
+ * @np: Device node pointer associated with the PM domain provider.
+ * @xlate: Callback for decoding PM domain from phandle arguments.
+ * @data: Context pointer for @xlate callback.
+ */
+int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
+                       void *data)
+{
+       struct of_genpd_provider *cp;
+
+       cp = kzalloc(sizeof(*cp), GFP_KERNEL);
+       if (!cp)
+               return -ENOMEM;
+
+       cp->node = of_node_get(np);
+       cp->data = data;
+       cp->xlate = xlate;
+
+       mutex_lock(&of_genpd_mutex);
+       list_add(&cp->link, &of_genpd_providers);
+       mutex_unlock(&of_genpd_mutex);
+       pr_debug("Added domain provider from %s\n", np->full_name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__of_genpd_add_provider);
+
+/**
+ * of_genpd_del_provider() - Remove a previously registered PM domain provider
+ * @np: Device node pointer associated with the PM domain provider
+ */
+void of_genpd_del_provider(struct device_node *np)
+{
+       struct of_genpd_provider *cp;
+
+       mutex_lock(&of_genpd_mutex);
+       list_for_each_entry(cp, &of_genpd_providers, link) {
+               if (cp->node == np) {
+                       list_del(&cp->link);
+                       of_node_put(cp->node);
+                       kfree(cp);
+                       break;
+               }
+       }
+       mutex_unlock(&of_genpd_mutex);
+}
+EXPORT_SYMBOL_GPL(of_genpd_del_provider);
+
+/**
+ * of_genpd_get_from_provider() - Look-up PM domain
+ * @genpdspec: OF phandle args to use for look-up
+ *
+ * Looks for a PM domain provider under the node specified by @genpdspec and if
+ * found, uses xlate function of the provider to map phandle args to a PM
+ * domain.
+ *
+ * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
+ * on failure.
+ */
+static struct generic_pm_domain *of_genpd_get_from_provider(
+                                       struct of_phandle_args *genpdspec)
+{
+       struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
+       struct of_genpd_provider *provider;
+
+       mutex_lock(&of_genpd_mutex);
+
+       /* Check if we have such a provider in our array */
+       list_for_each_entry(provider, &of_genpd_providers, link) {
+               if (provider->node == genpdspec->np)
+                       genpd = provider->xlate(genpdspec, provider->data);
+               if (!IS_ERR(genpd))
+                       break;
+       }
+
+       mutex_unlock(&of_genpd_mutex);
+
+       return genpd;
+}
+
+/**
+ * genpd_dev_pm_detach - Detach a device from its PM domain.
+ * @dev: Device to attach.
+ * @power_off: Currently not used
+ *
+ * Try to locate a corresponding generic PM domain, which the device was
+ * attached to previously. If such is found, the device is detached from it.
+ */
+static void genpd_dev_pm_detach(struct device *dev, bool power_off)
+{
+       struct generic_pm_domain *pd = NULL, *gpd;
+       int ret = 0;
+
+       if (!dev->pm_domain)
+               return;
+
+       mutex_lock(&gpd_list_lock);
+       list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
+               if (&gpd->domain == dev->pm_domain) {
+                       pd = gpd;
+                       break;
+               }
+       }
+       mutex_unlock(&gpd_list_lock);
+
+       if (!pd)
+               return;
+
+       dev_dbg(dev, "removing from PM domain %s\n", pd->name);
+
+       while (1) {
+               ret = pm_genpd_remove_device(pd, dev);
+               if (ret != -EAGAIN)
+                       break;
+               cond_resched();
+       }
+
+       if (ret < 0) {
+               dev_err(dev, "failed to remove from PM domain %s: %d",
+                       pd->name, ret);
+               return;
+       }
+
+       /* Check if PM domain can be powered off after removing this device. */
+       genpd_queue_power_off_work(pd);
+}
+
+/**
+ * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
+ * @dev: Device to attach.
+ *
+ * Parse device's OF node to find a PM domain specifier. If such is found,
+ * attaches the device to retrieved pm_domain ops.
+ *
+ * Both generic and legacy Samsung-specific DT bindings are supported to keep
+ * backwards compatibility with existing DTBs.
+ *
+ * Returns 0 on successfully attached PM domain or negative error code.
+ */
+int genpd_dev_pm_attach(struct device *dev)
+{
+       struct of_phandle_args pd_args;
+       struct generic_pm_domain *pd;
+       int ret;
+
+       if (!dev->of_node)
+               return -ENODEV;
+
+       if (dev->pm_domain)
+               return -EEXIST;
+
+       ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
+                                       "#power-domain-cells", 0, &pd_args);
+       if (ret < 0) {
+               if (ret != -ENOENT)
+                       return ret;
+
+               /*
+                * Try legacy Samsung-specific bindings
+                * (for backwards compatibility of DT ABI)
+                */
+               pd_args.args_count = 0;
+               pd_args.np = of_parse_phandle(dev->of_node,
+                                               "samsung,power-domain", 0);
+               if (!pd_args.np)
+                       return -ENOENT;
+       }
+
+       pd = of_genpd_get_from_provider(&pd_args);
+       if (IS_ERR(pd)) {
+               dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
+                       __func__, PTR_ERR(pd));
+               of_node_put(dev->of_node);
+               return PTR_ERR(pd);
+       }
+
+       dev_dbg(dev, "adding to PM domain %s\n", pd->name);
+
+       while (1) {
+               ret = pm_genpd_add_device(pd, dev);
+               if (ret != -EAGAIN)
+                       break;
+               cond_resched();
+       }
+
+       if (ret < 0) {
+               dev_err(dev, "failed to add to PM domain %s: %d",
+                       pd->name, ret);
+               of_node_put(dev->of_node);
+               return ret;
+       }
+
+       dev->pm_domain->detach = genpd_dev_pm_detach;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
+#endif
+
+
+/***        debugfs support        ***/
+
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+static struct dentry *pm_genpd_debugfs_dir;
+
+/*
+ * TODO: This function is a slightly modified version of rtpm_status_show
+ * from sysfs.c, but dependencies between PM_GENERIC_DOMAINS and PM_RUNTIME
+ * are too loose to generalize it.
+ */
+#ifdef CONFIG_PM_RUNTIME
+static void rtpm_status_str(struct seq_file *s, struct device *dev)
+{
+       static const char * const status_lookup[] = {
+               [RPM_ACTIVE] = "active",
+               [RPM_RESUMING] = "resuming",
+               [RPM_SUSPENDED] = "suspended",
+               [RPM_SUSPENDING] = "suspending"
+       };
+       const char *p = "";
+
+       if (dev->power.runtime_error)
+               p = "error";
+       else if (dev->power.disable_depth)
+               p = "unsupported";
+       else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup))
+               p = status_lookup[dev->power.runtime_status];
+       else
+               WARN_ON(1);
+
+       seq_puts(s, p);
+}
+#else
+static void rtpm_status_str(struct seq_file *s, struct device *dev)
+{
+       seq_puts(s, "active");
+}
+#endif
+
+static int pm_genpd_summary_one(struct seq_file *s,
+               struct generic_pm_domain *gpd)
+{
+       static const char * const status_lookup[] = {
+               [GPD_STATE_ACTIVE] = "on",
+               [GPD_STATE_WAIT_MASTER] = "wait-master",
+               [GPD_STATE_BUSY] = "busy",
+               [GPD_STATE_REPEAT] = "off-in-progress",
+               [GPD_STATE_POWER_OFF] = "off"
+       };
+       struct pm_domain_data *pm_data;
+       const char *kobj_path;
+       struct gpd_link *link;
+       int ret;
+
+       ret = mutex_lock_interruptible(&gpd->lock);
+       if (ret)
+               return -ERESTARTSYS;
+
+       if (WARN_ON(gpd->status >= ARRAY_SIZE(status_lookup)))
+               goto exit;
+       seq_printf(s, "%-30s  %-15s  ", gpd->name, status_lookup[gpd->status]);
+
+       /*
+        * Modifications on the list require holding locks on both
+        * master and slave, so we are safe.
+        * Also gpd->name is immutable.
+        */
+       list_for_each_entry(link, &gpd->master_links, master_node) {
+               seq_printf(s, "%s", link->slave->name);
+               if (!list_is_last(&link->master_node, &gpd->master_links))
+                       seq_puts(s, ", ");
+       }
+
+       list_for_each_entry(pm_data, &gpd->dev_list, list_node) {
+               kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL);
+               if (kobj_path == NULL)
+                       continue;
+
+               seq_printf(s, "\n    %-50s  ", kobj_path);
+               rtpm_status_str(s, pm_data->dev);
+               kfree(kobj_path);
+       }
+
+       seq_puts(s, "\n");
+exit:
+       mutex_unlock(&gpd->lock);
+
+       return 0;
+}
+
+static int pm_genpd_summary_show(struct seq_file *s, void *data)
+{
+       struct generic_pm_domain *gpd;
+       int ret = 0;
+
+       seq_puts(s, "    domain                      status         slaves\n");
+       seq_puts(s, "           /device                                      runtime status\n");
+       seq_puts(s, "----------------------------------------------------------------------\n");
+
+       ret = mutex_lock_interruptible(&gpd_list_lock);
+       if (ret)
+               return -ERESTARTSYS;
+
+       list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
+               ret = pm_genpd_summary_one(s, gpd);
+               if (ret)
+                       break;
+       }
+       mutex_unlock(&gpd_list_lock);
+
+       return ret;
+}
+
+static int pm_genpd_summary_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, pm_genpd_summary_show, NULL);
+}
+
+static const struct file_operations pm_genpd_summary_fops = {
+       .open = pm_genpd_summary_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int __init pm_genpd_debug_init(void)
+{
+       struct dentry *d;
+
+       pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL);
+
+       if (!pm_genpd_debugfs_dir)
+               return -ENOMEM;
+
+       d = debugfs_create_file("pm_genpd_summary", S_IRUGO,
+                       pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops);
+       if (!d)
+               return -ENOMEM;
+
+       return 0;
+}
+late_initcall(pm_genpd_debug_init);
+
+static void __exit pm_genpd_debug_exit(void)
+{
+       debugfs_remove_recursive(pm_genpd_debugfs_dir);
+}
+__exitcall(pm_genpd_debug_exit);
+#endif /* CONFIG_PM_ADVANCED_DEBUG */
index a089e3bcdfbc5d7ee4aa6850b90f34e9fd2a9efb..d88a62e104d4e03b55cf778b025e544072d7c701 100644 (file)
@@ -42,7 +42,7 @@ static int dev_update_qos_constraint(struct device *dev, void *data)
  * default_stop_ok - Default PM domain governor routine for stopping devices.
  * @dev: Device to check.
  */
-bool default_stop_ok(struct device *dev)
+static bool default_stop_ok(struct device *dev)
 {
        struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
        unsigned long flags;
@@ -229,10 +229,7 @@ static bool always_on_power_down_ok(struct dev_pm_domain *domain)
 
 #else /* !CONFIG_PM_RUNTIME */
 
-bool default_stop_ok(struct device *dev)
-{
-       return false;
-}
+static inline bool default_stop_ok(struct device *dev) { return false; }
 
 #define default_power_down_ok  NULL
 #define always_on_power_down_ok        NULL
index b67d9aef9fe431d58ad2da42e7a7328c87208aa4..44973196d3fd76d1b3a4b91bc728cf6cf87c2653 100644 (file)
@@ -540,7 +540,7 @@ static void async_resume_noirq(void *data, async_cookie_t cookie)
  * Call the "noirq" resume handlers for all devices in dpm_noirq_list and
  * enable device drivers to receive interrupts.
  */
-static void dpm_resume_noirq(pm_message_t state)
+void dpm_resume_noirq(pm_message_t state)
 {
        struct device *dev;
        ktime_t starttime = ktime_get();
@@ -662,7 +662,7 @@ static void async_resume_early(void *data, async_cookie_t cookie)
  * dpm_resume_early - Execute "early resume" callbacks for all devices.
  * @state: PM transition of the system being carried out.
  */
-static void dpm_resume_early(pm_message_t state)
+void dpm_resume_early(pm_message_t state)
 {
        struct device *dev;
        ktime_t starttime = ktime_get();
@@ -1093,7 +1093,7 @@ static int device_suspend_noirq(struct device *dev)
  * Prevent device drivers from receiving interrupts and call the "noirq" suspend
  * handlers for all non-sysdev devices.
  */
-static int dpm_suspend_noirq(pm_message_t state)
+int dpm_suspend_noirq(pm_message_t state)
 {
        ktime_t starttime = ktime_get();
        int error = 0;
@@ -1232,7 +1232,7 @@ static int device_suspend_late(struct device *dev)
  * dpm_suspend_late - Execute "late suspend" callbacks for all devices.
  * @state: PM transition of the system being carried out.
  */
-static int dpm_suspend_late(pm_message_t state)
+int dpm_suspend_late(pm_message_t state)
 {
        ktime_t starttime = ktime_get();
        int error = 0;
index 95b181d1ca6df76d1b3a355e6a7bffca3aa26854..a9d26ed11bf479f5e4a32c9ddaac3f51287dedaa 100644 (file)
@@ -92,9 +92,6 @@
  *     wakeup_count - Report the number of wakeup events related to the device
  */
 
-static const char enabled[] = "enabled";
-static const char disabled[] = "disabled";
-
 const char power_group_name[] = "power";
 EXPORT_SYMBOL_GPL(power_group_name);
 
@@ -336,11 +333,14 @@ static DEVICE_ATTR(pm_qos_remote_wakeup, 0644,
 #endif /* CONFIG_PM_RUNTIME */
 
 #ifdef CONFIG_PM_SLEEP
+static const char _enabled[] = "enabled";
+static const char _disabled[] = "disabled";
+
 static ssize_t
 wake_show(struct device * dev, struct device_attribute *attr, char * buf)
 {
        return sprintf(buf, "%s\n", device_can_wakeup(dev)
-               ? (device_may_wakeup(dev) ? enabled : disabled)
+               ? (device_may_wakeup(dev) ? _enabled : _disabled)
                : "");
 }
 
@@ -357,11 +357,11 @@ wake_store(struct device * dev, struct device_attribute *attr,
        cp = memchr(buf, '\n', n);
        if (cp)
                len = cp - buf;
-       if (len == sizeof enabled - 1
-                       && strncmp(buf, enabled, sizeof enabled - 1) == 0)
+       if (len == sizeof _enabled - 1
+                       && strncmp(buf, _enabled, sizeof _enabled - 1) == 0)
                device_set_wakeup_enable(dev, 1);
-       else if (len == sizeof disabled - 1
-                       && strncmp(buf, disabled, sizeof disabled - 1) == 0)
+       else if (len == sizeof _disabled - 1
+                       && strncmp(buf, _disabled, sizeof _disabled - 1) == 0)
                device_set_wakeup_enable(dev, 0);
        else
                return -EINVAL;
@@ -570,7 +570,8 @@ static ssize_t async_show(struct device *dev, struct device_attribute *attr,
                          char *buf)
 {
        return sprintf(buf, "%s\n",
-                       device_async_suspend_enabled(dev) ? enabled : disabled);
+                       device_async_suspend_enabled(dev) ?
+                               _enabled : _disabled);
 }
 
 static ssize_t async_store(struct device *dev, struct device_attribute *attr,
@@ -582,9 +583,10 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr,
        cp = memchr(buf, '\n', n);
        if (cp)
                len = cp - buf;
-       if (len == sizeof enabled - 1 && strncmp(buf, enabled, len) == 0)
+       if (len == sizeof _enabled - 1 && strncmp(buf, _enabled, len) == 0)
                device_enable_async_suspend(dev);
-       else if (len == sizeof disabled - 1 && strncmp(buf, disabled, len) == 0)
+       else if (len == sizeof _disabled - 1 &&
+                strncmp(buf, _disabled, len) == 0)
                device_disable_async_suspend(dev);
        else
                return -EINVAL;
index eb1bd2ecad8bf9e3854660d4c7a643f760415d01..c2744b30d5d92e9dde512e492cf9fdf44f21b5ef 100644 (file)
@@ -24,6 +24,9 @@
  */
 bool events_check_enabled __read_mostly;
 
+/* If set and the system is suspending, terminate the suspend. */
+static bool pm_abort_suspend __read_mostly;
+
 /*
  * Combined counters of registered wakeup events and wakeup events in progress.
  * They need to be modified together atomically, so it's better to use one
@@ -719,7 +722,18 @@ bool pm_wakeup_pending(void)
                pm_print_active_wakeup_sources();
        }
 
-       return ret;
+       return ret || pm_abort_suspend;
+}
+
+void pm_system_wakeup(void)
+{
+       pm_abort_suspend = true;
+       freeze_wake();
+}
+
+void pm_wakeup_clear(void)
+{
+       pm_abort_suspend = false;
 }
 
 /**
index dbb8350ea8dc232d713a10c9a4179e221dbcbb45..8d98a329f6ea63a2daf179bb3f15e5307c6a0d13 100644 (file)
@@ -9,7 +9,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/mutex.h>
 #include <linux/module.h>
-#include <linux/interrupt.h>
+#include <linux/suspend.h>
 #include <trace/events/power.h>
 
 static LIST_HEAD(syscore_ops_list);
@@ -54,9 +54,8 @@ int syscore_suspend(void)
        pr_debug("Checking wakeup interrupts\n");
 
        /* Return error code if there are any wakeup interrupts pending. */
-       ret = check_wakeup_irqs();
-       if (ret)
-               return ret;
+       if (pm_wakeup_pending())
+               return -EBUSY;
 
        WARN_ONCE(!irqs_disabled(),
                "Interrupts enabled before system core suspend.\n");
index 57ce5fe65364feec68eecd3927bb0b08a69cdcb1..706b9ae0dcfbdb82cf73ae41139d2e4b82ab4693 100644 (file)
@@ -255,5 +255,6 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
 int bcma_gpio_unregister(struct bcma_drv_cc *cc)
 {
        bcma_gpio_irq_domain_exit(cc);
-       return gpiochip_remove(&cc->gpio);
+       gpiochip_remove(&cc->gpio);
+       return 0;
 }
index 5814deb6963d52a875708e78a4a3a38eb148145e..756b8ec00f16d73851bca0c8d5ec754752c58710 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/blkdev.h>
 #include <linux/hdreg.h>
 #include <linux/genhd.h>
+#include <linux/cdrom.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/completion.h>
@@ -22,8 +23,8 @@
 
 #define DRV_MODULE_NAME                "sunvdc"
 #define PFX DRV_MODULE_NAME    ": "
-#define DRV_MODULE_VERSION     "1.0"
-#define DRV_MODULE_RELDATE     "June 25, 2007"
+#define DRV_MODULE_VERSION     "1.1"
+#define DRV_MODULE_RELDATE     "February 13, 2013"
 
 static char version[] =
        DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
@@ -32,7 +33,7 @@ MODULE_DESCRIPTION("Sun LDOM virtual disk client driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_MODULE_VERSION);
 
-#define VDC_TX_RING_SIZE       256
+#define VDC_TX_RING_SIZE       512
 
 #define WAITING_FOR_LINK_UP    0x01
 #define WAITING_FOR_TX_SPACE   0x02
@@ -65,10 +66,10 @@ struct vdc_port {
        u64                     operations;
        u32                     vdisk_size;
        u8                      vdisk_type;
+       u8                      vdisk_mtype;
 
        char                    disk_name[32];
 
-       struct vio_disk_geom    geom;
        struct vio_disk_vtoc    label;
 };
 
@@ -79,9 +80,16 @@ static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio)
 
 /* Ordered from largest major to lowest */
 static struct vio_version vdc_versions[] = {
+       { .major = 1, .minor = 1 },
        { .major = 1, .minor = 0 },
 };
 
+static inline int vdc_version_supported(struct vdc_port *port,
+                                       u16 major, u16 minor)
+{
+       return port->vio.ver.major == major && port->vio.ver.minor >= minor;
+}
+
 #define VDCBLK_NAME    "vdisk"
 static int vdc_major;
 #define PARTITION_SHIFT        3
@@ -94,18 +102,54 @@ static inline u32 vdc_tx_dring_avail(struct vio_dring_state *dr)
 static int vdc_getgeo(struct block_device *bdev, struct hd_geometry *geo)
 {
        struct gendisk *disk = bdev->bd_disk;
-       struct vdc_port *port = disk->private_data;
+       sector_t nsect = get_capacity(disk);
+       sector_t cylinders = nsect;
 
-       geo->heads = (u8) port->geom.num_hd;
-       geo->sectors = (u8) port->geom.num_sec;
-       geo->cylinders = port->geom.num_cyl;
+       geo->heads = 0xff;
+       geo->sectors = 0x3f;
+       sector_div(cylinders, geo->heads * geo->sectors);
+       geo->cylinders = cylinders;
+       if ((sector_t)(geo->cylinders + 1) * geo->heads * geo->sectors < nsect)
+               geo->cylinders = 0xffff;
 
        return 0;
 }
 
+/* Add ioctl/CDROM_GET_CAPABILITY to support cdrom_id in udev
+ * when vdisk_mtype is VD_MEDIA_TYPE_CD or VD_MEDIA_TYPE_DVD.
+ * Needed to be able to install inside an ldom from an iso image.
+ */
+static int vdc_ioctl(struct block_device *bdev, fmode_t mode,
+                    unsigned command, unsigned long argument)
+{
+       int i;
+       struct gendisk *disk;
+
+       switch (command) {
+       case CDROMMULTISESSION:
+               pr_debug(PFX "Multisession CDs not supported\n");
+               for (i = 0; i < sizeof(struct cdrom_multisession); i++)
+                       if (put_user(0, (char __user *)(argument + i)))
+                               return -EFAULT;
+               return 0;
+
+       case CDROM_GET_CAPABILITY:
+               disk = bdev->bd_disk;
+
+               if (bdev->bd_disk && (disk->flags & GENHD_FL_CD))
+                       return 0;
+               return -EINVAL;
+
+       default:
+               pr_debug(PFX "ioctl %08x not supported\n", command);
+               return -EINVAL;
+       }
+}
+
 static const struct block_device_operations vdc_fops = {
        .owner          = THIS_MODULE,
        .getgeo         = vdc_getgeo,
+       .ioctl          = vdc_ioctl,
 };
 
 static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for)
@@ -165,9 +209,9 @@ static int vdc_handle_attr(struct vio_driver_state *vio, void *arg)
        struct vio_disk_attr_info *pkt = arg;
 
        viodbg(HS, "GOT ATTR stype[0x%x] ops[%llx] disk_size[%llu] disk_type[%x] "
-              "xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n",
+              "mtype[0x%x] xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n",
               pkt->tag.stype, pkt->operations,
-              pkt->vdisk_size, pkt->vdisk_type,
+              pkt->vdisk_size, pkt->vdisk_type, pkt->vdisk_mtype,
               pkt->xfer_mode, pkt->vdisk_block_size,
               pkt->max_xfer_size);
 
@@ -192,8 +236,11 @@ static int vdc_handle_attr(struct vio_driver_state *vio, void *arg)
                }
 
                port->operations = pkt->operations;
-               port->vdisk_size = pkt->vdisk_size;
                port->vdisk_type = pkt->vdisk_type;
+               if (vdc_version_supported(port, 1, 1)) {
+                       port->vdisk_size = pkt->vdisk_size;
+                       port->vdisk_mtype = pkt->vdisk_mtype;
+               }
                if (pkt->max_xfer_size < port->max_xfer_size)
                        port->max_xfer_size = pkt->max_xfer_size;
                port->vdisk_block_size = pkt->vdisk_block_size;
@@ -236,7 +283,9 @@ static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr,
 
        __blk_end_request(req, (desc->status ? -EIO : 0), desc->size);
 
-       if (blk_queue_stopped(port->disk->queue))
+       /* restart blk queue when ring is half emptied */
+       if (blk_queue_stopped(port->disk->queue) &&
+           vdc_tx_dring_avail(dr) * 100 / VDC_TX_RING_SIZE >= 50)
                blk_start_queue(port->disk->queue);
 }
 
@@ -388,12 +437,6 @@ static int __send_request(struct request *req)
        for (i = 0; i < nsg; i++)
                len += sg[i].length;
 
-       if (unlikely(vdc_tx_dring_avail(dr) < 1)) {
-               blk_stop_queue(port->disk->queue);
-               err = -ENOMEM;
-               goto out;
-       }
-
        desc = vio_dring_cur(dr);
 
        err = ldc_map_sg(port->vio.lp, sg, nsg,
@@ -433,21 +476,32 @@ static int __send_request(struct request *req)
                port->req_id++;
                dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1);
        }
-out:
 
        return err;
 }
 
-static void do_vdc_request(struct request_queue *q)
+static void do_vdc_request(struct request_queue *rq)
 {
-       while (1) {
-               struct request *req = blk_fetch_request(q);
+       struct request *req;
 
-               if (!req)
-                       break;
+       while ((req = blk_peek_request(rq)) != NULL) {
+               struct vdc_port *port;
+               struct vio_dring_state *dr;
+
+               port = req->rq_disk->private_data;
+               dr = &port->vio.drings[VIO_DRIVER_TX_RING];
+               if (unlikely(vdc_tx_dring_avail(dr) < 1))
+                       goto wait;
+
+               blk_start_request(req);
 
-               if (__send_request(req) < 0)
-                       __blk_end_request_all(req, -EIO);
+               if (__send_request(req) < 0) {
+                       blk_requeue_request(rq, req);
+wait:
+                       /* Avoid pointless unplugs. */
+                       blk_stop_queue(rq);
+                       break;
+               }
        }
 }
 
@@ -663,18 +717,27 @@ static int probe_disk(struct vdc_port *port)
                return err;
        }
 
-       err = generic_request(port, VD_OP_GET_DISKGEOM,
-                             &port->geom, sizeof(port->geom));
-       if (err < 0) {
-               printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "
-                      "error %d\n", err);
-               return err;
+       if (vdc_version_supported(port, 1, 1)) {
+               /* vdisk_size should be set during the handshake, if it wasn't
+                * then the underlying disk is reserved by another system
+                */
+               if (port->vdisk_size == -1)
+                       return -ENODEV;
+       } else {
+               struct vio_disk_geom geom;
+
+               err = generic_request(port, VD_OP_GET_DISKGEOM,
+                                     &geom, sizeof(geom));
+               if (err < 0) {
+                       printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "
+                              "error %d\n", err);
+                       return err;
+               }
+               port->vdisk_size = ((u64)geom.num_cyl *
+                                   (u64)geom.num_hd *
+                                   (u64)geom.num_sec);
        }
 
-       port->vdisk_size = ((u64)port->geom.num_cyl *
-                           (u64)port->geom.num_hd *
-                           (u64)port->geom.num_sec);
-
        q = blk_init_queue(do_vdc_request, &port->vio.lock);
        if (!q) {
                printk(KERN_ERR PFX "%s: Could not allocate queue.\n",
@@ -691,6 +754,10 @@ static int probe_disk(struct vdc_port *port)
 
        port->disk = g;
 
+       /* Each segment in a request is up to an aligned page in size. */
+       blk_queue_segment_boundary(q, PAGE_SIZE - 1);
+       blk_queue_max_segment_size(q, PAGE_SIZE);
+
        blk_queue_max_segments(q, port->ring_cookies);
        blk_queue_max_hw_sectors(q, port->max_xfer_size);
        g->major = vdc_major;
@@ -704,9 +771,32 @@ static int probe_disk(struct vdc_port *port)
 
        set_capacity(g, port->vdisk_size);
 
-       printk(KERN_INFO PFX "%s: %u sectors (%u MB)\n",
+       if (vdc_version_supported(port, 1, 1)) {
+               switch (port->vdisk_mtype) {
+               case VD_MEDIA_TYPE_CD:
+                       pr_info(PFX "Virtual CDROM %s\n", port->disk_name);
+                       g->flags |= GENHD_FL_CD;
+                       g->flags |= GENHD_FL_REMOVABLE;
+                       set_disk_ro(g, 1);
+                       break;
+
+               case VD_MEDIA_TYPE_DVD:
+                       pr_info(PFX "Virtual DVD %s\n", port->disk_name);
+                       g->flags |= GENHD_FL_CD;
+                       g->flags |= GENHD_FL_REMOVABLE;
+                       set_disk_ro(g, 1);
+                       break;
+
+               case VD_MEDIA_TYPE_FIXED:
+                       pr_info(PFX "Virtual Hard disk %s\n", port->disk_name);
+                       break;
+               }
+       }
+
+       pr_info(PFX "%s: %u sectors (%u MB) protocol %d.%d\n",
               g->disk_name,
-              port->vdisk_size, (port->vdisk_size >> (20 - 9)));
+              port->vdisk_size, (port->vdisk_size >> (20 - 9)),
+              port->vio.ver.major, port->vio.ver.minor);
 
        add_disk(g);
 
@@ -765,6 +855,7 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
        else
                snprintf(port->disk_name, sizeof(port->disk_name),
                         VDCBLK_NAME "%c", 'a' + ((int)vdev->dev_no % 26));
+       port->vdisk_size = -1;
 
        err = vio_driver_init(&port->vio, vdev, VDEV_DISK,
                              vdc_versions, ARRAY_SIZE(vdc_versions),
index 3a8b810b4980582ddcb61bb1fc23f112adbe02d6..0b13b1c9a01e397fbb557e676beb77742c9737bc 100644 (file)
@@ -907,22 +907,17 @@ static int connect_ring(struct backend_info *be)
        return 0;
 }
 
-
-/* ** Driver Registration ** */
-
-
 static const struct xenbus_device_id xen_blkbk_ids[] = {
        { "vbd" },
        { "" }
 };
 
-
-static DEFINE_XENBUS_DRIVER(xen_blkbk, ,
+static struct xenbus_driver xen_blkbk_driver = {
+       .ids  = xen_blkbk_ids,
        .probe = xen_blkbk_probe,
        .remove = xen_blkbk_remove,
        .otherend_changed = frontend_changed
-);
-
+};
 
 int xen_blkif_xenbus_init(void)
 {
index 5deb235bd18fe07adb2efb09a7c77508aadfaeec..37af03e9d859d113f28c8ccb2be70be98a5ce831 100644 (file)
@@ -2055,13 +2055,14 @@ static const struct xenbus_device_id blkfront_ids[] = {
        { "" }
 };
 
-static DEFINE_XENBUS_DRIVER(blkfront, ,
+static struct xenbus_driver blkfront_driver = {
+       .ids  = blkfront_ids,
        .probe = blkfront_probe,
        .remove = blkfront_remove,
        .resume = blkfront_resume,
        .otherend_changed = blkback_changed,
        .is_ready = blkfront_is_ready,
-);
+};
 
 static int __init xlblk_init(void)
 {
index d00831c3d73136dc06df360445e0d9590c752eed..3b850164c65c5c97cdd9fb9473031582effa74fc 100644 (file)
@@ -103,10 +103,10 @@ static ssize_t mem_used_total_show(struct device *dev,
 
        down_read(&zram->init_lock);
        if (init_done(zram))
-               val = zs_get_total_size_bytes(meta->mem_pool);
+               val = zs_get_total_pages(meta->mem_pool);
        up_read(&zram->init_lock);
 
-       return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", val << PAGE_SHIFT);
 }
 
 static ssize_t max_comp_streams_show(struct device *dev,
@@ -122,6 +122,72 @@ static ssize_t max_comp_streams_show(struct device *dev,
        return scnprintf(buf, PAGE_SIZE, "%d\n", val);
 }
 
+static ssize_t mem_limit_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       u64 val;
+       struct zram *zram = dev_to_zram(dev);
+
+       down_read(&zram->init_lock);
+       val = zram->limit_pages;
+       up_read(&zram->init_lock);
+
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", val << PAGE_SHIFT);
+}
+
+static ssize_t mem_limit_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       u64 limit;
+       char *tmp;
+       struct zram *zram = dev_to_zram(dev);
+
+       limit = memparse(buf, &tmp);
+       if (buf == tmp) /* no chars parsed, invalid input */
+               return -EINVAL;
+
+       down_write(&zram->init_lock);
+       zram->limit_pages = PAGE_ALIGN(limit) >> PAGE_SHIFT;
+       up_write(&zram->init_lock);
+
+       return len;
+}
+
+static ssize_t mem_used_max_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       u64 val = 0;
+       struct zram *zram = dev_to_zram(dev);
+
+       down_read(&zram->init_lock);
+       if (init_done(zram))
+               val = atomic_long_read(&zram->stats.max_used_pages);
+       up_read(&zram->init_lock);
+
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", val << PAGE_SHIFT);
+}
+
+static ssize_t mem_used_max_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       int err;
+       unsigned long val;
+       struct zram *zram = dev_to_zram(dev);
+       struct zram_meta *meta = zram->meta;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err || val != 0)
+               return -EINVAL;
+
+       down_read(&zram->init_lock);
+       if (init_done(zram))
+               atomic_long_set(&zram->stats.max_used_pages,
+                               zs_get_total_pages(meta->mem_pool));
+       up_read(&zram->init_lock);
+
+       return len;
+}
+
 static ssize_t max_comp_streams_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t len)
 {
@@ -434,6 +500,21 @@ out_cleanup:
        return ret;
 }
 
+static inline void update_used_max(struct zram *zram,
+                                       const unsigned long pages)
+{
+       int old_max, cur_max;
+
+       old_max = atomic_long_read(&zram->stats.max_used_pages);
+
+       do {
+               cur_max = old_max;
+               if (pages > cur_max)
+                       old_max = atomic_long_cmpxchg(
+                               &zram->stats.max_used_pages, cur_max, pages);
+       } while (old_max != cur_max);
+}
+
 static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
                           int offset)
 {
@@ -445,6 +526,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
        struct zram_meta *meta = zram->meta;
        struct zcomp_strm *zstrm;
        bool locked = false;
+       unsigned long alloced_pages;
 
        page = bvec->bv_page;
        if (is_partial_io(bvec)) {
@@ -513,6 +595,16 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
                ret = -ENOMEM;
                goto out;
        }
+
+       alloced_pages = zs_get_total_pages(meta->mem_pool);
+       if (zram->limit_pages && alloced_pages > zram->limit_pages) {
+               zs_free(meta->mem_pool, handle);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       update_used_max(zram, alloced_pages);
+
        cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO);
 
        if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) {
@@ -606,6 +698,7 @@ static void zram_bio_discard(struct zram *zram, u32 index,
                bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
                zram_free_page(zram, index);
                bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
+               atomic64_inc(&zram->stats.notify_free);
                index++;
                n -= PAGE_SIZE;
        }
@@ -617,6 +710,9 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity)
        struct zram_meta *meta;
 
        down_write(&zram->init_lock);
+
+       zram->limit_pages = 0;
+
        if (!init_done(zram)) {
                up_write(&zram->init_lock);
                return;
@@ -857,6 +953,10 @@ static DEVICE_ATTR(initstate, S_IRUGO, initstate_show, NULL);
 static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store);
 static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL);
 static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL);
+static DEVICE_ATTR(mem_limit, S_IRUGO | S_IWUSR, mem_limit_show,
+               mem_limit_store);
+static DEVICE_ATTR(mem_used_max, S_IRUGO | S_IWUSR, mem_used_max_show,
+               mem_used_max_store);
 static DEVICE_ATTR(max_comp_streams, S_IRUGO | S_IWUSR,
                max_comp_streams_show, max_comp_streams_store);
 static DEVICE_ATTR(comp_algorithm, S_IRUGO | S_IWUSR,
@@ -885,6 +985,8 @@ static struct attribute *zram_disk_attrs[] = {
        &dev_attr_orig_data_size.attr,
        &dev_attr_compr_data_size.attr,
        &dev_attr_mem_used_total.attr,
+       &dev_attr_mem_limit.attr,
+       &dev_attr_mem_used_max.attr,
        &dev_attr_max_comp_streams.attr,
        &dev_attr_comp_algorithm.attr,
        NULL,
index e0f725c87cc617b4ade4c7574c18fa6ce15ca828..c6ee271317f5b344619bdc7c01bc16a858f18a9f 100644 (file)
@@ -90,6 +90,7 @@ struct zram_stats {
        atomic64_t notify_free; /* no. of swap slot free notifications */
        atomic64_t zero_pages;          /* no. of zero filled pages */
        atomic64_t pages_stored;        /* no. of pages currently stored */
+       atomic_long_t max_used_pages;   /* no. of maximum pages stored */
 };
 
 struct zram_meta {
@@ -112,6 +113,11 @@ struct zram {
        u64 disksize;   /* bytes */
        int max_comp_streams;
        struct zram_stats stats;
+       /*
+        * the number of pages zram can consume for storing compressed data
+        */
+       unsigned long limit_pages;
+
        char compressor[10];
 };
 #endif
index 2064b4527040be68920c3ef9e557b090ca587f7a..441b44e5422691c7ed1a38a84ca8d0fa661849e1 100644 (file)
@@ -367,12 +367,13 @@ static const struct xenbus_device_id tpmfront_ids[] = {
 };
 MODULE_ALIAS("xen:vtpm");
 
-static DEFINE_XENBUS_DRIVER(tpmfront, ,
-               .probe = tpmfront_probe,
-               .remove = tpmfront_remove,
-               .resume = tpmfront_resume,
-               .otherend_changed = backend_changed,
-       );
+static struct xenbus_driver tpmfront_driver = {
+       .ids = tpmfront_ids,
+       .probe = tpmfront_probe,
+       .remove = tpmfront_remove,
+       .resume = tpmfront_resume,
+       .otherend_changed = backend_changed,
+};
 
 static int __init xen_tpmfront_init(void)
 {
index 82a2ebe41e27706213a1670a2f3cdee3488b0a94..90420600e1eb1d6668d78eb6eb809e218d25673f 100644 (file)
@@ -30,6 +30,9 @@ config ARMADA_370_XP_TIMER
        bool
        select CLKSRC_OF
 
+config MESON6_TIMER
+       bool
+
 config ORION_TIMER
        select CLKSRC_OF
        select CLKSRC_MMIO
index e566f6c7ded4925c2904132f376f5380ce4bb6d1..756f6f10efa03de825a9b1ef48edf8315f331161 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_ARCH_PRIMA2)     += timer-prima2.o
 obj-$(CONFIG_ARCH_U300)                += timer-u300.o
 obj-$(CONFIG_SUN4I_TIMER)      += sun4i_timer.o
 obj-$(CONFIG_SUN5I_HSTIMER)    += timer-sun5i.o
+obj-$(CONFIG_MESON6_TIMER)     += meson6_timer.o
 obj-$(CONFIG_ARCH_TEGRA)       += tegra20_timer.o
 obj-$(CONFIG_VT8500_TIMER)     += vt8500_timer.o
 obj-$(CONFIG_ARCH_NSPIRE)      += zevio-timer.o
index 5163ec13429d1e37082b8d32c84684284217322f..2133f9d59d06323bbf1f69ca80c21ddee66904f6 100644 (file)
@@ -299,6 +299,21 @@ static void __arch_timer_setup(unsigned type,
        clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff);
 }
 
+static void arch_timer_evtstrm_enable(int divider)
+{
+       u32 cntkctl = arch_timer_get_cntkctl();
+
+       cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK;
+       /* Set the divider and enable virtual event stream */
+       cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT)
+                       | ARCH_TIMER_VIRT_EVT_EN;
+       arch_timer_set_cntkctl(cntkctl);
+       elf_hwcap |= HWCAP_EVTSTRM;
+#ifdef CONFIG_COMPAT
+       compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM;
+#endif
+}
+
 static void arch_timer_configure_evtstream(void)
 {
        int evt_stream_div, pos;
@@ -312,6 +327,23 @@ static void arch_timer_configure_evtstream(void)
        arch_timer_evtstrm_enable(min(pos, 15));
 }
 
+static void arch_counter_set_user_access(void)
+{
+       u32 cntkctl = arch_timer_get_cntkctl();
+
+       /* Disable user access to the timers and the physical counter */
+       /* Also disable virtual event stream */
+       cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
+                       | ARCH_TIMER_USR_VT_ACCESS_EN
+                       | ARCH_TIMER_VIRT_EVT_EN
+                       | ARCH_TIMER_USR_PCT_ACCESS_EN);
+
+       /* Enable user access to the virtual counter */
+       cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN;
+
+       arch_timer_set_cntkctl(cntkctl);
+}
+
 static int arch_timer_setup(struct clock_event_device *clk)
 {
        __arch_timer_setup(ARCH_CP15_TIMER, clk);
@@ -429,11 +461,19 @@ static void __init arch_counter_register(unsigned type)
        u64 start_count;
 
        /* Register the CP15 based counter if we have one */
-       if (type & ARCH_CP15_TIMER)
+       if (type & ARCH_CP15_TIMER) {
                arch_timer_read_counter = arch_counter_get_cntvct;
-       else
+       } else {
                arch_timer_read_counter = arch_counter_get_cntvct_mem;
 
+               /* If the clocksource name is "arch_sys_counter" the
+                * VDSO will attempt to read the CP15-based counter.
+                * Ensure this does not happen when CP15-based
+                * counter is not available.
+                */
+               clocksource_counter.name = "arch_mem_counter";
+       }
+
        start_count = arch_timer_read_counter();
        clocksource_register_hz(&clocksource_counter, arch_timer_rate);
        cyclecounter.mult = clocksource_counter.mult;
@@ -616,17 +656,29 @@ static const struct of_device_id arch_timer_mem_of_match[] __initconst = {
        {},
 };
 
+static bool __init
+arch_timer_probed(int type, const struct of_device_id *matches)
+{
+       struct device_node *dn;
+       bool probed = false;
+
+       dn = of_find_matching_node(NULL, matches);
+       if (dn && of_device_is_available(dn) && (arch_timers_present & type))
+               probed = true;
+       of_node_put(dn);
+
+       return probed;
+}
+
 static void __init arch_timer_common_init(void)
 {
        unsigned mask = ARCH_CP15_TIMER | ARCH_MEM_TIMER;
 
        /* Wait until both nodes are probed if we have two timers */
        if ((arch_timers_present & mask) != mask) {
-               if (of_find_matching_node(NULL, arch_timer_mem_of_match) &&
-                               !(arch_timers_present & ARCH_MEM_TIMER))
+               if (!arch_timer_probed(ARCH_MEM_TIMER, arch_timer_mem_of_match))
                        return;
-               if (of_find_matching_node(NULL, arch_timer_of_match) &&
-                               !(arch_timers_present & ARCH_CP15_TIMER))
+               if (!arch_timer_probed(ARCH_CP15_TIMER, arch_timer_of_match))
                        return;
        }
 
index 7a08811df9aa61505a763a3be3a5968339453120..510c8a1d37b375841bc49879e3dd0dfccb00470b 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/sched_clock.h>
 
 /*
- * This driver configures the 2 16-bit count-up timers as follows:
+ * This driver configures the 2 16/32-bit count-up timers as follows:
  *
  * T1: Timer 1, clocksource for generic timekeeping
  * T2: Timer 2, clockevent source for hrtimers
@@ -321,7 +321,8 @@ static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
        return NOTIFY_DONE;
 }
 
-static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
+static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base,
+                                        u32 timer_width)
 {
        struct ttc_timer_clocksource *ttccs;
        int err;
@@ -351,7 +352,7 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
        ttccs->cs.name = "ttc_clocksource";
        ttccs->cs.rating = 200;
        ttccs->cs.read = __ttc_clocksource_read;
-       ttccs->cs.mask = CLOCKSOURCE_MASK(16);
+       ttccs->cs.mask = CLOCKSOURCE_MASK(timer_width);
        ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
 
        /*
@@ -372,7 +373,8 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
        }
 
        ttc_sched_clock_val_reg = base + TTC_COUNT_VAL_OFFSET;
-       sched_clock_register(ttc_sched_clock_read, 16, ttccs->ttc.freq / PRESCALE);
+       sched_clock_register(ttc_sched_clock_read, timer_width,
+                            ttccs->ttc.freq / PRESCALE);
 }
 
 static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
@@ -467,6 +469,7 @@ static void __init ttc_timer_init(struct device_node *timer)
        struct clk *clk_cs, *clk_ce;
        static int initialized;
        int clksel;
+       u32 timer_width = 16;
 
        if (initialized)
                return;
@@ -490,6 +493,8 @@ static void __init ttc_timer_init(struct device_node *timer)
                BUG();
        }
 
+       of_property_read_u32(timer, "timer-width", &timer_width);
+
        clksel = readl_relaxed(timer_baseaddr + TTC_CLK_CNTRL_OFFSET);
        clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
        clk_cs = of_clk_get(timer, clksel);
@@ -506,7 +511,7 @@ static void __init ttc_timer_init(struct device_node *timer)
                BUG();
        }
 
-       ttc_setup_clocksource(clk_cs, timer_baseaddr);
+       ttc_setup_clocksource(clk_cs, timer_baseaddr, timer_width);
        ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq);
 
        pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
diff --git a/drivers/clocksource/meson6_timer.c b/drivers/clocksource/meson6_timer.c
new file mode 100644 (file)
index 0000000..5c15cba
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Amlogic Meson6 SoCs timer handling.
+ *
+ * Copyright (C) 2014 Carlo Caione <carlo@caione.org>
+ *
+ * Based on code from Amlogic, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/sched_clock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define CED_ID                 0
+#define CSD_ID                 4
+
+#define TIMER_ISA_MUX          0
+#define TIMER_ISA_VAL(t)       (((t) + 1) << 2)
+
+#define TIMER_INPUT_BIT(t)     (2 * (t))
+#define TIMER_ENABLE_BIT(t)    (16 + (t))
+#define TIMER_PERIODIC_BIT(t)  (12 + (t))
+
+#define TIMER_CED_INPUT_MASK   (3UL << TIMER_INPUT_BIT(CED_ID))
+#define TIMER_CSD_INPUT_MASK   (7UL << TIMER_INPUT_BIT(CSD_ID))
+
+#define TIMER_CED_UNIT_1US     0
+#define TIMER_CSD_UNIT_1US     1
+
+static void __iomem *timer_base;
+
+static u64 notrace meson6_timer_sched_read(void)
+{
+       return (u64)readl(timer_base + TIMER_ISA_VAL(CSD_ID));
+}
+
+static void meson6_clkevt_time_stop(unsigned char timer)
+{
+       u32 val = readl(timer_base + TIMER_ISA_MUX);
+
+       writel(val & ~TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
+}
+
+static void meson6_clkevt_time_setup(unsigned char timer, unsigned long delay)
+{
+       writel(delay, timer_base + TIMER_ISA_VAL(timer));
+}
+
+static void meson6_clkevt_time_start(unsigned char timer, bool periodic)
+{
+       u32 val = readl(timer_base + TIMER_ISA_MUX);
+
+       if (periodic)
+               val |= TIMER_PERIODIC_BIT(timer);
+       else
+               val &= ~TIMER_PERIODIC_BIT(timer);
+
+       writel(val | TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
+}
+
+static void meson6_clkevt_mode(enum clock_event_mode mode,
+                              struct clock_event_device *clk)
+{
+       switch (mode) {
+       case CLOCK_EVT_MODE_PERIODIC:
+               meson6_clkevt_time_stop(CED_ID);
+               meson6_clkevt_time_setup(CED_ID, USEC_PER_SEC/HZ - 1);
+               meson6_clkevt_time_start(CED_ID, true);
+               break;
+       case CLOCK_EVT_MODE_ONESHOT:
+               meson6_clkevt_time_stop(CED_ID);
+               meson6_clkevt_time_start(CED_ID, false);
+               break;
+       case CLOCK_EVT_MODE_UNUSED:
+       case CLOCK_EVT_MODE_SHUTDOWN:
+       default:
+               meson6_clkevt_time_stop(CED_ID);
+               break;
+       }
+}
+
+static int meson6_clkevt_next_event(unsigned long evt,
+                                   struct clock_event_device *unused)
+{
+       meson6_clkevt_time_stop(CED_ID);
+       meson6_clkevt_time_setup(CED_ID, evt);
+       meson6_clkevt_time_start(CED_ID, false);
+
+       return 0;
+}
+
+static struct clock_event_device meson6_clockevent = {
+       .name           = "meson6_tick",
+       .rating         = 400,
+       .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+       .set_mode       = meson6_clkevt_mode,
+       .set_next_event = meson6_clkevt_next_event,
+};
+
+static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id)
+{
+       struct clock_event_device *evt = (struct clock_event_device *)dev_id;
+
+       evt->event_handler(evt);
+
+       return IRQ_HANDLED;
+}
+
+static struct irqaction meson6_timer_irq = {
+       .name           = "meson6_timer",
+       .flags          = IRQF_TIMER | IRQF_IRQPOLL,
+       .handler        = meson6_timer_interrupt,
+       .dev_id         = &meson6_clockevent,
+};
+
+static void __init meson6_timer_init(struct device_node *node)
+{
+       u32 val;
+       int ret, irq;
+
+       timer_base = of_io_request_and_map(node, 0, "meson6-timer");
+       if (IS_ERR(timer_base))
+               panic("Can't map registers");
+
+       irq = irq_of_parse_and_map(node, 0);
+       if (irq <= 0)
+               panic("Can't parse IRQ");
+
+       /* Set 1us for timer E */
+       val = readl(timer_base + TIMER_ISA_MUX);
+       val &= ~TIMER_CSD_INPUT_MASK;
+       val |= TIMER_CSD_UNIT_1US << TIMER_INPUT_BIT(CSD_ID);
+       writel(val, timer_base + TIMER_ISA_MUX);
+
+       sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC);
+       clocksource_mmio_init(timer_base + TIMER_ISA_VAL(CSD_ID), node->name,
+                             1000 * 1000, 300, 32, clocksource_mmio_readl_up);
+
+       /* Timer A base 1us */
+       val &= ~TIMER_CED_INPUT_MASK;
+       val |= TIMER_CED_UNIT_1US << TIMER_INPUT_BIT(CED_ID);
+       writel(val, timer_base + TIMER_ISA_MUX);
+
+       /* Stop the timer A */
+       meson6_clkevt_time_stop(CED_ID);
+
+       ret = setup_irq(irq, &meson6_timer_irq);
+       if (ret)
+               pr_warn("failed to setup irq %d\n", irq);
+
+       meson6_clockevent.cpumask = cpu_possible_mask;
+       meson6_clockevent.irq = irq;
+
+       clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC,
+                                       1, 0xfffe);
+}
+CLOCKSOURCE_OF_DECLARE(meson6, "amlogic,meson6-timer",
+                      meson6_timer_init);
index 330e93064692b881c1245dd2b4a64ab5a29cabd4..caf7a20304617f3153b0ea56564d702f2a3c0c42 100644 (file)
@@ -63,7 +63,7 @@ static inline void sirfsoc_timer_count_disable(int idx)
 /* enable count and interrupt */
 static inline void sirfsoc_timer_count_enable(int idx)
 {
-       writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7,
+       writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x3,
                sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
 }
 
@@ -103,6 +103,9 @@ static int sirfsoc_timer_set_next_event(unsigned long delta,
 {
        int cpu = smp_processor_id();
 
+       /* disable timer first, then modify the related registers */
+       sirfsoc_timer_count_disable(cpu);
+
        writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 +
                4 * cpu);
        writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 +
index a918bc481c52c46a83f2e1ff1a50a96dc4180082..b45ac6229b57137d11898ede5ce11f7ec7c69439 100644 (file)
@@ -93,6 +93,10 @@ static void pit_set_mode(enum clock_event_mode mode,
        case CLOCK_EVT_MODE_PERIODIC:
                pit_set_next_event(cycle_per_jiffy, evt);
                break;
+       case CLOCK_EVT_MODE_SHUTDOWN:
+       case CLOCK_EVT_MODE_UNUSED:
+               pit_timer_disable();
+               break;
        default:
                break;
        }
index ffe350f86bca570e879177c63395efff5de6587d..3489f8f5fadabee1b8db494c7b5dbd996bd70e33 100644 (file)
@@ -183,14 +183,14 @@ config CPU_FREQ_GOV_CONSERVATIVE
 
          If in doubt, say N.
 
-config GENERIC_CPUFREQ_CPU0
-       tristate "Generic CPU0 cpufreq driver"
+config CPUFREQ_DT
+       tristate "Generic DT based cpufreq driver"
        depends on HAVE_CLK && OF
-       # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y:
+       # if CPU_THERMAL is on and THERMAL=m, CPUFREQ_DT cannot be =y:
        depends on !CPU_THERMAL || THERMAL
        select PM_OPP
        help
-         This adds a generic cpufreq driver for CPU0 frequency management.
+         This adds a generic DT based cpufreq driver for frequency management.
          It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
          systems which share clock and voltage across all CPUs.
 
index 28c666c8014969925697abbf1746f74705c8c809..83a75dc84761a3c4e9a4e385c66d19ab17706b31 100644 (file)
@@ -92,7 +92,7 @@ config ARM_EXYNOS_CPU_FREQ_BOOST_SW
 
 config ARM_HIGHBANK_CPUFREQ
        tristate "Calxeda Highbank-based"
-       depends on ARCH_HIGHBANK && GENERIC_CPUFREQ_CPU0 && REGULATOR
+       depends on ARCH_HIGHBANK && CPUFREQ_DT && REGULATOR
        default m
        help
          This adds the CPUFreq driver for Calxeda Highbank SoC
index db6d9a2fea4d534f135af08880f229d511633c91..40c53dc1937ec6a4fc22e61f497939ba2ada54bd 100644 (file)
@@ -13,7 +13,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND)   += cpufreq_ondemand.o
 obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE)        += cpufreq_conservative.o
 obj-$(CONFIG_CPU_FREQ_GOV_COMMON)              += cpufreq_governor.o
 
-obj-$(CONFIG_GENERIC_CPUFREQ_CPU0)     += cpufreq-cpu0.o
+obj-$(CONFIG_CPUFREQ_DT)               += cpufreq-dt.o
 
 ##################################################################################
 # x86 drivers.
diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c
deleted file mode 100644 (file)
index 0d2172b..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2012 Freescale Semiconductor, Inc.
- *
- * The OPP code in function cpu0_set_target() is reused from
- * drivers/cpufreq/omap-cpufreq.c
- *
- * 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 pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
-
-#include <linux/clk.h>
-#include <linux/cpu.h>
-#include <linux/cpu_cooling.h>
-#include <linux/cpufreq.h>
-#include <linux/cpumask.h>
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/pm_opp.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/thermal.h>
-
-static unsigned int transition_latency;
-static unsigned int voltage_tolerance; /* in percentage */
-
-static struct device *cpu_dev;
-static struct clk *cpu_clk;
-static struct regulator *cpu_reg;
-static struct cpufreq_frequency_table *freq_table;
-static struct thermal_cooling_device *cdev;
-
-static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index)
-{
-       struct dev_pm_opp *opp;
-       unsigned long volt = 0, volt_old = 0, tol = 0;
-       unsigned int old_freq, new_freq;
-       long freq_Hz, freq_exact;
-       int ret;
-
-       freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
-       if (freq_Hz <= 0)
-               freq_Hz = freq_table[index].frequency * 1000;
-
-       freq_exact = freq_Hz;
-       new_freq = freq_Hz / 1000;
-       old_freq = clk_get_rate(cpu_clk) / 1000;
-
-       if (!IS_ERR(cpu_reg)) {
-               rcu_read_lock();
-               opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
-               if (IS_ERR(opp)) {
-                       rcu_read_unlock();
-                       pr_err("failed to find OPP for %ld\n", freq_Hz);
-                       return PTR_ERR(opp);
-               }
-               volt = dev_pm_opp_get_voltage(opp);
-               rcu_read_unlock();
-               tol = volt * voltage_tolerance / 100;
-               volt_old = regulator_get_voltage(cpu_reg);
-       }
-
-       pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n",
-                old_freq / 1000, volt_old ? volt_old / 1000 : -1,
-                new_freq / 1000, volt ? volt / 1000 : -1);
-
-       /* scaling up?  scale voltage before frequency */
-       if (!IS_ERR(cpu_reg) && new_freq > old_freq) {
-               ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
-               if (ret) {
-                       pr_err("failed to scale voltage up: %d\n", ret);
-                       return ret;
-               }
-       }
-
-       ret = clk_set_rate(cpu_clk, freq_exact);
-       if (ret) {
-               pr_err("failed to set clock rate: %d\n", ret);
-               if (!IS_ERR(cpu_reg))
-                       regulator_set_voltage_tol(cpu_reg, volt_old, tol);
-               return ret;
-       }
-
-       /* scaling down?  scale voltage after frequency */
-       if (!IS_ERR(cpu_reg) && new_freq < old_freq) {
-               ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
-               if (ret) {
-                       pr_err("failed to scale voltage down: %d\n", ret);
-                       clk_set_rate(cpu_clk, old_freq * 1000);
-               }
-       }
-
-       return ret;
-}
-
-static int cpu0_cpufreq_init(struct cpufreq_policy *policy)
-{
-       policy->clk = cpu_clk;
-       return cpufreq_generic_init(policy, freq_table, transition_latency);
-}
-
-static struct cpufreq_driver cpu0_cpufreq_driver = {
-       .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
-       .verify = cpufreq_generic_frequency_table_verify,
-       .target_index = cpu0_set_target,
-       .get = cpufreq_generic_get,
-       .init = cpu0_cpufreq_init,
-       .name = "generic_cpu0",
-       .attr = cpufreq_generic_attr,
-};
-
-static int cpu0_cpufreq_probe(struct platform_device *pdev)
-{
-       struct device_node *np;
-       int ret;
-
-       cpu_dev = get_cpu_device(0);
-       if (!cpu_dev) {
-               pr_err("failed to get cpu0 device\n");
-               return -ENODEV;
-       }
-
-       np = of_node_get(cpu_dev->of_node);
-       if (!np) {
-               pr_err("failed to find cpu0 node\n");
-               return -ENOENT;
-       }
-
-       cpu_reg = regulator_get_optional(cpu_dev, "cpu0");
-       if (IS_ERR(cpu_reg)) {
-               /*
-                * If cpu0 regulator supply node is present, but regulator is
-                * not yet registered, we should try defering probe.
-                */
-               if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) {
-                       dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n");
-                       ret = -EPROBE_DEFER;
-                       goto out_put_node;
-               }
-               pr_warn("failed to get cpu0 regulator: %ld\n",
-                       PTR_ERR(cpu_reg));
-       }
-
-       cpu_clk = clk_get(cpu_dev, NULL);
-       if (IS_ERR(cpu_clk)) {
-               ret = PTR_ERR(cpu_clk);
-               pr_err("failed to get cpu0 clock: %d\n", ret);
-               goto out_put_reg;
-       }
-
-       /* OPPs might be populated at runtime, don't check for error here */
-       of_init_opp_table(cpu_dev);
-
-       ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
-       if (ret) {
-               pr_err("failed to init cpufreq table: %d\n", ret);
-               goto out_put_clk;
-       }
-
-       of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance);
-
-       if (of_property_read_u32(np, "clock-latency", &transition_latency))
-               transition_latency = CPUFREQ_ETERNAL;
-
-       if (!IS_ERR(cpu_reg)) {
-               struct dev_pm_opp *opp;
-               unsigned long min_uV, max_uV;
-               int i;
-
-               /*
-                * OPP is maintained in order of increasing frequency, and
-                * freq_table initialised from OPP is therefore sorted in the
-                * same order.
-                */
-               for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
-                       ;
-               rcu_read_lock();
-               opp = dev_pm_opp_find_freq_exact(cpu_dev,
-                               freq_table[0].frequency * 1000, true);
-               min_uV = dev_pm_opp_get_voltage(opp);
-               opp = dev_pm_opp_find_freq_exact(cpu_dev,
-                               freq_table[i-1].frequency * 1000, true);
-               max_uV = dev_pm_opp_get_voltage(opp);
-               rcu_read_unlock();
-               ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
-               if (ret > 0)
-                       transition_latency += ret * 1000;
-       }
-
-       ret = cpufreq_register_driver(&cpu0_cpufreq_driver);
-       if (ret) {
-               pr_err("failed register driver: %d\n", ret);
-               goto out_free_table;
-       }
-
-       /*
-        * For now, just loading the cooling device;
-        * thermal DT code takes care of matching them.
-        */
-       if (of_find_property(np, "#cooling-cells", NULL)) {
-               cdev = of_cpufreq_cooling_register(np, cpu_present_mask);
-               if (IS_ERR(cdev))
-                       pr_err("running cpufreq without cooling device: %ld\n",
-                              PTR_ERR(cdev));
-       }
-
-       of_node_put(np);
-       return 0;
-
-out_free_table:
-       dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
-out_put_clk:
-       if (!IS_ERR(cpu_clk))
-               clk_put(cpu_clk);
-out_put_reg:
-       if (!IS_ERR(cpu_reg))
-               regulator_put(cpu_reg);
-out_put_node:
-       of_node_put(np);
-       return ret;
-}
-
-static int cpu0_cpufreq_remove(struct platform_device *pdev)
-{
-       cpufreq_cooling_unregister(cdev);
-       cpufreq_unregister_driver(&cpu0_cpufreq_driver);
-       dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
-
-       return 0;
-}
-
-static struct platform_driver cpu0_cpufreq_platdrv = {
-       .driver = {
-               .name   = "cpufreq-cpu0",
-               .owner  = THIS_MODULE,
-       },
-       .probe          = cpu0_cpufreq_probe,
-       .remove         = cpu0_cpufreq_remove,
-};
-module_platform_driver(cpu0_cpufreq_platdrv);
-
-MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
-MODULE_DESCRIPTION("Generic CPU0 cpufreq driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
new file mode 100644 (file)
index 0000000..6bbb8b9
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *
+ * Copyright (C) 2014 Linaro.
+ * Viresh Kumar <viresh.kumar@linaro.org>
+ *
+ * The OPP code in function set_target() is reused from
+ * drivers/cpufreq/omap-cpufreq.c
+ *
+ * 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 pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpu_cooling.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_opp.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+struct private_data {
+       struct device *cpu_dev;
+       struct regulator *cpu_reg;
+       struct thermal_cooling_device *cdev;
+       unsigned int voltage_tolerance; /* in percentage */
+};
+
+static int set_target(struct cpufreq_policy *policy, unsigned int index)
+{
+       struct dev_pm_opp *opp;
+       struct cpufreq_frequency_table *freq_table = policy->freq_table;
+       struct clk *cpu_clk = policy->clk;
+       struct private_data *priv = policy->driver_data;
+       struct device *cpu_dev = priv->cpu_dev;
+       struct regulator *cpu_reg = priv->cpu_reg;
+       unsigned long volt = 0, volt_old = 0, tol = 0;
+       unsigned int old_freq, new_freq;
+       long freq_Hz, freq_exact;
+       int ret;
+
+       freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
+       if (freq_Hz <= 0)
+               freq_Hz = freq_table[index].frequency * 1000;
+
+       freq_exact = freq_Hz;
+       new_freq = freq_Hz / 1000;
+       old_freq = clk_get_rate(cpu_clk) / 1000;
+
+       if (!IS_ERR(cpu_reg)) {
+               rcu_read_lock();
+               opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
+               if (IS_ERR(opp)) {
+                       rcu_read_unlock();
+                       dev_err(cpu_dev, "failed to find OPP for %ld\n",
+                               freq_Hz);
+                       return PTR_ERR(opp);
+               }
+               volt = dev_pm_opp_get_voltage(opp);
+               rcu_read_unlock();
+               tol = volt * priv->voltage_tolerance / 100;
+               volt_old = regulator_get_voltage(cpu_reg);
+       }
+
+       dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n",
+               old_freq / 1000, volt_old ? volt_old / 1000 : -1,
+               new_freq / 1000, volt ? volt / 1000 : -1);
+
+       /* scaling up?  scale voltage before frequency */
+       if (!IS_ERR(cpu_reg) && new_freq > old_freq) {
+               ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
+               if (ret) {
+                       dev_err(cpu_dev, "failed to scale voltage up: %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       ret = clk_set_rate(cpu_clk, freq_exact);
+       if (ret) {
+               dev_err(cpu_dev, "failed to set clock rate: %d\n", ret);
+               if (!IS_ERR(cpu_reg))
+                       regulator_set_voltage_tol(cpu_reg, volt_old, tol);
+               return ret;
+       }
+
+       /* scaling down?  scale voltage after frequency */
+       if (!IS_ERR(cpu_reg) && new_freq < old_freq) {
+               ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
+               if (ret) {
+                       dev_err(cpu_dev, "failed to scale voltage down: %d\n",
+                               ret);
+                       clk_set_rate(cpu_clk, old_freq * 1000);
+               }
+       }
+
+       return ret;
+}
+
+static int allocate_resources(int cpu, struct device **cdev,
+                             struct regulator **creg, struct clk **cclk)
+{
+       struct device *cpu_dev;
+       struct regulator *cpu_reg;
+       struct clk *cpu_clk;
+       int ret = 0;
+       char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg;
+
+       cpu_dev = get_cpu_device(cpu);
+       if (!cpu_dev) {
+               pr_err("failed to get cpu%d device\n", cpu);
+               return -ENODEV;
+       }
+
+       /* Try "cpu0" for older DTs */
+       if (!cpu)
+               reg = reg_cpu0;
+       else
+               reg = reg_cpu;
+
+try_again:
+       cpu_reg = regulator_get_optional(cpu_dev, reg);
+       if (IS_ERR(cpu_reg)) {
+               /*
+                * If cpu's regulator supply node is present, but regulator is
+                * not yet registered, we should try defering probe.
+                */
+               if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) {
+                       dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n",
+                               cpu);
+                       return -EPROBE_DEFER;
+               }
+
+               /* Try with "cpu-supply" */
+               if (reg == reg_cpu0) {
+                       reg = reg_cpu;
+                       goto try_again;
+               }
+
+               dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n",
+                        cpu, PTR_ERR(cpu_reg));
+       }
+
+       cpu_clk = clk_get(cpu_dev, NULL);
+       if (IS_ERR(cpu_clk)) {
+               /* put regulator */
+               if (!IS_ERR(cpu_reg))
+                       regulator_put(cpu_reg);
+
+               ret = PTR_ERR(cpu_clk);
+
+               /*
+                * If cpu's clk node is present, but clock is not yet
+                * registered, we should try defering probe.
+                */
+               if (ret == -EPROBE_DEFER)
+                       dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu);
+               else
+                       dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret,
+                               cpu);
+       } else {
+               *cdev = cpu_dev;
+               *creg = cpu_reg;
+               *cclk = cpu_clk;
+       }
+
+       return ret;
+}
+
+static int cpufreq_init(struct cpufreq_policy *policy)
+{
+       struct cpufreq_frequency_table *freq_table;
+       struct thermal_cooling_device *cdev;
+       struct device_node *np;
+       struct private_data *priv;
+       struct device *cpu_dev;
+       struct regulator *cpu_reg;
+       struct clk *cpu_clk;
+       unsigned int transition_latency;
+       int ret;
+
+       ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk);
+       if (ret) {
+               pr_err("%s: Failed to allocate resources\n: %d", __func__, ret);
+               return ret;
+       }
+
+       np = of_node_get(cpu_dev->of_node);
+       if (!np) {
+               dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu);
+               ret = -ENOENT;
+               goto out_put_reg_clk;
+       }
+
+       /* OPPs might be populated at runtime, don't check for error here */
+       of_init_opp_table(cpu_dev);
+
+       ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
+       if (ret) {
+               dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
+               goto out_put_node;
+       }
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               ret = -ENOMEM;
+               goto out_free_table;
+       }
+
+       of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance);
+
+       if (of_property_read_u32(np, "clock-latency", &transition_latency))
+               transition_latency = CPUFREQ_ETERNAL;
+
+       if (!IS_ERR(cpu_reg)) {
+               struct dev_pm_opp *opp;
+               unsigned long min_uV, max_uV;
+               int i;
+
+               /*
+                * OPP is maintained in order of increasing frequency, and
+                * freq_table initialised from OPP is therefore sorted in the
+                * same order.
+                */
+               for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
+                       ;
+               rcu_read_lock();
+               opp = dev_pm_opp_find_freq_exact(cpu_dev,
+                               freq_table[0].frequency * 1000, true);
+               min_uV = dev_pm_opp_get_voltage(opp);
+               opp = dev_pm_opp_find_freq_exact(cpu_dev,
+                               freq_table[i-1].frequency * 1000, true);
+               max_uV = dev_pm_opp_get_voltage(opp);
+               rcu_read_unlock();
+               ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
+               if (ret > 0)
+                       transition_latency += ret * 1000;
+       }
+
+       /*
+        * For now, just loading the cooling device;
+        * thermal DT code takes care of matching them.
+        */
+       if (of_find_property(np, "#cooling-cells", NULL)) {
+               cdev = of_cpufreq_cooling_register(np, cpu_present_mask);
+               if (IS_ERR(cdev))
+                       dev_err(cpu_dev,
+                               "running cpufreq without cooling device: %ld\n",
+                               PTR_ERR(cdev));
+               else
+                       priv->cdev = cdev;
+       }
+
+       priv->cpu_dev = cpu_dev;
+       priv->cpu_reg = cpu_reg;
+       policy->driver_data = priv;
+
+       policy->clk = cpu_clk;
+       ret = cpufreq_generic_init(policy, freq_table, transition_latency);
+       if (ret)
+               goto out_cooling_unregister;
+
+       of_node_put(np);
+
+       return 0;
+
+out_cooling_unregister:
+       cpufreq_cooling_unregister(priv->cdev);
+       kfree(priv);
+out_free_table:
+       dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
+out_put_node:
+       of_node_put(np);
+out_put_reg_clk:
+       clk_put(cpu_clk);
+       if (!IS_ERR(cpu_reg))
+               regulator_put(cpu_reg);
+
+       return ret;
+}
+
+static int cpufreq_exit(struct cpufreq_policy *policy)
+{
+       struct private_data *priv = policy->driver_data;
+
+       cpufreq_cooling_unregister(priv->cdev);
+       dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
+       clk_put(policy->clk);
+       if (!IS_ERR(priv->cpu_reg))
+               regulator_put(priv->cpu_reg);
+       kfree(priv);
+
+       return 0;
+}
+
+static struct cpufreq_driver dt_cpufreq_driver = {
+       .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
+       .verify = cpufreq_generic_frequency_table_verify,
+       .target_index = set_target,
+       .get = cpufreq_generic_get,
+       .init = cpufreq_init,
+       .exit = cpufreq_exit,
+       .name = "cpufreq-dt",
+       .attr = cpufreq_generic_attr,
+};
+
+static int dt_cpufreq_probe(struct platform_device *pdev)
+{
+       struct device *cpu_dev;
+       struct regulator *cpu_reg;
+       struct clk *cpu_clk;
+       int ret;
+
+       /*
+        * All per-cluster (CPUs sharing clock/voltages) initialization is done
+        * from ->init(). In probe(), we just need to make sure that clk and
+        * regulators are available. Else defer probe and retry.
+        *
+        * FIXME: Is checking this only for CPU0 sufficient ?
+        */
+       ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk);
+       if (ret)
+               return ret;
+
+       clk_put(cpu_clk);
+       if (!IS_ERR(cpu_reg))
+               regulator_put(cpu_reg);
+
+       ret = cpufreq_register_driver(&dt_cpufreq_driver);
+       if (ret)
+               dev_err(cpu_dev, "failed register driver: %d\n", ret);
+
+       return ret;
+}
+
+static int dt_cpufreq_remove(struct platform_device *pdev)
+{
+       cpufreq_unregister_driver(&dt_cpufreq_driver);
+       return 0;
+}
+
+static struct platform_driver dt_cpufreq_platdrv = {
+       .driver = {
+               .name   = "cpufreq-dt",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = dt_cpufreq_probe,
+       .remove         = dt_cpufreq_remove,
+};
+module_platform_driver(dt_cpufreq_platdrv);
+
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
+MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
+MODULE_DESCRIPTION("Generic cpufreq driver");
+MODULE_LICENSE("GPL");
index 61190f6b48299ae7f89dbf6933b5e3d2333d84c1..24bf76fba141197eda0c2d4ec24ecbf4311860d1 100644 (file)
@@ -437,7 +437,7 @@ static struct cpufreq_governor *__find_governor(const char *str_governor)
        struct cpufreq_governor *t;
 
        list_for_each_entry(t, &cpufreq_governor_list, governor_list)
-               if (!strnicmp(str_governor, t->name, CPUFREQ_NAME_LEN))
+               if (!strncasecmp(str_governor, t->name, CPUFREQ_NAME_LEN))
                        return t;
 
        return NULL;
@@ -455,10 +455,10 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy,
                goto out;
 
        if (cpufreq_driver->setpolicy) {
-               if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {
+               if (!strncasecmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {
                        *policy = CPUFREQ_POLICY_PERFORMANCE;
                        err = 0;
-               } else if (!strnicmp(str_governor, "powersave",
+               } else if (!strncasecmp(str_governor, "powersave",
                                                CPUFREQ_NAME_LEN)) {
                        *policy = CPUFREQ_POLICY_POWERSAVE;
                        err = 0;
@@ -1382,7 +1382,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev,
                if (!cpufreq_suspended)
                        pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n",
                                 __func__, new_cpu, cpu);
-       } else if (cpufreq_driver->stop_cpu && cpufreq_driver->setpolicy) {
+       } else if (cpufreq_driver->stop_cpu) {
                cpufreq_driver->stop_cpu(policy);
        }
 
index 61a54310a1b9df6923a1fbfbb3ab6e1872c914eb..843ec824fd91051db1af8751d155018261d9043c 100644 (file)
@@ -127,7 +127,7 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info)
         * dependencies on platform headers. It is necessary to enable
         * Exynos multi-platform support and will be removed together with
         * this whole driver as soon as Exynos gets migrated to use
-        * cpufreq-cpu0 driver.
+        * cpufreq-dt driver.
         */
        np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock");
        if (!np) {
index 351a2074cfea784c8a522b3fa6080a67c59e7180..9e78a850e29f4dc967fceb422f22cdc6a571355c 100644 (file)
@@ -174,7 +174,7 @@ int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
         * dependencies on platform headers. It is necessary to enable
         * Exynos multi-platform support and will be removed together with
         * this whole driver as soon as Exynos gets migrated to use
-        * cpufreq-cpu0 driver.
+        * cpufreq-dt driver.
         */
        np = of_find_compatible_node(NULL, NULL, "samsung,exynos4412-clock");
        if (!np) {
index c91ce69dc63101d3a1f866b906acaf780c20070a..3eafdc7ba7877f4eedc2484fa7208c32f5cf49eb 100644 (file)
@@ -153,7 +153,7 @@ int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
         * dependencies on platform headers. It is necessary to enable
         * Exynos multi-platform support and will be removed together with
         * this whole driver as soon as Exynos gets migrated to use
-        * cpufreq-cpu0 driver.
+        * cpufreq-dt driver.
         */
        np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-clock");
        if (!np) {
index bf8902a0866dd4d767cdab4ba45d84ca397033fe..ec399ad2f059379891a4d384e5b24dc3b9c3ab59 100644 (file)
@@ -6,7 +6,7 @@
  * published by the Free Software Foundation.
  *
  * This driver provides the clk notifier callbacks that are used when
- * the cpufreq-cpu0 driver changes to frequency to alert the highbank
+ * the cpufreq-dt driver changes to frequency to alert the highbank
  * EnergyCore Management Engine (ECME) about the need to change
  * voltage. The ECME interfaces with the actual voltage regulators.
  */
@@ -60,7 +60,7 @@ static struct notifier_block hb_cpufreq_clk_nb = {
 
 static int hb_cpufreq_driver_init(void)
 {
-       struct platform_device_info devinfo = { .name = "cpufreq-cpu0", };
+       struct platform_device_info devinfo = { .name = "cpufreq-dt", };
        struct device *cpu_dev;
        struct clk *cpu_clk;
        struct device_node *np;
@@ -95,7 +95,7 @@ static int hb_cpufreq_driver_init(void)
                goto out_put_node;
        }
 
-       /* Instantiate cpufreq-cpu0 */
+       /* Instantiate cpufreq-dt */
        platform_device_register_full(&devinfo);
 
 out_put_node:
index 7615180d7ee3c497e915d72f18af11e9b3f09c5c..1f49d97a70ea164737570c3480a2dcac30265788 100644 (file)
@@ -611,7 +611,7 @@ static int __init pmac_cpufreq_setup(void)
        struct device_node      *cpunode;
        const u32               *value;
 
-       if (strstr(cmd_line, "nocpufreq"))
+       if (strstr(boot_command_line, "nocpufreq"))
                return 0;
 
        /* Get first CPU node */
index 379c0837f5a97d510f12077271f802bb832ace8b..2dfd4fdb5a52bd8d82fe2603acf550732803798b 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/cpufreq.h>
 #include <linux/smp.h>
 #include <linux/of.h>
+#include <linux/reboot.h>
 
 #include <asm/cputhreads.h>
 #include <asm/firmware.h>
@@ -35,6 +36,7 @@
 #define POWERNV_MAX_PSTATES    256
 
 static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1];
+static bool rebooting;
 
 /*
  * Note: The set of pstates consists of contiguous integers, the
@@ -283,6 +285,15 @@ static void set_pstate(void *freq_data)
        set_pmspr(SPRN_PMCR, val);
 }
 
+/*
+ * get_nominal_index: Returns the index corresponding to the nominal
+ * pstate in the cpufreq table
+ */
+static inline unsigned int get_nominal_index(void)
+{
+       return powernv_pstate_info.max - powernv_pstate_info.nominal;
+}
+
 /*
  * powernv_cpufreq_target_index: Sets the frequency corresponding to
  * the cpufreq table entry indexed by new_index on the cpus in the
@@ -293,6 +304,9 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy,
 {
        struct powernv_smp_call_data freq_data;
 
+       if (unlikely(rebooting) && new_index != get_nominal_index())
+               return 0;
+
        freq_data.pstate_id = powernv_freqs[new_index].driver_data;
 
        /*
@@ -317,6 +331,33 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy)
        return cpufreq_table_validate_and_show(policy, powernv_freqs);
 }
 
+static int powernv_cpufreq_reboot_notifier(struct notifier_block *nb,
+                               unsigned long action, void *unused)
+{
+       int cpu;
+       struct cpufreq_policy cpu_policy;
+
+       rebooting = true;
+       for_each_online_cpu(cpu) {
+               cpufreq_get_policy(&cpu_policy, cpu);
+               powernv_cpufreq_target_index(&cpu_policy, get_nominal_index());
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block powernv_cpufreq_reboot_nb = {
+       .notifier_call = powernv_cpufreq_reboot_notifier,
+};
+
+static void powernv_cpufreq_stop_cpu(struct cpufreq_policy *policy)
+{
+       struct powernv_smp_call_data freq_data;
+
+       freq_data.pstate_id = powernv_pstate_info.min;
+       smp_call_function_single(policy->cpu, set_pstate, &freq_data, 1);
+}
+
 static struct cpufreq_driver powernv_cpufreq_driver = {
        .name           = "powernv-cpufreq",
        .flags          = CPUFREQ_CONST_LOOPS,
@@ -324,6 +365,7 @@ static struct cpufreq_driver powernv_cpufreq_driver = {
        .verify         = cpufreq_generic_frequency_table_verify,
        .target_index   = powernv_cpufreq_target_index,
        .get            = powernv_cpufreq_get,
+       .stop_cpu       = powernv_cpufreq_stop_cpu,
        .attr           = powernv_cpu_freq_attr,
 };
 
@@ -342,12 +384,14 @@ static int __init powernv_cpufreq_init(void)
                return rc;
        }
 
+       register_reboot_notifier(&powernv_cpufreq_reboot_nb);
        return cpufreq_register_driver(&powernv_cpufreq_driver);
 }
 module_init(powernv_cpufreq_init);
 
 static void __exit powernv_cpufreq_exit(void)
 {
+       unregister_reboot_notifier(&powernv_cpufreq_reboot_nb);
        cpufreq_unregister_driver(&powernv_cpufreq_driver);
 }
 module_exit(powernv_cpufreq_exit);
index 3607070797af307f959d0f413a2497acd262ba95..bee5df7794d33d1078116c8ac2f3618075230c8c 100644 (file)
@@ -199,7 +199,6 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
        }
 
        data->table = table;
-       per_cpu(cpu_data, cpu) = data;
 
        /* update ->cpus if we have cluster, no harm if not */
        cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu));
index 3f9791f07b8ea05f745a2bcbce6b738379443087..567caa6313fffa447f19992d7af5df940c4d0068 100644 (file)
@@ -597,7 +597,7 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev)
         * and dependencies on platform headers. It is necessary to enable
         * S5PV210 multi-platform support and will be removed together with
         * this whole driver as soon as S5PV210 gets migrated to use
-        * cpufreq-cpu0 driver.
+        * cpufreq-dt driver.
         */
        np = of_find_compatible_node(NULL, NULL, "samsung,s5pv210-clock");
        if (!np) {
index 32748c36c477099cf00344b3410dd8c2331b9404..c5029c1209b4c0bbb6fe52b577345138b788de13 100644 (file)
@@ -25,11 +25,19 @@ config CPU_IDLE_GOV_MENU
        bool "Menu governor (for tickless system)"
        default y
 
+config DT_IDLE_STATES
+       bool
+
 menu "ARM CPU Idle Drivers"
 depends on ARM
 source "drivers/cpuidle/Kconfig.arm"
 endmenu
 
+menu "ARM64 CPU Idle Drivers"
+depends on ARM64
+source "drivers/cpuidle/Kconfig.arm64"
+endmenu
+
 menu "MIPS CPU Idle Drivers"
 depends on MIPS
 source "drivers/cpuidle/Kconfig.mips"
index 58bcd0d166ec3a4f42d00fb24d086512bae11710..8c16ab20fb15594cc409860ebb83a423d2dde2d6 100644 (file)
@@ -7,6 +7,7 @@ config ARM_BIG_LITTLE_CPUIDLE
        depends on MCPM
        select ARM_CPU_SUSPEND
        select CPU_IDLE_MULTIPLE_DRIVERS
+       select DT_IDLE_STATES
        help
          Select this option to enable CPU idle driver for big.LITTLE based
          ARM systems. Driver manages CPUs coordination through MCPM and
diff --git a/drivers/cpuidle/Kconfig.arm64 b/drivers/cpuidle/Kconfig.arm64
new file mode 100644 (file)
index 0000000..d0a08ed
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# ARM64 CPU Idle drivers
+#
+
+config ARM64_CPUIDLE
+       bool "Generic ARM64 CPU idle Driver"
+       select ARM64_CPU_SUSPEND
+       select DT_IDLE_STATES
+       help
+         Select this to enable generic cpuidle driver for ARM64.
+         It provides a generic idle driver whose idle states are configured
+         at run-time through DT nodes. The CPUidle suspend backend is
+         initialized by calling the CPU operations init idle hook
+         provided by architecture code.
index 11edb31c55e9862aa2e21e0df073f2d7dd49b721..4d177b916f75224325e0c37991f82d622e8e6392 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
 obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
+obj-$(CONFIG_DT_IDLE_STATES)             += dt_idle_states.o
 
 ##################################################################################
 # ARM SoC drivers
@@ -21,6 +22,10 @@ obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
 # MIPS drivers
 obj-$(CONFIG_MIPS_CPS_CPUIDLE)         += cpuidle-cps.o
 
+###############################################################################
+# ARM64 drivers
+obj-$(CONFIG_ARM64_CPUIDLE)            += cpuidle-arm64.o
+
 ###############################################################################
 # POWERPC drivers
 obj-$(CONFIG_PSERIES_CPUIDLE)          += cpuidle-pseries.o
diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c
new file mode 100644 (file)
index 0000000..50997ea
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * ARM64 generic CPU idle driver.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+ *
+ * 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 pr_fmt(fmt) "CPUidle arm64: " fmt
+
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_pm.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <asm/cpuidle.h>
+#include <asm/suspend.h>
+
+#include "dt_idle_states.h"
+
+/*
+ * arm64_enter_idle_state - Programs CPU to enter the specified state
+ *
+ * dev: cpuidle device
+ * drv: cpuidle driver
+ * idx: state index
+ *
+ * Called from the CPUidle framework to program the device to the
+ * specified target state selected by the governor.
+ */
+static int arm64_enter_idle_state(struct cpuidle_device *dev,
+                                 struct cpuidle_driver *drv, int idx)
+{
+       int ret;
+
+       if (!idx) {
+               cpu_do_idle();
+               return idx;
+       }
+
+       ret = cpu_pm_enter();
+       if (!ret) {
+               /*
+                * Pass idle state index to cpu_suspend which in turn will
+                * call the CPU ops suspend protocol with idle index as a
+                * parameter.
+                */
+               ret = cpu_suspend(idx);
+
+               cpu_pm_exit();
+       }
+
+       return ret ? -1 : idx;
+}
+
+static struct cpuidle_driver arm64_idle_driver = {
+       .name = "arm64_idle",
+       .owner = THIS_MODULE,
+       /*
+        * State at index 0 is standby wfi and considered standard
+        * on all ARM platforms. If in some platforms simple wfi
+        * can't be used as "state 0", DT bindings must be implemented
+        * to work around this issue and allow installing a special
+        * handler for idle state index 0.
+        */
+       .states[0] = {
+               .enter                  = arm64_enter_idle_state,
+               .exit_latency           = 1,
+               .target_residency       = 1,
+               .power_usage            = UINT_MAX,
+               .flags                  = CPUIDLE_FLAG_TIME_VALID,
+               .name                   = "WFI",
+               .desc                   = "ARM64 WFI",
+       }
+};
+
+static const struct of_device_id arm64_idle_state_match[] __initconst = {
+       { .compatible = "arm,idle-state",
+         .data = arm64_enter_idle_state },
+       { },
+};
+
+/*
+ * arm64_idle_init
+ *
+ * Registers the arm64 specific cpuidle driver with the cpuidle
+ * framework. It relies on core code to parse the idle states
+ * and initialize them using driver data structures accordingly.
+ */
+static int __init arm64_idle_init(void)
+{
+       int cpu, ret;
+       struct cpuidle_driver *drv = &arm64_idle_driver;
+
+       /*
+        * Initialize idle states data, starting at index 1.
+        * This driver is DT only, if no DT idle states are detected (ret == 0)
+        * let the driver initialization fail accordingly since there is no
+        * reason to initialize the idle driver if only wfi is supported.
+        */
+       ret = dt_init_idle_driver(drv, arm64_idle_state_match, 1);
+       if (ret <= 0) {
+               if (ret)
+                       pr_err("failed to initialize idle states\n");
+               return ret ? : -ENODEV;
+       }
+
+       /*
+        * Call arch CPU operations in order to initialize
+        * idle states suspend back-end specific data
+        */
+       for_each_possible_cpu(cpu) {
+               ret = cpu_init_idle(cpu);
+               if (ret) {
+                       pr_err("CPU %d failed to init idle CPU ops\n", cpu);
+                       return ret;
+               }
+       }
+
+       ret = cpuidle_register(drv, NULL);
+       if (ret) {
+               pr_err("failed to register cpuidle driver\n");
+               return ret;
+       }
+
+       return 0;
+}
+device_initcall(arm64_idle_init);
index ef94c3b81f18048c6feee67368d2fd24da416424..fbc00a1d3c486a5e9d98f31ebecaf5149b2f3d5c 100644 (file)
@@ -24,6 +24,8 @@
 #include <asm/smp_plat.h>
 #include <asm/suspend.h>
 
+#include "dt_idle_states.h"
+
 static int bl_enter_powerdown(struct cpuidle_device *dev,
                              struct cpuidle_driver *drv, int idx);
 
@@ -73,6 +75,12 @@ static struct cpuidle_driver bl_idle_little_driver = {
        .state_count = 2,
 };
 
+static const struct of_device_id bl_idle_state_match[] __initconst = {
+       { .compatible = "arm,idle-state",
+         .data = bl_enter_powerdown },
+       { },
+};
+
 static struct cpuidle_driver bl_idle_big_driver = {
        .name = "big_idle",
        .owner = THIS_MODULE,
@@ -159,6 +167,7 @@ static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int part_id)
 static const struct of_device_id compatible_machine_match[] = {
        { .compatible = "arm,vexpress,v2p-ca15_a7" },
        { .compatible = "samsung,exynos5420" },
+       { .compatible = "samsung,exynos5800" },
        {},
 };
 
@@ -190,6 +199,17 @@ static int __init bl_idle_init(void)
        if (ret)
                goto out_uninit_little;
 
+       /* Start at index 1, index 0 standard WFI */
+       ret = dt_init_idle_driver(&bl_idle_big_driver, bl_idle_state_match, 1);
+       if (ret < 0)
+               goto out_uninit_big;
+
+       /* Start at index 1, index 0 standard WFI */
+       ret = dt_init_idle_driver(&bl_idle_little_driver,
+                                 bl_idle_state_match, 1);
+       if (ret < 0)
+               goto out_uninit_big;
+
        ret = cpuidle_register(&bl_idle_little_driver, NULL);
        if (ret)
                goto out_uninit_big;
diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c
new file mode 100644 (file)
index 0000000..52f4d11
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * DT idle states parsing code.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+ *
+ * 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 pr_fmt(fmt) "DT idle-states: " fmt
+
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "dt_idle_states.h"
+
+static int init_state_node(struct cpuidle_state *idle_state,
+                          const struct of_device_id *matches,
+                          struct device_node *state_node)
+{
+       int err;
+       const struct of_device_id *match_id;
+
+       match_id = of_match_node(matches, state_node);
+       if (!match_id)
+               return -ENODEV;
+       /*
+        * CPUidle drivers are expected to initialize the const void *data
+        * pointer of the passed in struct of_device_id array to the idle
+        * state enter function.
+        */
+       idle_state->enter = match_id->data;
+
+       err = of_property_read_u32(state_node, "wakeup-latency-us",
+                                  &idle_state->exit_latency);
+       if (err) {
+               u32 entry_latency, exit_latency;
+
+               err = of_property_read_u32(state_node, "entry-latency-us",
+                                          &entry_latency);
+               if (err) {
+                       pr_debug(" * %s missing entry-latency-us property\n",
+                                state_node->full_name);
+                       return -EINVAL;
+               }
+
+               err = of_property_read_u32(state_node, "exit-latency-us",
+                                          &exit_latency);
+               if (err) {
+                       pr_debug(" * %s missing exit-latency-us property\n",
+                                state_node->full_name);
+                       return -EINVAL;
+               }
+               /*
+                * If wakeup-latency-us is missing, default to entry+exit
+                * latencies as defined in idle states bindings
+                */
+               idle_state->exit_latency = entry_latency + exit_latency;
+       }
+
+       err = of_property_read_u32(state_node, "min-residency-us",
+                                  &idle_state->target_residency);
+       if (err) {
+               pr_debug(" * %s missing min-residency-us property\n",
+                            state_node->full_name);
+               return -EINVAL;
+       }
+
+       idle_state->flags = CPUIDLE_FLAG_TIME_VALID;
+       if (of_property_read_bool(state_node, "local-timer-stop"))
+               idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP;
+       /*
+        * TODO:
+        *      replace with kstrdup and pointer assignment when name
+        *      and desc become string pointers
+        */
+       strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1);
+       strncpy(idle_state->desc, state_node->name, CPUIDLE_DESC_LEN - 1);
+       return 0;
+}
+
+/*
+ * Check that the idle state is uniform across all CPUs in the CPUidle driver
+ * cpumask
+ */
+static bool idle_state_valid(struct device_node *state_node, unsigned int idx,
+                            const cpumask_t *cpumask)
+{
+       int cpu;
+       struct device_node *cpu_node, *curr_state_node;
+       bool valid = true;
+
+       /*
+        * Compare idle state phandles for index idx on all CPUs in the
+        * CPUidle driver cpumask. Start from next logical cpu following
+        * cpumask_first(cpumask) since that's the CPU state_node was
+        * retrieved from. If a mismatch is found bail out straight
+        * away since we certainly hit a firmware misconfiguration.
+        */
+       for (cpu = cpumask_next(cpumask_first(cpumask), cpumask);
+            cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) {
+               cpu_node = of_cpu_device_node_get(cpu);
+               curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
+                                                  idx);
+               if (state_node != curr_state_node)
+                       valid = false;
+
+               of_node_put(curr_state_node);
+               of_node_put(cpu_node);
+               if (!valid)
+                       break;
+       }
+
+       return valid;
+}
+
+/**
+ * dt_init_idle_driver() - Parse the DT idle states and initialize the
+ *                        idle driver states array
+ * @drv:         Pointer to CPU idle driver to be initialized
+ * @matches:     Array of of_device_id match structures to search in for
+ *               compatible idle state nodes. The data pointer for each valid
+ *               struct of_device_id entry in the matches array must point to
+ *               a function with the following signature, that corresponds to
+ *               the CPUidle state enter function signature:
+ *
+ *               int (*)(struct cpuidle_device *dev,
+ *                       struct cpuidle_driver *drv,
+ *                       int index);
+ *
+ * @start_idx:    First idle state index to be initialized
+ *
+ * If DT idle states are detected and are valid the state count and states
+ * array entries in the cpuidle driver are initialized accordingly starting
+ * from index start_idx.
+ *
+ * Return: number of valid DT idle states parsed, <0 on failure
+ */
+int dt_init_idle_driver(struct cpuidle_driver *drv,
+                       const struct of_device_id *matches,
+                       unsigned int start_idx)
+{
+       struct cpuidle_state *idle_state;
+       struct device_node *state_node, *cpu_node;
+       int i, err = 0;
+       const cpumask_t *cpumask;
+       unsigned int state_idx = start_idx;
+
+       if (state_idx >= CPUIDLE_STATE_MAX)
+               return -EINVAL;
+       /*
+        * We get the idle states for the first logical cpu in the
+        * driver mask (or cpu_possible_mask if the driver cpumask is not set)
+        * and we check through idle_state_valid() if they are uniform
+        * across CPUs, otherwise we hit a firmware misconfiguration.
+        */
+       cpumask = drv->cpumask ? : cpu_possible_mask;
+       cpu_node = of_cpu_device_node_get(cpumask_first(cpumask));
+
+       for (i = 0; ; i++) {
+               state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+               if (!state_node)
+                       break;
+
+               if (!idle_state_valid(state_node, i, cpumask)) {
+                       pr_warn("%s idle state not valid, bailing out\n",
+                               state_node->full_name);
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (state_idx == CPUIDLE_STATE_MAX) {
+                       pr_warn("State index reached static CPU idle driver states array size\n");
+                       break;
+               }
+
+               idle_state = &drv->states[state_idx++];
+               err = init_state_node(idle_state, matches, state_node);
+               if (err) {
+                       pr_err("Parsing idle state node %s failed with err %d\n",
+                              state_node->full_name, err);
+                       err = -EINVAL;
+                       break;
+               }
+               of_node_put(state_node);
+       }
+
+       of_node_put(state_node);
+       of_node_put(cpu_node);
+       if (err)
+               return err;
+       /*
+        * Update the driver state count only if some valid DT idle states
+        * were detected
+        */
+       if (i)
+               drv->state_count = state_idx;
+
+       /*
+        * Return the number of present and valid DT idle states, which can
+        * also be 0 on platforms with missing DT idle states or legacy DT
+        * configuration predating the DT idle states bindings.
+        */
+       return i;
+}
+EXPORT_SYMBOL_GPL(dt_init_idle_driver);
diff --git a/drivers/cpuidle/dt_idle_states.h b/drivers/cpuidle/dt_idle_states.h
new file mode 100644 (file)
index 0000000..4818134
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __DT_IDLE_STATES
+#define __DT_IDLE_STATES
+
+int dt_init_idle_driver(struct cpuidle_driver *drv,
+                       const struct of_device_id *matches,
+                       unsigned int start_idx);
+#endif
index ca89412f512243a64a05d765708f0e02a848a6be..fb9f511cca23724b4da463714bd49a8e53fba588 100644 (file)
@@ -28,7 +28,7 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
        struct cpuidle_governor *gov;
 
        list_for_each_entry(gov, &cpuidle_governors, governor_list)
-               if (!strnicmp(str, gov->name, CPUIDLE_NAME_LEN))
+               if (!strncasecmp(str, gov->name, CPUIDLE_NAME_LEN))
                        return gov;
 
        return NULL;
index 3dced0a9eae3038feae328815aee028e8ee86b36..faf4e70c42e0467f072cde73d6cc972ff27509ae 100644 (file)
@@ -78,9 +78,8 @@ config ARM_EXYNOS4_BUS_DEVFREQ
          This does not yet operate with optimal voltages.
 
 config ARM_EXYNOS5_BUS_DEVFREQ
-       bool "ARM Exynos5250 Bus DEVFREQ Driver"
+       tristate "ARM Exynos5250 Bus DEVFREQ Driver"
        depends on SOC_EXYNOS5250
-       select ARCH_HAS_OPP
        select DEVFREQ_GOV_SIMPLE_ONDEMAND
        select PM_OPP
        help
index 9f90369dd6bdd208832b9da0c7bb4ccadfe0dadf..30b538d8cc90a5cb5e6baa88172c5711aa804b93 100644 (file)
@@ -1119,6 +1119,7 @@ struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
 
        return opp;
 }
+EXPORT_SYMBOL(devfreq_recommended_opp);
 
 /**
  * devfreq_register_opp_notifier() - Helper function to get devfreq notified
@@ -1142,6 +1143,7 @@ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)
 
        return ret;
 }
+EXPORT_SYMBOL(devfreq_register_opp_notifier);
 
 /**
  * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq
@@ -1168,6 +1170,7 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
 
        return ret;
 }
+EXPORT_SYMBOL(devfreq_unregister_opp_notifier);
 
 static void devm_devfreq_opp_release(struct device *dev, void *res)
 {
index 75fcc5140ffb47267ea327f9f2c2796d9ea451a2..97b75e513d29123c9901e2d06195f6ae40dc1596 100644 (file)
@@ -73,6 +73,7 @@ void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data)
                exynos_ppmu_start(ppmu_base);
        }
 }
+EXPORT_SYMBOL(busfreq_mon_reset);
 
 void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data)
 {
@@ -97,6 +98,7 @@ void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data)
 
        busfreq_mon_reset(ppmu_data);
 }
+EXPORT_SYMBOL(exynos_read_ppmu);
 
 int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data)
 {
@@ -114,3 +116,4 @@ int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data)
 
        return busy;
 }
+EXPORT_SYMBOL(exynos_get_busier_ppmu);
index 0034c48444280e7c40d21fd0f94d8faf4a9b7df2..e9bb1af67c8dfbae01408c16211e93c71207ee02 100644 (file)
@@ -52,36 +52,6 @@ static int probed;
 #define GET_BITFIELD(v, lo, hi)        \
        (((v) & GENMASK_ULL(hi, lo)) >> (lo))
 
-/*
- * sbridge Memory Controller Registers
- */
-
-/*
- * FIXME: For now, let's order by device function, as it makes
- * easier for driver's development process. This table should be
- * moved to pci_id.h when submitted upstream
- */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0       0x3cf4  /* 12.6 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1       0x3cf6  /* 12.7 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_BR         0x3cf5  /* 13.6 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0    0x3ca0  /* 14.0 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA     0x3ca8  /* 15.0 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS    0x3c71  /* 15.1 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0   0x3caa  /* 15.2 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1   0x3cab  /* 15.3 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2   0x3cac  /* 15.4 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3   0x3cad  /* 15.5 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO  0x3cb8  /* 17.0 */
-
-       /*
-        * Currently, unused, but will be needed in the future
-        * implementations, as they hold the error counters
-        */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR0   0x3c72  /* 16.2 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR1   0x3c73  /* 16.3 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR2   0x3c76  /* 16.6 */
-#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR3   0x3c77  /* 16.7 */
-
 /* Devices 12 Function 6, Offsets 0x80 to 0xcc */
 static const u32 sbridge_dram_rule[] = {
        0x80, 0x88, 0x90, 0x98, 0xa0,
@@ -283,8 +253,9 @@ static const u32 correrrthrsld[] = {
  * sbridge structs
  */
 
-#define NUM_CHANNELS   4
-#define MAX_DIMMS      3               /* Max DIMMS per channel */
+#define NUM_CHANNELS           4
+#define MAX_DIMMS              3       /* Max DIMMS per channel */
+#define CHANNEL_UNSPECIFIED    0xf     /* Intel IA32 SDM 15-14 */
 
 enum type {
        SANDY_BRIDGE,
@@ -529,7 +500,7 @@ static const struct pci_id_table pci_dev_descr_haswell_table[] = {
  *     pci_device_id   table for which devices we are looking for
  */
 static const struct pci_device_id sbridge_pci_tbl[] = {
-       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0)},
        {0,}                    /* 0 terminated list. */
@@ -1991,6 +1962,9 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
 
        /* FIXME: need support for channel mask */
 
+       if (channel == CHANNEL_UNSPECIFIED)
+               channel = -1;
+
        /* Call the helper to output message */
        edac_mc_handle_error(tp_event, mci, core_err_cnt,
                             m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
index 79f18e6d9c4f346e5f04430a67cf7b1cb7dbd97c..cc016c615c19ab50d0818330423c75313c17c8b5 100644 (file)
@@ -184,6 +184,9 @@ static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry)
        static int map_entries_nr;
        static struct kset *mmap_kset;
 
+       if (entry->kobj.state_in_sysfs)
+               return -EEXIST;
+
        if (!mmap_kset) {
                mmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj);
                if (!mmap_kset)
index 9de1515e58081e6b5bef565433e3dd749e725023..0959ca9b6b27b7ecea6c616c2d7e416233961d4b 100644 (file)
@@ -136,7 +136,6 @@ config GPIO_DWAPB
        tristate "Synopsys DesignWare APB GPIO driver"
        select GPIO_GENERIC
        select GENERIC_IRQ_CHIP
-       depends on OF_GPIO
        help
          Say Y or M here to build support for the Synopsys DesignWare APB
          GPIO block.
@@ -334,6 +333,15 @@ config GPIO_TZ1090_PDC
        help
          Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs.
 
+config GPIO_XGENE
+       bool "APM X-Gene GPIO controller support"
+       depends on ARM64 && OF_GPIO
+       help
+         This driver is to support the GPIO block within the APM X-Gene SoC
+         platform's generic flash controller. The GPIO pins are muxed with
+         the generic flash controller's address and data pins. Say yes
+         here to enable the GFC GPIO functionality.
+
 config GPIO_XILINX
        bool "Xilinx GPIO support"
        depends on PPC_OF || MICROBLAZE || ARCH_ZYNQ
@@ -681,6 +689,7 @@ config GPIO_ADP5588_IRQ
 config GPIO_ADNP
        tristate "Avionic Design N-bit GPIO expander"
        depends on I2C && OF_GPIO
+       select GPIOLIB_IRQCHIP
        help
          This option enables support for N GPIOs found on Avionic Design
          I2C GPIO expanders. The register space will be extended by powers
@@ -796,7 +805,6 @@ config GPIO_MAX7301
 
 config GPIO_MCP23S08
        tristate "Microchip MCP23xxx I/O expander"
-       depends on OF_GPIO
        depends on (SPI_MASTER && !I2C) || I2C
        help
          SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
@@ -880,7 +888,7 @@ config GPIO_MSIC
 
 config GPIO_BCM_KONA
        bool "Broadcom Kona GPIO"
-       depends on OF_GPIO
+       depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
        help
          Turn on GPIO support for Broadcom "Kona" chips.
 
index 5d024e396622ee00ebf956fbe7963edd69398bbd..e5d346cf3b6e8a1d7cc564fe444d1941f02ffec6 100644 (file)
@@ -101,6 +101,7 @@ obj-$(CONFIG_GPIO_VX855)    += gpio-vx855.o
 obj-$(CONFIG_GPIO_WM831X)      += gpio-wm831x.o
 obj-$(CONFIG_GPIO_WM8350)      += gpio-wm8350.o
 obj-$(CONFIG_GPIO_WM8994)      += gpio-wm8994.o
+obj-$(CONFIG_GPIO_XGENE)       += gpio-xgene.o
 obj-$(CONFIG_GPIO_XILINX)      += gpio-xilinx.o
 obj-$(CONFIG_GPIO_XTENSA)      += gpio-xtensa.o
 obj-$(CONFIG_GPIO_ZEVIO)       += gpio-zevio.o
index 416b2200d4f1b8f7aa385153c9ba50c6f870a54d..d3d0a90fe5420853afeb5442e155005b415949b4 100644 (file)
@@ -6,10 +6,9 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
-#include <linux/irqdomain.h>
 #include <linux/module.h>
 #include <linux/of_irq.h>
 #include <linux/seq_file.h>
@@ -27,8 +26,6 @@ struct adnp {
        unsigned int reg_shift;
 
        struct mutex i2c_lock;
-
-       struct irq_domain *domain;
        struct mutex irq_lock;
 
        u8 *irq_enable;
@@ -253,6 +250,7 @@ static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
 static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios)
 {
        struct gpio_chip *chip = &adnp->gpio;
+       int err;
 
        adnp->reg_shift = get_count_order(num_gpios) - 3;
 
@@ -272,6 +270,10 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios)
        chip->of_node = chip->dev->of_node;
        chip->owner = THIS_MODULE;
 
+       err = gpiochip_add(chip);
+       if (err)
+               return err;
+
        return 0;
 }
 
@@ -326,7 +328,8 @@ static irqreturn_t adnp_irq(int irq, void *data)
 
                for_each_set_bit(bit, &pending, 8) {
                        unsigned int child_irq;
-                       child_irq = irq_find_mapping(adnp->domain, base + bit);
+                       child_irq = irq_find_mapping(adnp->gpio.irqdomain,
+                                                    base + bit);
                        handle_nested_irq(child_irq);
                }
        }
@@ -334,35 +337,32 @@ static irqreturn_t adnp_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static int adnp_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
-       struct adnp *adnp = to_adnp(chip);
-       return irq_create_mapping(adnp->domain, offset);
-}
-
-static void adnp_irq_mask(struct irq_data *data)
+static void adnp_irq_mask(struct irq_data *d)
 {
-       struct adnp *adnp = irq_data_get_irq_chip_data(data);
-       unsigned int reg = data->hwirq >> adnp->reg_shift;
-       unsigned int pos = data->hwirq & 7;
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct adnp *adnp = to_adnp(gc);
+       unsigned int reg = d->hwirq >> adnp->reg_shift;
+       unsigned int pos = d->hwirq & 7;
 
        adnp->irq_enable[reg] &= ~BIT(pos);
 }
 
-static void adnp_irq_unmask(struct irq_data *data)
+static void adnp_irq_unmask(struct irq_data *d)
 {
-       struct adnp *adnp = irq_data_get_irq_chip_data(data);
-       unsigned int reg = data->hwirq >> adnp->reg_shift;
-       unsigned int pos = data->hwirq & 7;
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct adnp *adnp = to_adnp(gc);
+       unsigned int reg = d->hwirq >> adnp->reg_shift;
+       unsigned int pos = d->hwirq & 7;
 
        adnp->irq_enable[reg] |= BIT(pos);
 }
 
-static int adnp_irq_set_type(struct irq_data *data, unsigned int type)
+static int adnp_irq_set_type(struct irq_data *d, unsigned int type)
 {
-       struct adnp *adnp = irq_data_get_irq_chip_data(data);
-       unsigned int reg = data->hwirq >> adnp->reg_shift;
-       unsigned int pos = data->hwirq & 7;
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct adnp *adnp = to_adnp(gc);
+       unsigned int reg = d->hwirq >> adnp->reg_shift;
+       unsigned int pos = d->hwirq & 7;
 
        if (type & IRQ_TYPE_EDGE_RISING)
                adnp->irq_rise[reg] |= BIT(pos);
@@ -387,16 +387,18 @@ static int adnp_irq_set_type(struct irq_data *data, unsigned int type)
        return 0;
 }
 
-static void adnp_irq_bus_lock(struct irq_data *data)
+static void adnp_irq_bus_lock(struct irq_data *d)
 {
-       struct adnp *adnp = irq_data_get_irq_chip_data(data);
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct adnp *adnp = to_adnp(gc);
 
        mutex_lock(&adnp->irq_lock);
 }
 
-static void adnp_irq_bus_unlock(struct irq_data *data)
+static void adnp_irq_bus_unlock(struct irq_data *d)
 {
-       struct adnp *adnp = irq_data_get_irq_chip_data(data);
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct adnp *adnp = to_adnp(gc);
        unsigned int num_regs = 1 << adnp->reg_shift, i;
 
        mutex_lock(&adnp->i2c_lock);
@@ -408,26 +410,6 @@ static void adnp_irq_bus_unlock(struct irq_data *data)
        mutex_unlock(&adnp->irq_lock);
 }
 
-static int adnp_irq_reqres(struct irq_data *data)
-{
-       struct adnp *adnp = irq_data_get_irq_chip_data(data);
-
-       if (gpio_lock_as_irq(&adnp->gpio, data->hwirq)) {
-               dev_err(adnp->gpio.dev,
-                       "unable to lock HW IRQ %lu for IRQ\n",
-                       data->hwirq);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static void adnp_irq_relres(struct irq_data *data)
-{
-       struct adnp *adnp = irq_data_get_irq_chip_data(data);
-
-       gpio_unlock_as_irq(&adnp->gpio, data->hwirq);
-}
-
 static struct irq_chip adnp_irq_chip = {
        .name = "gpio-adnp",
        .irq_mask = adnp_irq_mask,
@@ -435,29 +417,6 @@ static struct irq_chip adnp_irq_chip = {
        .irq_set_type = adnp_irq_set_type,
        .irq_bus_lock = adnp_irq_bus_lock,
        .irq_bus_sync_unlock = adnp_irq_bus_unlock,
-       .irq_request_resources = adnp_irq_reqres,
-       .irq_release_resources = adnp_irq_relres,
-};
-
-static int adnp_irq_map(struct irq_domain *domain, unsigned int irq,
-                       irq_hw_number_t hwirq)
-{
-       irq_set_chip_data(irq, domain->host_data);
-       irq_set_chip(irq, &adnp_irq_chip);
-       irq_set_nested_thread(irq, true);
-
-#ifdef CONFIG_ARM
-       set_irq_flags(irq, IRQF_VALID);
-#else
-       irq_set_noprobe(irq);
-#endif
-
-       return 0;
-}
-
-static const struct irq_domain_ops adnp_irq_domain_ops = {
-       .map = adnp_irq_map,
-       .xlate = irq_domain_xlate_twocell,
 };
 
 static int adnp_irq_setup(struct adnp *adnp)
@@ -503,35 +462,28 @@ static int adnp_irq_setup(struct adnp *adnp)
                adnp->irq_enable[i] = 0x00;
        }
 
-       adnp->domain = irq_domain_add_linear(chip->of_node, chip->ngpio,
-                                            &adnp_irq_domain_ops, adnp);
-
-       err = request_threaded_irq(adnp->client->irq, NULL, adnp_irq,
-                                  IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-                                  dev_name(chip->dev), adnp);
+       err = devm_request_threaded_irq(chip->dev, adnp->client->irq,
+                                       NULL, adnp_irq,
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       dev_name(chip->dev), adnp);
        if (err != 0) {
                dev_err(chip->dev, "can't request IRQ#%d: %d\n",
                        adnp->client->irq, err);
                return err;
        }
 
-       chip->to_irq = adnp_gpio_to_irq;
-       return 0;
-}
-
-static void adnp_irq_teardown(struct adnp *adnp)
-{
-       unsigned int irq, i;
-
-       free_irq(adnp->client->irq, adnp);
-
-       for (i = 0; i < adnp->gpio.ngpio; i++) {
-               irq = irq_find_mapping(adnp->domain, i);
-               if (irq > 0)
-                       irq_dispose_mapping(irq);
+       err = gpiochip_irqchip_add(chip,
+                                  &adnp_irq_chip,
+                                  0,
+                                  handle_simple_irq,
+                                  IRQ_TYPE_NONE);
+       if (err) {
+               dev_err(chip->dev,
+                       "could not connect irqchip to gpiochip\n");
+               return err;
        }
 
-       irq_domain_remove(adnp->domain);
+       return 0;
 }
 
 static int adnp_i2c_probe(struct i2c_client *client,
@@ -558,38 +510,25 @@ static int adnp_i2c_probe(struct i2c_client *client,
        adnp->client = client;
 
        err = adnp_gpio_setup(adnp, num_gpios);
-       if (err < 0)
+       if (err)
                return err;
 
        if (of_find_property(np, "interrupt-controller", NULL)) {
                err = adnp_irq_setup(adnp);
-               if (err < 0)
-                       goto teardown;
+               if (err)
+                       return err;
        }
 
-       err = gpiochip_add(&adnp->gpio);
-       if (err < 0)
-               goto teardown;
-
        i2c_set_clientdata(client, adnp);
-       return 0;
 
-teardown:
-       if (of_find_property(np, "interrupt-controller", NULL))
-               adnp_irq_teardown(adnp);
-
-       return err;
+       return 0;
 }
 
 static int adnp_i2c_remove(struct i2c_client *client)
 {
        struct adnp *adnp = i2c_get_clientdata(client);
-       struct device_node *np = client->dev.of_node;
 
        gpiochip_remove(&adnp->gpio);
-       if (of_find_property(np, "interrupt-controller", NULL))
-               adnp_irq_teardown(adnp);
-
        return 0;
 }
 
index 3f6b33ce9bd4094797f3a55076708228a4ce8281..de0801e9767a098484542f22fea1c9e02cdb0fa5 100644 (file)
@@ -496,7 +496,7 @@ static struct irq_chip bcm_gpio_irq_chip = {
        .irq_release_resources = bcm_kona_gpio_irq_relres,
 };
 
-static struct __initconst of_device_id bcm_kona_gpio_of_match[] = {
+static struct of_device_id const bcm_kona_gpio_of_match[] = {
        { .compatible = "brcm,kona-gpio" },
        {}
 };
index 934462f5bd2224ef872fa4c155fe5998ff47e273..bbfe7f508502b43ea11c3897ba80ae3f3a1f4a5c 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/mfd/intel_soc_pmic.h>
 
 #define CRYSTALCOVE_GPIO_NUM   16
+#define CRYSTALCOVE_VGPIO_NUM  94
 
 #define UPDATE_IRQ_TYPE                BIT(0)
 #define UPDATE_IRQ_MASK                BIT(1)
@@ -130,6 +131,9 @@ static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio)
 {
        struct crystalcove_gpio *cg = to_cg(chip);
 
+       if (gpio > CRYSTALCOVE_VGPIO_NUM)
+               return 0;
+
        return regmap_write(cg->regmap, to_reg(gpio, CTRL_OUT),
                            CTLO_INPUT_SET);
 }
@@ -139,6 +143,9 @@ static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio,
 {
        struct crystalcove_gpio *cg = to_cg(chip);
 
+       if (gpio > CRYSTALCOVE_VGPIO_NUM)
+               return 0;
+
        return regmap_write(cg->regmap, to_reg(gpio, CTRL_OUT),
                            CTLO_OUTPUT_SET | value);
 }
@@ -149,6 +156,9 @@ static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio)
        int ret;
        unsigned int val;
 
+       if (gpio > CRYSTALCOVE_VGPIO_NUM)
+               return 0;
+
        ret = regmap_read(cg->regmap, to_reg(gpio, CTRL_IN), &val);
        if (ret)
                return ret;
@@ -161,6 +171,9 @@ static void crystalcove_gpio_set(struct gpio_chip *chip,
 {
        struct crystalcove_gpio *cg = to_cg(chip);
 
+       if (gpio > CRYSTALCOVE_VGPIO_NUM)
+               return;
+
        if (value)
                regmap_update_bits(cg->regmap, to_reg(gpio, CTRL_OUT), 1, 1);
        else
@@ -256,7 +269,7 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
 
        pending = p0 | p1 << 8;
 
-       for (gpio = 0; gpio < cg->chip.ngpio; gpio++) {
+       for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) {
                if (pending & BIT(gpio)) {
                        virq = irq_find_mapping(cg->chip.irqdomain, gpio);
                        generic_handle_irq(virq);
@@ -273,7 +286,7 @@ static void crystalcove_gpio_dbg_show(struct seq_file *s,
        int gpio, offset;
        unsigned int ctlo, ctli, mirqs0, mirqsx, irq;
 
-       for (gpio = 0; gpio < cg->chip.ngpio; gpio++) {
+       for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) {
                regmap_read(cg->regmap, to_reg(gpio, CTRL_OUT), &ctlo);
                regmap_read(cg->regmap, to_reg(gpio, CTRL_IN), &ctli);
                regmap_read(cg->regmap, gpio < 8 ? MGPIO0IRQS0 : MGPIO1IRQS0,
@@ -320,7 +333,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
        cg->chip.get = crystalcove_gpio_get;
        cg->chip.set = crystalcove_gpio_set;
        cg->chip.base = -1;
-       cg->chip.ngpio = CRYSTALCOVE_GPIO_NUM;
+       cg->chip.ngpio = CRYSTALCOVE_VGPIO_NUM;
        cg->chip.can_sleep = true;
        cg->chip.dev = dev;
        cg->chip.dbg_show = crystalcove_gpio_dbg_show;
@@ -346,7 +359,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
        return 0;
 
 out_remove_gpio:
-       WARN_ON(gpiochip_remove(&cg->chip));
+       gpiochip_remove(&cg->chip);
        return retval;
 }
 
@@ -354,14 +367,11 @@ static int crystalcove_gpio_remove(struct platform_device *pdev)
 {
        struct crystalcove_gpio *cg = platform_get_drvdata(pdev);
        int irq = platform_get_irq(pdev, 0);
-       int err;
-
-       err = gpiochip_remove(&cg->chip);
 
+       gpiochip_remove(&cg->chip);
        if (irq >= 0)
                free_irq(irq, cg);
-
-       return err;
+       return 0;
 }
 
 static struct platform_driver crystalcove_gpio_driver = {
index 92ec58fa92369c8a7717719219a9b7bdfa600826..668127fe90ef7dc63e5b4d9b7c8023af97e76069 100644 (file)
@@ -201,7 +201,8 @@ EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event);
 
 static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
 {
-       struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+       struct cs5535_gpio_chip *chip =
+               container_of(c, struct cs5535_gpio_chip, chip);
        unsigned long flags;
 
        spin_lock_irqsave(&chip->lock, flags);
@@ -241,7 +242,8 @@ static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
 
 static int chip_direction_input(struct gpio_chip *c, unsigned offset)
 {
-       struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+       struct cs5535_gpio_chip *chip =
+               container_of(c, struct cs5535_gpio_chip, chip);
        unsigned long flags;
 
        spin_lock_irqsave(&chip->lock, flags);
@@ -254,7 +256,8 @@ static int chip_direction_input(struct gpio_chip *c, unsigned offset)
 
 static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
 {
-       struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+       struct cs5535_gpio_chip *chip =
+               container_of(c, struct cs5535_gpio_chip, chip);
        unsigned long flags;
 
        spin_lock_irqsave(&chip->lock, flags);
index d6618a6e2399ff22ffb4772aa4badcb5075ba63b..b43cd84b61f161302d592dd2d0cc29161586c0fa 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
+#include <linux/platform_data/gpio-dwapb.h>
+#include <linux/slab.h>
 
 #define GPIO_SWPORTA_DR                0x00
 #define GPIO_SWPORTA_DDR       0x04
@@ -35,6 +37,7 @@
 #define GPIO_INTTYPE_LEVEL     0x38
 #define GPIO_INT_POLARITY      0x3c
 #define GPIO_INTSTATUS         0x40
+#define GPIO_PORTA_DEBOUNCE    0x48
 #define GPIO_PORTA_EOI         0x4c
 #define GPIO_EXT_PORTA         0x50
 #define GPIO_EXT_PORTB         0x54
 
 struct dwapb_gpio;
 
+#ifdef CONFIG_PM_SLEEP
+/* Store GPIO context across system-wide suspend/resume transitions */
+struct dwapb_context {
+       u32 data;
+       u32 dir;
+       u32 ext;
+       u32 int_en;
+       u32 int_mask;
+       u32 int_type;
+       u32 int_pol;
+       u32 int_deb;
+};
+#endif
+
 struct dwapb_gpio_port {
        struct bgpio_chip       bgc;
        bool                    is_registered;
        struct dwapb_gpio       *gpio;
+#ifdef CONFIG_PM_SLEEP
+       struct dwapb_context    *ctx;
+#endif
+       unsigned int            idx;
 };
 
 struct dwapb_gpio {
@@ -62,11 +83,33 @@ struct dwapb_gpio {
        struct irq_domain       *domain;
 };
 
+static inline struct dwapb_gpio_port *
+to_dwapb_gpio_port(struct bgpio_chip *bgc)
+{
+       return container_of(bgc, struct dwapb_gpio_port, bgc);
+}
+
+static inline u32 dwapb_read(struct dwapb_gpio *gpio, unsigned int offset)
+{
+       struct bgpio_chip *bgc  = &gpio->ports[0].bgc;
+       void __iomem *reg_base  = gpio->regs;
+
+       return bgc->read_reg(reg_base + offset);
+}
+
+static inline void dwapb_write(struct dwapb_gpio *gpio, unsigned int offset,
+                              u32 val)
+{
+       struct bgpio_chip *bgc  = &gpio->ports[0].bgc;
+       void __iomem *reg_base  = gpio->regs;
+
+       bgc->write_reg(reg_base + offset, val);
+}
+
 static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
 {
        struct bgpio_chip *bgc = to_bgpio_chip(gc);
-       struct dwapb_gpio_port *port = container_of(bgc, struct
-                                                   dwapb_gpio_port, bgc);
+       struct dwapb_gpio_port *port = to_dwapb_gpio_port(bgc);
        struct dwapb_gpio *gpio = port->gpio;
 
        return irq_find_mapping(gpio->domain, offset);
@@ -74,21 +117,20 @@ static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
 
 static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
 {
-       u32 v = readl(gpio->regs + GPIO_INT_POLARITY);
+       u32 v = dwapb_read(gpio, GPIO_INT_POLARITY);
 
        if (gpio_get_value(gpio->ports[0].bgc.gc.base + offs))
                v &= ~BIT(offs);
        else
                v |= BIT(offs);
 
-       writel(v, gpio->regs + GPIO_INT_POLARITY);
+       dwapb_write(gpio, GPIO_INT_POLARITY, v);
 }
 
-static void dwapb_irq_handler(u32 irq, struct irq_desc *desc)
+static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
 {
-       struct dwapb_gpio *gpio = irq_get_handler_data(irq);
-       struct irq_chip *chip = irq_desc_get_chip(desc);
        u32 irq_status = readl_relaxed(gpio->regs + GPIO_INTSTATUS);
+       u32 ret = irq_status;
 
        while (irq_status) {
                int hwirq = fls(irq_status) - 1;
@@ -102,6 +144,16 @@ static void dwapb_irq_handler(u32 irq, struct irq_desc *desc)
                        dwapb_toggle_trigger(gpio, hwirq);
        }
 
+       return ret;
+}
+
+static void dwapb_irq_handler(u32 irq, struct irq_desc *desc)
+{
+       struct dwapb_gpio *gpio = irq_get_handler_data(irq);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+
+       dwapb_do_irq(gpio);
+
        if (chip->irq_eoi)
                chip->irq_eoi(irq_desc_get_irq_data(desc));
 }
@@ -115,9 +167,9 @@ static void dwapb_irq_enable(struct irq_data *d)
        u32 val;
 
        spin_lock_irqsave(&bgc->lock, flags);
-       val = readl(gpio->regs + GPIO_INTEN);
+       val = dwapb_read(gpio, GPIO_INTEN);
        val |= BIT(d->hwirq);
-       writel(val, gpio->regs + GPIO_INTEN);
+       dwapb_write(gpio, GPIO_INTEN, val);
        spin_unlock_irqrestore(&bgc->lock, flags);
 }
 
@@ -130,9 +182,9 @@ static void dwapb_irq_disable(struct irq_data *d)
        u32 val;
 
        spin_lock_irqsave(&bgc->lock, flags);
-       val = readl(gpio->regs + GPIO_INTEN);
+       val = dwapb_read(gpio, GPIO_INTEN);
        val &= ~BIT(d->hwirq);
-       writel(val, gpio->regs + GPIO_INTEN);
+       dwapb_write(gpio, GPIO_INTEN, val);
        spin_unlock_irqrestore(&bgc->lock, flags);
 }
 
@@ -172,8 +224,8 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
                return -EINVAL;
 
        spin_lock_irqsave(&bgc->lock, flags);
-       level = readl(gpio->regs + GPIO_INTTYPE_LEVEL);
-       polarity = readl(gpio->regs + GPIO_INT_POLARITY);
+       level = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
+       polarity = dwapb_read(gpio, GPIO_INT_POLARITY);
 
        switch (type) {
        case IRQ_TYPE_EDGE_BOTH:
@@ -200,29 +252,55 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
 
        irq_setup_alt_chip(d, type);
 
-       writel(level, gpio->regs + GPIO_INTTYPE_LEVEL);
-       writel(polarity, gpio->regs + GPIO_INT_POLARITY);
+       dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level);
+       dwapb_write(gpio, GPIO_INT_POLARITY, polarity);
+       spin_unlock_irqrestore(&bgc->lock, flags);
+
+       return 0;
+}
+
+static int dwapb_gpio_set_debounce(struct gpio_chip *gc,
+                                  unsigned offset, unsigned debounce)
+{
+       struct bgpio_chip *bgc = to_bgpio_chip(gc);
+       struct dwapb_gpio_port *port = to_dwapb_gpio_port(bgc);
+       struct dwapb_gpio *gpio = port->gpio;
+       unsigned long flags, val_deb;
+       unsigned long mask = bgc->pin2mask(bgc, offset);
+
+       spin_lock_irqsave(&bgc->lock, flags);
+
+       val_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
+       if (debounce)
+               dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb | mask);
+       else
+               dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb & ~mask);
+
        spin_unlock_irqrestore(&bgc->lock, flags);
 
        return 0;
 }
 
+static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id)
+{
+       u32 worked;
+       struct dwapb_gpio *gpio = dev_id;
+
+       worked = dwapb_do_irq(gpio);
+
+       return worked ? IRQ_HANDLED : IRQ_NONE;
+}
+
 static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
-                                struct dwapb_gpio_port *port)
+                                struct dwapb_gpio_port *port,
+                                struct dwapb_port_property *pp)
 {
        struct gpio_chip *gc = &port->bgc.gc;
-       struct device_node *node =  gc->of_node;
-       struct irq_chip_generic *irq_gc;
+       struct device_node *node = pp->node;
+       struct irq_chip_generic *irq_gc = NULL;
        unsigned int hwirq, ngpio = gc->ngpio;
        struct irq_chip_type *ct;
-       int err, irq, i;
-
-       irq = irq_of_parse_and_map(node, 0);
-       if (!irq) {
-               dev_warn(gpio->dev, "no irq for bank %s\n",
-                       port->bgc.gc.of_node->full_name);
-               return;
-       }
+       int err, i;
 
        gpio->domain = irq_domain_add_linear(node, ngpio,
                                             &irq_generic_chip_ops, gpio);
@@ -269,8 +347,24 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
        irq_gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
        irq_gc->chip_types[1].handler = handle_edge_irq;
 
-       irq_set_chained_handler(irq, dwapb_irq_handler);
-       irq_set_handler_data(irq, gpio);
+       if (!pp->irq_shared) {
+               irq_set_chained_handler(pp->irq, dwapb_irq_handler);
+               irq_set_handler_data(pp->irq, gpio);
+       } else {
+               /*
+                * Request a shared IRQ since where MFD would have devices
+                * using the same irq pin
+                */
+               err = devm_request_irq(gpio->dev, pp->irq,
+                                      dwapb_irq_handler_mfd,
+                                      IRQF_SHARED, "gpio-dwapb-mfd", gpio);
+               if (err) {
+                       dev_err(gpio->dev, "error requesting IRQ\n");
+                       irq_domain_remove(gpio->domain);
+                       gpio->domain = NULL;
+                       return;
+               }
+       }
 
        for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
                irq_create_mapping(gpio->domain, hwirq);
@@ -296,57 +390,53 @@ static void dwapb_irq_teardown(struct dwapb_gpio *gpio)
 }
 
 static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
-                              struct device_node *port_np,
+                              struct dwapb_port_property *pp,
                               unsigned int offs)
 {
        struct dwapb_gpio_port *port;
-       u32 port_idx, ngpio;
        void __iomem *dat, *set, *dirout;
        int err;
 
-       if (of_property_read_u32(port_np, "reg", &port_idx) ||
-               port_idx >= DWAPB_MAX_PORTS) {
-               dev_err(gpio->dev, "missing/invalid port index for %s\n",
-                       port_np->full_name);
-               return -EINVAL;
-       }
-
        port = &gpio->ports[offs];
        port->gpio = gpio;
+       port->idx = pp->idx;
 
-       if (of_property_read_u32(port_np, "snps,nr-gpios", &ngpio)) {
-               dev_info(gpio->dev, "failed to get number of gpios for %s\n",
-                        port_np->full_name);
-               ngpio = 32;
-       }
+#ifdef CONFIG_PM_SLEEP
+       port->ctx = devm_kzalloc(gpio->dev, sizeof(*port->ctx), GFP_KERNEL);
+       if (!port->ctx)
+               return -ENOMEM;
+#endif
 
-       dat = gpio->regs + GPIO_EXT_PORTA + (port_idx * GPIO_EXT_PORT_SIZE);
-       set = gpio->regs + GPIO_SWPORTA_DR + (port_idx * GPIO_SWPORT_DR_SIZE);
+       dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE);
+       set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE);
        dirout = gpio->regs + GPIO_SWPORTA_DDR +
-               (port_idx * GPIO_SWPORT_DDR_SIZE);
+               (pp->idx * GPIO_SWPORT_DDR_SIZE);
 
        err = bgpio_init(&port->bgc, gpio->dev, 4, dat, set, NULL, dirout,
                         NULL, false);
        if (err) {
                dev_err(gpio->dev, "failed to init gpio chip for %s\n",
-                       port_np->full_name);
+                       pp->name);
                return err;
        }
 
-       port->bgc.gc.ngpio = ngpio;
-       port->bgc.gc.of_node = port_np;
+#ifdef CONFIG_OF_GPIO
+       port->bgc.gc.of_node = pp->node;
+#endif
+       port->bgc.gc.ngpio = pp->ngpio;
+       port->bgc.gc.base = pp->gpio_base;
 
-       /*
-        * Only port A can provide interrupts in all configurations of the IP.
-        */
-       if (port_idx == 0 &&
-           of_property_read_bool(port_np, "interrupt-controller"))
-               dwapb_configure_irqs(gpio, port);
+       /* Only port A support debounce */
+       if (pp->idx == 0)
+               port->bgc.gc.set_debounce = dwapb_gpio_set_debounce;
+
+       if (pp->irq)
+               dwapb_configure_irqs(gpio, port, pp);
 
        err = gpiochip_add(&port->bgc.gc);
        if (err)
                dev_err(gpio->dev, "failed to register gpiochip for %s\n",
-                       port_np->full_name);
+                       pp->name);
        else
                port->is_registered = true;
 
@@ -362,25 +452,116 @@ static void dwapb_gpio_unregister(struct dwapb_gpio *gpio)
                        gpiochip_remove(&gpio->ports[m].bgc.gc);
 }
 
+static struct dwapb_platform_data *
+dwapb_gpio_get_pdata_of(struct device *dev)
+{
+       struct device_node *node, *port_np;
+       struct dwapb_platform_data *pdata;
+       struct dwapb_port_property *pp;
+       int nports;
+       int i;
+
+       node = dev->of_node;
+       if (!IS_ENABLED(CONFIG_OF_GPIO) || !node)
+               return ERR_PTR(-ENODEV);
+
+       nports = of_get_child_count(node);
+       if (nports == 0)
+               return ERR_PTR(-ENODEV);
+
+       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       pdata->properties = kcalloc(nports, sizeof(*pp), GFP_KERNEL);
+       if (!pdata->properties) {
+               kfree(pdata);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       pdata->nports = nports;
+
+       i = 0;
+       for_each_child_of_node(node, port_np) {
+               pp = &pdata->properties[i++];
+               pp->node = port_np;
+
+               if (of_property_read_u32(port_np, "reg", &pp->idx) ||
+                   pp->idx >= DWAPB_MAX_PORTS) {
+                       dev_err(dev, "missing/invalid port index for %s\n",
+                               port_np->full_name);
+                       kfree(pdata->properties);
+                       kfree(pdata);
+                       return ERR_PTR(-EINVAL);
+               }
+
+               if (of_property_read_u32(port_np, "snps,nr-gpios",
+                                        &pp->ngpio)) {
+                       dev_info(dev, "failed to get number of gpios for %s\n",
+                                port_np->full_name);
+                       pp->ngpio = 32;
+               }
+
+               /*
+                * Only port A can provide interrupts in all configurations of
+                * the IP.
+                */
+               if (pp->idx == 0 &&
+                   of_property_read_bool(port_np, "interrupt-controller")) {
+                       pp->irq = irq_of_parse_and_map(port_np, 0);
+                       if (!pp->irq) {
+                               dev_warn(dev, "no irq for bank %s\n",
+                                        port_np->full_name);
+                       }
+               }
+
+               pp->irq_shared  = false;
+               pp->gpio_base   = -1;
+               pp->name        = port_np->full_name;
+       }
+
+       return pdata;
+}
+
+static inline void dwapb_free_pdata_of(struct dwapb_platform_data *pdata)
+{
+       if (!IS_ENABLED(CONFIG_OF_GPIO) || !pdata)
+               return;
+
+       kfree(pdata->properties);
+       kfree(pdata);
+}
+
 static int dwapb_gpio_probe(struct platform_device *pdev)
 {
+       unsigned int i;
        struct resource *res;
        struct dwapb_gpio *gpio;
-       struct device_node *np;
        int err;
-       unsigned int offs = 0;
+       struct device *dev = &pdev->dev;
+       struct dwapb_platform_data *pdata = dev_get_platdata(dev);
+       bool is_pdata_alloc = !pdata;
+
+       if (is_pdata_alloc) {
+               pdata = dwapb_gpio_get_pdata_of(dev);
+               if (IS_ERR(pdata))
+                       return PTR_ERR(pdata);
+       }
 
-       gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
-       if (!gpio)
-               return -ENOMEM;
-       gpio->dev = &pdev->dev;
+       if (!pdata->nports) {
+               err = -ENODEV;
+               goto out_err;
+       }
 
-       gpio->nr_ports = of_get_child_count(pdev->dev.of_node);
-       if (!gpio->nr_ports) {
-               err = -EINVAL;
+       gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+       if (!gpio) {
+               err = -ENOMEM;
                goto out_err;
        }
-       gpio->ports = devm_kzalloc(&pdev->dev, gpio->nr_ports *
+       gpio->dev = &pdev->dev;
+       gpio->nr_ports = pdata->nports;
+
+       gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
                                   sizeof(*gpio->ports), GFP_KERNEL);
        if (!gpio->ports) {
                err = -ENOMEM;
@@ -394,20 +575,23 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
                goto out_err;
        }
 
-       for_each_child_of_node(pdev->dev.of_node, np) {
-               err = dwapb_gpio_add_port(gpio, np, offs++);
+       for (i = 0; i < gpio->nr_ports; i++) {
+               err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i);
                if (err)
                        goto out_unregister;
        }
        platform_set_drvdata(pdev, gpio);
 
-       return 0;
+       goto out_err;
 
 out_unregister:
        dwapb_gpio_unregister(gpio);
        dwapb_irq_teardown(gpio);
 
 out_err:
+       if (is_pdata_alloc)
+               dwapb_free_pdata_of(pdata);
+
        return err;
 }
 
@@ -427,10 +611,100 @@ static const struct of_device_id dwapb_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, dwapb_of_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int dwapb_gpio_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
+       struct bgpio_chip *bgc  = &gpio->ports[0].bgc;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&bgc->lock, flags);
+       for (i = 0; i < gpio->nr_ports; i++) {
+               unsigned int offset;
+               unsigned int idx = gpio->ports[i].idx;
+               struct dwapb_context *ctx = gpio->ports[i].ctx;
+
+               BUG_ON(!ctx);
+
+               offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
+               ctx->dir = dwapb_read(gpio, offset);
+
+               offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
+               ctx->data = dwapb_read(gpio, offset);
+
+               offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+               ctx->ext = dwapb_read(gpio, offset);
+
+               /* Only port A can provide interrupts */
+               if (idx == 0) {
+                       ctx->int_mask   = dwapb_read(gpio, GPIO_INTMASK);
+                       ctx->int_en     = dwapb_read(gpio, GPIO_INTEN);
+                       ctx->int_pol    = dwapb_read(gpio, GPIO_INT_POLARITY);
+                       ctx->int_type   = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
+                       ctx->int_deb    = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
+
+                       /* Mask out interrupts */
+                       dwapb_write(gpio, GPIO_INTMASK, 0xffffffff);
+               }
+       }
+       spin_unlock_irqrestore(&bgc->lock, flags);
+
+       return 0;
+}
+
+static int dwapb_gpio_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
+       struct bgpio_chip *bgc  = &gpio->ports[0].bgc;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&bgc->lock, flags);
+       for (i = 0; i < gpio->nr_ports; i++) {
+               unsigned int offset;
+               unsigned int idx = gpio->ports[i].idx;
+               struct dwapb_context *ctx = gpio->ports[i].ctx;
+
+               BUG_ON(!ctx);
+
+               offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
+               dwapb_write(gpio, offset, ctx->data);
+
+               offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
+               dwapb_write(gpio, offset, ctx->dir);
+
+               offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+               dwapb_write(gpio, offset, ctx->ext);
+
+               /* Only port A can provide interrupts */
+               if (idx == 0) {
+                       dwapb_write(gpio, GPIO_INTTYPE_LEVEL, ctx->int_type);
+                       dwapb_write(gpio, GPIO_INT_POLARITY, ctx->int_pol);
+                       dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, ctx->int_deb);
+                       dwapb_write(gpio, GPIO_INTEN, ctx->int_en);
+                       dwapb_write(gpio, GPIO_INTMASK, ctx->int_mask);
+
+                       /* Clear out spurious interrupts */
+                       dwapb_write(gpio, GPIO_PORTA_EOI, 0xffffffff);
+               }
+       }
+       spin_unlock_irqrestore(&bgc->lock, flags);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend,
+                        dwapb_gpio_resume);
+
 static struct platform_driver dwapb_gpio_driver = {
        .driver         = {
                .name   = "gpio-dwapb",
                .owner  = THIS_MODULE,
+               .pm     = &dwapb_gpio_pm_ops,
                .of_match_table = of_match_ptr(dwapb_of_match),
        },
        .probe          = dwapb_gpio_probe,
index 464a83de0d6a48fea14125fc81134e6d1bda21f5..cc09b237e88c85bc49c72849b789f014764c9674 100644 (file)
@@ -265,29 +265,27 @@ static int ks8695_gpio_show(struct seq_file *s, void *unused)
                                seq_printf(s, "EXT%i ", i);
 
                                switch ((ctrl & intmask[i]) >> (4 * i)) {
-                                       case IOPC_TM_LOW:
-                                               seq_printf(s, "(Low)");         break;
-                                       case IOPC_TM_HIGH:
-                                               seq_printf(s, "(High)");        break;
-                                       case IOPC_TM_RISING:
-                                               seq_printf(s, "(Rising)");      break;
-                                       case IOPC_TM_FALLING:
-                                               seq_printf(s, "(Falling)");     break;
-                                       case IOPC_TM_EDGE:
-                                               seq_printf(s, "(Edges)");       break;
+                               case IOPC_TM_LOW:
+                                       seq_printf(s, "(Low)");         break;
+                               case IOPC_TM_HIGH:
+                                       seq_printf(s, "(High)");        break;
+                               case IOPC_TM_RISING:
+                                       seq_printf(s, "(Rising)");      break;
+                               case IOPC_TM_FALLING:
+                                       seq_printf(s, "(Falling)");     break;
+                               case IOPC_TM_EDGE:
+                                       seq_printf(s, "(Edges)");       break;
                                }
-                       }
-                       else
+                       } else
                                seq_printf(s, "GPIO\t");
-               }
-               else if (i <= KS8695_GPIO_5) {
+               } else if (i <= KS8695_GPIO_5) {
                        if (ctrl & enable[i])
                                seq_printf(s, "TOUT%i\t", i - KS8695_GPIO_4);
                        else
                                seq_printf(s, "GPIO\t");
-               }
-               else
+               } else {
                        seq_printf(s, "GPIO\t");
+               }
 
                seq_printf(s, "\t");
 
index 6f183d9b487eaee65f01906573af1ca48d3341ec..8488e2fd307c399ee24bad8284f0c16a43c803fd 100644 (file)
@@ -479,7 +479,7 @@ static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
 
        mutex_init(&mcp->irq_lock);
 
-       mcp->irq_domain = irq_domain_add_linear(chip->of_node, chip->ngpio,
+       mcp->irq_domain = irq_domain_add_linear(chip->dev->of_node, chip->ngpio,
                                                &irq_domain_simple_ops, mcp);
        if (!mcp->irq_domain)
                return -ENODEV;
@@ -581,7 +581,7 @@ done:
 
 static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
                              void *data, unsigned addr, unsigned type,
-                             unsigned base, unsigned pullups)
+                             struct mcp23s08_platform_data *pdata, int cs)
 {
        int status;
        bool mirror = false;
@@ -635,7 +635,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
                return -EINVAL;
        }
 
-       mcp->chip.base = base;
+       mcp->chip.base = pdata->base;
        mcp->chip.can_sleep = true;
        mcp->chip.dev = dev;
        mcp->chip.owner = THIS_MODULE;
@@ -648,11 +648,9 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
        if (status < 0)
                goto fail;
 
-       mcp->irq_controller = of_property_read_bool(mcp->chip.of_node,
-                                               "interrupt-controller");
+       mcp->irq_controller = pdata->irq_controller;
        if (mcp->irq && mcp->irq_controller && (type == MCP_TYPE_017))
-               mirror = of_property_read_bool(mcp->chip.of_node,
-                                               "microchip,irq-mirror");
+               mirror = pdata->mirror;
 
        if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror) {
                /* mcp23s17 has IOCON twice, make sure they are in sync */
@@ -668,7 +666,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
        }
 
        /* configure ~100K pullups */
-       status = mcp->ops->write(mcp, MCP_GPPU, pullups);
+       status = mcp->ops->write(mcp, MCP_GPPU, pdata->chip[cs].pullups);
        if (status < 0)
                goto fail;
 
@@ -768,25 +766,29 @@ MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match);
 static int mcp230xx_probe(struct i2c_client *client,
                                    const struct i2c_device_id *id)
 {
-       struct mcp23s08_platform_data *pdata;
+       struct mcp23s08_platform_data *pdata, local_pdata;
        struct mcp23s08 *mcp;
-       int status, base, pullups;
+       int status;
        const struct of_device_id *match;
 
        match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match),
                                        &client->dev);
-       pdata = dev_get_platdata(&client->dev);
-       if (match || !pdata) {
-               base = -1;
-               pullups = 0;
+       if (match) {
+               pdata = &local_pdata;
+               pdata->base = -1;
+               pdata->chip[0].pullups = 0;
+               pdata->irq_controller = of_property_read_bool(
+                                       client->dev.of_node,
+                                       "interrupt-controller");
+               pdata->mirror = of_property_read_bool(client->dev.of_node,
+                                                     "microchip,irq-mirror");
                client->irq = irq_of_parse_and_map(client->dev.of_node, 0);
        } else {
-               if (!gpio_is_valid(pdata->base)) {
+               pdata = dev_get_platdata(&client->dev);
+               if (!pdata || !gpio_is_valid(pdata->base)) {
                        dev_dbg(&client->dev, "invalid platform data\n");
                        return -EINVAL;
                }
-               base = pdata->base;
-               pullups = pdata->chip[0].pullups;
        }
 
        mcp = kzalloc(sizeof(*mcp), GFP_KERNEL);
@@ -795,7 +797,7 @@ static int mcp230xx_probe(struct i2c_client *client,
 
        mcp->irq = client->irq;
        status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
-                                   id->driver_data, base, pullups);
+                                   id->driver_data, pdata, 0);
        if (status)
                goto fail;
 
@@ -863,14 +865,12 @@ static void mcp23s08_i2c_exit(void) { }
 
 static int mcp23s08_probe(struct spi_device *spi)
 {
-       struct mcp23s08_platform_data   *pdata;
+       struct mcp23s08_platform_data   *pdata, local_pdata;
        unsigned                        addr;
        int                             chips = 0;
        struct mcp23s08_driver_data     *data;
        int                             status, type;
-       unsigned                        base = -1,
-                                       ngpio = 0,
-                                       pullups[ARRAY_SIZE(pdata->chip)];
+       unsigned                        ngpio = 0;
        const struct                    of_device_id *match;
        u32                             spi_present_mask = 0;
 
@@ -893,11 +893,18 @@ static int mcp23s08_probe(struct spi_device *spi)
                        return -ENODEV;
                }
 
+               pdata = &local_pdata;
+               pdata->base = -1;
                for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
-                       pullups[addr] = 0;
+                       pdata->chip[addr].pullups = 0;
                        if (spi_present_mask & (1 << addr))
                                chips++;
                }
+               pdata->irq_controller = of_property_read_bool(
+                                       spi->dev.of_node,
+                                       "interrupt-controller");
+               pdata->mirror = of_property_read_bool(spi->dev.of_node,
+                                                     "microchip,irq-mirror");
        } else {
                type = spi_get_device_id(spi)->driver_data;
                pdata = dev_get_platdata(&spi->dev);
@@ -917,10 +924,7 @@ static int mcp23s08_probe(struct spi_device *spi)
                                return -EINVAL;
                        }
                        spi_present_mask |= 1 << addr;
-                       pullups[addr] = pdata->chip[addr].pullups;
                }
-
-               base = pdata->base;
        }
 
        if (!chips)
@@ -938,13 +942,13 @@ static int mcp23s08_probe(struct spi_device *spi)
                chips--;
                data->mcp[addr] = &data->chip[chips];
                status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi,
-                                           0x40 | (addr << 1), type, base,
-                                           pullups[addr]);
+                                           0x40 | (addr << 1), type, pdata,
+                                           addr);
                if (status < 0)
                        goto fail;
 
-               if (base != -1)
-                       base += (type == MCP_TYPE_S17) ? 16 : 8;
+               if (pdata->base != -1)
+                       pdata->base += (type == MCP_TYPE_S17) ? 16 : 8;
                ngpio += (type == MCP_TYPE_S17) ? 16 : 8;
        }
        data->ngpio = ngpio;
index 174932165fcb5d8795c992506bd0584c0bebb8e5..415682f692145ded2cc02653d2286a7c0f3665f5 100644 (file)
@@ -857,16 +857,6 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
        spin_unlock_irqrestore(&bank->lock, flags);
 }
 
-static struct irq_chip gpio_irq_chip = {
-       .name           = "GPIO",
-       .irq_shutdown   = omap_gpio_irq_shutdown,
-       .irq_ack        = omap_gpio_ack_irq,
-       .irq_mask       = omap_gpio_mask_irq,
-       .irq_unmask     = omap_gpio_unmask_irq,
-       .irq_set_type   = omap_gpio_irq_type,
-       .irq_set_wake   = omap_gpio_wake_enable,
-};
-
 /*---------------------------------------------------------------------*/
 
 static int omap_mpuio_suspend_noirq(struct device *dev)
@@ -1088,7 +1078,7 @@ omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start,
                               IRQ_NOREQUEST | IRQ_NOPROBE, 0);
 }
 
-static int omap_gpio_chip_init(struct gpio_bank *bank)
+static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
 {
        int j;
        static int gpio;
@@ -1137,17 +1127,17 @@ static int omap_gpio_chip_init(struct gpio_bank *bank)
        }
 #endif
 
-       ret = gpiochip_irqchip_add(&bank->chip, &gpio_irq_chip,
+       ret = gpiochip_irqchip_add(&bank->chip, irqc,
                                   irq_base, omap_gpio_irq_handler,
                                   IRQ_TYPE_NONE);
 
        if (ret) {
                dev_err(bank->dev, "Couldn't add irqchip to gpiochip %d\n", ret);
-               ret = gpiochip_remove(&bank->chip);
+               gpiochip_remove(&bank->chip);
                return -ENODEV;
        }
 
-       gpiochip_set_chained_irqchip(&bank->chip, &gpio_irq_chip,
+       gpiochip_set_chained_irqchip(&bank->chip, irqc,
                                     bank->irq, omap_gpio_irq_handler);
 
        for (j = 0; j < bank->width; j++) {
@@ -1172,6 +1162,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
        const struct omap_gpio_platform_data *pdata;
        struct resource *res;
        struct gpio_bank *bank;
+       struct irq_chip *irqc;
        int ret;
 
        match = of_match_device(of_match_ptr(omap_gpio_match), dev);
@@ -1186,6 +1177,18 @@ static int omap_gpio_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       irqc = devm_kzalloc(dev, sizeof(*irqc), GFP_KERNEL);
+       if (!irqc)
+               return -ENOMEM;
+
+       irqc->irq_shutdown = omap_gpio_irq_shutdown,
+       irqc->irq_ack = omap_gpio_ack_irq,
+       irqc->irq_mask = omap_gpio_mask_irq,
+       irqc->irq_unmask = omap_gpio_unmask_irq,
+       irqc->irq_set_type = omap_gpio_irq_type,
+       irqc->irq_set_wake = omap_gpio_wake_enable,
+       irqc->name = dev_name(&pdev->dev);
+
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (unlikely(!res)) {
                dev_err(dev, "Invalid IRQ resource\n");
@@ -1241,7 +1244,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
 
        omap_gpio_mod_init(bank);
 
-       ret = omap_gpio_chip_init(bank);
+       ret = omap_gpio_chip_init(bank, irqc);
        if (ret)
                return ret;
 
index f9961eea2120a051b0ada70a3f6483baaa6a826c..e2da64abbccd9a8ebc42a4b2f29b76f707c82e0b 100644 (file)
@@ -520,7 +520,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
        struct i2c_client *client = chip->client;
        int ret, i, offset = 0;
 
-       if (irq_base != -1
+       if (client->irq && irq_base != -1
                        && (id->driver_data & PCA_INT)) {
 
                switch (chip->chip_type) {
@@ -586,50 +586,6 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
 }
 #endif
 
-/*
- * Handlers for alternative sources of platform_data
- */
-#ifdef CONFIG_OF_GPIO
-/*
- * Translate OpenFirmware node properties into platform_data
- * WARNING: This is DEPRECATED and will be removed eventually!
- */
-static void
-pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)
-{
-       struct device_node *node;
-       const __be32 *val;
-       int size;
-
-       *gpio_base = -1;
-
-       node = client->dev.of_node;
-       if (node == NULL)
-               return;
-
-       val = of_get_property(node, "linux,gpio-base", &size);
-       WARN(val, "%s: device-tree property 'linux,gpio-base' is deprecated!", __func__);
-       if (val) {
-               if (size != sizeof(*val))
-                       dev_warn(&client->dev, "%s: wrong linux,gpio-base\n",
-                                node->full_name);
-               else
-                       *gpio_base = be32_to_cpup(val);
-       }
-
-       val = of_get_property(node, "polarity", NULL);
-       WARN(val, "%s: device-tree property 'polarity' is deprecated!", __func__);
-       if (val)
-               *invert = *val;
-}
-#else
-static void
-pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)
-{
-       *gpio_base = -1;
-}
-#endif
-
 static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
 {
        int ret;
@@ -704,12 +660,8 @@ static int pca953x_probe(struct i2c_client *client,
                invert = pdata->invert;
                chip->names = pdata->names;
        } else {
-               pca953x_get_alt_pdata(client, &chip->gpio_start, &invert);
-#ifdef CONFIG_OF_GPIO
-               /* If I2C node has no interrupts property, disable GPIO interrupts */
-               if (of_find_property(client->dev.of_node, "interrupts", NULL) == NULL)
-                       irq_base = -1;
-#endif
+               chip->gpio_start = -1;
+               irq_base = 0;
        }
 
        chip->client = client;
index e0ac549dccb54c43261d71df59da196144502d67..2d9a950ca2d46d1b65f26e7c85ac6861b1892ae0 100644 (file)
@@ -171,6 +171,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
        return 0;
 }
 
+#ifdef CONFIG_PM
 /*
  * Save register configuration and disable interrupts.
  */
@@ -206,6 +207,7 @@ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
                iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg,
                          &chip->reg->gpio_use_sel);
 }
+#endif
 
 static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
 {
index 3810da47043f5f0c8c155e98341de5825e3d77b7..7c288ba4dc87fe0ea9e6274be22d7c7719752142 100644 (file)
@@ -1309,56 +1309,6 @@ samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin)
 }
 EXPORT_SYMBOL(s3c_gpio_getpull);
 
-#ifdef CONFIG_S5P_GPIO_DRVSTR
-s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin)
-{
-       struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
-       unsigned int off;
-       void __iomem *reg;
-       int shift;
-       u32 drvstr;
-
-       if (!chip)
-               return -EINVAL;
-
-       off = pin - chip->chip.base;
-       shift = off * 2;
-       reg = chip->base + 0x0C;
-
-       drvstr = __raw_readl(reg);
-       drvstr = drvstr >> shift;
-       drvstr &= 0x3;
-
-       return (__force s5p_gpio_drvstr_t)drvstr;
-}
-EXPORT_SYMBOL(s5p_gpio_get_drvstr);
-
-int s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr)
-{
-       struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
-       unsigned int off;
-       void __iomem *reg;
-       int shift;
-       u32 tmp;
-
-       if (!chip)
-               return -EINVAL;
-
-       off = pin - chip->chip.base;
-       shift = off * 2;
-       reg = chip->base + 0x0C;
-
-       tmp = __raw_readl(reg);
-       tmp &= ~(0x3 << shift);
-       tmp |= drvstr << shift;
-
-       __raw_writel(tmp, reg);
-
-       return 0;
-}
-EXPORT_SYMBOL(s5p_gpio_set_drvstr);
-#endif /* CONFIG_S5P_GPIO_DRVSTR */
-
 #ifdef CONFIG_PLAT_S3C24XX
 unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change)
 {
index 845025a57240ac3517c7401c3bbe7c0f7c4ebe8a..85c5b19742949708300bcc0501152779f62adaed 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/interrupt.h>
 #include <linux/of.h>
 #include <linux/mfd/stmpe.h>
+#include <linux/seq_file.h>
 
 /*
  * These registers are modified under the irq bus lock and cached to avoid
@@ -127,19 +128,19 @@ static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
        int regoffset = offset / 8;
        int mask = 1 << (offset % 8);
 
-       if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
+       if (type & IRQ_TYPE_LEVEL_LOW || type & IRQ_TYPE_LEVEL_HIGH)
                return -EINVAL;
 
        /* STMPE801 doesn't have RE and FE registers */
        if (stmpe_gpio->stmpe->partnum == STMPE801)
                return 0;
 
-       if (type == IRQ_TYPE_EDGE_RISING)
+       if (type & IRQ_TYPE_EDGE_RISING)
                stmpe_gpio->regs[REG_RE][regoffset] |= mask;
        else
                stmpe_gpio->regs[REG_RE][regoffset] &= ~mask;
 
-       if (type == IRQ_TYPE_EDGE_FALLING)
+       if (type & IRQ_TYPE_EDGE_FALLING)
                stmpe_gpio->regs[REG_FE][regoffset] |= mask;
        else
                stmpe_gpio->regs[REG_FE][regoffset] &= ~mask;
@@ -211,6 +212,77 @@ static void stmpe_gpio_irq_unmask(struct irq_data *d)
        stmpe_gpio->regs[REG_IE][regoffset] |= mask;
 }
 
+static void stmpe_dbg_show_one(struct seq_file *s,
+                              struct gpio_chip *gc,
+                              unsigned offset, unsigned gpio)
+{
+       struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(gc);
+       struct stmpe *stmpe = stmpe_gpio->stmpe;
+       const char *label = gpiochip_is_requested(gc, offset);
+       int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+       bool val = !!stmpe_gpio_get(gc, offset);
+       u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+       u8 mask = 1 << (offset % 8);
+       int ret;
+       u8 dir;
+
+       ret = stmpe_reg_read(stmpe, dir_reg);
+       if (ret < 0)
+               return;
+       dir = !!(ret & mask);
+
+       if (dir) {
+               seq_printf(s, " gpio-%-3d (%-20.20s) out %s",
+                          gpio, label ?: "(none)",
+                          val ? "hi" : "lo");
+       } else {
+               u8 edge_det_reg = stmpe->regs[STMPE_IDX_GPEDR_MSB] + num_banks - 1 - (offset / 8);
+               u8 rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB] - (offset / 8);
+               u8 fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB] - (offset / 8);
+               u8 irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB] - (offset / 8);
+               bool edge_det;
+               bool rise;
+               bool fall;
+               bool irqen;
+
+               ret = stmpe_reg_read(stmpe, edge_det_reg);
+               if (ret < 0)
+                       return;
+               edge_det = !!(ret & mask);
+               ret = stmpe_reg_read(stmpe, rise_reg);
+               if (ret < 0)
+                       return;
+               rise = !!(ret & mask);
+               ret = stmpe_reg_read(stmpe, fall_reg);
+               if (ret < 0)
+                       return;
+               fall = !!(ret & mask);
+               ret = stmpe_reg_read(stmpe, irqen_reg);
+               if (ret < 0)
+                       return;
+               irqen = !!(ret & mask);
+
+               seq_printf(s, " gpio-%-3d (%-20.20s) in  %s %s %s%s%s",
+                          gpio, label ?: "(none)",
+                          val ? "hi" : "lo",
+                          edge_det ? "edge-asserted" : "edge-inactive",
+                          irqen ? "IRQ-enabled" : "",
+                          rise ? " rising-edge-detection" : "",
+                          fall ? " falling-edge-detection" : "");
+       }
+}
+
+static void stmpe_dbg_show(struct seq_file *s, struct gpio_chip *gc)
+{
+       unsigned i;
+       unsigned gpio = gc->base;
+
+       for (i = 0; i < gc->ngpio; i++, gpio++) {
+               stmpe_dbg_show_one(s, gc, i, gpio);
+               seq_printf(s, "\n");
+       }
+}
+
 static struct irq_chip stmpe_gpio_irq_chip = {
        .name                   = "stmpe-gpio",
        .irq_bus_lock           = stmpe_gpio_irq_lock,
@@ -293,6 +365,9 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
 #endif
        stmpe_gpio->chip.base = -1;
 
+       if (IS_ENABLED(CONFIG_DEBUG_FS))
+                stmpe_gpio->chip.dbg_show = stmpe_dbg_show;
+
        if (pdata)
                stmpe_gpio->norequest_mask = pdata->norequest_mask;
        else if (np)
@@ -308,6 +383,12 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
        if (ret)
                goto out_free;
 
+       ret = gpiochip_add(&stmpe_gpio->chip);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
+               goto out_disable;
+       }
+
        if (irq > 0) {
                ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                stmpe_gpio_irq, IRQF_ONESHOT,
@@ -324,14 +405,13 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
                if (ret) {
                        dev_err(&pdev->dev,
                                "could not connect irqchip to gpiochip\n");
-                       return ret;
+                       goto out_disable;
                }
-       }
 
-       ret = gpiochip_add(&stmpe_gpio->chip);
-       if (ret) {
-               dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
-               goto out_disable;
+               gpiochip_set_chained_irqchip(&stmpe_gpio->chip,
+                                            &stmpe_gpio_irq_chip,
+                                            irq,
+                                            NULL);
        }
 
        if (pdata && pdata->setup)
@@ -343,6 +423,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
 
 out_disable:
        stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
+       gpiochip_remove(&stmpe_gpio->chip);
 out_free:
        kfree(stmpe_gpio);
        return ret;
index 04882a911b6577f6cd3f5b333d1a91312e55ae51..7e359b7cce1b2190ecd7553a6a312d14fa9f32ff 100644 (file)
@@ -292,7 +292,7 @@ static struct platform_driver xway_stp_driver = {
        },
 };
 
-int __init xway_stp_init(void)
+static int __init xway_stp_init(void)
 {
        return platform_driver_register(&xway_stp_driver);
 }
index 30884fbc750de5e74f7322a415bfab7650185e2d..e82fde4b6898dc6a44c8a15b922f3866b1c4a7bb 100644 (file)
@@ -37,6 +37,8 @@
  * dat_bit_offset:     Offset (in bits) to the first GPIO bit.
  * dir_bit_offset:     Optional offset (in bits) to the first bit to switch
  *                     GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag).
+ * set:                HW specific callback to assigns output value
+ *                     for signal "offset"
  */
 
 struct syscon_gpio_data {
@@ -45,12 +47,16 @@ struct syscon_gpio_data {
        unsigned int    bit_count;
        unsigned int    dat_bit_offset;
        unsigned int    dir_bit_offset;
+       void            (*set)(struct gpio_chip *chip,
+                              unsigned offset, int value);
 };
 
 struct syscon_gpio_priv {
        struct gpio_chip                chip;
        struct regmap                   *syscon;
        const struct syscon_gpio_data   *data;
+       u32                             dreg_offset;
+       u32                             dir_reg_offset;
 };
 
 static inline struct syscon_gpio_priv *to_syscon_gpio(struct gpio_chip *chip)
@@ -61,9 +67,11 @@ static inline struct syscon_gpio_priv *to_syscon_gpio(struct gpio_chip *chip)
 static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
        struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
-       unsigned int val, offs = priv->data->dat_bit_offset + offset;
+       unsigned int val, offs;
        int ret;
 
+       offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
+
        ret = regmap_read(priv->syscon,
                          (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, &val);
        if (ret)
@@ -75,7 +83,9 @@ static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)
 static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
 {
        struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
-       unsigned int offs = priv->data->dat_bit_offset + offset;
+       unsigned int offs;
+
+       offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
 
        regmap_update_bits(priv->syscon,
                           (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
@@ -88,7 +98,10 @@ static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
        struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
 
        if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
-               unsigned int offs = priv->data->dir_bit_offset + offset;
+               unsigned int offs;
+
+               offs = priv->dir_reg_offset +
+                      priv->data->dir_bit_offset + offset;
 
                regmap_update_bits(priv->syscon,
                                   (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
@@ -103,7 +116,10 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
        struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
 
        if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
-               unsigned int offs = priv->data->dir_bit_offset + offset;
+               unsigned int offs;
+
+               offs = priv->dir_reg_offset +
+                      priv->data->dir_bit_offset + offset;
 
                regmap_update_bits(priv->syscon,
                                   (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
@@ -111,7 +127,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
                                   BIT(offs % SYSCON_REG_BITS));
        }
 
-       syscon_gpio_set(chip, offset, val);
+       priv->data->set(chip, offset, val);
 
        return 0;
 }
@@ -124,11 +140,46 @@ static const struct syscon_gpio_data clps711x_mctrl_gpio = {
        .dat_bit_offset = 0x40 * 8 + 8,
 };
 
+#define KEYSTONE_LOCK_BIT BIT(0)
+
+static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+       struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+       unsigned int offs;
+       int ret;
+
+       offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
+
+       if (!val)
+               return;
+
+       ret = regmap_update_bits(
+                       priv->syscon,
+                       (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+                       BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT,
+                       BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT);
+       if (ret < 0)
+               dev_err(chip->dev, "gpio write failed ret(%d)\n", ret);
+}
+
+static const struct syscon_gpio_data keystone_dsp_gpio = {
+       /* ARM Keystone 2 */
+       .compatible     = NULL,
+       .flags          = GPIO_SYSCON_FEAT_OUT,
+       .bit_count      = 28,
+       .dat_bit_offset = 4,
+       .set            = keystone_gpio_set,
+};
+
 static const struct of_device_id syscon_gpio_ids[] = {
        {
                .compatible     = "cirrus,clps711x-mctrl-gpio",
                .data           = &clps711x_mctrl_gpio,
        },
+       {
+               .compatible     = "ti,keystone-dsp-gpio",
+               .data           = &keystone_dsp_gpio,
+       },
        { }
 };
 MODULE_DEVICE_TABLE(of, syscon_gpio_ids);
@@ -138,6 +189,8 @@ static int syscon_gpio_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        const struct of_device_id *of_id = of_match_device(syscon_gpio_ids, dev);
        struct syscon_gpio_priv *priv;
+       struct device_node *np = dev->of_node;
+       int ret;
 
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -145,10 +198,31 @@ static int syscon_gpio_probe(struct platform_device *pdev)
 
        priv->data = of_id->data;
 
-       priv->syscon =
-               syscon_regmap_lookup_by_compatible(priv->data->compatible);
-       if (IS_ERR(priv->syscon))
-               return PTR_ERR(priv->syscon);
+       if (priv->data->compatible) {
+               priv->syscon = syscon_regmap_lookup_by_compatible(
+                                       priv->data->compatible);
+               if (IS_ERR(priv->syscon))
+                       return PTR_ERR(priv->syscon);
+       } else {
+               priv->syscon =
+                       syscon_regmap_lookup_by_phandle(np, "gpio,syscon-dev");
+               if (IS_ERR(priv->syscon))
+                       return PTR_ERR(priv->syscon);
+
+               ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1,
+                                                &priv->dreg_offset);
+               if (ret)
+                       dev_err(dev, "can't read the data register offset!\n");
+
+               priv->dreg_offset <<= 3;
+
+               ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
+                                                &priv->dir_reg_offset);
+               if (ret)
+                       dev_err(dev, "can't read the dir register offset!\n");
+
+               priv->dir_reg_offset <<= 3;
+       }
 
        priv->chip.dev = dev;
        priv->chip.owner = THIS_MODULE;
@@ -159,7 +233,7 @@ static int syscon_gpio_probe(struct platform_device *pdev)
        if (priv->data->flags & GPIO_SYSCON_FEAT_IN)
                priv->chip.direction_input = syscon_gpio_dir_in;
        if (priv->data->flags & GPIO_SYSCON_FEAT_OUT) {
-               priv->chip.set = syscon_gpio_set;
+               priv->chip.set = priv->data->set ? : syscon_gpio_set;
                priv->chip.direction_output = syscon_gpio_dir_out;
        }
 
index 7324869c38e0a01d82444da901ecaef719b8469e..ae0f6466eb09bf60e941b14966c62e0fbe7d1b77 100644 (file)
@@ -300,6 +300,11 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
                return ret;
        }
 
+       gpiochip_set_chained_irqchip(&tc3589x_gpio->chip,
+                                    &tc3589x_gpio_irq_chip,
+                                    irq,
+                                    NULL);
+
        if (pdata && pdata->setup)
                pdata->setup(tc3589x, tc3589x_gpio->chip.base);
 
diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c
new file mode 100644 (file)
index 0000000..7d48922
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * AppliedMicro X-Gene SoC GPIO Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Feng Kan <fkan@apm.com>.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/driver.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+#define GPIO_SET_DR_OFFSET     0x0C
+#define GPIO_DATA_OFFSET       0x14
+#define GPIO_BANK_STRIDE       0x0C
+
+#define XGENE_GPIOS_PER_BANK   16
+#define XGENE_MAX_GPIO_BANKS   3
+#define XGENE_MAX_GPIOS                (XGENE_GPIOS_PER_BANK * XGENE_MAX_GPIO_BANKS)
+
+#define GPIO_BIT_OFFSET(x)     (x % XGENE_GPIOS_PER_BANK)
+#define GPIO_BANK_OFFSET(x)    ((x / XGENE_GPIOS_PER_BANK) * GPIO_BANK_STRIDE)
+
+struct xgene_gpio {
+       struct gpio_chip        chip;
+       void __iomem            *base;
+       spinlock_t              lock;
+#ifdef CONFIG_PM
+       u32                     set_dr_val[XGENE_MAX_GPIO_BANKS];
+#endif
+};
+
+static inline struct xgene_gpio *to_xgene_gpio(struct gpio_chip *chip)
+{
+       return container_of(chip, struct xgene_gpio, chip);
+}
+
+static int xgene_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+       struct xgene_gpio *chip = to_xgene_gpio(gc);
+       unsigned long bank_offset;
+       u32 bit_offset;
+
+       bank_offset = GPIO_DATA_OFFSET + GPIO_BANK_OFFSET(offset);
+       bit_offset = GPIO_BIT_OFFSET(offset);
+       return !!(ioread32(chip->base + bank_offset) & BIT(bit_offset));
+}
+
+static void __xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+       struct xgene_gpio *chip = to_xgene_gpio(gc);
+       unsigned long bank_offset;
+       u32 setval, bit_offset;
+
+       bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
+       bit_offset = GPIO_BIT_OFFSET(offset) + XGENE_GPIOS_PER_BANK;
+
+       setval = ioread32(chip->base + bank_offset);
+       if (val)
+               setval |= BIT(bit_offset);
+       else
+               setval &= ~BIT(bit_offset);
+       iowrite32(setval, chip->base + bank_offset);
+}
+
+static void xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+       struct xgene_gpio *chip = to_xgene_gpio(gc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&chip->lock, flags);
+       __xgene_gpio_set(gc, offset, val);
+       spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int xgene_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
+{
+       struct xgene_gpio *chip = to_xgene_gpio(gc);
+       unsigned long flags, bank_offset;
+       u32 dirval, bit_offset;
+
+       bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
+       bit_offset = GPIO_BIT_OFFSET(offset);
+
+       spin_lock_irqsave(&chip->lock, flags);
+
+       dirval = ioread32(chip->base + bank_offset);
+       dirval |= BIT(bit_offset);
+       iowrite32(dirval, chip->base + bank_offset);
+
+       spin_unlock_irqrestore(&chip->lock, flags);
+
+       return 0;
+}
+
+static int xgene_gpio_dir_out(struct gpio_chip *gc,
+                                       unsigned int offset, int val)
+{
+       struct xgene_gpio *chip = to_xgene_gpio(gc);
+       unsigned long flags, bank_offset;
+       u32 dirval, bit_offset;
+
+       bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
+       bit_offset = GPIO_BIT_OFFSET(offset);
+
+       spin_lock_irqsave(&chip->lock, flags);
+
+       dirval = ioread32(chip->base + bank_offset);
+       dirval &= ~BIT(bit_offset);
+       iowrite32(dirval, chip->base + bank_offset);
+       __xgene_gpio_set(gc, offset, val);
+
+       spin_unlock_irqrestore(&chip->lock, flags);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int xgene_gpio_suspend(struct device *dev)
+{
+       struct xgene_gpio *gpio = dev_get_drvdata(dev);
+       unsigned long bank_offset;
+       unsigned int bank;
+
+       for (bank = 0; bank < XGENE_MAX_GPIO_BANKS; bank++) {
+               bank_offset = GPIO_SET_DR_OFFSET + bank * GPIO_BANK_STRIDE;
+               gpio->set_dr_val[bank] = ioread32(gpio->base + bank_offset);
+       }
+       return 0;
+}
+
+static int xgene_gpio_resume(struct device *dev)
+{
+       struct xgene_gpio *gpio = dev_get_drvdata(dev);
+       unsigned long bank_offset;
+       unsigned int bank;
+
+       for (bank = 0; bank < XGENE_MAX_GPIO_BANKS; bank++) {
+               bank_offset = GPIO_SET_DR_OFFSET + bank * GPIO_BANK_STRIDE;
+               iowrite32(gpio->set_dr_val[bank], gpio->base + bank_offset);
+       }
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume);
+#define XGENE_GPIO_PM_OPS      (&xgene_gpio_pm)
+#else
+#define XGENE_GPIO_PM_OPS      NULL
+#endif
+
+static int xgene_gpio_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct xgene_gpio *gpio;
+       int err = 0;
+
+       gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+       if (!gpio) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       gpio->base = devm_ioremap_nocache(&pdev->dev, res->start,
+                                                       resource_size(res));
+       if (!gpio->base) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       gpio->chip.ngpio = XGENE_MAX_GPIOS;
+
+       spin_lock_init(&gpio->lock);
+       gpio->chip.dev = &pdev->dev;
+       gpio->chip.direction_input = xgene_gpio_dir_in;
+       gpio->chip.direction_output = xgene_gpio_dir_out;
+       gpio->chip.get = xgene_gpio_get;
+       gpio->chip.set = xgene_gpio_set;
+       gpio->chip.label = dev_name(&pdev->dev);
+       gpio->chip.base = -1;
+
+       platform_set_drvdata(pdev, gpio);
+
+       err = gpiochip_add(&gpio->chip);
+       if (err) {
+               dev_err(&pdev->dev,
+                       "failed to register gpiochip.\n");
+               goto err;
+       }
+
+       dev_info(&pdev->dev, "X-Gene GPIO driver registered.\n");
+       return 0;
+err:
+       dev_err(&pdev->dev, "X-Gene GPIO driver registration failed.\n");
+       return err;
+}
+
+static int xgene_gpio_remove(struct platform_device *pdev)
+{
+       struct xgene_gpio *gpio = platform_get_drvdata(pdev);
+
+       gpiochip_remove(&gpio->chip);
+       return 0;
+}
+
+static const struct of_device_id xgene_gpio_of_match[] = {
+       { .compatible = "apm,xgene-gpio", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, xgene_gpio_of_match);
+
+static struct platform_driver xgene_gpio_driver = {
+       .driver = {
+               .name = "xgene-gpio",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_gpio_of_match,
+               .pm     = XGENE_GPIO_PM_OPS,
+       },
+       .probe = xgene_gpio_probe,
+       .remove = xgene_gpio_remove,
+};
+
+module_platform_driver(xgene_gpio_driver);
+
+MODULE_AUTHOR("Feng Kan <fkan@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene GPIO driver");
+MODULE_LICENSE("GPL");
index 12481867daf11e8fa580df58e266225d9e332ade..ba18b06c9a21e8249c46f784a26993a365980370 100644 (file)
@@ -197,6 +197,7 @@ static int xgpio_of_probe(struct device_node *np)
        struct xgpio_instance *chip;
        int status = 0;
        const u32 *tree_info;
+       u32 ngpio;
 
        chip = kzalloc(sizeof(*chip), GFP_KERNEL);
        if (!chip)
@@ -211,12 +212,13 @@ static int xgpio_of_probe(struct device_node *np)
        /* Update GPIO direction shadow register with default value */
        of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir);
 
-       /* By default assume full GPIO controller */
-       chip->mmchip.gc.ngpio = 32;
-
-       /* Check device node and parent device node for device width */
-       of_property_read_u32(np, "xlnx,gpio-width",
-                             (u32 *)&chip->mmchip.gc.ngpio);
+       /*
+        * Check device node and parent device node for device width
+        * and assume default width of 32
+        */
+       if (of_property_read_u32(np, "xlnx,gpio-width", &ngpio))
+               ngpio = 32;
+       chip->mmchip.gc.ngpio = (u16)ngpio;
 
        spin_lock_init(&chip->gpio_lock);
 
@@ -258,12 +260,13 @@ static int xgpio_of_probe(struct device_node *np)
                /* Update GPIO direction shadow register with default value */
                of_property_read_u32(np, "xlnx,tri-default-2", &chip->gpio_dir);
 
-               /* By default assume full GPIO controller */
-               chip->mmchip.gc.ngpio = 32;
-
-               /* Check device node and parent device node for device width */
-               of_property_read_u32(np, "xlnx,gpio2-width",
-                                    (u32 *)&chip->mmchip.gc.ngpio);
+               /*
+                * Check device node and parent device node for device width
+                * and assume default width of 32
+                */
+               if (of_property_read_u32(np, "xlnx,gpio2-width", &ngpio))
+                       ngpio = 32;
+               chip->mmchip.gc.ngpio = (u16)ngpio;
 
                spin_lock_init(&chip->gpio_lock);
 
index 31ad5df5dbc95d58c395dc27f865b0eb0ce036bc..74cd480bf8de6975a8a3be485798255eca2cc431 100644 (file)
  * @chip:      instance of the gpio_chip
  * @base_addr: base address of the GPIO device
  * @clk:       clock resource for this controller
+ * @irq:       interrupt for the GPIO device
  */
 struct zynq_gpio {
        struct gpio_chip chip;
        void __iomem *base_addr;
        struct clk *clk;
+       int irq;
 };
 
 static struct irq_chip zynq_gpio_level_irqchip;
 static struct irq_chip zynq_gpio_edge_irqchip;
-
 /**
  * zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank
  * for a given pin in the GPIO device
@@ -138,6 +139,13 @@ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num,
        }
 }
 
+static const unsigned int zynq_gpio_bank_offset[] = {
+       ZYNQ_GPIO_BANK0_PIN_MIN,
+       ZYNQ_GPIO_BANK1_PIN_MIN,
+       ZYNQ_GPIO_BANK2_PIN_MIN,
+       ZYNQ_GPIO_BANK3_PIN_MIN,
+};
+
 /**
  * zynq_gpio_get_value - Get the state of the specified pin of GPIO device
  * @chip:      gpio_chip instance to be worked on
@@ -427,10 +435,9 @@ static int zynq_gpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
 
 static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on)
 {
-       if (on)
-               zynq_gpio_irq_unmask(data);
-       else
-               zynq_gpio_irq_mask(data);
+       struct zynq_gpio *gpio = irq_data_get_irq_chip_data(data);
+
+       irq_set_irq_wake(gpio->irq, on);
 
        return 0;
 }
@@ -444,7 +451,8 @@ static struct irq_chip zynq_gpio_level_irqchip = {
        .irq_unmask     = zynq_gpio_irq_unmask,
        .irq_set_type   = zynq_gpio_set_irq_type,
        .irq_set_wake   = zynq_gpio_set_wake,
-       .flags          = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED,
+       .flags          = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED |
+                         IRQCHIP_MASK_ON_SUSPEND,
 };
 
 static struct irq_chip zynq_gpio_edge_irqchip = {
@@ -455,8 +463,28 @@ static struct irq_chip zynq_gpio_edge_irqchip = {
        .irq_unmask     = zynq_gpio_irq_unmask,
        .irq_set_type   = zynq_gpio_set_irq_type,
        .irq_set_wake   = zynq_gpio_set_wake,
+       .flags          = IRQCHIP_MASK_ON_SUSPEND,
 };
 
+static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio,
+                                     unsigned int bank_num,
+                                     unsigned long pending)
+{
+       unsigned int bank_offset = zynq_gpio_bank_offset[bank_num];
+       struct irq_domain *irqdomain = gpio->chip.irqdomain;
+       int offset;
+
+       if (!pending)
+               return;
+
+       for_each_set_bit(offset, &pending, 32) {
+               unsigned int gpio_irq;
+
+               gpio_irq = irq_find_mapping(irqdomain, offset + bank_offset);
+               generic_handle_irq(gpio_irq);
+       }
+}
+
 /**
  * zynq_gpio_irqhandler - IRQ handler for the gpio banks of a gpio device
  * @irq:       irq number of the gpio bank where interrupt has occurred
@@ -482,18 +510,7 @@ static void zynq_gpio_irqhandler(unsigned int irq, struct irq_desc *desc)
                                        ZYNQ_GPIO_INTSTS_OFFSET(bank_num));
                int_enb = readl_relaxed(gpio->base_addr +
                                        ZYNQ_GPIO_INTMASK_OFFSET(bank_num));
-               int_sts &= ~int_enb;
-               if (int_sts) {
-                       int offset;
-                       unsigned long pending = int_sts;
-
-                       for_each_set_bit(offset, &pending, 32) {
-                               unsigned int gpio_irq =
-                                       irq_find_mapping(gpio->chip.irqdomain,
-                                                       offset);
-                               generic_handle_irq(gpio_irq);
-                       }
-               }
+               zynq_gpio_handle_bank_irq(gpio, bank_num, int_sts & ~int_enb);
        }
 
        chained_irq_exit(irqchip, desc);
@@ -501,7 +518,11 @@ static void zynq_gpio_irqhandler(unsigned int irq, struct irq_desc *desc)
 
 static int __maybe_unused zynq_gpio_suspend(struct device *dev)
 {
-       if (!device_may_wakeup(dev))
+       struct platform_device *pdev = to_platform_device(dev);
+       int irq = platform_get_irq(pdev, 0);
+       struct irq_data *data = irq_get_irq_data(irq);
+
+       if (!irqd_is_wakeup_set(data))
                return pm_runtime_force_suspend(dev);
 
        return 0;
@@ -509,7 +530,11 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev)
 
 static int __maybe_unused zynq_gpio_resume(struct device *dev)
 {
-       if (!device_may_wakeup(dev))
+       struct platform_device *pdev = to_platform_device(dev);
+       int irq = platform_get_irq(pdev, 0);
+       struct irq_data *data = irq_get_irq_data(irq);
+
+       if (!irqd_is_wakeup_set(data))
                return pm_runtime_force_resume(dev);
 
        return 0;
@@ -570,7 +595,7 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
  */
 static int zynq_gpio_probe(struct platform_device *pdev)
 {
-       int ret, bank_num, irq;
+       int ret, bank_num;
        struct zynq_gpio *gpio;
        struct gpio_chip *chip;
        struct resource *res;
@@ -586,10 +611,10 @@ static int zynq_gpio_probe(struct platform_device *pdev)
        if (IS_ERR(gpio->base_addr))
                return PTR_ERR(gpio->base_addr);
 
-       irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
+       gpio->irq = platform_get_irq(pdev, 0);
+       if (gpio->irq < 0) {
                dev_err(&pdev->dev, "invalid IRQ\n");
-               return irq;
+               return gpio->irq;
        }
 
        /* configure the gpio chip */
@@ -637,19 +662,16 @@ static int zynq_gpio_probe(struct platform_device *pdev)
                goto err_rm_gpiochip;
        }
 
-       gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, irq,
+       gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, gpio->irq,
                                     zynq_gpio_irqhandler);
 
        pm_runtime_set_active(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
 
-       device_set_wakeup_capable(&pdev->dev, 1);
-
        return 0;
 
 err_rm_gpiochip:
-       if (gpiochip_remove(chip))
-               dev_err(&pdev->dev, "Failed to remove gpio chip\n");
+       gpiochip_remove(chip);
 err_disable_clk:
        clk_disable_unprepare(gpio->clk);
 
@@ -664,16 +686,10 @@ err_disable_clk:
  */
 static int zynq_gpio_remove(struct platform_device *pdev)
 {
-       int ret;
        struct zynq_gpio *gpio = platform_get_drvdata(pdev);
 
        pm_runtime_get_sync(&pdev->dev);
-
-       ret = gpiochip_remove(&gpio->chip);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to remove gpio chip\n");
-               return ret;
-       }
+       gpiochip_remove(&gpio->chip);
        clk_disable_unprepare(gpio->clk);
        device_set_wakeup_capable(&pdev->dev, 0);
        return 0;
@@ -688,7 +704,6 @@ MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
 static struct platform_driver zynq_gpio_driver = {
        .driver = {
                .name = DRIVER_NAME,
-               .owner = THIS_MODULE,
                .pm = &zynq_gpio_dev_pm_ops,
                .of_match_table = zynq_gpio_of_match,
        },
index 687476fb39e311b0d09ac0a5b20458fb94e2c605..05c6275da224bcf6d287f51da038bdb1c8438962 100644 (file)
@@ -25,10 +25,12 @@ struct acpi_gpio_event {
        acpi_handle handle;
        unsigned int pin;
        unsigned int irq;
+       struct gpio_desc *desc;
 };
 
 struct acpi_gpio_connection {
        struct list_head node;
+       unsigned int pin;
        struct gpio_desc *desc;
 };
 
@@ -143,14 +145,8 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
        if (!handler)
                return AE_BAD_PARAMETER;
 
-       desc = gpiochip_get_desc(chip, pin);
+       desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event");
        if (IS_ERR(desc)) {
-               dev_err(chip->dev, "Failed to get GPIO descriptor\n");
-               return AE_ERROR;
-       }
-
-       ret = gpiochip_request_own_desc(desc, "ACPI:Event");
-       if (ret) {
                dev_err(chip->dev, "Failed to request GPIO\n");
                return AE_ERROR;
        }
@@ -197,6 +193,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
        event->handle = evt_handle;
        event->irq = irq;
        event->pin = pin;
+       event->desc = desc;
 
        ret = request_threaded_irq(event->irq, NULL, handler, irqflags,
                                   "ACPI:Event", event);
@@ -280,7 +277,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
                struct gpio_desc *desc;
 
                free_irq(event->irq, event);
-               desc = gpiochip_get_desc(chip, event->pin);
+               desc = event->desc;
                if (WARN_ON(IS_ERR(desc)))
                        continue;
                gpio_unlock_as_irq(chip, event->pin);
@@ -409,26 +406,20 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
                struct gpio_desc *desc;
                bool found;
 
-               desc = gpiochip_get_desc(chip, pin);
-               if (IS_ERR(desc)) {
-                       status = AE_ERROR;
-                       goto out;
-               }
-
                mutex_lock(&achip->conn_lock);
 
                found = false;
                list_for_each_entry(conn, &achip->conns, node) {
-                       if (conn->desc == desc) {
+                       if (conn->pin == pin) {
                                found = true;
+                               desc = conn->desc;
                                break;
                        }
                }
                if (!found) {
-                       int ret;
-
-                       ret = gpiochip_request_own_desc(desc, "ACPI:OpRegion");
-                       if (ret) {
+                       desc = gpiochip_request_own_desc(chip, pin,
+                                                        "ACPI:OpRegion");
+                       if (IS_ERR(desc)) {
                                status = AE_ERROR;
                                mutex_unlock(&achip->conn_lock);
                                goto out;
@@ -465,6 +456,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
                                goto out;
                        }
 
+                       conn->pin = pin;
                        conn->desc = desc;
                        list_add_tail(&conn->node, &achip->conns);
                }
index c68d037de656d68b5808df8174de7bd57bac734e..e8e98ca25ec7e468c95164a791866ae477f74f2b 100644 (file)
@@ -308,10 +308,9 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
  *
  * A gpio_chip with any GPIOs still requested may not be removed.
  */
-int gpiochip_remove(struct gpio_chip *chip)
+void gpiochip_remove(struct gpio_chip *chip)
 {
        unsigned long   flags;
-       int             status = 0;
        unsigned        id;
 
        acpi_gpiochip_remove(chip);
@@ -323,24 +322,15 @@ int gpiochip_remove(struct gpio_chip *chip)
        of_gpiochip_remove(chip);
 
        for (id = 0; id < chip->ngpio; id++) {
-               if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) {
-                       status = -EBUSY;
-                       break;
-               }
-       }
-       if (status == 0) {
-               for (id = 0; id < chip->ngpio; id++)
-                       chip->desc[id].chip = NULL;
-
-               list_del(&chip->list);
+               if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags))
+                       dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
        }
+       for (id = 0; id < chip->ngpio; id++)
+               chip->desc[id].chip = NULL;
 
+       list_del(&chip->list);
        spin_unlock_irqrestore(&gpio_lock, flags);
-
-       if (status == 0)
-               gpiochip_unexport(chip);
-
-       return status;
+       gpiochip_unexport(chip);
 }
 EXPORT_SYMBOL_GPL(gpiochip_remove);
 
@@ -395,30 +385,47 @@ static struct gpio_chip *find_chip_by_name(const char *name)
  */
 
 /**
- * gpiochip_add_chained_irqchip() - adds a chained irqchip to a gpiochip
- * @gpiochip: the gpiochip to add the irqchip to
- * @irqchip: the irqchip to add to the gpiochip
+ * gpiochip_set_chained_irqchip() - sets a chained irqchip to a gpiochip
+ * @gpiochip: the gpiochip to set the irqchip chain to
+ * @irqchip: the irqchip to chain to the gpiochip
  * @parent_irq: the irq number corresponding to the parent IRQ for this
  * chained irqchip
  * @parent_handler: the parent interrupt handler for the accumulated IRQ
- * coming out of the gpiochip
+ * coming out of the gpiochip. If the interrupt is nested rather than
+ * cascaded, pass NULL in this handler argument
  */
 void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
                                  struct irq_chip *irqchip,
                                  int parent_irq,
                                  irq_flow_handler_t parent_handler)
 {
-       if (gpiochip->can_sleep) {
-               chip_err(gpiochip, "you cannot have chained interrupts on a chip that may sleep\n");
+       unsigned int offset;
+
+       if (!gpiochip->irqdomain) {
+               chip_err(gpiochip, "called %s before setting up irqchip\n",
+                        __func__);
                return;
        }
 
-       /*
-        * The parent irqchip is already using the chip_data for this
-        * irqchip, so our callbacks simply use the handler_data.
-        */
-       irq_set_handler_data(parent_irq, gpiochip);
-       irq_set_chained_handler(parent_irq, parent_handler);
+       if (parent_handler) {
+               if (gpiochip->can_sleep) {
+                       chip_err(gpiochip,
+                                "you cannot have chained interrupts on a "
+                                "chip that may sleep\n");
+                       return;
+               }
+               /*
+                * The parent irqchip is already using the chip_data for this
+                * irqchip, so our callbacks simply use the handler_data.
+                */
+               irq_set_handler_data(parent_irq, gpiochip);
+               irq_set_chained_handler(parent_irq, parent_handler);
+       }
+
+       /* Set the parent IRQ for all affected IRQs */
+       for (offset = 0; offset < gpiochip->ngpio; offset++)
+               irq_set_parent(irq_find_mapping(gpiochip->irqdomain, offset),
+                              parent_irq);
 }
 EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
 
@@ -447,7 +454,7 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
        irq_set_lockdep_class(irq, &gpiochip_irq_lock_class);
        irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler);
        /* Chips that can sleep need nested thread handlers */
-       if (chip->can_sleep)
+       if (chip->can_sleep && !chip->irq_not_threaded)
                irq_set_nested_thread(irq, 1);
 #ifdef CONFIG_ARM
        set_irq_flags(irq, IRQF_VALID);
@@ -524,7 +531,8 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
        /* Remove all IRQ mappings and delete the domain */
        if (gpiochip->irqdomain) {
                for (offset = 0; offset < gpiochip->ngpio; offset++)
-                       irq_dispose_mapping(gpiochip->irq_base + offset);
+                       irq_dispose_mapping(
+                               irq_find_mapping(gpiochip->irqdomain, offset));
                irq_domain_remove(gpiochip->irqdomain);
        }
 
@@ -895,12 +903,22 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
  * allows the GPIO chip module to be unloaded as needed (we assume that the
  * GPIO chip driver handles freeing the GPIOs it has requested).
  */
-int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label)
+struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
+                                           const char *label)
 {
-       if (!desc || !desc->chip)
-               return -EINVAL;
+       struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum);
+       int err;
 
-       return __gpiod_request(desc, label);
+       if (IS_ERR(desc)) {
+               chip_err(chip, "failed to get GPIO descriptor\n");
+               return desc;
+       }
+
+       err = __gpiod_request(desc, label);
+       if (err < 0)
+               return ERR_PTR(err);
+
+       return desc;
 }
 EXPORT_SYMBOL_GPL(gpiochip_request_own_desc);
 
@@ -1652,7 +1670,7 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
         * a result. In that case, use platform lookup as a fallback.
         */
        if (!desc || desc == ERR_PTR(-ENOENT)) {
-               dev_dbg(dev, "using lookup tables for GPIO lookup");
+               dev_dbg(dev, "using lookup tables for GPIO lookup\n");
                desc = gpiod_find(dev, con_id, idx, &lookupflags);
        }
 
index 11323dd5196f495e5f2dd24b388c1720f3b6c1ce..e4259c2c1accdf7114cff9498b093c7afae0807a 100644 (file)
@@ -35,7 +35,6 @@
 /*
  * PCI device IDs.
  */
-#define PCI_VENDOR_ID_VMWARE            0x15AD
 #define PCI_DEVICE_ID_VMWARE_SVGA2      0x0405
 
 /*
index a822db5a8338090125688d286ddd8fba0cbbd77d..3318de690e00666bf7da60c84189a1fc54a0a544 100644 (file)
@@ -1069,8 +1069,7 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
        return ret;
 
 err_gpiochip_remove:
-       if (gpiochip_remove(&dev->gc) < 0)
-               hid_err(hdev, "error removing gpio chip\n");
+       gpiochip_remove(&dev->gc);
 err_free_i2c:
        i2c_del_adapter(&dev->adap);
 err_free_dev:
@@ -1089,8 +1088,7 @@ static void cp2112_remove(struct hid_device *hdev)
        struct cp2112_device *dev = hid_get_drvdata(hdev);
 
        sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
-       if (gpiochip_remove(&dev->gc))
-               hid_err(hdev, "unable to remove gpio chip\n");
+       gpiochip_remove(&dev->gc);
        i2c_del_adapter(&dev->adap);
        /* i2c_del_adapter has finished removing all i2c devices from our
         * adapter. Well behaved devices should no longer call our cp2112_xfer
index f00d048aa583591c45aca5a7e683002c5bbc4721..5286d7ce1f9eb77a4a92902317411a7bda069490 100644 (file)
@@ -280,8 +280,8 @@ config SENSORS_K10TEMP
          If you say yes here you get support for the temperature
          sensor(s) inside your CPU. Supported are later revisions of
          the AMD Family 10h and all revisions of the AMD Family 11h,
-         12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity/Kaveri) and
-         16h (Kabini/Mullins) microarchitectures.
+         12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity/Kaveri/Carrizo)
+         and 16h (Kabini/Mullins) microarchitectures.
 
          This driver can also be built as a module.  If so, the module
          will be called k10temp.
@@ -839,6 +839,16 @@ config SENSORS_MCP3021
          This driver can also be built as a module.  If so, the module
          will be called mcp3021.
 
+config SENSORS_MENF21BMC_HWMON
+       tristate "MEN 14F021P00 BMC Hardware Monitoring"
+       depends on MFD_MENF21BMC
+       help
+         Say Y here to include support for the MEN 14F021P00 BMC
+         hardware monitoring.
+
+         This driver can also be built as a module. If so the module
+         will be called menf21bmc_hwmon.
+
 config SENSORS_ADCXX
        tristate "National Semiconductor ADCxxxSxxx"
        depends on SPI_MASTER
@@ -1077,6 +1087,7 @@ config SENSORS_PC87427
 config SENSORS_NTC_THERMISTOR
        tristate "NTC thermistor support from Murata"
        depends on !OF || IIO=n || IIO
+       depends on THERMAL || !THERMAL_OF
        help
          This driver supports NTC thermistors sensor reading and its
          interpretation. The driver can also monitor the temperature and
index be28152c9848806391f58abecce93914f7edf384..c90a7611efaabc26c35c8b66cc90fbe50b4e9c96 100644 (file)
@@ -115,6 +115,7 @@ obj-$(CONFIG_SENSORS_MAX6650)       += max6650.o
 obj-$(CONFIG_SENSORS_MAX6697)  += max6697.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_MCP3021)  += mcp3021.o
+obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
 obj-$(CONFIG_SENSORS_NCT6683)  += nct6683.o
 obj-$(CONFIG_SENSORS_NCT6775)  += nct6775.o
 obj-$(CONFIG_SENSORS_NTC_THERMISTOR)   += ntc_thermistor.o
index d844dc806853d2cbc06760af1a92790dd1392faa..8b6a4f4c8774b10322ecfb56c6bbe992b693dab2 100644 (file)
@@ -6,7 +6,7 @@
  *
  * When the AB8500 thermal warning temperature is reached (threshold cannot
  * be changed by SW), an interrupt is set, and if no further action is taken
- * within a certain time frame, pm_power off will be called.
+ * within a certain time frame, kernel_power_off will be called.
  *
  * When AB8500 thermal shutdown temperature is reached a hardware shutdown of
  * the AB8500 will occur.
@@ -21,6 +21,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/power/ab8500.h>
+#include <linux/reboot.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
 #include "abx500.h"
@@ -106,7 +107,7 @@ static void ab8500_thermal_power_off(struct work_struct *work)
 
        dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n");
 
-       pm_power_off();
+       kernel_power_off();
 }
 
 static ssize_t ab8500_show_name(struct device *dev,
index 126516414c114f309161924e49d5dbef3d60d3bb..f155b83804819ff97455ba5312f62de7f14991e2 100644 (file)
@@ -184,20 +184,18 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
                return -EINVAL;
 
        for_each_child_of_node(client->dev.of_node, node) {
-               const __be32 *property;
-               int len;
+               u32 pval;
                unsigned int channel;
                unsigned int pga = ADS1015_DEFAULT_PGA;
                unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
 
-               property = of_get_property(node, "reg", &len);
-               if (!property || len != sizeof(int)) {
+               if (of_property_read_u32(node, "reg", &pval)) {
                        dev_err(&client->dev, "invalid reg on %s\n",
                                node->full_name);
                        continue;
                }
 
-               channel = be32_to_cpup(property);
+               channel = pval;
                if (channel >= ADS1015_CHANNELS) {
                        dev_err(&client->dev,
                                "invalid channel index %d on %s\n",
@@ -205,20 +203,17 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
                        continue;
                }
 
-               property = of_get_property(node, "ti,gain", &len);
-               if (property && len == sizeof(int)) {
-                       pga = be32_to_cpup(property);
+               if (!of_property_read_u32(node, "ti,gain", &pval)) {
+                       pga = pval;
                        if (pga > 6) {
-                               dev_err(&client->dev,
-                                       "invalid gain on %s\n",
+                               dev_err(&client->dev, "invalid gain on %s\n",
                                        node->full_name);
                                return -EINVAL;
                        }
                }
 
-               property = of_get_property(node, "ti,datarate", &len);
-               if (property && len == sizeof(int)) {
-                       data_rate = be32_to_cpup(property);
+               if (!of_property_read_u32(node, "ti,datarate", &pval)) {
+                       data_rate = pval;
                        if (data_rate > 7) {
                                dev_err(&client->dev,
                                        "invalid data_rate on %s\n",
index d14ab3c45daa32c88c7423f8508b38ed015aad49..692b3f34d88c92fcee79231fc013093f169ff7ce 100644 (file)
@@ -26,7 +26,6 @@
 
 struct da9052_hwmon {
        struct da9052   *da9052;
-       struct device   *class_device;
        struct mutex    hwmon_lock;
 };
 
@@ -190,13 +189,6 @@ static ssize_t da9052_read_vbbat(struct device *dev,
        return sprintf(buf, "%d\n", vbbat_reg_to_mv(ret));
 }
 
-static ssize_t da9052_hwmon_show_name(struct device *dev,
-                                     struct device_attribute *devattr,
-                                     char *buf)
-{
-       return sprintf(buf, "da9052\n");
-}
-
 static ssize_t show_label(struct device *dev,
                          struct device_attribute *devattr, char *buf)
 {
@@ -243,10 +235,7 @@ static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, da9052_read_tjunc, NULL,
 static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL,
                          DA9052_ADC_TJUNC);
 
-static DEVICE_ATTR(name, S_IRUGO, da9052_hwmon_show_name, NULL);
-
-static struct attribute *da9052_attr[] = {
-       &dev_attr_name.attr,
+static struct attribute *da9052_attrs[] = {
        &sensor_dev_attr_in0_input.dev_attr.attr,
        &sensor_dev_attr_in0_label.dev_attr.attr,
        &sensor_dev_attr_in3_input.dev_attr.attr,
@@ -268,54 +257,29 @@ static struct attribute *da9052_attr[] = {
        NULL
 };
 
-static const struct attribute_group da9052_attr_group = {.attrs = da9052_attr};
+ATTRIBUTE_GROUPS(da9052);
 
 static int da9052_hwmon_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct da9052_hwmon *hwmon;
-       int ret;
+       struct device *hwmon_dev;
 
-       hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9052_hwmon),
-                            GFP_KERNEL);
+       hwmon = devm_kzalloc(dev, sizeof(struct da9052_hwmon), GFP_KERNEL);
        if (!hwmon)
                return -ENOMEM;
 
        mutex_init(&hwmon->hwmon_lock);
        hwmon->da9052 = dev_get_drvdata(pdev->dev.parent);
 
-       platform_set_drvdata(pdev, hwmon);
-
-       ret = sysfs_create_group(&pdev->dev.kobj, &da9052_attr_group);
-       if (ret)
-               goto err_mem;
-
-       hwmon->class_device = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(hwmon->class_device)) {
-               ret = PTR_ERR(hwmon->class_device);
-               goto err_sysfs;
-       }
-
-       return 0;
-
-err_sysfs:
-       sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group);
-err_mem:
-       return ret;
-}
-
-static int da9052_hwmon_remove(struct platform_device *pdev)
-{
-       struct da9052_hwmon *hwmon = platform_get_drvdata(pdev);
-
-       hwmon_device_unregister(hwmon->class_device);
-       sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group);
-
-       return 0;
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9052",
+                                                          hwmon,
+                                                          da9052_groups);
+       return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 
 static struct platform_driver da9052_hwmon_driver = {
        .probe = da9052_hwmon_probe,
-       .remove = da9052_hwmon_remove,
        .driver = {
                .name = "da9052-hwmon",
                .owner = THIS_MODULE,
index 35eb7738d7119cf2d706d5cd60323b04e9e76988..9916a3fb4bb9d4eb8221db0ea264eae184dcb42b 100644 (file)
@@ -36,7 +36,6 @@
 
 struct da9055_hwmon {
        struct da9055   *da9055;
-       struct device   *class_device;
        struct mutex    hwmon_lock;
        struct mutex    irq_lock;
        struct completion done;
@@ -200,13 +199,6 @@ static ssize_t da9055_read_tjunc(struct device *dev,
                                                        + 3076332, 10000));
 }
 
-static ssize_t da9055_hwmon_show_name(struct device *dev,
-                                     struct device_attribute *devattr,
-                                     char *buf)
-{
-       return sprintf(buf, "da9055\n");
-}
-
 static ssize_t show_label(struct device *dev,
                          struct device_attribute *devattr, char *buf)
 {
@@ -236,10 +228,7 @@ static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, da9055_read_tjunc, NULL,
 static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL,
                          DA9055_ADC_TJUNC);
 
-static DEVICE_ATTR(name, S_IRUGO, da9055_hwmon_show_name, NULL);
-
-static struct attribute *da9055_attr[] = {
-       &dev_attr_name.attr,
+static struct attribute *da9055_attrs[] = {
        &sensor_dev_attr_in0_input.dev_attr.attr,
        &sensor_dev_attr_in0_label.dev_attr.attr,
        &sensor_dev_attr_in1_input.dev_attr.attr,
@@ -254,15 +243,16 @@ static struct attribute *da9055_attr[] = {
        NULL
 };
 
-static const struct attribute_group da9055_attr_group = {.attrs = da9055_attr};
+ATTRIBUTE_GROUPS(da9055);
 
 static int da9055_hwmon_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct da9055_hwmon *hwmon;
+       struct device *hwmon_dev;
        int hwmon_irq, ret;
 
-       hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9055_hwmon),
-                            GFP_KERNEL);
+       hwmon = devm_kzalloc(dev, sizeof(struct da9055_hwmon), GFP_KERNEL);
        if (!hwmon)
                return -ENOMEM;
 
@@ -272,8 +262,6 @@ static int da9055_hwmon_probe(struct platform_device *pdev)
        init_completion(&hwmon->done);
        hwmon->da9055 = dev_get_drvdata(pdev->dev.parent);
 
-       platform_set_drvdata(pdev, hwmon);
-
        hwmon_irq = platform_get_irq_byname(pdev, "HWMON");
        if (hwmon_irq < 0)
                return hwmon_irq;
@@ -288,36 +276,14 @@ static int da9055_hwmon_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = sysfs_create_group(&pdev->dev.kobj, &da9055_attr_group);
-       if (ret)
-               return ret;
-
-       hwmon->class_device = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(hwmon->class_device)) {
-               ret = PTR_ERR(hwmon->class_device);
-               goto err;
-       }
-
-       return 0;
-
-err:
-       sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group);
-       return ret;
-}
-
-static int da9055_hwmon_remove(struct platform_device *pdev)
-{
-       struct da9055_hwmon *hwmon = platform_get_drvdata(pdev);
-
-       sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group);
-       hwmon_device_unregister(hwmon->class_device);
-
-       return 0;
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9055",
+                                                          hwmon,
+                                                          da9055_groups);
+       return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 
 static struct platform_driver da9055_hwmon_driver = {
        .probe = da9055_hwmon_probe,
-       .remove = da9055_hwmon_remove,
        .driver = {
                .name = "da9055-hwmon",
                .owner = THIS_MODULE,
index f7b46f68ef43aeaa838fdee97227b097b8bbc453..1e7bdcdcb2955d22e0c9492d55b29eb11255cd1b 100644 (file)
@@ -33,6 +33,9 @@ static bool force;
 module_param(force, bool, 0444);
 MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
 
+/* Provide lock for writing to NB_SMU_IND_ADDR */
+static DEFINE_MUTEX(nb_smu_ind_mutex);
+
 /* CPUID function 0x80000001, ebx */
 #define CPUID_PKGTYPE_MASK     0xf0000000
 #define CPUID_PKGTYPE_F                0x00000000
@@ -51,13 +54,38 @@ MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
 #define REG_NORTHBRIDGE_CAPABILITIES   0xe8
 #define  NB_CAP_HTC                    0x00000400
 
+/*
+ * For F15h M60h, functionality of REG_REPORTED_TEMPERATURE
+ * has been moved to D0F0xBC_xD820_0CA4 [Reported Temperature
+ * Control]
+ */
+#define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET    0xd8200ca4
+#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F3       0x1573
+
+static void amd_nb_smu_index_read(struct pci_dev *pdev, unsigned int devfn,
+                                 int offset, u32 *val)
+{
+       mutex_lock(&nb_smu_ind_mutex);
+       pci_bus_write_config_dword(pdev->bus, devfn,
+                                  0xb8, offset);
+       pci_bus_read_config_dword(pdev->bus, devfn,
+                                 0xbc, val);
+       mutex_unlock(&nb_smu_ind_mutex);
+}
+
 static ssize_t show_temp(struct device *dev,
                         struct device_attribute *attr, char *buf)
 {
        u32 regval;
-
-       pci_read_config_dword(to_pci_dev(dev),
-                             REG_REPORTED_TEMPERATURE, &regval);
+       struct pci_dev *pdev = dev_get_drvdata(dev);
+
+       if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model == 0x60) {
+               amd_nb_smu_index_read(pdev, PCI_DEVFN(0, 0),
+                                     F15H_M60H_REPORTED_TEMP_CTRL_OFFSET,
+                                     &regval);
+       } else {
+               pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, &regval);
+       }
        return sprintf(buf, "%u\n", (regval >> 21) * 125);
 }
 
@@ -75,7 +103,7 @@ static ssize_t show_temp_crit(struct device *dev,
        u32 regval;
        int value;
 
-       pci_read_config_dword(to_pci_dev(dev),
+       pci_read_config_dword(dev_get_drvdata(dev),
                              REG_HARDWARE_THERMAL_CONTROL, &regval);
        value = ((regval >> 16) & 0x7f) * 500 + 52000;
        if (show_hyst)
@@ -83,17 +111,43 @@ static ssize_t show_temp_crit(struct device *dev,
        return sprintf(buf, "%d\n", value);
 }
 
-static ssize_t show_name(struct device *dev,
-                        struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "k10temp\n");
-}
-
 static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
 static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL);
 static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0);
 static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit, NULL, 1);
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static umode_t k10temp_is_visible(struct kobject *kobj,
+                                 struct attribute *attr, int index)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct pci_dev *pdev = dev_get_drvdata(dev);
+
+       if (index >= 2) {
+               u32 reg_caps, reg_htc;
+
+               pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES,
+                                     &reg_caps);
+               pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL,
+                                     &reg_htc);
+               if (!(reg_caps & NB_CAP_HTC) || !(reg_htc & HTC_ENABLE))
+                       return 0;
+       }
+       return attr->mode;
+}
+
+static struct attribute *k10temp_attrs[] = {
+       &dev_attr_temp1_input.attr,
+       &dev_attr_temp1_max.attr,
+       &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group k10temp_group = {
+       .attrs = k10temp_attrs,
+       .is_visible = k10temp_is_visible,
+};
+__ATTRIBUTE_GROUPS(k10temp);
 
 static bool has_erratum_319(struct pci_dev *pdev)
 {
@@ -132,76 +186,23 @@ static bool has_erratum_319(struct pci_dev *pdev)
 static int k10temp_probe(struct pci_dev *pdev,
                                   const struct pci_device_id *id)
 {
-       struct device *hwmon_dev;
-       u32 reg_caps, reg_htc;
        int unreliable = has_erratum_319(pdev);
-       int err;
-
-       if (unreliable && !force) {
-               dev_err(&pdev->dev,
-                       "unreliable CPU thermal sensor; monitoring disabled\n");
-               err = -ENODEV;
-               goto exit;
-       }
-
-       err = device_create_file(&pdev->dev, &dev_attr_temp1_input);
-       if (err)
-               goto exit;
-       err = device_create_file(&pdev->dev, &dev_attr_temp1_max);
-       if (err)
-               goto exit_remove;
-
-       pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES, &reg_caps);
-       pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, &reg_htc);
-       if ((reg_caps & NB_CAP_HTC) && (reg_htc & HTC_ENABLE)) {
-               err = device_create_file(&pdev->dev,
-                               &sensor_dev_attr_temp1_crit.dev_attr);
-               if (err)
-                       goto exit_remove;
-               err = device_create_file(&pdev->dev,
-                               &sensor_dev_attr_temp1_crit_hyst.dev_attr);
-               if (err)
-                       goto exit_remove;
-       }
-
-       err = device_create_file(&pdev->dev, &dev_attr_name);
-       if (err)
-               goto exit_remove;
-
-       hwmon_dev = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(hwmon_dev)) {
-               err = PTR_ERR(hwmon_dev);
-               goto exit_remove;
-       }
-       pci_set_drvdata(pdev, hwmon_dev);
+       struct device *dev = &pdev->dev;
+       struct device *hwmon_dev;
 
-       if (unreliable && force)
-               dev_warn(&pdev->dev,
+       if (unreliable) {
+               if (!force) {
+                       dev_err(dev,
+                               "unreliable CPU thermal sensor; monitoring disabled\n");
+                       return -ENODEV;
+               }
+               dev_warn(dev,
                         "unreliable CPU thermal sensor; check erratum 319\n");
-       return 0;
-
-exit_remove:
-       device_remove_file(&pdev->dev, &dev_attr_name);
-       device_remove_file(&pdev->dev, &dev_attr_temp1_input);
-       device_remove_file(&pdev->dev, &dev_attr_temp1_max);
-       device_remove_file(&pdev->dev,
-                          &sensor_dev_attr_temp1_crit.dev_attr);
-       device_remove_file(&pdev->dev,
-                          &sensor_dev_attr_temp1_crit_hyst.dev_attr);
-exit:
-       return err;
-}
+       }
 
-static void k10temp_remove(struct pci_dev *pdev)
-{
-       hwmon_device_unregister(pci_get_drvdata(pdev));
-       device_remove_file(&pdev->dev, &dev_attr_name);
-       device_remove_file(&pdev->dev, &dev_attr_temp1_input);
-       device_remove_file(&pdev->dev, &dev_attr_temp1_max);
-       device_remove_file(&pdev->dev,
-                          &sensor_dev_attr_temp1_crit.dev_attr);
-       device_remove_file(&pdev->dev,
-                          &sensor_dev_attr_temp1_crit_hyst.dev_attr);
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev, "k10temp", pdev,
+                                                          k10temp_groups);
+       return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 
 static const struct pci_device_id k10temp_id_table[] = {
@@ -211,6 +212,7 @@ static const struct pci_device_id k10temp_id_table[] = {
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
+       { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
        {}
@@ -221,7 +223,6 @@ static struct pci_driver k10temp_driver = {
        .name = "k10temp",
        .id_table = k10temp_id_table,
        .probe = k10temp_probe,
-       .remove = k10temp_remove,
 };
 
 module_pci_driver(k10temp_driver);
diff --git a/drivers/hwmon/menf21bmc_hwmon.c b/drivers/hwmon/menf21bmc_hwmon.c
new file mode 100644 (file)
index 0000000..c92229d
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *  MEN 14F021P00 Board Management Controller (BMC) hwmon driver.
+ *
+ *  This is the core hwmon driver of the MEN 14F021P00 BMC.
+ *  The BMC monitors the board voltages which can be access with this
+ *  driver through sysfs.
+ *
+ *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ *  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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+#define DRV_NAME  "menf21bmc_hwmon"
+
+#define BMC_VOLT_COUNT 5
+#define MENF21BMC_V33  0
+#define MENF21BMC_V5   1
+#define MENF21BMC_V12  2
+#define MENF21BMC_V5_SB        3
+#define MENF21BMC_VBAT 4
+
+#define IDX_TO_VOLT_MIN_CMD(idx) (0x40 + idx)
+#define IDX_TO_VOLT_MAX_CMD(idx) (0x50 + idx)
+#define IDX_TO_VOLT_INP_CMD(idx) (0x60 + idx)
+
+struct menf21bmc_hwmon {
+       bool valid;
+       struct i2c_client *i2c_client;
+       unsigned long last_update;
+       int in_val[BMC_VOLT_COUNT];
+       int in_min[BMC_VOLT_COUNT];
+       int in_max[BMC_VOLT_COUNT];
+};
+
+static const char *const input_names[] = {
+       [MENF21BMC_V33]         = "MON_3_3V",
+       [MENF21BMC_V5]          = "MON_5V",
+       [MENF21BMC_V12]         = "MON_12V",
+       [MENF21BMC_V5_SB]       = "5V_STANDBY",
+       [MENF21BMC_VBAT]        = "VBAT"
+};
+
+static struct menf21bmc_hwmon *menf21bmc_hwmon_update(struct device *dev)
+{
+       int i;
+       int val;
+       struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
+       struct menf21bmc_hwmon *data_ret = drv_data;
+
+       if (time_after(jiffies, drv_data->last_update + HZ)
+           || !drv_data->valid) {
+               for (i = 0; i < BMC_VOLT_COUNT; i++) {
+                       val = i2c_smbus_read_word_data(drv_data->i2c_client,
+                                                      IDX_TO_VOLT_INP_CMD(i));
+                       if (val < 0) {
+                               data_ret = ERR_PTR(val);
+                               goto abort;
+                       }
+                       drv_data->in_val[i] = val;
+               }
+               drv_data->last_update = jiffies;
+               drv_data->valid = true;
+       }
+abort:
+       return data_ret;
+}
+
+static int menf21bmc_hwmon_get_volt_limits(struct menf21bmc_hwmon *drv_data)
+{
+       int i, val;
+
+       for (i = 0; i < BMC_VOLT_COUNT; i++) {
+               val = i2c_smbus_read_word_data(drv_data->i2c_client,
+                                              IDX_TO_VOLT_MIN_CMD(i));
+               if (val < 0)
+                       return val;
+
+               drv_data->in_min[i] = val;
+
+               val = i2c_smbus_read_word_data(drv_data->i2c_client,
+                                              IDX_TO_VOLT_MAX_CMD(i));
+               if (val < 0)
+                       return val;
+
+               drv_data->in_max[i] = val;
+       }
+       return 0;
+}
+
+static ssize_t
+show_label(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%s\n", input_names[attr->index]);
+}
+
+static ssize_t
+show_in(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct menf21bmc_hwmon *drv_data = menf21bmc_hwmon_update(dev);
+
+       if (IS_ERR(drv_data))
+               return PTR_ERR(drv_data);
+
+       return sprintf(buf, "%d\n", drv_data->in_val[attr->index]);
+}
+
+static ssize_t
+show_min(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", drv_data->in_min[attr->index]);
+}
+
+static ssize_t
+show_max(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", drv_data->in_max[attr->index]);
+}
+
+#define create_voltage_sysfs(idx)                      \
+static SENSOR_DEVICE_ATTR(in##idx##_input, S_IRUGO,    \
+                       show_in, NULL, idx);            \
+static SENSOR_DEVICE_ATTR(in##idx##_min, S_IRUGO,      \
+                       show_min, NULL, idx);           \
+static SENSOR_DEVICE_ATTR(in##idx##_max, S_IRUGO,      \
+                       show_max, NULL, idx);           \
+static SENSOR_DEVICE_ATTR(in##idx##_label, S_IRUGO,    \
+                       show_label, NULL, idx);
+
+create_voltage_sysfs(0);
+create_voltage_sysfs(1);
+create_voltage_sysfs(2);
+create_voltage_sysfs(3);
+create_voltage_sysfs(4);
+
+static struct attribute *menf21bmc_hwmon_attrs[] = {
+       &sensor_dev_attr_in0_input.dev_attr.attr,
+       &sensor_dev_attr_in0_min.dev_attr.attr,
+       &sensor_dev_attr_in0_max.dev_attr.attr,
+       &sensor_dev_attr_in0_label.dev_attr.attr,
+
+       &sensor_dev_attr_in1_input.dev_attr.attr,
+       &sensor_dev_attr_in1_min.dev_attr.attr,
+       &sensor_dev_attr_in1_max.dev_attr.attr,
+       &sensor_dev_attr_in1_label.dev_attr.attr,
+
+       &sensor_dev_attr_in2_input.dev_attr.attr,
+       &sensor_dev_attr_in2_min.dev_attr.attr,
+       &sensor_dev_attr_in2_max.dev_attr.attr,
+       &sensor_dev_attr_in2_label.dev_attr.attr,
+
+       &sensor_dev_attr_in3_input.dev_attr.attr,
+       &sensor_dev_attr_in3_min.dev_attr.attr,
+       &sensor_dev_attr_in3_max.dev_attr.attr,
+       &sensor_dev_attr_in3_label.dev_attr.attr,
+
+       &sensor_dev_attr_in4_input.dev_attr.attr,
+       &sensor_dev_attr_in4_min.dev_attr.attr,
+       &sensor_dev_attr_in4_max.dev_attr.attr,
+       &sensor_dev_attr_in4_label.dev_attr.attr,
+       NULL
+};
+
+ATTRIBUTE_GROUPS(menf21bmc_hwmon);
+
+static int menf21bmc_hwmon_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct menf21bmc_hwmon *drv_data;
+       struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent);
+       struct device *hwmon_dev;
+
+       drv_data = devm_kzalloc(&pdev->dev, sizeof(struct menf21bmc_hwmon),
+                               GFP_KERNEL);
+       if (!drv_data)
+               return -ENOMEM;
+
+       drv_data->i2c_client = i2c_client;
+
+       ret = menf21bmc_hwmon_get_volt_limits(drv_data);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to read sensor limits");
+               return ret;
+       }
+
+       hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
+                                                  "menf21bmc", drv_data,
+                                                  menf21bmc_hwmon_groups);
+       if (IS_ERR(hwmon_dev))
+               return PTR_ERR(hwmon_dev);
+
+       dev_info(&pdev->dev, "MEN 14F021P00 BMC hwmon device enabled");
+
+       return 0;
+}
+
+static struct platform_driver menf21bmc_hwmon = {
+       .probe          = menf21bmc_hwmon_probe,
+       .driver         = {
+               .name           = DRV_NAME,
+               .owner          = THIS_MODULE,
+       },
+};
+
+module_platform_driver(menf21bmc_hwmon);
+
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_DESCRIPTION("MEN 14F021P00 BMC hwmon");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:menf21bmc_hwmon");
index bd410722cd4bacef0eae6401e5c12f044dd94102..4ff89b2482e48ae12a4f48d2dd269613e1f2c58b 100644 (file)
@@ -38,6 +38,7 @@
 
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
+#include <linux/thermal.h>
 
 struct ntc_compensation {
        int             temp_c;
@@ -182,6 +183,7 @@ struct ntc_data {
        struct device *dev;
        int n_comp;
        char name[PLATFORM_NAME_SIZE];
+       struct thermal_zone_device *tz;
 };
 
 #if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO)
@@ -428,6 +430,20 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data)
        return -EINVAL;
 }
 
+static int ntc_read_temp(void *dev, long *temp)
+{
+       struct ntc_data *data = dev_get_drvdata(dev);
+       int ohm;
+
+       ohm = ntc_thermistor_get_ohm(data);
+       if (ohm < 0)
+               return ohm;
+
+       *temp = get_temp_mc(data, ohm);
+
+       return 0;
+}
+
 static ssize_t ntc_show_name(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
@@ -562,6 +578,13 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
        dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n",
                                                                pdev_id->name);
 
+       data->tz = thermal_zone_of_sensor_register(data->dev, 0, data->dev,
+                                               ntc_read_temp, NULL);
+       if (IS_ERR(data->tz)) {
+               dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n");
+               data->tz = NULL;
+       }
+
        return 0;
 err_after_sysfs:
        sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
@@ -578,6 +601,8 @@ static int ntc_thermistor_remove(struct platform_device *pdev)
        sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
        ntc_iio_channel_release(pdata);
 
+       thermal_zone_of_sensor_unregister(data->dev, data->tz);
+
        return 0;
 }
 
index bd89e87bd6ae3297b655609e92f6e1f62c98afbf..221f0931bf1c26af626a6720085b5a0460a6540f 100644 (file)
@@ -100,8 +100,6 @@ static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
 
 struct smsc47b397_data {
        unsigned short addr;
-       const char *name;
-       struct device *hwmon_dev;
        struct mutex lock;
 
        struct mutex update_lock;
@@ -202,15 +200,7 @@ static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
 static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
 static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3);
 
-static ssize_t show_name(struct device *dev, struct device_attribute
-                        *devattr, char *buf)
-{
-       struct smsc47b397_data *data = dev_get_drvdata(dev);
-       return sprintf(buf, "%s\n", data->name);
-}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-
-static struct attribute *smsc47b397_attributes[] = {
+static struct attribute *smsc47b397_attrs[] = {
        &sensor_dev_attr_temp1_input.dev_attr.attr,
        &sensor_dev_attr_temp2_input.dev_attr.attr,
        &sensor_dev_attr_temp3_input.dev_attr.attr,
@@ -220,23 +210,10 @@ static struct attribute *smsc47b397_attributes[] = {
        &sensor_dev_attr_fan3_input.dev_attr.attr,
        &sensor_dev_attr_fan4_input.dev_attr.attr,
 
-       &dev_attr_name.attr,
        NULL
 };
 
-static const struct attribute_group smsc47b397_group = {
-       .attrs = smsc47b397_attributes,
-};
-
-static int smsc47b397_remove(struct platform_device *pdev)
-{
-       struct smsc47b397_data *data = platform_get_drvdata(pdev);
-
-       hwmon_device_unregister(data->hwmon_dev);
-       sysfs_remove_group(&pdev->dev.kobj, &smsc47b397_group);
-
-       return 0;
-}
+ATTRIBUTE_GROUPS(smsc47b397);
 
 static int smsc47b397_probe(struct platform_device *pdev);
 
@@ -246,15 +223,14 @@ static struct platform_driver smsc47b397_driver = {
                .name   = DRVNAME,
        },
        .probe          = smsc47b397_probe,
-       .remove         = smsc47b397_remove,
 };
 
 static int smsc47b397_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct smsc47b397_data *data;
+       struct device *hwmon_dev;
        struct resource *res;
-       int err = 0;
 
        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
        if (!devm_request_region(dev, res->start, SMSC_EXTENT,
@@ -270,26 +246,13 @@ static int smsc47b397_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        data->addr = res->start;
-       data->name = "smsc47b397";
        mutex_init(&data->lock);
        mutex_init(&data->update_lock);
-       platform_set_drvdata(pdev, data);
-
-       err = sysfs_create_group(&dev->kobj, &smsc47b397_group);
-       if (err)
-               return err;
 
-       data->hwmon_dev = hwmon_device_register(dev);
-       if (IS_ERR(data->hwmon_dev)) {
-               err = PTR_ERR(data->hwmon_dev);
-               goto error_remove;
-       }
-
-       return 0;
-
-error_remove:
-       sysfs_remove_group(&dev->kobj, &smsc47b397_group);
-       return err;
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev, "smsc47b397",
+                                                          data,
+                                                          smsc47b397_groups);
+       return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 
 static int __init smsc47b397_device_add(unsigned short address)
index ccfbbab82a157da532fb039f7032d0de38680d0f..2f90ac6a7f794ad8e79577a2c5e59ed853bf413f 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/irqflags.h>
 #include <linux/rwsem.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
 #include <linux/acpi.h>
 #include <linux/jump_label.h>
 #include <asm/uaccess.h>
@@ -643,10 +644,13 @@ static int i2c_device_probe(struct device *dev)
        if (status < 0)
                return status;
 
-       acpi_dev_pm_attach(&client->dev, true);
-       status = driver->probe(client, i2c_match_id(driver->id_table, client));
-       if (status)
-               acpi_dev_pm_detach(&client->dev, true);
+       status = dev_pm_domain_attach(&client->dev, true);
+       if (status != -EPROBE_DEFER) {
+               status = driver->probe(client, i2c_match_id(driver->id_table,
+                                       client));
+               if (status)
+                       dev_pm_domain_detach(&client->dev, true);
+       }
 
        return status;
 }
@@ -666,7 +670,7 @@ static int i2c_device_remove(struct device *dev)
                status = driver->remove(client);
        }
 
-       acpi_dev_pm_detach(&client->dev, true);
+       dev_pm_domain_detach(&client->dev, true);
        return status;
 }
 
index 5ef7fcf0e2509b8196cf69a993247febae4c20b7..b97ed443e0a49bd02288f8d74cdcb669af730f17 100644 (file)
@@ -251,9 +251,7 @@ static void adp5588_gpio_remove(struct adp5588_kpad *kpad)
                        dev_warn(dev, "teardown failed %d\n", error);
        }
 
-       error = gpiochip_remove(&kpad->gc);
-       if (error)
-               dev_warn(dev, "gpiochip_remove failed %d\n", error);
+       gpiochip_remove(&kpad->gc);
 }
 #else
 static inline int adp5588_gpio_add(struct adp5588_kpad *kpad)
index 6329549bf6add071e19c6c07b16e5db0dd8131c7..a45267729dfcfde8bcc6b1799b69b53de14f9f58 100644 (file)
@@ -567,9 +567,7 @@ static void adp5589_gpio_remove(struct adp5589_kpad *kpad)
                        dev_warn(dev, "teardown failed %d\n", error);
        }
 
-       error = gpiochip_remove(&kpad->gc);
-       if (error)
-               dev_warn(dev, "gpiochip_remove failed %d\n", error);
+       gpiochip_remove(&kpad->gc);
 }
 #else
 static inline int adp5589_gpio_add(struct adp5589_kpad *kpad)
index fbfdc10573be00bd471b440be7860b24f261caaa..1af28b06c713b80aafc1edbf6ff31cc49d5a5fe5 100644 (file)
@@ -365,12 +365,13 @@ static const struct xenbus_device_id xenkbd_ids[] = {
        { "" }
 };
 
-static DEFINE_XENBUS_DRIVER(xenkbd, ,
+static struct xenbus_driver xenkbd_driver = {
+       .ids = xenkbd_ids,
        .probe = xenkbd_probe,
        .remove = xenkbd_remove,
        .resume = xenkbd_resume,
        .otherend_changed = xenkbd_backend_changed,
-);
+};
 
 static int __init xenkbd_init(void)
 {
index fce590677b7b0e4515dbcb0b938569ecbac1e9d6..1eb9d3c20886fc3cfd119921c593b2ddb771cbd2 100644 (file)
@@ -470,14 +470,10 @@ static int ad7879_gpio_add(struct ad7879 *ts,
 static void ad7879_gpio_remove(struct ad7879 *ts)
 {
        const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);
-       int ret;
 
-       if (pdata->gpio_export) {
-               ret = gpiochip_remove(&ts->gc);
-               if (ret)
-                       dev_err(ts->dev, "failed to remove gpio %d\n",
-                               ts->gc.base);
-       }
+       if (pdata->gpio_export)
+               gpiochip_remove(&ts->gc);
+
 }
 #else
 static inline int ad7879_gpio_add(struct ad7879 *ts,
index 78d4ff551590d44612d17e9d05f2cc5f36e48c1e..b21f12f1766dea301c7b6749d0e77bc6bfaa3ebd 100644 (file)
@@ -118,3 +118,10 @@ config IRQ_CROSSBAR
          The primary irqchip invokes the crossbar's callback which inturn allocates
          a free irq and configures the IP. Thus the peripheral interrupts are
          routed to one of the free irqchip interrupt lines.
+
+config KEYSTONE_IRQ
+       tristate "Keystone 2 IRQ controller IP"
+       depends on ARCH_KEYSTONE
+       help
+               Support for Texas Instruments Keystone 2 IRQ controller IP which
+               is part of the Keystone 2 IPC mechanism
index d0a2613c73bc3fbf17ca2b4063f6dd344ca6bb4f..173bb5fa2cc945f6a5c60e7532754519b377ae17 100644 (file)
@@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP)                   += irqchip.o
 
 obj-$(CONFIG_ARCH_BCM2835)             += irq-bcm2835.o
 obj-$(CONFIG_ARCH_EXYNOS)              += exynos-combiner.o
+obj-$(CONFIG_ARCH_HIP04)               += irq-hip04.o
 obj-$(CONFIG_ARCH_MMP)                 += irq-mmp.o
 obj-$(CONFIG_ARCH_MVEBU)               += irq-armada-370-xp.o
 obj-$(CONFIG_ARCH_MXS)                 += irq-mxs.o
@@ -34,4 +35,6 @@ obj-$(CONFIG_TB10X_IRQC)              += irq-tb10x.o
 obj-$(CONFIG_XTENSA)                   += irq-xtensa-pic.o
 obj-$(CONFIG_XTENSA_MX)                        += irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)             += irq-crossbar.o
-obj-$(CONFIG_BRCMSTB_L2_IRQ)           += irq-brcmstb-l2.o
+obj-$(CONFIG_BRCMSTB_L2_IRQ)           += irq-brcmstb-l2.o \
+                                          irq-bcm7120-l2.o
+obj-$(CONFIG_KEYSTONE_IRQ)             += irq-keystone.o
index 574aba0eba4e0c64d2c6eb13ee4c2119c0ef1873..3e238cd049e602540984d770e7ba1c930841703a 100644 (file)
@@ -136,6 +136,10 @@ static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
        struct msi_msg msg;
        int virq, hwirq;
 
+       /* We support MSI, but not MSI-X */
+       if (desc->msi_attrib.is_msix)
+               return -EINVAL;
+
        hwirq = armada_370_xp_alloc_msi();
        if (hwirq < 0)
                return hwirq;
@@ -166,15 +170,6 @@ static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
        armada_370_xp_free_msi(hwirq);
 }
 
-static int armada_370_xp_check_msi_device(struct msi_chip *chip, struct pci_dev *dev,
-                                         int nvec, int type)
-{
-       /* We support MSI, but not MSI-X */
-       if (type == PCI_CAP_ID_MSI)
-               return 0;
-       return -EINVAL;
-}
-
 static struct irq_chip armada_370_xp_msi_irq_chip = {
        .name = "armada_370_xp_msi_irq",
        .irq_enable = unmask_msi_irq,
@@ -213,7 +208,6 @@ static int armada_370_xp_msi_init(struct device_node *node,
 
        msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
        msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
-       msi_chip->check_device = armada_370_xp_check_msi_device;
        msi_chip->of_node = node;
 
        armada_370_xp_msi_domain =
@@ -393,13 +387,15 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
                if (!(msimask & BIT(msinr)))
                        continue;
 
-               irq = irq_find_mapping(armada_370_xp_msi_domain,
-                                      msinr - 16);
-
-               if (is_chained)
+               if (is_chained) {
+                       irq = irq_find_mapping(armada_370_xp_msi_domain,
+                                              msinr - 16);
                        generic_handle_irq(irq);
-               else
-                       handle_IRQ(irq, regs);
+               } else {
+                       irq = msinr - 16;
+                       handle_domain_irq(armada_370_xp_msi_domain,
+                                         irq, regs);
+               }
        }
 }
 #else
@@ -444,9 +440,8 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
                        break;
 
                if (irqnr > 1) {
-                       irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
-                                       irqnr);
-                       handle_IRQ(irqnr, regs);
+                       handle_domain_irq(armada_370_xp_mpic_domain,
+                                         irqnr, regs);
                        continue;
                }
 
index a82869e9fb264eeb131a5d6c60a77b33438118e9..9a2cf3c1a3a57810f283fd55fd3bd15d78323f81 100644 (file)
@@ -68,12 +68,10 @@ aic_handle(struct pt_regs *regs)
        irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR);
        irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR);
 
-       irqnr = irq_find_mapping(aic_domain, irqnr);
-
        if (!irqstat)
                irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
        else
-               handle_IRQ(irqnr, regs);
+               handle_domain_irq(aic_domain, irqnr, regs);
 }
 
 static int aic_retrigger(struct irq_data *d)
index edb2270815246cf8aece1406fdab20ec40de2b53..a11aae8fb006abfbe6782b85dd3a1eb10ea54162 100644 (file)
@@ -78,12 +78,10 @@ aic5_handle(struct pt_regs *regs)
        irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR);
        irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR);
 
-       irqnr = irq_find_mapping(aic5_domain, irqnr);
-
        if (!irqstat)
                irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
        else
-               handle_IRQ(irqnr, regs);
+               handle_domain_irq(aic5_domain, irqnr, regs);
 }
 
 static void aic5_mask(struct irq_data *d)
@@ -297,6 +295,7 @@ static void __init sama5d3_aic_irq_fixup(struct device_node *root)
 
 static const struct of_device_id __initdata aic5_irq_fixups[] = {
        { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
+       { .compatible = "atmel,sama5d4", .data = sama5d3_aic_irq_fixup },
        { /* sentinel */ },
 };
 
@@ -343,7 +342,7 @@ static int __init aic5_of_init(struct device_node *node,
        return 0;
 }
 
-#define NR_SAMA5D3_IRQS                50
+#define NR_SAMA5D3_IRQS                48
 
 static int __init sama5d3_aic5_of_init(struct device_node *node,
                                       struct device_node *parent)
@@ -351,3 +350,12 @@ static int __init sama5d3_aic5_of_init(struct device_node *node,
        return aic5_of_init(node, parent, NR_SAMA5D3_IRQS);
 }
 IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init);
+
+#define NR_SAMA5D4_IRQS                68
+
+static int __init sama5d4_aic5_of_init(struct device_node *node,
+                                      struct device_node *parent)
+{
+       return aic5_of_init(node, parent, NR_SAMA5D4_IRQS);
+}
+IRQCHIP_DECLARE(sama5d4_aic5, "atmel,sama5d4-aic", sama5d4_aic5_of_init);
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c
new file mode 100644 (file)
index 0000000..b9f4fb8
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Broadcom BCM7120 style Level 2 interrupt controller driver
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * 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 pr_fmt(fmt)    KBUILD_MODNAME  ": " fmt
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/reboot.h>
+#include <linux/irqchip/chained_irq.h>
+
+#include "irqchip.h"
+
+#include <asm/mach/irq.h>
+
+/* Register offset in the L2 interrupt controller */
+#define IRQEN          0x00
+#define IRQSTAT                0x04
+
+struct bcm7120_l2_intc_data {
+       void __iomem *base;
+       struct irq_domain *domain;
+       bool can_wake;
+       u32 irq_fwd_mask;
+       u32 irq_map_mask;
+       u32 saved_mask;
+};
+
+static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
+{
+       struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       u32 status;
+
+       chained_irq_enter(chip, desc);
+
+       status = __raw_readl(b->base + IRQSTAT);
+
+       if (status == 0) {
+               do_bad_IRQ(irq, desc);
+               goto out;
+       }
+
+       do {
+               irq = ffs(status) - 1;
+               status &= ~(1 << irq);
+               generic_handle_irq(irq_find_mapping(b->domain, irq));
+       } while (status);
+
+out:
+       chained_irq_exit(chip, desc);
+}
+
+static void bcm7120_l2_intc_suspend(struct irq_data *d)
+{
+       struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+       struct bcm7120_l2_intc_data *b = gc->private;
+       u32 reg;
+
+       irq_gc_lock(gc);
+       /* Save the current mask and the interrupt forward mask */
+       b->saved_mask = __raw_readl(b->base) | b->irq_fwd_mask;
+       if (b->can_wake) {
+               reg = b->saved_mask | gc->wake_active;
+               __raw_writel(reg, b->base);
+       }
+       irq_gc_unlock(gc);
+}
+
+static void bcm7120_l2_intc_resume(struct irq_data *d)
+{
+       struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+       struct bcm7120_l2_intc_data *b = gc->private;
+
+       /* Restore the saved mask */
+       irq_gc_lock(gc);
+       __raw_writel(b->saved_mask, b->base);
+       irq_gc_unlock(gc);
+}
+
+static int bcm7120_l2_intc_init_one(struct device_node *dn,
+                                       struct bcm7120_l2_intc_data *data,
+                                       int irq, const __be32 *map_mask)
+{
+       int parent_irq;
+
+       parent_irq = irq_of_parse_and_map(dn, irq);
+       if (parent_irq < 0) {
+               pr_err("failed to map interrupt %d\n", irq);
+               return parent_irq;
+       }
+
+       data->irq_map_mask |= be32_to_cpup(map_mask + irq);
+
+       irq_set_handler_data(parent_irq, data);
+       irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle);
+
+       return 0;
+}
+
+int __init bcm7120_l2_intc_of_init(struct device_node *dn,
+                                       struct device_node *parent)
+{
+       unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+       struct bcm7120_l2_intc_data *data;
+       struct irq_chip_generic *gc;
+       struct irq_chip_type *ct;
+       const __be32 *map_mask;
+       int num_parent_irqs;
+       int ret = 0, len, irq;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->base = of_iomap(dn, 0);
+       if (!data->base) {
+               pr_err("failed to remap intc L2 registers\n");
+               ret = -ENOMEM;
+               goto out_free;
+       }
+
+       if (of_property_read_u32(dn, "brcm,int-fwd-mask", &data->irq_fwd_mask))
+               data->irq_fwd_mask = 0;
+
+       /* Enable all interrupt specified in the interrupt forward mask and have
+        * the other disabled
+        */
+       __raw_writel(data->irq_fwd_mask, data->base + IRQEN);
+
+       num_parent_irqs = of_irq_count(dn);
+       if (num_parent_irqs <= 0) {
+               pr_err("invalid number of parent interrupts\n");
+               ret = -ENOMEM;
+               goto out_unmap;
+       }
+
+       map_mask = of_get_property(dn, "brcm,int-map-mask", &len);
+       if (!map_mask || (len != (sizeof(*map_mask) * num_parent_irqs))) {
+               pr_err("invalid brcm,int-map-mask property\n");
+               ret = -EINVAL;
+               goto out_unmap;
+       }
+
+       for (irq = 0; irq < num_parent_irqs; irq++) {
+               ret = bcm7120_l2_intc_init_one(dn, data, irq, map_mask);
+               if (ret)
+                       goto out_unmap;
+       }
+
+       data->domain = irq_domain_add_linear(dn, 32,
+                                       &irq_generic_chip_ops, NULL);
+       if (!data->domain) {
+               ret = -ENOMEM;
+               goto out_unmap;
+       }
+
+       ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
+                               dn->full_name, handle_level_irq, clr, 0,
+                               IRQ_GC_INIT_MASK_CACHE);
+       if (ret) {
+               pr_err("failed to allocate generic irq chip\n");
+               goto out_free_domain;
+       }
+
+       gc = irq_get_domain_generic_chip(data->domain, 0);
+       gc->unused = 0xfffffff & ~data->irq_map_mask;
+       gc->reg_base = data->base;
+       gc->private = data;
+       ct = gc->chip_types;
+
+       ct->regs.mask = IRQEN;
+       ct->chip.irq_mask = irq_gc_mask_clr_bit;
+       ct->chip.irq_unmask = irq_gc_mask_set_bit;
+       ct->chip.irq_ack = irq_gc_noop;
+       ct->chip.irq_suspend = bcm7120_l2_intc_suspend;
+       ct->chip.irq_resume = bcm7120_l2_intc_resume;
+
+       if (of_property_read_bool(dn, "brcm,irq-can-wake")) {
+               data->can_wake = true;
+               /* This IRQ chip can wake the system, set all relevant child
+                * interupts in wake_enabled mask
+                */
+               gc->wake_enabled = 0xffffffff;
+               gc->wake_enabled &= ~gc->unused;
+               ct->chip.irq_set_wake = irq_gc_set_wake;
+       }
+
+       pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n",
+                       data->base, num_parent_irqs);
+
+       return 0;
+
+out_free_domain:
+       irq_domain_remove(data->domain);
+out_unmap:
+       iounmap(data->base);
+out_free:
+       kfree(data);
+       return ret;
+}
+IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,bcm7120-l2-intc",
+               bcm7120_l2_intc_of_init);
index 33340dc97d1da14977dcf0ad1c88cef1250b1f94..33127f131d7840b0c0ec4c8ca15b0aa46b9ab5ba 100644 (file)
@@ -76,24 +76,20 @@ static struct {
 
 static asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs)
 {
-       u32 irqnr, irqstat;
+       u32 irqstat;
 
        do {
                irqstat = readw_relaxed(clps711x_intc->intmr[0]) &
                          readw_relaxed(clps711x_intc->intsr[0]);
-               if (irqstat) {
-                       irqnr = irq_find_mapping(clps711x_intc->domain,
-                                                fls(irqstat) - 1);
-                       handle_IRQ(irqnr, regs);
-               }
+               if (irqstat)
+                       handle_domain_irq(clps711x_intc->domain,
+                                         fls(irqstat) - 1, regs);
 
                irqstat = readw_relaxed(clps711x_intc->intmr[1]) &
                          readw_relaxed(clps711x_intc->intsr[1]);
-               if (irqstat) {
-                       irqnr = irq_find_mapping(clps711x_intc->domain,
-                                                fls(irqstat) - 1 + 16);
-                       handle_IRQ(irqnr, regs);
-               }
+               if (irqstat)
+                       handle_domain_irq(clps711x_intc->domain,
+                                         fls(irqstat) - 1 + 16, regs);
        } while (irqstat);
 }
 
index 60ac704d2090283e32d485a1a909fcebb49e474d..61541ff24397b0a92a5933c708dfb3a87dcd6fbf 100644 (file)
@@ -74,20 +74,22 @@ void __init gic_dist_config(void __iomem *base, int gic_irqs,
         * Set all global interrupts to be level triggered, active low.
         */
        for (i = 32; i < gic_irqs; i += 16)
-               writel_relaxed(0, base + GIC_DIST_CONFIG + i / 4);
+               writel_relaxed(GICD_INT_ACTLOW_LVLTRIG,
+                                       base + GIC_DIST_CONFIG + i / 4);
 
        /*
         * Set priority on all global interrupts.
         */
        for (i = 32; i < gic_irqs; i += 4)
-               writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i);
+               writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i);
 
        /*
         * Disable all interrupts.  Leave the PPI and SGIs alone
         * as they are enabled by redistributor registers.
         */
        for (i = 32; i < gic_irqs; i += 32)
-               writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i / 8);
+               writel_relaxed(GICD_INT_EN_CLR_X32,
+                                       base + GIC_DIST_ENABLE_CLEAR + i / 8);
 
        if (sync_access)
                sync_access();
@@ -101,14 +103,15 @@ void gic_cpu_config(void __iomem *base, void (*sync_access)(void))
         * Deal with the banked PPI and SGI interrupts - disable all
         * PPI interrupts, ensure all SGI interrupts are enabled.
         */
-       writel_relaxed(0xffff0000, base + GIC_DIST_ENABLE_CLEAR);
-       writel_relaxed(0x0000ffff, base + GIC_DIST_ENABLE_SET);
+       writel_relaxed(GICD_INT_EN_CLR_PPI, base + GIC_DIST_ENABLE_CLEAR);
+       writel_relaxed(GICD_INT_EN_SET_SGI, base + GIC_DIST_ENABLE_SET);
 
        /*
         * Set priority on PPI and SGI interrupts
         */
        for (i = 0; i < 32; i += 4)
-               writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
+               writel_relaxed(GICD_INT_DEF_PRI_X4,
+                                       base + GIC_DIST_PRI + i * 4 / 4);
 
        if (sync_access)
                sync_access();
index a0698b4f03037f8c8717e34b896b6f454fe28e59..aa17ae805a703c80fe6584c019e18617741caaab 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <linux/cpu.h>
+#include <linux/cpu_pm.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/of.h>
@@ -155,7 +156,7 @@ static void gic_enable_sre(void)
                pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
 }
 
-static void gic_enable_redist(void)
+static void gic_enable_redist(bool enable)
 {
        void __iomem *rbase;
        u32 count = 1000000;    /* 1s! */
@@ -163,20 +164,30 @@ static void gic_enable_redist(void)
 
        rbase = gic_data_rdist_rd_base();
 
-       /* Wake up this CPU redistributor */
        val = readl_relaxed(rbase + GICR_WAKER);
-       val &= ~GICR_WAKER_ProcessorSleep;
+       if (enable)
+               /* Wake up this CPU redistributor */
+               val &= ~GICR_WAKER_ProcessorSleep;
+       else
+               val |= GICR_WAKER_ProcessorSleep;
        writel_relaxed(val, rbase + GICR_WAKER);
 
-       while (readl_relaxed(rbase + GICR_WAKER) & GICR_WAKER_ChildrenAsleep) {
-               count--;
-               if (!count) {
-                       pr_err_ratelimited("redist didn't wake up...\n");
-                       return;
-               }
+       if (!enable) {          /* Check that GICR_WAKER is writeable */
+               val = readl_relaxed(rbase + GICR_WAKER);
+               if (!(val & GICR_WAKER_ProcessorSleep))
+                       return; /* No PM support in this redistributor */
+       }
+
+       while (count--) {
+               val = readl_relaxed(rbase + GICR_WAKER);
+               if (enable ^ (val & GICR_WAKER_ChildrenAsleep))
+                       break;
                cpu_relax();
                udelay(1);
        };
+       if (!count)
+               pr_err_ratelimited("redistributor failed to %s...\n",
+                                  enable ? "wakeup" : "sleep");
 }
 
 /*
@@ -261,14 +272,13 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
                irqnr = gic_read_iar();
 
                if (likely(irqnr > 15 && irqnr < 1020)) {
-                       u64 irq = irq_find_mapping(gic_data.domain, irqnr);
-                       if (likely(irq)) {
-                               handle_IRQ(irq, regs);
-                               continue;
+                       int err;
+                       err = handle_domain_irq(gic_data.domain, irqnr, regs);
+                       if (err) {
+                               WARN_ONCE(true, "Unexpected SPI received!\n");
+                               gic_write_eoir(irqnr);
                        }
-
-                       WARN_ONCE(true, "Unexpected SPI received!\n");
-                       gic_write_eoir(irqnr);
+                       continue;
                }
                if (irqnr < 16) {
                        gic_write_eoir(irqnr);
@@ -360,6 +370,21 @@ static int gic_populate_rdist(void)
        return -ENODEV;
 }
 
+static void gic_cpu_sys_reg_init(void)
+{
+       /* Enable system registers */
+       gic_enable_sre();
+
+       /* Set priority mask register */
+       gic_write_pmr(DEFAULT_PMR_VALUE);
+
+       /* EOI deactivates interrupt too (mode 0) */
+       gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
+
+       /* ... and let's hit the road... */
+       gic_write_grpen1(1);
+}
+
 static void gic_cpu_init(void)
 {
        void __iomem *rbase;
@@ -368,23 +393,14 @@ static void gic_cpu_init(void)
        if (gic_populate_rdist())
                return;
 
-       gic_enable_redist();
+       gic_enable_redist(true);
 
        rbase = gic_data_rdist_sgi_base();
 
        gic_cpu_config(rbase, gic_redist_wait_for_rwp);
 
-       /* Enable system registers */
-       gic_enable_sre();
-
-       /* Set priority mask register */
-       gic_write_pmr(DEFAULT_PMR_VALUE);
-
-       /* EOI deactivates interrupt too (mode 0) */
-       gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
-
-       /* ... and let's hit the road... */
-       gic_write_grpen1(1);
+       /* initialise system registers */
+       gic_cpu_sys_reg_init();
 }
 
 #ifdef CONFIG_SMP
@@ -533,6 +549,33 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
 #define gic_smp_init()         do { } while(0)
 #endif
 
+#ifdef CONFIG_CPU_PM
+static int gic_cpu_pm_notifier(struct notifier_block *self,
+                              unsigned long cmd, void *v)
+{
+       if (cmd == CPU_PM_EXIT) {
+               gic_enable_redist(true);
+               gic_cpu_sys_reg_init();
+       } else if (cmd == CPU_PM_ENTER) {
+               gic_write_grpen1(0);
+               gic_enable_redist(false);
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block gic_cpu_pm_notifier_block = {
+       .notifier_call = gic_cpu_pm_notifier,
+};
+
+static void gic_cpu_pm_init(void)
+{
+       cpu_pm_register_notifier(&gic_cpu_pm_notifier_block);
+}
+
+#else
+static inline void gic_cpu_pm_init(void) { }
+#endif /* CONFIG_CPU_PM */
+
 static struct irq_chip gic_chip = {
        .name                   = "GICv3",
        .irq_mask               = gic_mask_irq,
@@ -672,6 +715,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
        gic_smp_init();
        gic_dist_init();
        gic_cpu_init();
+       gic_cpu_pm_init();
 
        return 0;
 
index dda6dbc23565aa48593d5e3112da13a58acdc3f7..f0a4800a15b0d2725c3f06dce4e2eec453ddc958 100644 (file)
@@ -270,8 +270,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
                irqnr = irqstat & GICC_IAR_INT_ID_MASK;
 
                if (likely(irqnr > 15 && irqnr < 1021)) {
-                       irqnr = irq_find_mapping(gic->domain, irqnr);
-                       handle_IRQ(irqnr, regs);
+                       handle_domain_irq(gic->domain, irqnr, regs);
                        continue;
                }
                if (irqnr < 16) {
@@ -298,8 +297,8 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
        status = readl_relaxed(gic_data_cpu_base(chip_data) + GIC_CPU_INTACK);
        raw_spin_unlock(&irq_controller_lock);
 
-       gic_irq = (status & 0x3ff);
-       if (gic_irq == 1023)
+       gic_irq = (status & GICC_IAR_INT_ID_MASK);
+       if (gic_irq == GICC_INT_SPURIOUS)
                goto out;
 
        cascade_irq = irq_find_mapping(chip_data->domain, gic_irq);
@@ -353,6 +352,21 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic)
        return mask;
 }
 
+static void gic_cpu_if_up(void)
+{
+       void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
+       u32 bypass = 0;
+
+       /*
+       * Preserve bypass disable bits to be written back later
+       */
+       bypass = readl(cpu_base + GIC_CPU_CTRL);
+       bypass &= GICC_DIS_BYPASS_MASK;
+
+       writel_relaxed(bypass | GICC_ENABLE, cpu_base + GIC_CPU_CTRL);
+}
+
+
 static void __init gic_dist_init(struct gic_chip_data *gic)
 {
        unsigned int i;
@@ -360,7 +374,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
        unsigned int gic_irqs = gic->gic_irqs;
        void __iomem *base = gic_data_dist_base(gic);
 
-       writel_relaxed(0, base + GIC_DIST_CTRL);
+       writel_relaxed(GICD_DISABLE, base + GIC_DIST_CTRL);
 
        /*
         * Set all global interrupts to this CPU only.
@@ -373,7 +387,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
 
        gic_dist_config(base, gic_irqs, NULL);
 
-       writel_relaxed(1, base + GIC_DIST_CTRL);
+       writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL);
 }
 
 static void gic_cpu_init(struct gic_chip_data *gic)
@@ -400,14 +414,18 @@ static void gic_cpu_init(struct gic_chip_data *gic)
 
        gic_cpu_config(dist_base, NULL);
 
-       writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
-       writel_relaxed(1, base + GIC_CPU_CTRL);
+       writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK);
+       gic_cpu_if_up();
 }
 
 void gic_cpu_if_down(void)
 {
        void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
-       writel_relaxed(0, cpu_base + GIC_CPU_CTRL);
+       u32 val = 0;
+
+       val = readl(cpu_base + GIC_CPU_CTRL);
+       val &= ~GICC_ENABLE;
+       writel_relaxed(val, cpu_base + GIC_CPU_CTRL);
 }
 
 #ifdef CONFIG_CPU_PM
@@ -467,14 +485,14 @@ static void gic_dist_restore(unsigned int gic_nr)
        if (!dist_base)
                return;
 
-       writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+       writel_relaxed(GICD_DISABLE, dist_base + GIC_DIST_CTRL);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
                writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
                        dist_base + GIC_DIST_CONFIG + i * 4);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
-               writel_relaxed(0xa0a0a0a0,
+               writel_relaxed(GICD_INT_DEF_PRI_X4,
                        dist_base + GIC_DIST_PRI + i * 4);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
@@ -485,7 +503,7 @@ static void gic_dist_restore(unsigned int gic_nr)
                writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
                        dist_base + GIC_DIST_ENABLE_SET + i * 4);
 
-       writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+       writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL);
 }
 
 static void gic_cpu_save(unsigned int gic_nr)
@@ -539,10 +557,11 @@ static void gic_cpu_restore(unsigned int gic_nr)
                writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
 
        for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
-               writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);
+               writel_relaxed(GICD_INT_DEF_PRI_X4,
+                                       dist_base + GIC_DIST_PRI + i * 4);
 
-       writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
-       writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
+       writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK);
+       gic_cpu_if_up();
 }
 
 static int gic_notifier(struct notifier_block *self, unsigned long cmd,        void *v)
diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c
new file mode 100644 (file)
index 0000000..9c8f833
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * Hisilicon HiP04 INTC
+ *
+ * Copyright (C) 2002-2014 ARM Limited.
+ * Copyright (c) 2013-2014 Hisilicon Ltd.
+ * Copyright (c) 2013-2014 Linaro Ltd.
+ *
+ * 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.
+ *
+ * Interrupt architecture for the HIP04 INTC:
+ *
+ * o There is one Interrupt Distributor, which receives interrupts
+ *   from system devices and sends them to the Interrupt Controllers.
+ *
+ * o There is one CPU Interface per CPU, which sends interrupts sent
+ *   by the Distributor, and interrupts generated locally, to the
+ *   associated CPU. The base address of the CPU interface is usually
+ *   aliased so that the same address points to different chips depending
+ *   on the CPU it is accessed from.
+ *
+ * Note that IRQs 0-31 are special - they are local to each CPU.
+ * As such, the enable set/clear, pending set/clear and active bit
+ * registers are banked per-cpu for these sources.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
+#include <linux/cpumask.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/irqchip/arm-gic.h>
+
+#include <asm/irq.h>
+#include <asm/exception.h>
+#include <asm/smp_plat.h>
+
+#include "irq-gic-common.h"
+#include "irqchip.h"
+
+#define HIP04_MAX_IRQS         510
+
+struct hip04_irq_data {
+       void __iomem *dist_base;
+       void __iomem *cpu_base;
+       struct irq_domain *domain;
+       unsigned int nr_irqs;
+};
+
+static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+
+/*
+ * The GIC mapping of CPU interfaces does not necessarily match
+ * the logical CPU numbering.  Let's use a mapping as returned
+ * by the GIC itself.
+ */
+#define NR_HIP04_CPU_IF 16
+static u16 hip04_cpu_map[NR_HIP04_CPU_IF] __read_mostly;
+
+static struct hip04_irq_data hip04_data __read_mostly;
+
+static inline void __iomem *hip04_dist_base(struct irq_data *d)
+{
+       struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d);
+       return hip04_data->dist_base;
+}
+
+static inline void __iomem *hip04_cpu_base(struct irq_data *d)
+{
+       struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d);
+       return hip04_data->cpu_base;
+}
+
+static inline unsigned int hip04_irq(struct irq_data *d)
+{
+       return d->hwirq;
+}
+
+/*
+ * Routines to acknowledge, disable and enable interrupts
+ */
+static void hip04_mask_irq(struct irq_data *d)
+{
+       u32 mask = 1 << (hip04_irq(d) % 32);
+
+       raw_spin_lock(&irq_controller_lock);
+       writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_CLEAR +
+                      (hip04_irq(d) / 32) * 4);
+       raw_spin_unlock(&irq_controller_lock);
+}
+
+static void hip04_unmask_irq(struct irq_data *d)
+{
+       u32 mask = 1 << (hip04_irq(d) % 32);
+
+       raw_spin_lock(&irq_controller_lock);
+       writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_SET +
+                      (hip04_irq(d) / 32) * 4);
+       raw_spin_unlock(&irq_controller_lock);
+}
+
+static void hip04_eoi_irq(struct irq_data *d)
+{
+       writel_relaxed(hip04_irq(d), hip04_cpu_base(d) + GIC_CPU_EOI);
+}
+
+static int hip04_irq_set_type(struct irq_data *d, unsigned int type)
+{
+       void __iomem *base = hip04_dist_base(d);
+       unsigned int irq = hip04_irq(d);
+
+       /* Interrupt configuration for SGIs can't be changed */
+       if (irq < 16)
+               return -EINVAL;
+
+       if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
+               return -EINVAL;
+
+       raw_spin_lock(&irq_controller_lock);
+
+       gic_configure_irq(irq, type, base, NULL);
+
+       raw_spin_unlock(&irq_controller_lock);
+
+       return 0;
+}
+
+#ifdef CONFIG_SMP
+static int hip04_irq_set_affinity(struct irq_data *d,
+                                 const struct cpumask *mask_val,
+                                 bool force)
+{
+       void __iomem *reg;
+       unsigned int cpu, shift = (hip04_irq(d) % 2) * 16;
+       u32 val, mask, bit;
+
+       if (!force)
+               cpu = cpumask_any_and(mask_val, cpu_online_mask);
+       else
+               cpu = cpumask_first(mask_val);
+
+       if (cpu >= NR_HIP04_CPU_IF || cpu >= nr_cpu_ids)
+               return -EINVAL;
+
+       raw_spin_lock(&irq_controller_lock);
+       reg = hip04_dist_base(d) + GIC_DIST_TARGET + ((hip04_irq(d) * 2) & ~3);
+       mask = 0xffff << shift;
+       bit = hip04_cpu_map[cpu] << shift;
+       val = readl_relaxed(reg) & ~mask;
+       writel_relaxed(val | bit, reg);
+       raw_spin_unlock(&irq_controller_lock);
+
+       return IRQ_SET_MASK_OK;
+}
+#endif
+
+static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
+{
+       u32 irqstat, irqnr;
+       void __iomem *cpu_base = hip04_data.cpu_base;
+
+       do {
+               irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
+               irqnr = irqstat & GICC_IAR_INT_ID_MASK;
+
+               if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) {
+                       irqnr = irq_find_mapping(hip04_data.domain, irqnr);
+                       handle_IRQ(irqnr, regs);
+                       continue;
+               }
+               if (irqnr < 16) {
+                       writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
+#ifdef CONFIG_SMP
+                       handle_IPI(irqnr, regs);
+#endif
+                       continue;
+               }
+               break;
+       } while (1);
+}
+
+static struct irq_chip hip04_irq_chip = {
+       .name                   = "HIP04 INTC",
+       .irq_mask               = hip04_mask_irq,
+       .irq_unmask             = hip04_unmask_irq,
+       .irq_eoi                = hip04_eoi_irq,
+       .irq_set_type           = hip04_irq_set_type,
+#ifdef CONFIG_SMP
+       .irq_set_affinity       = hip04_irq_set_affinity,
+#endif
+};
+
+static u16 hip04_get_cpumask(struct hip04_irq_data *intc)
+{
+       void __iomem *base = intc->dist_base;
+       u32 mask, i;
+
+       for (i = mask = 0; i < 32; i += 2) {
+               mask = readl_relaxed(base + GIC_DIST_TARGET + i * 2);
+               mask |= mask >> 16;
+               if (mask)
+                       break;
+       }
+
+       if (!mask)
+               pr_crit("GIC CPU mask not found - kernel will fail to boot.\n");
+
+       return mask;
+}
+
+static void __init hip04_irq_dist_init(struct hip04_irq_data *intc)
+{
+       unsigned int i;
+       u32 cpumask;
+       unsigned int nr_irqs = intc->nr_irqs;
+       void __iomem *base = intc->dist_base;
+
+       writel_relaxed(0, base + GIC_DIST_CTRL);
+
+       /*
+        * Set all global interrupts to this CPU only.
+        */
+       cpumask = hip04_get_cpumask(intc);
+       cpumask |= cpumask << 16;
+       for (i = 32; i < nr_irqs; i += 2)
+               writel_relaxed(cpumask, base + GIC_DIST_TARGET + ((i * 2) & ~3));
+
+       gic_dist_config(base, nr_irqs, NULL);
+
+       writel_relaxed(1, base + GIC_DIST_CTRL);
+}
+
+static void hip04_irq_cpu_init(struct hip04_irq_data *intc)
+{
+       void __iomem *dist_base = intc->dist_base;
+       void __iomem *base = intc->cpu_base;
+       unsigned int cpu_mask, cpu = smp_processor_id();
+       int i;
+
+       /*
+        * Get what the GIC says our CPU mask is.
+        */
+       BUG_ON(cpu >= NR_HIP04_CPU_IF);
+       cpu_mask = hip04_get_cpumask(intc);
+       hip04_cpu_map[cpu] = cpu_mask;
+
+       /*
+        * Clear our mask from the other map entries in case they're
+        * still undefined.
+        */
+       for (i = 0; i < NR_HIP04_CPU_IF; i++)
+               if (i != cpu)
+                       hip04_cpu_map[i] &= ~cpu_mask;
+
+       gic_cpu_config(dist_base, NULL);
+
+       writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
+       writel_relaxed(1, base + GIC_CPU_CTRL);
+}
+
+#ifdef CONFIG_SMP
+static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq)
+{
+       int cpu;
+       unsigned long flags, map = 0;
+
+       raw_spin_lock_irqsave(&irq_controller_lock, flags);
+
+       /* Convert our logical CPU mask into a physical one. */
+       for_each_cpu(cpu, mask)
+               map |= hip04_cpu_map[cpu];
+
+       /*
+        * Ensure that stores to Normal memory are visible to the
+        * other CPUs before they observe us issuing the IPI.
+        */
+       dmb(ishst);
+
+       /* this always happens on GIC0 */
+       writel_relaxed(map << 8 | irq, hip04_data.dist_base + GIC_DIST_SOFTINT);
+
+       raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
+}
+#endif
+
+static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq,
+                               irq_hw_number_t hw)
+{
+       if (hw < 32) {
+               irq_set_percpu_devid(irq);
+               irq_set_chip_and_handler(irq, &hip04_irq_chip,
+                                        handle_percpu_devid_irq);
+               set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+       } else {
+               irq_set_chip_and_handler(irq, &hip04_irq_chip,
+                                        handle_fasteoi_irq);
+               set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+       }
+       irq_set_chip_data(irq, d->host_data);
+       return 0;
+}
+
+static int hip04_irq_domain_xlate(struct irq_domain *d,
+                                 struct device_node *controller,
+                                 const u32 *intspec, unsigned int intsize,
+                                 unsigned long *out_hwirq,
+                                 unsigned int *out_type)
+{
+       unsigned long ret = 0;
+
+       if (d->of_node != controller)
+               return -EINVAL;
+       if (intsize < 3)
+               return -EINVAL;
+
+       /* Get the interrupt number and add 16 to skip over SGIs */
+       *out_hwirq = intspec[1] + 16;
+
+       /* For SPIs, we need to add 16 more to get the irq ID number */
+       if (!intspec[0])
+               *out_hwirq += 16;
+
+       *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+
+       return ret;
+}
+
+#ifdef CONFIG_SMP
+static int hip04_irq_secondary_init(struct notifier_block *nfb,
+                                   unsigned long action,
+                                   void *hcpu)
+{
+       if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
+               hip04_irq_cpu_init(&hip04_data);
+       return NOTIFY_OK;
+}
+
+/*
+ * Notifier for enabling the INTC CPU interface. Set an arbitrarily high
+ * priority because the GIC needs to be up before the ARM generic timers.
+ */
+static struct notifier_block hip04_irq_cpu_notifier = {
+       .notifier_call  = hip04_irq_secondary_init,
+       .priority       = 100,
+};
+#endif
+
+static const struct irq_domain_ops hip04_irq_domain_ops = {
+       .map    = hip04_irq_domain_map,
+       .xlate  = hip04_irq_domain_xlate,
+};
+
+static int __init
+hip04_of_init(struct device_node *node, struct device_node *parent)
+{
+       irq_hw_number_t hwirq_base = 16;
+       int nr_irqs, irq_base, i;
+
+       if (WARN_ON(!node))
+               return -ENODEV;
+
+       hip04_data.dist_base = of_iomap(node, 0);
+       WARN(!hip04_data.dist_base, "fail to map hip04 intc dist registers\n");
+
+       hip04_data.cpu_base = of_iomap(node, 1);
+       WARN(!hip04_data.cpu_base, "unable to map hip04 intc cpu registers\n");
+
+       /*
+        * Initialize the CPU interface map to all CPUs.
+        * It will be refined as each CPU probes its ID.
+        */
+       for (i = 0; i < NR_HIP04_CPU_IF; i++)
+               hip04_cpu_map[i] = 0xff;
+
+       /*
+        * Find out how many interrupts are supported.
+        * The HIP04 INTC only supports up to 510 interrupt sources.
+        */
+       nr_irqs = readl_relaxed(hip04_data.dist_base + GIC_DIST_CTR) & 0x1f;
+       nr_irqs = (nr_irqs + 1) * 32;
+       if (nr_irqs > HIP04_MAX_IRQS)
+               nr_irqs = HIP04_MAX_IRQS;
+       hip04_data.nr_irqs = nr_irqs;
+
+       nr_irqs -= hwirq_base; /* calculate # of irqs to allocate */
+
+       irq_base = irq_alloc_descs(-1, hwirq_base, nr_irqs, numa_node_id());
+       if (IS_ERR_VALUE(irq_base)) {
+               pr_err("failed to allocate IRQ numbers\n");
+               return -EINVAL;
+       }
+
+       hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base,
+                                                 hwirq_base,
+                                                 &hip04_irq_domain_ops,
+                                                 &hip04_data);
+
+       if (WARN_ON(!hip04_data.domain))
+               return -EINVAL;
+
+#ifdef CONFIG_SMP
+       set_smp_cross_call(hip04_raise_softirq);
+       register_cpu_notifier(&hip04_irq_cpu_notifier);
+#endif
+       set_handle_irq(hip04_handle_irq);
+
+       hip04_irq_dist_init(&hip04_data);
+       hip04_irq_cpu_init(&hip04_data);
+
+       return 0;
+}
+IRQCHIP_DECLARE(hip04_intc, "hisilicon,hip04-intc", hip04_of_init);
diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c
new file mode 100644 (file)
index 0000000..608abf9
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Texas Instruments Keystone IRQ controller IP driver
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ * Author: Sajesh Kumar Saran <sajesh@ti.com>
+ *        Grygorii Strashko <grygorii.strashko@ti.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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include "irqchip.h"
+
+
+/* The source ID bits start from 4 to 31 (total 28 bits)*/
+#define BIT_OFS                        4
+#define KEYSTONE_N_IRQ         (32 - BIT_OFS)
+
+struct keystone_irq_device {
+       struct device           *dev;
+       struct irq_chip          chip;
+       u32                      mask;
+       int                      irq;
+       struct irq_domain       *irqd;
+       struct regmap           *devctrl_regs;
+       u32                     devctrl_offset;
+};
+
+static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq)
+{
+       int ret;
+       u32 val = 0;
+
+       ret = regmap_read(kirq->devctrl_regs, kirq->devctrl_offset, &val);
+       if (ret < 0)
+               dev_dbg(kirq->dev, "irq read failed ret(%d)\n", ret);
+       return val;
+}
+
+static inline void
+keystone_irq_writel(struct keystone_irq_device *kirq, u32 value)
+{
+       int ret;
+
+       ret = regmap_write(kirq->devctrl_regs, kirq->devctrl_offset, value);
+       if (ret < 0)
+               dev_dbg(kirq->dev, "irq write failed ret(%d)\n", ret);
+}
+
+static void keystone_irq_setmask(struct irq_data *d)
+{
+       struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d);
+
+       kirq->mask |= BIT(d->hwirq);
+       dev_dbg(kirq->dev, "mask %lu [%x]\n", d->hwirq, kirq->mask);
+}
+
+static void keystone_irq_unmask(struct irq_data *d)
+{
+       struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d);
+
+       kirq->mask &= ~BIT(d->hwirq);
+       dev_dbg(kirq->dev, "unmask %lu [%x]\n", d->hwirq, kirq->mask);
+}
+
+static void keystone_irq_ack(struct irq_data *d)
+{
+       /* nothing to do here */
+}
+
+static void keystone_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+       struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc);
+       unsigned long pending;
+       int src, virq;
+
+       dev_dbg(kirq->dev, "start irq %d\n", irq);
+
+       chained_irq_enter(irq_desc_get_chip(desc), desc);
+
+       pending = keystone_irq_readl(kirq);
+       keystone_irq_writel(kirq, pending);
+
+       dev_dbg(kirq->dev, "pending 0x%lx, mask 0x%x\n", pending, kirq->mask);
+
+       pending = (pending >> BIT_OFS) & ~kirq->mask;
+
+       dev_dbg(kirq->dev, "pending after mask 0x%lx\n", pending);
+
+       for (src = 0; src < KEYSTONE_N_IRQ; src++) {
+               if (BIT(src) & pending) {
+                       virq = irq_find_mapping(kirq->irqd, src);
+                       dev_dbg(kirq->dev, "dispatch bit %d, virq %d\n",
+                               src, virq);
+                       if (!virq)
+                               dev_warn(kirq->dev, "sporious irq detected hwirq %d, virq %d\n",
+                                        src, virq);
+                       generic_handle_irq(virq);
+               }
+       }
+
+       chained_irq_exit(irq_desc_get_chip(desc), desc);
+
+       dev_dbg(kirq->dev, "end irq %d\n", irq);
+}
+
+static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
+                               irq_hw_number_t hw)
+{
+       struct keystone_irq_device *kirq = h->host_data;
+
+       irq_set_chip_data(virq, kirq);
+       irq_set_chip_and_handler(virq, &kirq->chip, handle_level_irq);
+       set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+       return 0;
+}
+
+static struct irq_domain_ops keystone_irq_ops = {
+       .map    = keystone_irq_map,
+       .xlate  = irq_domain_xlate_onecell,
+};
+
+static int keystone_irq_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct keystone_irq_device *kirq;
+       int ret;
+
+       if (np == NULL)
+               return -EINVAL;
+
+       kirq = devm_kzalloc(dev, sizeof(*kirq), GFP_KERNEL);
+       if (!kirq)
+               return -ENOMEM;
+
+       kirq->devctrl_regs =
+               syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev");
+       if (IS_ERR(kirq->devctrl_regs))
+               return PTR_ERR(kirq->devctrl_regs);
+
+       ret = of_property_read_u32_index(np, "ti,syscon-dev", 1,
+                                        &kirq->devctrl_offset);
+       if (ret) {
+               dev_err(dev, "couldn't read the devctrl_offset offset!\n");
+               return ret;
+       }
+
+       kirq->irq = platform_get_irq(pdev, 0);
+       if (kirq->irq < 0) {
+               dev_err(dev, "no irq resource %d\n", kirq->irq);
+               return kirq->irq;
+       }
+
+       kirq->dev = dev;
+       kirq->mask = ~0x0;
+       kirq->chip.name         = "keystone-irq";
+       kirq->chip.irq_ack      = keystone_irq_ack;
+       kirq->chip.irq_mask     = keystone_irq_setmask;
+       kirq->chip.irq_unmask   = keystone_irq_unmask;
+
+       kirq->irqd = irq_domain_add_linear(np, KEYSTONE_N_IRQ,
+                                          &keystone_irq_ops, kirq);
+       if (!kirq->irqd) {
+               dev_err(dev, "IRQ domain registration failed\n");
+               return -ENODEV;
+       }
+
+       platform_set_drvdata(pdev, kirq);
+
+       irq_set_chained_handler(kirq->irq, keystone_irq_handler);
+       irq_set_handler_data(kirq->irq, kirq);
+
+       /* clear all source bits */
+       keystone_irq_writel(kirq, ~0x0);
+
+       dev_info(dev, "irqchip registered, nr_irqs %u\n", KEYSTONE_N_IRQ);
+
+       return 0;
+}
+
+static int keystone_irq_remove(struct platform_device *pdev)
+{
+       struct keystone_irq_device *kirq = platform_get_drvdata(pdev);
+       int hwirq;
+
+       for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++)
+               irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq));
+
+       irq_domain_remove(kirq->irqd);
+       return 0;
+}
+
+static const struct of_device_id keystone_irq_dt_ids[] = {
+       { .compatible = "ti,keystone-irq", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, keystone_irq_dt_ids);
+
+static struct platform_driver keystone_irq_device_driver = {
+       .probe          = keystone_irq_probe,
+       .remove         = keystone_irq_remove,
+       .driver         = {
+               .name   = "keystone_irq",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(keystone_irq_dt_ids),
+       }
+};
+
+module_platform_driver(keystone_irq_device_driver);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_AUTHOR("Sajesh Kumar Saran");
+MODULE_AUTHOR("Grygorii Strashko");
+MODULE_DESCRIPTION("Keystone IRQ chip");
+MODULE_LICENSE("GPL v2");
index 1c3e2c9b46bae59c96e9f2851d988af4d13b33c6..c0da57bdb89dfcf0c0e2224180715989a88086a6 100644 (file)
@@ -196,26 +196,24 @@ static struct mmp_intc_conf mmp2_conf = {
 
 static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs)
 {
-       int irq, hwirq;
+       int hwirq;
 
        hwirq = readl_relaxed(mmp_icu_base + PJ1_INT_SEL);
        if (!(hwirq & SEL_INT_PENDING))
                return;
        hwirq &= SEL_INT_NUM_MASK;
-       irq = irq_find_mapping(icu_data[0].domain, hwirq);
-       handle_IRQ(irq, regs);
+       handle_domain_irq(icu_data[0].domain, hwirq, regs);
 }
 
 static void __exception_irq_entry mmp2_handle_irq(struct pt_regs *regs)
 {
-       int irq, hwirq;
+       int hwirq;
 
        hwirq = readl_relaxed(mmp_icu_base + PJ4_INT_SEL);
        if (!(hwirq & SEL_INT_PENDING))
                return;
        hwirq &= SEL_INT_NUM_MASK;
-       irq = irq_find_mapping(icu_data[0].domain, hwirq);
-       handle_IRQ(irq, regs);
+       handle_domain_irq(icu_data[0].domain, hwirq, regs);
 }
 
 /* MMP (ARMv5) */
index 4044ff2876633d671021c84dd631b8a033bf1574..e4acf1e3f8e3b0df693707709787202f78998c4e 100644 (file)
@@ -78,8 +78,7 @@ asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
 
        irqnr = __raw_readl(icoll_base + HW_ICOLL_STAT_OFFSET);
        __raw_writel(irqnr, icoll_base + HW_ICOLL_VECTOR);
-       irqnr = irq_find_mapping(icoll_domain, irqnr);
-       handle_IRQ(irqnr, regs);
+       handle_domain_irq(icoll_domain, irqnr, regs);
 }
 
 static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
index f3814e79192d7ede486b71f15973bc9b2bf7fea6..28718d3e8281032d422d2c9985d7ca450b7763a1 100644 (file)
@@ -334,8 +334,7 @@ out:
                irqnr &= ACTIVEIRQ_MASK;
 
                if (irqnr) {
-                       irqnr = irq_find_mapping(domain, irqnr);
-                       handle_IRQ(irqnr, regs);
+                       handle_domain_irq(domain, irqnr, regs);
                        handled_irq = 1;
                }
        } while (irqnr);
index 17ff033d992575b4940144138b4b742e5609420b..e93d079fe069eb11a55f6ff422063ec19620a826 100644 (file)
@@ -113,7 +113,7 @@ static inline int pic_get_irq(int first)
        else
                hwirq = hwirq + first - 1;
 
-       return irq_find_mapping(root_domain, hwirq);
+       return hwirq;
 }
 
 static void or1k_pic_handle_irq(struct pt_regs *regs)
@@ -121,7 +121,7 @@ static void or1k_pic_handle_irq(struct pt_regs *regs)
        int irq = -1;
 
        while ((irq = pic_get_irq(irq + 1)) != NO_IRQ)
-               handle_IRQ(irq, regs);
+               handle_domain_irq(root_domain, irq, regs);
 }
 
 static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
index 34d18b48bb78fe11d130f2dcd10304b261ed98eb..ad0c0f6f1d65ac6b4cf7d8db80f635cf698a9875 100644 (file)
@@ -43,9 +43,8 @@ __exception_irq_entry orion_handle_irq(struct pt_regs *regs)
                        gc->mask_cache;
                while (stat) {
                        u32 hwirq = __fls(stat);
-                       u32 irq = irq_find_mapping(orion_irq_domain,
-                                                  gc->irq_base + hwirq);
-                       handle_IRQ(irq, regs);
+                       handle_domain_irq(orion_irq_domain,
+                                         gc->irq_base + hwirq, regs);
                        stat &= ~(1 << hwirq);
                }
        }
index 3ee78f02e5d7d940d531aa6ab03c8dc681c9cb3c..542e850f4946808ebc19cbf80f71b7e2b9b7b4d8 100644 (file)
@@ -17,6 +17,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include <linux/clk.h>
 #include <linux/init.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -30,6 +31,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/platform_data/irq-renesas-intc-irqpin.h>
+#include <linux/pm_runtime.h>
 
 #define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */
 
@@ -75,6 +77,7 @@ struct intc_irqpin_priv {
        struct platform_device *pdev;
        struct irq_chip irq_chip;
        struct irq_domain *irq_domain;
+       struct clk *clk;
        bool shared_irqs;
        u8 shared_irq_mask;
 };
@@ -270,6 +273,21 @@ static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type)
                                     value ^ INTC_IRQ_SENSE_VALID);
 }
 
+static int intc_irqpin_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+       struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
+
+       if (!p->clk)
+               return 0;
+
+       if (on)
+               clk_enable(p->clk);
+       else
+               clk_disable(p->clk);
+
+       return 0;
+}
+
 static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id)
 {
        struct intc_irqpin_irq *i = dev_id;
@@ -329,7 +347,8 @@ static struct irq_domain_ops intc_irqpin_irq_domain_ops = {
 
 static int intc_irqpin_probe(struct platform_device *pdev)
 {
-       struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data;
+       struct device *dev = &pdev->dev;
+       struct renesas_intc_irqpin_config *pdata = dev->platform_data;
        struct intc_irqpin_priv *p;
        struct intc_irqpin_iomem *i;
        struct resource *io[INTC_IRQPIN_REG_NR];
@@ -337,25 +356,24 @@ static int intc_irqpin_probe(struct platform_device *pdev)
        struct irq_chip *irq_chip;
        void (*enable_fn)(struct irq_data *d);
        void (*disable_fn)(struct irq_data *d);
-       const char *name = dev_name(&pdev->dev);
+       const char *name = dev_name(dev);
        int ref_irq;
        int ret;
        int k;
 
-       p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+       p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
        if (!p) {
-               dev_err(&pdev->dev, "failed to allocate driver data\n");
-               ret = -ENOMEM;
-               goto err0;
+               dev_err(dev, "failed to allocate driver data\n");
+               return -ENOMEM;
        }
 
        /* deal with driver instance configuration */
        if (pdata) {
                memcpy(&p->config, pdata, sizeof(*pdata));
        } else {
-               of_property_read_u32(pdev->dev.of_node, "sense-bitfield-width",
+               of_property_read_u32(dev->of_node, "sense-bitfield-width",
                                     &p->config.sense_bitfield_width);
-               p->config.control_parent = of_property_read_bool(pdev->dev.of_node,
+               p->config.control_parent = of_property_read_bool(dev->of_node,
                                                                 "control-parent");
        }
        if (!p->config.sense_bitfield_width)
@@ -364,11 +382,20 @@ static int intc_irqpin_probe(struct platform_device *pdev)
        p->pdev = pdev;
        platform_set_drvdata(pdev, p);
 
+       p->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(p->clk)) {
+               dev_warn(dev, "unable to get clock\n");
+               p->clk = NULL;
+       }
+
+       pm_runtime_enable(dev);
+       pm_runtime_get_sync(dev);
+
        /* get hold of manadatory IOMEM */
        for (k = 0; k < INTC_IRQPIN_REG_NR; k++) {
                io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k);
                if (!io[k]) {
-                       dev_err(&pdev->dev, "not enough IOMEM resources\n");
+                       dev_err(dev, "not enough IOMEM resources\n");
                        ret = -EINVAL;
                        goto err0;
                }
@@ -386,7 +413,7 @@ static int intc_irqpin_probe(struct platform_device *pdev)
 
        p->number_of_irqs = k;
        if (p->number_of_irqs < 1) {
-               dev_err(&pdev->dev, "not enough IRQ resources\n");
+               dev_err(dev, "not enough IRQ resources\n");
                ret = -EINVAL;
                goto err0;
        }
@@ -407,15 +434,15 @@ static int intc_irqpin_probe(struct platform_device *pdev)
                        i->write = intc_irqpin_write32;
                        break;
                default:
-                       dev_err(&pdev->dev, "IOMEM size mismatch\n");
+                       dev_err(dev, "IOMEM size mismatch\n");
                        ret = -EINVAL;
                        goto err0;
                }
 
-               i->iomem = devm_ioremap_nocache(&pdev->dev, io[k]->start,
+               i->iomem = devm_ioremap_nocache(dev, io[k]->start,
                                                resource_size(io[k]));
                if (!i->iomem) {
-                       dev_err(&pdev->dev, "failed to remap IOMEM\n");
+                       dev_err(dev, "failed to remap IOMEM\n");
                        ret = -ENXIO;
                        goto err0;
                }
@@ -454,39 +481,36 @@ static int intc_irqpin_probe(struct platform_device *pdev)
        irq_chip->name = name;
        irq_chip->irq_mask = disable_fn;
        irq_chip->irq_unmask = enable_fn;
-       irq_chip->irq_enable = enable_fn;
-       irq_chip->irq_disable = disable_fn;
        irq_chip->irq_set_type = intc_irqpin_irq_set_type;
-       irq_chip->flags = IRQCHIP_SKIP_SET_WAKE;
+       irq_chip->irq_set_wake = intc_irqpin_irq_set_wake;
+       irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND;
 
-       p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
+       p->irq_domain = irq_domain_add_simple(dev->of_node,
                                              p->number_of_irqs,
                                              p->config.irq_base,
                                              &intc_irqpin_irq_domain_ops, p);
        if (!p->irq_domain) {
                ret = -ENXIO;
-               dev_err(&pdev->dev, "cannot initialize irq domain\n");
+               dev_err(dev, "cannot initialize irq domain\n");
                goto err0;
        }
 
        if (p->shared_irqs) {
                /* request one shared interrupt */
-               if (devm_request_irq(&pdev->dev, p->irq[0].requested_irq,
+               if (devm_request_irq(dev, p->irq[0].requested_irq,
                                intc_irqpin_shared_irq_handler,
                                IRQF_SHARED, name, p)) {
-                       dev_err(&pdev->dev, "failed to request low IRQ\n");
+                       dev_err(dev, "failed to request low IRQ\n");
                        ret = -ENOENT;
                        goto err1;
                }
        } else {
                /* request interrupts one by one */
                for (k = 0; k < p->number_of_irqs; k++) {
-                       if (devm_request_irq(&pdev->dev,
-                                       p->irq[k].requested_irq,
-                                       intc_irqpin_irq_handler,
-                                       0, name, &p->irq[k])) {
-                               dev_err(&pdev->dev,
-                                       "failed to request low IRQ\n");
+                       if (devm_request_irq(dev, p->irq[k].requested_irq,
+                                            intc_irqpin_irq_handler, 0, name,
+                                            &p->irq[k])) {
+                               dev_err(dev, "failed to request low IRQ\n");
                                ret = -ENOENT;
                                goto err1;
                        }
@@ -497,12 +521,12 @@ static int intc_irqpin_probe(struct platform_device *pdev)
        for (k = 0; k < p->number_of_irqs; k++)
                intc_irqpin_mask_unmask_prio(p, k, 0);
 
-       dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
+       dev_info(dev, "driving %d irqs\n", p->number_of_irqs);
 
        /* warn in case of mismatch if irq base is specified */
        if (p->config.irq_base) {
                if (p->config.irq_base != p->irq[0].domain_irq)
-                       dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n",
+                       dev_warn(dev, "irq base mismatch (%d/%d)\n",
                                 p->config.irq_base, p->irq[0].domain_irq);
        }
 
@@ -511,6 +535,8 @@ static int intc_irqpin_probe(struct platform_device *pdev)
 err1:
        irq_domain_remove(p->irq_domain);
 err0:
+       pm_runtime_put(dev);
+       pm_runtime_disable(dev);
        return ret;
 }
 
@@ -519,7 +545,8 @@ static int intc_irqpin_remove(struct platform_device *pdev)
        struct intc_irqpin_priv *p = platform_get_drvdata(pdev);
 
        irq_domain_remove(p->irq_domain);
-
+       pm_runtime_put(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
        return 0;
 }
 
index 78a6accd205f2dc5bf446af176ef8eaf94937b44..c8d373fcd823bd40e78f4003a77ea69aa357ff39 100644 (file)
@@ -339,7 +339,6 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
 {
        int pnd;
        int offset;
-       int irq;
 
        pnd = __raw_readl(intc->reg_intpnd);
        if (!pnd)
@@ -365,8 +364,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
        if (!(pnd & (1 << offset)))
                offset =  __ffs(pnd);
 
-       irq = irq_find_mapping(intc->domain, intc_offset + offset);
-       handle_IRQ(irq, regs);
+       handle_domain_irq(intc->domain, intc_offset + offset, regs);
        return true;
 }
 
index 5e54f6d71e777c937f8f97185e3ef7ec790cfbb2..a469355df352723cc415753c4e4ec7111c51d3ff 100644 (file)
@@ -50,12 +50,10 @@ sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num)
 static void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs)
 {
        void __iomem *base = sirfsoc_irqdomain->host_data;
-       u32 irqstat, irqnr;
+       u32 irqstat;
 
        irqstat = readl_relaxed(base + SIRFSOC_INIT_IRQ_ID);
-       irqnr = irq_find_mapping(sirfsoc_irqdomain, irqstat & 0xff);
-
-       handle_IRQ(irqnr, regs);
+       handle_domain_irq(sirfsoc_irqdomain, irqstat & 0xff, regs);
 }
 
 static int __init sirfsoc_irq_init(struct device_node *np,
index 6fcef4a95a18af9462431a1d1ef39cd0f100aaa2..64155b686081b81080fc8818fe5f9ac790e44ce5 100644 (file)
@@ -136,7 +136,7 @@ IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_of_init);
 
 static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs)
 {
-       u32 irq, hwirq;
+       u32 hwirq;
 
        /*
         * hwirq == 0 can mean one of 3 things:
@@ -154,8 +154,7 @@ static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs)
                return;
 
        do {
-               irq = irq_find_mapping(sun4i_irq_domain, hwirq);
-               handle_IRQ(irq, regs);
+               handle_domain_irq(sun4i_irq_domain, hwirq, regs);
                hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
        } while (hwirq != 0);
 }
index ccf58548b1612a4012219d7873a05b89a14a5ece..1ab451729a5c5cd23936bf1b078c8a440253bfd9 100644 (file)
@@ -96,7 +96,7 @@ static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs)
 
        while ((status  = readl(f->base + IRQ_STATUS))) {
                irq = ffs(status) - 1;
-               handle_IRQ(irq_find_mapping(f->domain, irq), regs);
+               handle_domain_irq(f->domain, irq, regs);
                handled = 1;
        }
 
index 7d35287f9e90dcc1b73ac1948fed167e5407606b..54089debf2dca8b0b95baf0e31710be29d2c7c2e 100644 (file)
@@ -219,7 +219,7 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs)
 
        while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) {
                irq = ffs(stat) - 1;
-               handle_IRQ(irq_find_mapping(vic->domain, irq), regs);
+               handle_domain_irq(vic->domain, irq, regs);
                handled = 1;
        }
 
index eb6e91efdec8bca0babc0d098e9e11aa7b66f257..b7af816f276933c914c89e91e8f3305d47bb0e48 100644 (file)
@@ -181,7 +181,7 @@ static struct irq_domain_ops vt8500_irq_domain_ops = {
 static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs)
 {
        u32 stat, i;
-       int irqnr, virq;
+       int irqnr;
        void __iomem *base;
 
        /* Loop through each active controller */
@@ -198,8 +198,7 @@ static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs)
                                continue;
                }
 
-               virq = irq_find_mapping(intc[i].domain, irqnr);
-               handle_IRQ(virq, regs);
+               handle_domain_irq(intc[i].domain, irqnr, regs);
        }
 }
 
index ceb3a4318f73a2be0f8cad589b5e45da00becdf0..e4ef74ed454a8d6826947924523498191efb474a 100644 (file)
@@ -56,8 +56,7 @@ static void __exception_irq_entry zevio_handle_irq(struct pt_regs *regs)
 
        while (readl(zevio_irq_io + IO_STATUS)) {
                irqnr = readl(zevio_irq_io + IO_CURRENT);
-               irqnr = irq_find_mapping(zevio_irq_domain, irqnr);
-               handle_IRQ(irqnr, regs);
+               handle_domain_irq(zevio_irq_domain, irqnr, regs);
        };
 }
 
index 90e108f9e22ee29b04c94da98d74460eac4a7ff6..a210338cfeb182f6cb255041a0a120db7bafdce6 100644 (file)
@@ -468,6 +468,15 @@ config LEDS_OT200
          This option enables support for the LEDs on the Bachmann OT200.
          Say Y to enable LEDs on the Bachmann OT200.
 
+config LEDS_MENF21BMC
+       tristate "LED support for the MEN 14F021P00 BMC"
+       depends on LEDS_CLASS && MFD_MENF21BMC
+       help
+         Say Y here to include support for the MEN 14F021P00 BMC LEDs.
+
+         This driver can also be built as a module. If so the module
+         will be called leds-menf21bmc.
+
 comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
 
 config LEDS_BLINKM
index 822dd83ef97a7c904439d42b66ebd07049497f0b..a2b1647414655a8e3ec69a7e82eb8e26e8ba72eb 100644 (file)
@@ -55,6 +55,7 @@ obj-$(CONFIG_LEDS_LM355x)             += leds-lm355x.o
 obj-$(CONFIG_LEDS_BLINKM)              += leds-blinkm.o
 obj-$(CONFIG_LEDS_SYSCON)              += leds-syscon.o
 obj-$(CONFIG_LEDS_VERSATILE)           += leds-versatile.o
+obj-$(CONFIG_LEDS_MENF21BMC)           += leds-menf21bmc.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
diff --git a/drivers/leds/leds-menf21bmc.c b/drivers/leds/leds-menf21bmc.c
new file mode 100644 (file)
index 0000000..89dd577
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *  MEN 14F021P00 Board Management Controller (BMC) LEDs Driver.
+ *
+ *  This is the core LED driver of the MEN 14F021P00 BMC.
+ *  There are four LEDs available which can be switched on and off.
+ *  STATUS LED, HOT SWAP LED, USER LED 1, USER LED 2
+ *
+ *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ *  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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/i2c.h>
+
+#define BMC_CMD_LED_GET_SET    0xA0
+#define BMC_BIT_LED_STATUS     BIT(0)
+#define BMC_BIT_LED_HOTSWAP    BIT(1)
+#define BMC_BIT_LED_USER1      BIT(2)
+#define BMC_BIT_LED_USER2      BIT(3)
+
+struct menf21bmc_led {
+       struct led_classdev cdev;
+       u8 led_bit;
+       const char *name;
+       struct i2c_client *i2c_client;
+};
+
+static struct menf21bmc_led leds[] = {
+       {
+               .name = "menf21bmc:led_status",
+               .led_bit = BMC_BIT_LED_STATUS,
+       },
+       {
+               .name = "menf21bmc:led_hotswap",
+               .led_bit = BMC_BIT_LED_HOTSWAP,
+       },
+       {
+               .name = "menf21bmc:led_user1",
+               .led_bit = BMC_BIT_LED_USER1,
+       },
+       {
+               .name = "menf21bmc:led_user2",
+               .led_bit = BMC_BIT_LED_USER2,
+       }
+};
+
+static DEFINE_MUTEX(led_lock);
+
+static void
+menf21bmc_led_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+       int led_val;
+       struct menf21bmc_led *led = container_of(led_cdev,
+                                       struct menf21bmc_led, cdev);
+
+       mutex_lock(&led_lock);
+       led_val = i2c_smbus_read_byte_data(led->i2c_client,
+                                          BMC_CMD_LED_GET_SET);
+       if (led_val < 0)
+               goto err_out;
+
+       if (value == LED_OFF)
+               led_val &= ~led->led_bit;
+       else
+               led_val |= led->led_bit;
+
+       i2c_smbus_write_byte_data(led->i2c_client,
+                                 BMC_CMD_LED_GET_SET, led_val);
+err_out:
+       mutex_unlock(&led_lock);
+}
+
+static int menf21bmc_led_probe(struct platform_device *pdev)
+{
+       int i;
+       int ret;
+       struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent);
+
+       for (i = 0; i < ARRAY_SIZE(leds); i++) {
+               leds[i].cdev.name = leds[i].name;
+               leds[i].cdev.brightness_set = menf21bmc_led_set;
+               leds[i].i2c_client = i2c_client;
+               ret = led_classdev_register(&pdev->dev, &leds[i].cdev);
+               if (ret < 0)
+                       goto err_free_leds;
+       }
+       dev_info(&pdev->dev, "MEN 140F21P00 BMC LED device enabled\n");
+
+       return 0;
+
+err_free_leds:
+       dev_err(&pdev->dev, "failed to register LED device\n");
+
+       for (i = i - 1; i >= 0; i--)
+               led_classdev_unregister(&leds[i].cdev);
+
+       return ret;
+}
+
+static int menf21bmc_led_remove(struct platform_device *pdev)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(leds); i++)
+               led_classdev_unregister(&leds[i].cdev);
+
+       return 0;
+}
+
+static struct platform_driver menf21bmc_led = {
+       .probe          = menf21bmc_led_probe,
+       .remove         = menf21bmc_led_remove,
+       .driver         = {
+               .name           = "menf21bmc_led",
+               .owner          = THIS_MODULE,
+       },
+};
+
+module_platform_driver(menf21bmc_led);
+
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_DESCRIPTION("MEN 14F021P00 BMC led driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:menf21bmc_led");
index 4a0e786b7832bb8834ea4e6a46e20f4e24cf7898..5a6363d161a2f91e203a58923a3b09cc26a980b9 100644 (file)
@@ -319,14 +319,8 @@ static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
        }
 
 #ifdef CONFIG_LEDS_PCA9532_GPIO
-       if (data->gpio.dev) {
-               int err = gpiochip_remove(&data->gpio);
-               if (err) {
-                       dev_err(&data->client->dev, "%s failed, %d\n",
-                                               "gpiochip_remove()", err);
-                       return err;
-               }
-       }
+       if (data->gpio.dev)
+               gpiochip_remove(&data->gpio);
 #endif
 
        return 0;
index 3d9e267a56c428580bfaa074ce08053bd86ae34a..20fa8e77f1863c5aa9bbe78a5eb028d6c32cc49c 100644 (file)
@@ -667,11 +667,8 @@ static int tca6507_probe_gpios(struct i2c_client *client,
 
 static void tca6507_remove_gpio(struct tca6507_chip *tca)
 {
-       if (tca->gpio.ngpio) {
-               int err = gpiochip_remove(&tca->gpio);
-               dev_err(&tca->client->dev, "%s failed, %d\n",
-                       "gpiochip_remove()", err);
-       }
+       if (tca->gpio.ngpio)
+               gpiochip_remove(&tca->gpio);
 }
 #else /* CONFIG_GPIOLIB */
 static int tca6507_probe_gpios(struct i2c_client *client,
index 9e9c56758a0897ab5c7e7b0dfc14ac690e1bb043..226179b975a04eade83480bf94adad14c3b8373e 100644 (file)
@@ -411,6 +411,7 @@ adb_poll(void)
                return;
        adb_controller->poll();
 }
+EXPORT_SYMBOL(adb_poll);
 
 static void adb_sync_req_done(struct adb_request *req)
 {
@@ -460,6 +461,7 @@ adb_request(struct adb_request *req, void (*done)(struct adb_request *),
 
        return rc;
 }
+EXPORT_SYMBOL(adb_request);
 
  /* Ultimately this should return the number of devices with
     the given default id.
@@ -495,6 +497,7 @@ adb_register(int default_id, int handler_id, struct adb_ids *ids,
        mutex_unlock(&adb_handler_mutex);
        return ids->nids;
 }
+EXPORT_SYMBOL(adb_register);
 
 int
 adb_unregister(int index)
@@ -516,6 +519,7 @@ adb_unregister(int index)
        mutex_unlock(&adb_handler_mutex);
        return ret;
 }
+EXPORT_SYMBOL(adb_unregister);
 
 void
 adb_input(unsigned char *buf, int nb, int autopoll)
@@ -582,6 +586,7 @@ adb_try_handler_change(int address, int new_id)
        mutex_unlock(&adb_handler_mutex);
        return ret;
 }
+EXPORT_SYMBOL(adb_try_handler_change);
 
 int
 adb_get_infos(int address, int *original_address, int *handler_id)
index d61f271d22078ac13166e49a79d9c03c94d5f71c..bad18130f125f94fc23281e3edbcade0b39871d1 100644 (file)
@@ -379,6 +379,7 @@ cuda_request(struct adb_request *req, void (*done)(struct adb_request *),
     req->reply_expected = 1;
     return cuda_write(req);
 }
+EXPORT_SYMBOL(cuda_request);
 
 static int
 cuda_write(struct adb_request *req)
@@ -441,6 +442,7 @@ cuda_poll(void)
     if (cuda_irq)
        enable_irq(cuda_irq);
 }
+EXPORT_SYMBOL(cuda_poll);
 
 static irqreturn_t
 cuda_interrupt(int irq, void *arg)
index 897b10c85ad9273b75b9cb911b2f0c2853b9a167..8942bdacbf61befba5640d845d95d00e566cbc42 100644 (file)
@@ -4,7 +4,7 @@
  * see flexcop.c for copyright information
  */
 #ifndef __FLEXCOP_H__
-#define __FLEXCOP_H___
+#define __FLEXCOP_H__
 
 #define FC_LOG_PREFIX "b2c2-flexcop"
 #include "flexcop-common.h"
index 6c47f3fe9b0fc2ba2f8eb9263a2f0e9d0396d870..b7d63933dae61a6adbac2d95edfe1bb19c6aacf1 100644 (file)
@@ -311,7 +311,6 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma)
                }
        default:
                BUG();
-               return 0;
        }
 
        if (mutex_lock_interruptible(vdev->lock))
@@ -399,7 +398,6 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof
                return -EINVAL;
        default:
                BUG();
-               return 0;
        }
 }
 
@@ -423,7 +421,6 @@ static ssize_t fops_write(struct file *file, const char __user *data, size_t cou
                return -EINVAL;
        default:
                BUG();
-               return -EINVAL;
        }
 }
 
index 82769993eeb7b390cc5441cffc9bf5096b6411a9..82c7a1289f053313bb7f866bb08a9288e2a67db6 100644 (file)
@@ -157,6 +157,12 @@ static struct sms_board sms_boards[] = {
                .type = SMS_DENVER_2160,
                .default_mode = DEVICE_MODE_DAB_TDMB,
        },
+       [SMS1XXX_BOARD_PCTV_77E] = {
+               .name   = "Hauppauge microStick 77e",
+               .type   = SMS_NOVA_B0,
+               .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ_B0,
+               .default_mode = DEVICE_MODE_DVBT_BDA,
+       },
 };
 
 struct sms_board *sms_get_board(unsigned id)
index c63b544c49c5b721769131d8065f044eebd76e91..4c4caddf986984e2aa94ce2da8bf7f1229718767 100644 (file)
@@ -45,6 +45,7 @@
 #define SMS1XXX_BOARD_SIANO_RIO                18
 #define SMS1XXX_BOARD_SIANO_DENVER_1530        19
 #define SMS1XXX_BOARD_SIANO_DENVER_2160 20
+#define SMS1XXX_BOARD_PCTV_77E         21
 
 struct sms_board_gpio_cfg {
        int lna_vhf_exist;
index 050984c5b1e3701aa00ea0d074bca9700754d9d3..a3677438205e8c88824e6f10f854168755725da9 100644 (file)
@@ -2129,8 +2129,6 @@ int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num,
 
 static int __init smscore_module_init(void)
 {
-       int rc = 0;
-
        INIT_LIST_HEAD(&g_smscore_notifyees);
        INIT_LIST_HEAD(&g_smscore_devices);
        kmutex_init(&g_smscore_deviceslock);
@@ -2138,7 +2136,7 @@ static int __init smscore_module_init(void)
        INIT_LIST_HEAD(&g_smscore_registry);
        kmutex_init(&g_smscore_registrylock);
 
-       return rc;
+       return 0;
 }
 
 static void __exit smscore_module_exit(void)
index c0363f1b6c90e06c34409766e4a69c56fb98164f..abff803ad69a7e9e55818e5fd42d04ff475cd2d5 100644 (file)
@@ -1087,8 +1087,8 @@ static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
        struct dmxdev_filter *dmxdevfilter = file->private_data;
        unsigned int mask = 0;
 
-       if (!dmxdevfilter)
-               return -EINVAL;
+       if ((!dmxdevfilter) || dmxdevfilter->dev->exit)
+               return POLLERR;
 
        poll_wait(file, &dmxdevfilter->buffer.queue, wait);
 
@@ -1181,6 +1181,9 @@ static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
 
        dprintk("function : %s\n", __func__);
 
+       if (dmxdev->exit)
+               return POLLERR;
+
        poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
 
        if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
index 12ce19c98ded144bc37da6d7498edb5381b0d5ee..e07a84e7bc56d494ac795895b1b0a2ca4682bb2d 100644 (file)
 #define USB_PID_ITETECH_IT9135                         0x9135
 #define USB_PID_ITETECH_IT9135_9005                    0x9005
 #define USB_PID_ITETECH_IT9135_9006                    0x9006
+#define USB_PID_ITETECH_IT9303                         0x9306
 #define USB_PID_KWORLD_399U                            0xe399
 #define USB_PID_KWORLD_399U_2                          0xe400
 #define USB_PID_KWORLD_395U                            0xe396
 #define USB_PID_TECHNOTREND_CONNECT_S2400               0x3006
 #define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM     0x3009
 #define USB_PID_TECHNOTREND_CONNECT_CT3650             0x300d
+#define USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI                0x3012
 #define USB_PID_TECHNOTREND_TVSTICK_CT2_4400           0x3014
 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY       0x005a
 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2     0x0081
index c2a6a0a858130b4db284ca845134656afcffc2f0..b8579ee68bd603567fa8a8827b279683f00a7928 100644 (file)
@@ -1934,15 +1934,13 @@ static int dvb_frontend_ioctl_properties(struct file *file,
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int err = 0;
 
-       struct dtv_properties *tvps = NULL;
+       struct dtv_properties *tvps = parg;
        struct dtv_property *tvp = NULL;
        int i;
 
        dev_dbg(fe->dvb->device, "%s:\n", __func__);
 
-       if(cmd == FE_SET_PROPERTY) {
-               tvps = (struct dtv_properties __user *)parg;
-
+       if (cmd == FE_SET_PROPERTY) {
                dev_dbg(fe->dvb->device, "%s: properties.num = %d\n", __func__, tvps->num);
                dev_dbg(fe->dvb->device, "%s: properties.props = %p\n", __func__, tvps->props);
 
@@ -1957,7 +1955,8 @@ static int dvb_frontend_ioctl_properties(struct file *file,
                        goto out;
                }
 
-               if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+               if (copy_from_user(tvp, (void __user *)tvps->props,
+                                  tvps->num * sizeof(struct dtv_property))) {
                        err = -EFAULT;
                        goto out;
                }
@@ -1972,10 +1971,7 @@ static int dvb_frontend_ioctl_properties(struct file *file,
                if (c->state == DTV_TUNE)
                        dev_dbg(fe->dvb->device, "%s: Property cache is full, tuning\n", __func__);
 
-       } else
-       if(cmd == FE_GET_PROPERTY) {
-               tvps = (struct dtv_properties __user *)parg;
-
+       } else if (cmd == FE_GET_PROPERTY) {
                dev_dbg(fe->dvb->device, "%s: properties.num = %d\n", __func__, tvps->num);
                dev_dbg(fe->dvb->device, "%s: properties.props = %p\n", __func__, tvps->props);
 
@@ -1990,7 +1986,8 @@ static int dvb_frontend_ioctl_properties(struct file *file,
                        goto out;
                }
 
-               if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+               if (copy_from_user(tvp, (void __user *)tvps->props,
+                                  tvps->num * sizeof(struct dtv_property))) {
                        err = -EFAULT;
                        goto out;
                }
@@ -2012,7 +2009,8 @@ static int dvb_frontend_ioctl_properties(struct file *file,
                        (tvp + i)->result = err;
                }
 
-               if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) {
+               if (copy_to_user((void __user *)tvps->props, tvp,
+                                tvps->num * sizeof(struct dtv_property))) {
                        err = -EFAULT;
                        goto out;
                }
@@ -2072,6 +2070,23 @@ static int dtv_set_frontend(struct dvb_frontend *fe)
        case SYS_DVBC_ANNEX_C:
                rolloff = 113;
                break;
+       case SYS_DVBS:
+       case SYS_TURBO:
+               rolloff = 135;
+               break;
+       case SYS_DVBS2:
+               switch (c->rolloff) {
+               case ROLLOFF_20:
+                       rolloff = 120;
+                       break;
+               case ROLLOFF_25:
+                       rolloff = 125;
+                       break;
+               default:
+               case ROLLOFF_35:
+                       rolloff = 135;
+               }
+               break;
        default:
                break;
        }
@@ -2550,7 +2565,9 @@ int dvb_frontend_suspend(struct dvb_frontend *fe)
        dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num,
                        fe->id);
 
-       if (fe->ops.tuner_ops.sleep)
+       if (fe->ops.tuner_ops.suspend)
+               ret = fe->ops.tuner_ops.suspend(fe);
+       else if (fe->ops.tuner_ops.sleep)
                ret = fe->ops.tuner_ops.sleep(fe);
 
        if (fe->ops.sleep)
@@ -2572,7 +2589,9 @@ int dvb_frontend_resume(struct dvb_frontend *fe)
        if (fe->ops.init)
                ret = fe->ops.init(fe);
 
-       if (fe->ops.tuner_ops.init)
+       if (fe->ops.tuner_ops.resume)
+               ret = fe->ops.tuner_ops.resume(fe);
+       else if (fe->ops.tuner_ops.init)
                ret = fe->ops.tuner_ops.init(fe);
 
        fe->exit = DVB_FE_NO_EXIT;
index d398de4b6ef468cb7639ed8ce649ea0c4869291c..816269e5f7068b639a852651f4ad16817aea80a1 100644 (file)
@@ -201,6 +201,8 @@ struct dvb_tuner_ops {
        int (*release)(struct dvb_frontend *fe);
        int (*init)(struct dvb_frontend *fe);
        int (*sleep)(struct dvb_frontend *fe);
+       int (*suspend)(struct dvb_frontend *fe);
+       int (*resume)(struct dvb_frontend *fe);
 
        /** This is for simple PLLs - set all parameters in one go. */
        int (*set_params)(struct dvb_frontend *fe);
index a5712cd7c65ff084845cc86599d63f802ac81722..1100e98a7b1d36eaefe4440767d823d49d1ebf3b 100644 (file)
@@ -166,6 +166,31 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t
        return len;
 }
 
+ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
+                                 const u8 __user *buf, size_t len)
+{
+       int status;
+       size_t todo = len;
+       size_t split;
+
+       split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
+
+       if (split > 0) {
+               status = copy_from_user(rbuf->data+rbuf->pwrite, buf, split);
+               if (status)
+                       return len - todo;
+               buf += split;
+               todo -= split;
+               rbuf->pwrite = 0;
+       }
+       status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo);
+       if (status)
+               return len - todo;
+       rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+
+       return len;
+}
+
 ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
 {
        int status;
@@ -297,3 +322,4 @@ EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
 EXPORT_SYMBOL(dvb_ringbuffer_read_user);
 EXPORT_SYMBOL(dvb_ringbuffer_read);
 EXPORT_SYMBOL(dvb_ringbuffer_write);
+EXPORT_SYMBOL(dvb_ringbuffer_write_user);
index 41f04dae69b618badd91e783a1ad463b6bd0a393..9e1e11b7c39cb24113084234537cdf4c82254002 100644 (file)
@@ -133,6 +133,8 @@ extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf,
 */
 extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
                                    size_t len);
+extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
+                                        const u8 __user *buf, size_t len);
 
 
 /**
index fe0ddcca192c0f33f0bb7c3224bf0954b9fb5a35..5a134547e32579c3c6c59668c90986599982e82f 100644 (file)
@@ -471,6 +471,11 @@ config DVB_SI2168
        help
          Say Y when you want to support this frontend.
 
+config DVB_AS102_FE
+       tristate
+       depends on DVB_CORE
+       default DVB_AS102
+
 comment "DVB-C (cable) frontends"
        depends on DVB_CORE
 
@@ -643,6 +648,14 @@ config DVB_MB86A20S
          A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator.
          Say Y when you want to support this frontend.
 
+config DVB_TC90522
+       tristate "Toshiba TC90522"
+       depends on DVB_CORE && I2C
+       default m if !MEDIA_SUBDRV_AUTOSELECT
+       help
+         A Toshiba TC90522 2xISDB-T + 2xISDB-S demodulator.
+         Say Y when you want to support this frontend.
+
 comment "Digital terrestrial only tuners/PLL"
        depends on DVB_CORE
 
@@ -720,6 +733,13 @@ config DVB_A8293
        depends on DVB_CORE && I2C
        default m if !MEDIA_SUBDRV_AUTOSELECT
 
+config DVB_SP2
+       tristate "CIMaX SP2"
+       depends on DVB_CORE && I2C
+       default m if !MEDIA_SUBDRV_AUTOSELECT
+       help
+         CIMaX SP2/SP2HF Common Interface module.
+
 config DVB_LGS8GL5
        tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
        depends on DVB_CORE && I2C
index edf103d45920238df61e41917fb64126f9983eb1..ba59df63d05082062ca03bb2f4ca6ffe2418e7b0 100644 (file)
@@ -107,10 +107,12 @@ obj-$(CONFIG_DVB_DRXK) += drxk.o
 obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
 obj-$(CONFIG_DVB_SI2165) += si2165.o
 obj-$(CONFIG_DVB_A8293) += a8293.o
+obj-$(CONFIG_DVB_SP2) += sp2.o
 obj-$(CONFIG_DVB_TDA10071) += tda10071.o
 obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
 obj-$(CONFIG_DVB_RTL2832) += rtl2832.o
 obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o
 obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
 obj-$(CONFIG_DVB_AF9033) += af9033.o
-
+obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o
+obj-$(CONFIG_DVB_TC90522) += tc90522.o
index ecf6388d2200608bd5d4f5327d7349e55a0ad6ed..8001690d7576c21579db9f0a8ac08846a5981328 100644 (file)
@@ -683,7 +683,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
 
        switch (c->transmission_mode) {
        case TRANSMISSION_MODE_AUTO:
-               auto_mode = 1;
+               auto_mode = true;
                break;
        case TRANSMISSION_MODE_2K:
                break;
@@ -693,12 +693,12 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
        default:
                dev_dbg(&state->i2c->dev, "%s: invalid transmission_mode\n",
                                __func__);
-               auto_mode = 1;
+               auto_mode = true;
        }
 
        switch (c->guard_interval) {
        case GUARD_INTERVAL_AUTO:
-               auto_mode = 1;
+               auto_mode = true;
                break;
        case GUARD_INTERVAL_1_32:
                break;
@@ -714,12 +714,12 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
        default:
                dev_dbg(&state->i2c->dev, "%s: invalid guard_interval\n",
                                __func__);
-               auto_mode = 1;
+               auto_mode = true;
        }
 
        switch (c->hierarchy) {
        case HIERARCHY_AUTO:
-               auto_mode = 1;
+               auto_mode = true;
                break;
        case HIERARCHY_NONE:
                break;
@@ -734,12 +734,12 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
                break;
        default:
                dev_dbg(&state->i2c->dev, "%s: invalid hierarchy\n", __func__);
-               auto_mode = 1;
+               auto_mode = true;
        }
 
        switch (c->modulation) {
        case QAM_AUTO:
-               auto_mode = 1;
+               auto_mode = true;
                break;
        case QPSK:
                break;
@@ -751,7 +751,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
                break;
        default:
                dev_dbg(&state->i2c->dev, "%s: invalid modulation\n", __func__);
-               auto_mode = 1;
+               auto_mode = true;
        }
 
        /* Use HP. How and which case we can switch to LP? */
@@ -759,7 +759,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
 
        switch (c->code_rate_HP) {
        case FEC_AUTO:
-               auto_mode = 1;
+               auto_mode = true;
                break;
        case FEC_1_2:
                break;
@@ -778,12 +778,12 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
        default:
                dev_dbg(&state->i2c->dev, "%s: invalid code_rate_HP\n",
                                __func__);
-               auto_mode = 1;
+               auto_mode = true;
        }
 
        switch (c->code_rate_LP) {
        case FEC_AUTO:
-               auto_mode = 1;
+               auto_mode = true;
                break;
        case FEC_1_2:
                break;
@@ -804,7 +804,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
        default:
                dev_dbg(&state->i2c->dev, "%s: invalid code_rate_LP\n",
                                __func__);
-               auto_mode = 1;
+               auto_mode = true;
        }
 
        switch (c->bandwidth_hz) {
index 5c90ea683a7e732d3431c8973e43b4ffea649c55..63a89c1c59ff7454293f24604fecbe562cf3cabe 100644 (file)
 /* Max transfer size done by I2C transfer functions */
 #define MAX_XFER_SIZE  64
 
-struct af9033_state {
-       struct i2c_adapter *i2c;
+struct af9033_dev {
+       struct i2c_client *client;
        struct dvb_frontend fe;
        struct af9033_config cfg;
+       bool is_af9035;
+       bool is_it9135;
 
        u32 bandwidth_hz;
        bool ts_mode_parallel;
        bool ts_mode_serial;
 
-       u32 ber;
-       u32 ucb;
-       unsigned long last_stat_check;
+       fe_status_t fe_status;
+       u64 post_bit_error_prev; /* for old read_ber we return (curr - prev) */
+       u64 post_bit_error;
+       u64 post_bit_count;
+       u64 error_block_count;
+       u64 total_block_count;
+       struct delayed_work stat_work;
 };
 
 /* write multiple registers */
-static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val,
+static int af9033_wr_regs(struct af9033_dev *dev, u32 reg, const u8 *val,
                int len)
 {
        int ret;
        u8 buf[MAX_XFER_SIZE];
        struct i2c_msg msg[1] = {
                {
-                       .addr = state->cfg.i2c_addr,
+                       .addr = dev->client->addr,
                        .flags = 0,
                        .len = 3 + len,
                        .buf = buf,
@@ -54,9 +60,9 @@ static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val,
        };
 
        if (3 + len > sizeof(buf)) {
-               dev_warn(&state->i2c->dev,
-                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
-                        KBUILD_MODNAME, reg, len);
+               dev_warn(&dev->client->dev,
+                               "i2c wr reg=%04x: len=%d is too big!\n",
+                               reg, len);
                return -EINVAL;
        }
 
@@ -65,12 +71,12 @@ static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val,
        buf[2] = (reg >>  0) & 0xff;
        memcpy(&buf[3], val, len);
 
-       ret = i2c_transfer(state->i2c, msg, 1);
+       ret = i2c_transfer(dev->client->adapter, msg, 1);
        if (ret == 1) {
                ret = 0;
        } else {
-               dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%06x " \
-                               "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+               dev_warn(&dev->client->dev, "i2c wr failed=%d reg=%06x len=%d\n",
+                               ret, reg, len);
                ret = -EREMOTEIO;
        }
 
@@ -78,31 +84,31 @@ static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val,
 }
 
 /* read multiple registers */
-static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len)
+static int af9033_rd_regs(struct af9033_dev *dev, u32 reg, u8 *val, int len)
 {
        int ret;
        u8 buf[3] = { (reg >> 16) & 0xff, (reg >> 8) & 0xff,
                        (reg >> 0) & 0xff };
        struct i2c_msg msg[2] = {
                {
-                       .addr = state->cfg.i2c_addr,
+                       .addr = dev->client->addr,
                        .flags = 0,
                        .len = sizeof(buf),
                        .buf = buf
                }, {
-                       .addr = state->cfg.i2c_addr,
+                       .addr = dev->client->addr,
                        .flags = I2C_M_RD,
                        .len = len,
                        .buf = val
                }
        };
 
-       ret = i2c_transfer(state->i2c, msg, 2);
+       ret = i2c_transfer(dev->client->adapter, msg, 2);
        if (ret == 2) {
                ret = 0;
        } else {
-               dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%06x " \
-                               "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+               dev_warn(&dev->client->dev, "i2c rd failed=%d reg=%06x len=%d\n",
+                               ret, reg, len);
                ret = -EREMOTEIO;
        }
 
@@ -111,19 +117,19 @@ static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len)
 
 
 /* write single register */
-static int af9033_wr_reg(struct af9033_state *state, u32 reg, u8 val)
+static int af9033_wr_reg(struct af9033_dev *dev, u32 reg, u8 val)
 {
-       return af9033_wr_regs(state, reg, &val, 1);
+       return af9033_wr_regs(dev, reg, &val, 1);
 }
 
 /* read single register */
-static int af9033_rd_reg(struct af9033_state *state, u32 reg, u8 *val)
+static int af9033_rd_reg(struct af9033_dev *dev, u32 reg, u8 *val)
 {
-       return af9033_rd_regs(state, reg, val, 1);
+       return af9033_rd_regs(dev, reg, val, 1);
 }
 
 /* write single register with mask */
-static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val,
+static int af9033_wr_reg_mask(struct af9033_dev *dev, u32 reg, u8 val,
                u8 mask)
 {
        int ret;
@@ -131,7 +137,7 @@ static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val,
 
        /* no need for read if whole reg is written */
        if (mask != 0xff) {
-               ret = af9033_rd_regs(state, reg, &tmp, 1);
+               ret = af9033_rd_regs(dev, reg, &tmp, 1);
                if (ret)
                        return ret;
 
@@ -140,17 +146,17 @@ static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val,
                val |= tmp;
        }
 
-       return af9033_wr_regs(state, reg, &val, 1);
+       return af9033_wr_regs(dev, reg, &val, 1);
 }
 
 /* read single register with mask */
-static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val,
+static int af9033_rd_reg_mask(struct af9033_dev *dev, u32 reg, u8 *val,
                u8 mask)
 {
        int ret, i;
        u8 tmp;
 
-       ret = af9033_rd_regs(state, reg, &tmp, 1);
+       ret = af9033_rd_regs(dev, reg, &tmp, 1);
        if (ret)
                return ret;
 
@@ -167,18 +173,17 @@ static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val,
 }
 
 /* write reg val table using reg addr auto increment */
-static int af9033_wr_reg_val_tab(struct af9033_state *state,
+static int af9033_wr_reg_val_tab(struct af9033_dev *dev,
                const struct reg_val *tab, int tab_len)
 {
 #define MAX_TAB_LEN 212
        int ret, i, j;
        u8 buf[1 + MAX_TAB_LEN];
 
-       dev_dbg(&state->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len);
+       dev_dbg(&dev->client->dev, "tab_len=%d\n", tab_len);
 
        if (tab_len > sizeof(buf)) {
-               dev_warn(&state->i2c->dev, "%s: tab len %d is too big\n",
-                               KBUILD_MODNAME, tab_len);
+               dev_warn(&dev->client->dev, "tab len %d is too big\n", tab_len);
                return -EINVAL;
        }
 
@@ -186,7 +191,7 @@ static int af9033_wr_reg_val_tab(struct af9033_state *state,
                buf[j] = tab[i].val;
 
                if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1) {
-                       ret = af9033_wr_regs(state, tab[i].reg - j, buf, j + 1);
+                       ret = af9033_wr_regs(dev, tab[i].reg - j, buf, j + 1);
                        if (ret < 0)
                                goto err;
 
@@ -199,16 +204,16 @@ static int af9033_wr_reg_val_tab(struct af9033_state *state,
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
-static u32 af9033_div(struct af9033_state *state, u32 a, u32 b, u32 x)
+static u32 af9033_div(struct af9033_dev *dev, u32 a, u32 b, u32 x)
 {
        u32 r = 0, c = 0, i;
 
-       dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d\n", __func__, a, b, x);
+       dev_dbg(&dev->client->dev, "a=%d b=%d x=%d\n", a, b, x);
 
        if (a > b) {
                c = a / b;
@@ -225,22 +230,15 @@ static u32 af9033_div(struct af9033_state *state, u32 a, u32 b, u32 x)
        }
        r = (c << (u32)x) + r;
 
-       dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d r=%d r=%x\n",
-                       __func__, a, b, x, r, r);
+       dev_dbg(&dev->client->dev, "a=%d b=%d x=%d r=%d r=%x\n", a, b, x, r, r);
 
        return r;
 }
 
-static void af9033_release(struct dvb_frontend *fe)
-{
-       struct af9033_state *state = fe->demodulator_priv;
-
-       kfree(state);
-}
-
 static int af9033_init(struct dvb_frontend *fe)
 {
-       struct af9033_state *state = fe->demodulator_priv;
+       struct af9033_dev *dev = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int ret, i, len;
        const struct reg_val *init;
        u8 buf[4];
@@ -248,7 +246,7 @@ static int af9033_init(struct dvb_frontend *fe)
        struct reg_val_mask tab[] = {
                { 0x80fb24, 0x00, 0x08 },
                { 0x80004c, 0x00, 0xff },
-               { 0x00f641, state->cfg.tuner, 0xff },
+               { 0x00f641, dev->cfg.tuner, 0xff },
                { 0x80f5ca, 0x01, 0x01 },
                { 0x80f715, 0x01, 0x01 },
                { 0x00f41f, 0x04, 0x04 },
@@ -267,88 +265,82 @@ static int af9033_init(struct dvb_frontend *fe)
                { 0x00d830, 0x01, 0xff },
                { 0x00d831, 0x00, 0xff },
                { 0x00d832, 0x00, 0xff },
-               { 0x80f985, state->ts_mode_serial, 0x01 },
-               { 0x80f986, state->ts_mode_parallel, 0x01 },
+               { 0x80f985, dev->ts_mode_serial, 0x01 },
+               { 0x80f986, dev->ts_mode_parallel, 0x01 },
                { 0x00d827, 0x00, 0xff },
                { 0x00d829, 0x00, 0xff },
-               { 0x800045, state->cfg.adc_multiplier, 0xff },
+               { 0x800045, dev->cfg.adc_multiplier, 0xff },
        };
 
        /* program clock control */
-       clock_cw = af9033_div(state, state->cfg.clock, 1000000ul, 19ul);
+       clock_cw = af9033_div(dev, dev->cfg.clock, 1000000ul, 19ul);
        buf[0] = (clock_cw >>  0) & 0xff;
        buf[1] = (clock_cw >>  8) & 0xff;
        buf[2] = (clock_cw >> 16) & 0xff;
        buf[3] = (clock_cw >> 24) & 0xff;
 
-       dev_dbg(&state->i2c->dev, "%s: clock=%d clock_cw=%08x\n",
-                       __func__, state->cfg.clock, clock_cw);
+       dev_dbg(&dev->client->dev, "clock=%d clock_cw=%08x\n",
+                       dev->cfg.clock, clock_cw);
 
-       ret = af9033_wr_regs(state, 0x800025, buf, 4);
+       ret = af9033_wr_regs(dev, 0x800025, buf, 4);
        if (ret < 0)
                goto err;
 
        /* program ADC control */
        for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
-               if (clock_adc_lut[i].clock == state->cfg.clock)
+               if (clock_adc_lut[i].clock == dev->cfg.clock)
                        break;
        }
 
-       adc_cw = af9033_div(state, clock_adc_lut[i].adc, 1000000ul, 19ul);
+       adc_cw = af9033_div(dev, clock_adc_lut[i].adc, 1000000ul, 19ul);
        buf[0] = (adc_cw >>  0) & 0xff;
        buf[1] = (adc_cw >>  8) & 0xff;
        buf[2] = (adc_cw >> 16) & 0xff;
 
-       dev_dbg(&state->i2c->dev, "%s: adc=%d adc_cw=%06x\n",
-                       __func__, clock_adc_lut[i].adc, adc_cw);
+       dev_dbg(&dev->client->dev, "adc=%d adc_cw=%06x\n",
+                       clock_adc_lut[i].adc, adc_cw);
 
-       ret = af9033_wr_regs(state, 0x80f1cd, buf, 3);
+       ret = af9033_wr_regs(dev, 0x80f1cd, buf, 3);
        if (ret < 0)
                goto err;
 
        /* program register table */
        for (i = 0; i < ARRAY_SIZE(tab); i++) {
-               ret = af9033_wr_reg_mask(state, tab[i].reg, tab[i].val,
+               ret = af9033_wr_reg_mask(dev, tab[i].reg, tab[i].val,
                                tab[i].mask);
                if (ret < 0)
                        goto err;
        }
 
-       /* feed clock to RF tuner */
-       switch (state->cfg.tuner) {
-       case AF9033_TUNER_IT9135_38:
-       case AF9033_TUNER_IT9135_51:
-       case AF9033_TUNER_IT9135_52:
-       case AF9033_TUNER_IT9135_60:
-       case AF9033_TUNER_IT9135_61:
-       case AF9033_TUNER_IT9135_62:
-               ret = af9033_wr_reg(state, 0x80fba8, 0x00);
+       /* clock output */
+       if (dev->cfg.dyn0_clk) {
+               ret = af9033_wr_reg(dev, 0x80fba8, 0x00);
                if (ret < 0)
                        goto err;
        }
 
        /* settings for TS interface */
-       if (state->cfg.ts_mode == AF9033_TS_MODE_USB) {
-               ret = af9033_wr_reg_mask(state, 0x80f9a5, 0x00, 0x01);
+       if (dev->cfg.ts_mode == AF9033_TS_MODE_USB) {
+               ret = af9033_wr_reg_mask(dev, 0x80f9a5, 0x00, 0x01);
                if (ret < 0)
                        goto err;
 
-               ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x01, 0x01);
+               ret = af9033_wr_reg_mask(dev, 0x80f9b5, 0x01, 0x01);
                if (ret < 0)
                        goto err;
        } else {
-               ret = af9033_wr_reg_mask(state, 0x80f990, 0x00, 0x01);
+               ret = af9033_wr_reg_mask(dev, 0x80f990, 0x00, 0x01);
                if (ret < 0)
                        goto err;
 
-               ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x00, 0x01);
+               ret = af9033_wr_reg_mask(dev, 0x80f9b5, 0x00, 0x01);
                if (ret < 0)
                        goto err;
        }
 
        /* load OFSM settings */
-       dev_dbg(&state->i2c->dev, "%s: load ofsm settings\n", __func__);
-       switch (state->cfg.tuner) {
+       dev_dbg(&dev->client->dev, "load ofsm settings\n");
+       switch (dev->cfg.tuner) {
        case AF9033_TUNER_IT9135_38:
        case AF9033_TUNER_IT9135_51:
        case AF9033_TUNER_IT9135_52:
@@ -367,14 +359,13 @@ static int af9033_init(struct dvb_frontend *fe)
                break;
        }
 
-       ret = af9033_wr_reg_val_tab(state, init, len);
+       ret = af9033_wr_reg_val_tab(dev, init, len);
        if (ret < 0)
                goto err;
 
        /* load tuner specific settings */
-       dev_dbg(&state->i2c->dev, "%s: load tuner specific settings\n",
-                       __func__);
-       switch (state->cfg.tuner) {
+       dev_dbg(&dev->client->dev, "load tuner specific settings\n");
+       switch (dev->cfg.tuner) {
        case AF9033_TUNER_TUA9001:
                len = ARRAY_SIZE(tuner_init_tua9001);
                init = tuner_init_tua9001;
@@ -424,90 +415,108 @@ static int af9033_init(struct dvb_frontend *fe)
                init = tuner_init_it9135_62;
                break;
        default:
-               dev_dbg(&state->i2c->dev, "%s: unsupported tuner ID=%d\n",
-                               __func__, state->cfg.tuner);
+               dev_dbg(&dev->client->dev, "unsupported tuner ID=%d\n",
+                               dev->cfg.tuner);
                ret = -ENODEV;
                goto err;
        }
 
-       ret = af9033_wr_reg_val_tab(state, init, len);
+       ret = af9033_wr_reg_val_tab(dev, init, len);
        if (ret < 0)
                goto err;
 
-       if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) {
-               ret = af9033_wr_reg_mask(state, 0x00d91c, 0x01, 0x01);
+       if (dev->cfg.ts_mode == AF9033_TS_MODE_SERIAL) {
+               ret = af9033_wr_reg_mask(dev, 0x00d91c, 0x01, 0x01);
                if (ret < 0)
                        goto err;
 
-               ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01);
+               ret = af9033_wr_reg_mask(dev, 0x00d917, 0x00, 0x01);
                if (ret < 0)
                        goto err;
 
-               ret = af9033_wr_reg_mask(state, 0x00d916, 0x00, 0x01);
+               ret = af9033_wr_reg_mask(dev, 0x00d916, 0x00, 0x01);
                if (ret < 0)
                        goto err;
        }
 
-       switch (state->cfg.tuner) {
+       switch (dev->cfg.tuner) {
        case AF9033_TUNER_IT9135_60:
        case AF9033_TUNER_IT9135_61:
        case AF9033_TUNER_IT9135_62:
-               ret = af9033_wr_reg(state, 0x800000, 0x01);
+               ret = af9033_wr_reg(dev, 0x800000, 0x01);
                if (ret < 0)
                        goto err;
        }
 
-       state->bandwidth_hz = 0; /* force to program all parameters */
+       dev->bandwidth_hz = 0; /* force to program all parameters */
+       /* init stats here in order signal app which stats are supported */
+       c->strength.len = 1;
+       c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       c->cnr.len = 1;
+       c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       c->block_count.len = 1;
+       c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       c->block_error.len = 1;
+       c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       c->post_bit_count.len = 1;
+       c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       c->post_bit_error.len = 1;
+       c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       /* start statistics polling */
+       schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
 
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
 static int af9033_sleep(struct dvb_frontend *fe)
 {
-       struct af9033_state *state = fe->demodulator_priv;
+       struct af9033_dev *dev = fe->demodulator_priv;
        int ret, i;
        u8 tmp;
 
-       ret = af9033_wr_reg(state, 0x80004c, 1);
+       /* stop statistics polling */
+       cancel_delayed_work_sync(&dev->stat_work);
+
+       ret = af9033_wr_reg(dev, 0x80004c, 1);
        if (ret < 0)
                goto err;
 
-       ret = af9033_wr_reg(state, 0x800000, 0);
+       ret = af9033_wr_reg(dev, 0x800000, 0);
        if (ret < 0)
                goto err;
 
        for (i = 100, tmp = 1; i && tmp; i--) {
-               ret = af9033_rd_reg(state, 0x80004c, &tmp);
+               ret = af9033_rd_reg(dev, 0x80004c, &tmp);
                if (ret < 0)
                        goto err;
 
                usleep_range(200, 10000);
        }
 
-       dev_dbg(&state->i2c->dev, "%s: loop=%d\n", __func__, i);
+       dev_dbg(&dev->client->dev, "loop=%d\n", i);
 
        if (i == 0) {
                ret = -ETIMEDOUT;
                goto err;
        }
 
-       ret = af9033_wr_reg_mask(state, 0x80fb24, 0x08, 0x08);
+       ret = af9033_wr_reg_mask(dev, 0x80fb24, 0x08, 0x08);
        if (ret < 0)
                goto err;
 
        /* prevent current leak (?) */
-       if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) {
+       if (dev->cfg.ts_mode == AF9033_TS_MODE_SERIAL) {
                /* enable parallel TS */
-               ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01);
+               ret = af9033_wr_reg_mask(dev, 0x00d917, 0x00, 0x01);
                if (ret < 0)
                        goto err;
 
-               ret = af9033_wr_reg_mask(state, 0x00d916, 0x01, 0x01);
+               ret = af9033_wr_reg_mask(dev, 0x00d916, 0x01, 0x01);
                if (ret < 0)
                        goto err;
        }
@@ -515,7 +524,7 @@ static int af9033_sleep(struct dvb_frontend *fe)
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -533,14 +542,14 @@ static int af9033_get_tune_settings(struct dvb_frontend *fe,
 
 static int af9033_set_frontend(struct dvb_frontend *fe)
 {
-       struct af9033_state *state = fe->demodulator_priv;
+       struct af9033_dev *dev = fe->demodulator_priv;
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int ret, i, spec_inv, sampling_freq;
        u8 tmp, buf[3], bandwidth_reg_val;
        u32 if_frequency, freq_cw, adc_freq;
 
-       dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n",
-                       __func__, c->frequency, c->bandwidth_hz);
+       dev_dbg(&dev->client->dev, "frequency=%d bandwidth_hz=%d\n",
+                       c->frequency, c->bandwidth_hz);
 
        /* check bandwidth */
        switch (c->bandwidth_hz) {
@@ -554,8 +563,7 @@ static int af9033_set_frontend(struct dvb_frontend *fe)
                bandwidth_reg_val = 0x02;
                break;
        default:
-               dev_dbg(&state->i2c->dev, "%s: invalid bandwidth_hz\n",
-                               __func__);
+               dev_dbg(&dev->client->dev, "invalid bandwidth_hz\n");
                ret = -EINVAL;
                goto err;
        }
@@ -565,23 +573,23 @@ static int af9033_set_frontend(struct dvb_frontend *fe)
                fe->ops.tuner_ops.set_params(fe);
 
        /* program CFOE coefficients */
-       if (c->bandwidth_hz != state->bandwidth_hz) {
+       if (c->bandwidth_hz != dev->bandwidth_hz) {
                for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) {
-                       if (coeff_lut[i].clock == state->cfg.clock &&
+                       if (coeff_lut[i].clock == dev->cfg.clock &&
                                coeff_lut[i].bandwidth_hz == c->bandwidth_hz) {
                                break;
                        }
                }
-               ret =  af9033_wr_regs(state, 0x800001,
+               ret =  af9033_wr_regs(dev, 0x800001,
                                coeff_lut[i].val, sizeof(coeff_lut[i].val));
        }
 
        /* program frequency control */
-       if (c->bandwidth_hz != state->bandwidth_hz) {
-               spec_inv = state->cfg.spec_inv ? -1 : 1;
+       if (c->bandwidth_hz != dev->bandwidth_hz) {
+               spec_inv = dev->cfg.spec_inv ? -1 : 1;
 
                for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
-                       if (clock_adc_lut[i].clock == state->cfg.clock)
+                       if (clock_adc_lut[i].clock == dev->cfg.clock)
                                break;
                }
                adc_freq = clock_adc_lut[i].adc;
@@ -602,12 +610,12 @@ static int af9033_set_frontend(struct dvb_frontend *fe)
                else
                        sampling_freq *= -1;
 
-               freq_cw = af9033_div(state, sampling_freq, adc_freq, 23ul);
+               freq_cw = af9033_div(dev, sampling_freq, adc_freq, 23ul);
 
                if (spec_inv == -1)
                        freq_cw = 0x800000 - freq_cw;
 
-               if (state->cfg.adc_multiplier == AF9033_ADC_MULTIPLIER_2X)
+               if (dev->cfg.adc_multiplier == AF9033_ADC_MULTIPLIER_2X)
                        freq_cw /= 2;
 
                buf[0] = (freq_cw >>  0) & 0xff;
@@ -618,26 +626,26 @@ static int af9033_set_frontend(struct dvb_frontend *fe)
                if (if_frequency == 0)
                        buf[2] = 0;
 
-               ret = af9033_wr_regs(state, 0x800029, buf, 3);
+               ret = af9033_wr_regs(dev, 0x800029, buf, 3);
                if (ret < 0)
                        goto err;
 
-               state->bandwidth_hz = c->bandwidth_hz;
+               dev->bandwidth_hz = c->bandwidth_hz;
        }
 
-       ret = af9033_wr_reg_mask(state, 0x80f904, bandwidth_reg_val, 0x03);
+       ret = af9033_wr_reg_mask(dev, 0x80f904, bandwidth_reg_val, 0x03);
        if (ret < 0)
                goto err;
 
-       ret = af9033_wr_reg(state, 0x800040, 0x00);
+       ret = af9033_wr_reg(dev, 0x800040, 0x00);
        if (ret < 0)
                goto err;
 
-       ret = af9033_wr_reg(state, 0x800047, 0x00);
+       ret = af9033_wr_reg(dev, 0x800047, 0x00);
        if (ret < 0)
                goto err;
 
-       ret = af9033_wr_reg_mask(state, 0x80f999, 0x00, 0x01);
+       ret = af9033_wr_reg_mask(dev, 0x80f999, 0x00, 0x01);
        if (ret < 0)
                goto err;
 
@@ -646,33 +654,33 @@ static int af9033_set_frontend(struct dvb_frontend *fe)
        else
                tmp = 0x01; /* UHF */
 
-       ret = af9033_wr_reg(state, 0x80004b, tmp);
+       ret = af9033_wr_reg(dev, 0x80004b, tmp);
        if (ret < 0)
                goto err;
 
-       ret = af9033_wr_reg(state, 0x800000, 0x00);
+       ret = af9033_wr_reg(dev, 0x800000, 0x00);
        if (ret < 0)
                goto err;
 
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
 static int af9033_get_frontend(struct dvb_frontend *fe)
 {
-       struct af9033_state *state = fe->demodulator_priv;
+       struct af9033_dev *dev = fe->demodulator_priv;
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int ret;
        u8 buf[8];
 
-       dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+       dev_dbg(&dev->client->dev, "\n");
 
        /* read all needed registers */
-       ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf));
+       ret = af9033_rd_regs(dev, 0x80f900, buf, sizeof(buf));
        if (ret < 0)
                goto err;
 
@@ -784,21 +792,21 @@ static int af9033_get_frontend(struct dvb_frontend *fe)
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
 static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status)
 {
-       struct af9033_state *state = fe->demodulator_priv;
+       struct af9033_dev *dev = fe->demodulator_priv;
        int ret;
        u8 tmp;
 
        *status = 0;
 
        /* radio channel status, 0=no result, 1=has signal, 2=no signal */
-       ret = af9033_rd_reg(state, 0x800047, &tmp);
+       ret = af9033_rd_reg(dev, 0x800047, &tmp);
        if (ret < 0)
                goto err;
 
@@ -808,7 +816,7 @@ static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status)
 
        if (tmp != 0x02) {
                /* TPS lock */
-               ret = af9033_rd_reg_mask(state, 0x80f5a9, &tmp, 0x01);
+               ret = af9033_rd_reg_mask(dev, 0x80f5a9, &tmp, 0x01);
                if (ret < 0)
                        goto err;
 
@@ -817,7 +825,7 @@ static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status)
                                        FE_HAS_VITERBI;
 
                /* full lock */
-               ret = af9033_rd_reg_mask(state, 0x80f999, &tmp, 0x01);
+               ret = af9033_rd_reg_mask(dev, 0x80f999, &tmp, 0x01);
                if (ret < 0)
                        goto err;
 
@@ -827,76 +835,38 @@ static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status)
                                        FE_HAS_LOCK;
        }
 
+       dev->fe_status = *status;
+
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
 static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
-       struct af9033_state *state = fe->demodulator_priv;
-       int ret, i, len;
-       u8 buf[3], tmp;
-       u32 snr_val;
-       const struct val_snr *uninitialized_var(snr_lut);
-
-       /* read value */
-       ret = af9033_rd_regs(state, 0x80002c, buf, 3);
-       if (ret < 0)
-               goto err;
+       struct af9033_dev *dev = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
 
-       snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0];
-
-       /* read current modulation */
-       ret = af9033_rd_reg(state, 0x80f903, &tmp);
-       if (ret < 0)
-               goto err;
-
-       switch ((tmp >> 0) & 3) {
-       case 0:
-               len = ARRAY_SIZE(qpsk_snr_lut);
-               snr_lut = qpsk_snr_lut;
-               break;
-       case 1:
-               len = ARRAY_SIZE(qam16_snr_lut);
-               snr_lut = qam16_snr_lut;
-               break;
-       case 2:
-               len = ARRAY_SIZE(qam64_snr_lut);
-               snr_lut = qam64_snr_lut;
-               break;
-       default:
-               goto err;
-       }
-
-       for (i = 0; i < len; i++) {
-               tmp = snr_lut[i].snr;
-
-               if (snr_val < snr_lut[i].val)
-                       break;
-       }
-
-       *snr = tmp * 10; /* dB/10 */
+       /* use DVBv5 CNR */
+       if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
+               *snr = div_s64(c->cnr.stat[0].svalue, 100); /* 1000x => 10x */
+       else
+               *snr = 0;
 
        return 0;
-
-err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
-
-       return ret;
 }
 
 static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
 {
-       struct af9033_state *state = fe->demodulator_priv;
+       struct af9033_dev *dev = fe->demodulator_priv;
        int ret;
        u8 strength2;
 
        /* read signal strength of 0-100 scale */
-       ret = af9033_rd_reg(state, 0x800048, &strength2);
+       ret = af9033_rd_reg(dev, 0x800048, &strength2);
        if (ret < 0)
                goto err;
 
@@ -906,244 +876,225 @@ static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
-
-       return ret;
-}
-
-static int af9033_update_ch_stat(struct af9033_state *state)
-{
-       int ret = 0;
-       u32 err_cnt, bit_cnt;
-       u16 abort_cnt;
-       u8 buf[7];
-
-       /* only update data every half second */
-       if (time_after(jiffies, state->last_stat_check + msecs_to_jiffies(500))) {
-               ret = af9033_rd_regs(state, 0x800032, buf, sizeof(buf));
-               if (ret < 0)
-                       goto err;
-               /* in 8 byte packets? */
-               abort_cnt = (buf[1] << 8) + buf[0];
-               /* in bits */
-               err_cnt = (buf[4] << 16) + (buf[3] << 8) + buf[2];
-               /* in 8 byte packets? always(?) 0x2710 = 10000 */
-               bit_cnt = (buf[6] << 8) + buf[5];
-
-               if (bit_cnt < abort_cnt) {
-                       abort_cnt = 1000;
-                       state->ber = 0xffffffff;
-               } else {
-                       /* 8 byte packets, that have not been rejected already */
-                       bit_cnt -= (u32)abort_cnt;
-                       if (bit_cnt == 0) {
-                               state->ber = 0xffffffff;
-                       } else {
-                               err_cnt -= (u32)abort_cnt * 8 * 8;
-                               bit_cnt *= 8 * 8;
-                               state->ber = err_cnt * (0xffffffff / bit_cnt);
-                       }
-               }
-               state->ucb += abort_cnt;
-               state->last_stat_check = jiffies;
-       }
-
-       return 0;
-err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
 static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber)
 {
-       struct af9033_state *state = fe->demodulator_priv;
-       int ret;
-
-       ret = af9033_update_ch_stat(state);
-       if (ret < 0)
-               return ret;
+       struct af9033_dev *dev = fe->demodulator_priv;
 
-       *ber = state->ber;
+       *ber = (dev->post_bit_error - dev->post_bit_error_prev);
+       dev->post_bit_error_prev = dev->post_bit_error;
 
        return 0;
 }
 
 static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 {
-       struct af9033_state *state = fe->demodulator_priv;
-       int ret;
-
-       ret = af9033_update_ch_stat(state);
-       if (ret < 0)
-               return ret;
-
-       *ucblocks = state->ucb;
+       struct af9033_dev *dev = fe->demodulator_priv;
 
+       *ucblocks = dev->error_block_count;
        return 0;
 }
 
 static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
 {
-       struct af9033_state *state = fe->demodulator_priv;
+       struct af9033_dev *dev = fe->demodulator_priv;
        int ret;
 
-       dev_dbg(&state->i2c->dev, "%s: enable=%d\n", __func__, enable);
+       dev_dbg(&dev->client->dev, "enable=%d\n", enable);
 
-       ret = af9033_wr_reg_mask(state, 0x00fa04, enable, 0x01);
+       ret = af9033_wr_reg_mask(dev, 0x00fa04, enable, 0x01);
        if (ret < 0)
                goto err;
 
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
 static int af9033_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
 {
-       struct af9033_state *state = fe->demodulator_priv;
+       struct af9033_dev *dev = fe->demodulator_priv;
        int ret;
 
-       dev_dbg(&state->i2c->dev, "%s: onoff=%d\n", __func__, onoff);
+       dev_dbg(&dev->client->dev, "onoff=%d\n", onoff);
 
-       ret = af9033_wr_reg_mask(state, 0x80f993, onoff, 0x01);
+       ret = af9033_wr_reg_mask(dev, 0x80f993, onoff, 0x01);
        if (ret < 0)
                goto err;
 
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
-static int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid, int onoff)
+static int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid,
+               int onoff)
 {
-       struct af9033_state *state = fe->demodulator_priv;
+       struct af9033_dev *dev = fe->demodulator_priv;
        int ret;
        u8 wbuf[2] = {(pid >> 0) & 0xff, (pid >> 8) & 0xff};
 
-       dev_dbg(&state->i2c->dev, "%s: index=%d pid=%04x onoff=%d\n",
-                       __func__, index, pid, onoff);
+       dev_dbg(&dev->client->dev, "index=%d pid=%04x onoff=%d\n",
+                       index, pid, onoff);
 
        if (pid > 0x1fff)
                return 0;
 
-       ret = af9033_wr_regs(state, 0x80f996, wbuf, 2);
+       ret = af9033_wr_regs(dev, 0x80f996, wbuf, 2);
        if (ret < 0)
                goto err;
 
-       ret = af9033_wr_reg(state, 0x80f994, onoff);
+       ret = af9033_wr_reg(dev, 0x80f994, onoff);
        if (ret < 0)
                goto err;
 
-       ret = af9033_wr_reg(state, 0x80f995, index);
+       ret = af9033_wr_reg(dev, 0x80f995, index);
        if (ret < 0)
                goto err;
 
        return 0;
 
 err:
-       dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
-static struct dvb_frontend_ops af9033_ops;
-
-struct dvb_frontend *af9033_attach(const struct af9033_config *config,
-                                  struct i2c_adapter *i2c,
-                                  struct af9033_ops *ops)
+static void af9033_stat_work(struct work_struct *work)
 {
-       int ret;
-       struct af9033_state *state;
-       u8 buf[8];
+       struct af9033_dev *dev = container_of(work, struct af9033_dev, stat_work.work);
+       struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+       int ret, tmp, i, len;
+       u8 u8tmp, buf[7];
+
+       dev_dbg(&dev->client->dev, "\n");
+
+       /* signal strength */
+       if (dev->fe_status & FE_HAS_SIGNAL) {
+               if (dev->is_af9035) {
+                       ret = af9033_rd_reg(dev, 0x80004a, &u8tmp);
+                       tmp = -u8tmp * 1000;
+               } else {
+                       ret = af9033_rd_reg(dev, 0x8000f7, &u8tmp);
+                       tmp = (u8tmp - 100) * 1000;
+               }
+               if (ret)
+                       goto err;
 
-       dev_dbg(&i2c->dev, "%s:\n", __func__);
+               c->strength.len = 1;
+               c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+               c->strength.stat[0].svalue = tmp;
+       } else {
+               c->strength.len = 1;
+               c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       }
 
-       /* allocate memory for the internal state */
-       state = kzalloc(sizeof(struct af9033_state), GFP_KERNEL);
-       if (state == NULL)
-               goto err;
+       /* CNR */
+       if (dev->fe_status & FE_HAS_VITERBI) {
+               u32 snr_val;
+               const struct val_snr *snr_lut;
 
-       /* setup the state */
-       state->i2c = i2c;
-       memcpy(&state->cfg, config, sizeof(struct af9033_config));
+               /* read value */
+               ret = af9033_rd_regs(dev, 0x80002c, buf, 3);
+               if (ret)
+                       goto err;
 
-       if (state->cfg.clock != 12000000) {
-               dev_err(&state->i2c->dev, "%s: af9033: unsupported clock=%d, " \
-                               "only 12000000 Hz is supported currently\n",
-                               KBUILD_MODNAME, state->cfg.clock);
-               goto err;
-       }
+               snr_val = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
 
-       /* firmware version */
-       ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4);
-       if (ret < 0)
-               goto err;
+               /* read current modulation */
+               ret = af9033_rd_reg(dev, 0x80f903, &u8tmp);
+               if (ret)
+                       goto err;
 
-       ret = af9033_rd_regs(state, 0x804191, &buf[4], 4);
-       if (ret < 0)
-               goto err;
+               switch ((u8tmp >> 0) & 3) {
+               case 0:
+                       len = ARRAY_SIZE(qpsk_snr_lut);
+                       snr_lut = qpsk_snr_lut;
+                       break;
+               case 1:
+                       len = ARRAY_SIZE(qam16_snr_lut);
+                       snr_lut = qam16_snr_lut;
+                       break;
+               case 2:
+                       len = ARRAY_SIZE(qam64_snr_lut);
+                       snr_lut = qam64_snr_lut;
+                       break;
+               default:
+                       goto err_schedule_delayed_work;
+               }
 
-       dev_info(&state->i2c->dev, "%s: firmware version: LINK=%d.%d.%d.%d " \
-                       "OFDM=%d.%d.%d.%d\n", KBUILD_MODNAME, buf[0], buf[1],
-                       buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+               for (i = 0; i < len; i++) {
+                       tmp = snr_lut[i].snr * 1000;
+                       if (snr_val < snr_lut[i].val)
+                               break;
+               }
 
-       /* sleep */
-       switch (state->cfg.tuner) {
-       case AF9033_TUNER_IT9135_38:
-       case AF9033_TUNER_IT9135_51:
-       case AF9033_TUNER_IT9135_52:
-       case AF9033_TUNER_IT9135_60:
-       case AF9033_TUNER_IT9135_61:
-       case AF9033_TUNER_IT9135_62:
-               /* IT9135 did not like to sleep at that early */
-               break;
-       default:
-               ret = af9033_wr_reg(state, 0x80004c, 1);
-               if (ret < 0)
-                       goto err;
+               c->cnr.len = 1;
+               c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+               c->cnr.stat[0].svalue = tmp;
+       } else {
+               c->cnr.len = 1;
+               c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       }
 
-               ret = af9033_wr_reg(state, 0x800000, 0);
-               if (ret < 0)
+       /* UCB/PER/BER */
+       if (dev->fe_status & FE_HAS_LOCK) {
+               /* outer FEC, 204 byte packets */
+               u16 abort_packet_count, rsd_packet_count;
+               /* inner FEC, bits */
+               u32 rsd_bit_err_count;
+
+               /*
+                * Packet count used for measurement is 10000
+                * (rsd_packet_count). Maybe it should be increased?
+                */
+
+               ret = af9033_rd_regs(dev, 0x800032, buf, 7);
+               if (ret)
                        goto err;
-       }
 
-       /* configure internal TS mode */
-       switch (state->cfg.ts_mode) {
-       case AF9033_TS_MODE_PARALLEL:
-               state->ts_mode_parallel = true;
-               break;
-       case AF9033_TS_MODE_SERIAL:
-               state->ts_mode_serial = true;
-               break;
-       case AF9033_TS_MODE_USB:
-               /* usb mode for AF9035 */
-       default:
-               break;
-       }
+               abort_packet_count = (buf[1] << 8) | (buf[0] << 0);
+               rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2];
+               rsd_packet_count = (buf[6] << 8) | (buf[5] << 0);
 
-       /* create dvb_frontend */
-       memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops));
-       state->fe.demodulator_priv = state;
+               dev->error_block_count += abort_packet_count;
+               dev->total_block_count += rsd_packet_count;
+               dev->post_bit_error += rsd_bit_err_count;
+               dev->post_bit_count += rsd_packet_count * 204 * 8;
 
-       if (ops) {
-               ops->pid_filter = af9033_pid_filter;
-               ops->pid_filter_ctrl = af9033_pid_filter_ctrl;
-       }
+               c->block_count.len = 1;
+               c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+               c->block_count.stat[0].uvalue = dev->total_block_count;
+
+               c->block_error.len = 1;
+               c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+               c->block_error.stat[0].uvalue = dev->error_block_count;
+
+               c->post_bit_count.len = 1;
+               c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+               c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
 
-       return &state->fe;
+               c->post_bit_error.len = 1;
+               c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+               c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
+       }
 
+err_schedule_delayed_work:
+       schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
+       return;
 err:
-       kfree(state);
-       return NULL;
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 }
-EXPORT_SYMBOL(af9033_attach);
 
 static struct dvb_frontend_ops af9033_ops = {
        .delsys = { SYS_DVBT },
@@ -1170,8 +1121,6 @@ static struct dvb_frontend_ops af9033_ops = {
                        FE_CAN_MUTE_TS
        },
 
-       .release = af9033_release,
-
        .init = af9033_init,
        .sleep = af9033_sleep,
 
@@ -1188,6 +1137,150 @@ static struct dvb_frontend_ops af9033_ops = {
        .i2c_gate_ctrl = af9033_i2c_gate_ctrl,
 };
 
+static int af9033_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct af9033_config *cfg = client->dev.platform_data;
+       struct af9033_dev *dev;
+       int ret;
+       u8 buf[8];
+       u32 reg;
+
+       /* allocate memory for the internal state */
+       dev = kzalloc(sizeof(struct af9033_dev), GFP_KERNEL);
+       if (dev == NULL) {
+               ret = -ENOMEM;
+               dev_err(&client->dev, "Could not allocate memory for state\n");
+               goto err;
+       }
+
+       /* setup the state */
+       dev->client = client;
+       INIT_DELAYED_WORK(&dev->stat_work, af9033_stat_work);
+       memcpy(&dev->cfg, cfg, sizeof(struct af9033_config));
+
+       if (dev->cfg.clock != 12000000) {
+               ret = -ENODEV;
+               dev_err(&dev->client->dev,
+                               "unsupported clock %d Hz, only 12000000 Hz is supported currently\n",
+                               dev->cfg.clock);
+               goto err_kfree;
+       }
+
+       /* firmware version */
+       switch (dev->cfg.tuner) {
+       case AF9033_TUNER_IT9135_38:
+       case AF9033_TUNER_IT9135_51:
+       case AF9033_TUNER_IT9135_52:
+       case AF9033_TUNER_IT9135_60:
+       case AF9033_TUNER_IT9135_61:
+       case AF9033_TUNER_IT9135_62:
+               dev->is_it9135 = true;
+               reg = 0x004bfc;
+               break;
+       default:
+               dev->is_af9035 = true;
+               reg = 0x0083e9;
+               break;
+       }
+
+       ret = af9033_rd_regs(dev, reg, &buf[0], 4);
+       if (ret < 0)
+               goto err_kfree;
+
+       ret = af9033_rd_regs(dev, 0x804191, &buf[4], 4);
+       if (ret < 0)
+               goto err_kfree;
+
+       dev_info(&dev->client->dev,
+                       "firmware version: LINK %d.%d.%d.%d - OFDM %d.%d.%d.%d\n",
+                       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
+                       buf[7]);
+
+       /* sleep */
+       switch (dev->cfg.tuner) {
+       case AF9033_TUNER_IT9135_38:
+       case AF9033_TUNER_IT9135_51:
+       case AF9033_TUNER_IT9135_52:
+       case AF9033_TUNER_IT9135_60:
+       case AF9033_TUNER_IT9135_61:
+       case AF9033_TUNER_IT9135_62:
+               /* IT9135 did not like to sleep at that early */
+               break;
+       default:
+               ret = af9033_wr_reg(dev, 0x80004c, 1);
+               if (ret < 0)
+                       goto err_kfree;
+
+               ret = af9033_wr_reg(dev, 0x800000, 0);
+               if (ret < 0)
+                       goto err_kfree;
+       }
+
+       /* configure internal TS mode */
+       switch (dev->cfg.ts_mode) {
+       case AF9033_TS_MODE_PARALLEL:
+               dev->ts_mode_parallel = true;
+               break;
+       case AF9033_TS_MODE_SERIAL:
+               dev->ts_mode_serial = true;
+               break;
+       case AF9033_TS_MODE_USB:
+               /* usb mode for AF9035 */
+       default:
+               break;
+       }
+
+       /* create dvb_frontend */
+       memcpy(&dev->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops));
+       dev->fe.demodulator_priv = dev;
+       *cfg->fe = &dev->fe;
+       if (cfg->ops) {
+               cfg->ops->pid_filter = af9033_pid_filter;
+               cfg->ops->pid_filter_ctrl = af9033_pid_filter_ctrl;
+       }
+       i2c_set_clientdata(client, dev);
+
+       dev_info(&dev->client->dev, "Afatech AF9033 successfully attached\n");
+       return 0;
+err_kfree:
+       kfree(dev);
+err:
+       dev_dbg(&client->dev, "failed=%d\n", ret);
+       return ret;
+}
+
+static int af9033_remove(struct i2c_client *client)
+{
+       struct af9033_dev *dev = i2c_get_clientdata(client);
+
+       dev_dbg(&dev->client->dev, "\n");
+
+       dev->fe.ops.release = NULL;
+       dev->fe.demodulator_priv = NULL;
+       kfree(dev);
+
+       return 0;
+}
+
+static const struct i2c_device_id af9033_id_table[] = {
+       {"af9033", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, af9033_id_table);
+
+static struct i2c_driver af9033_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "af9033",
+       },
+       .probe          = af9033_probe,
+       .remove         = af9033_remove,
+       .id_table       = af9033_id_table,
+};
+
+module_i2c_driver(af9033_driver);
+
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
 MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver");
 MODULE_LICENSE("GPL");
index 539f4db678b87f0076093f8f85b9139e182dc31d..6ad22b69a63605e6b14c5516614fc40640e9085e 100644 (file)
 
 #include <linux/kconfig.h>
 
+/*
+ * I2C address (TODO: are these in 8-bit format?)
+ * 0x38, 0x3a, 0x3c, 0x3e
+ */
 struct af9033_config {
-       /*
-        * I2C address
-        */
-       u8 i2c_addr;
-
        /*
         * clock Hz
         * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000,
@@ -75,8 +74,23 @@ struct af9033_config {
         * input spectrum inversion
         */
        bool spec_inv;
-};
 
+       /*
+        *
+        */
+       bool dyn0_clk;
+
+       /*
+        * PID filter ops
+        */
+       struct af9033_ops *ops;
+
+       /*
+        * frontend
+        * returned by that driver
+        */
+       struct dvb_frontend **fe;
+};
 
 struct af9033_ops {
        int (*pid_filter_ctrl)(struct dvb_frontend *fe, int onoff);
@@ -84,36 +98,4 @@ struct af9033_ops {
                          int onoff);
 };
 
-
-#if IS_ENABLED(CONFIG_DVB_AF9033)
-extern
-struct dvb_frontend *af9033_attach(const struct af9033_config *config,
-                                  struct i2c_adapter *i2c,
-                                  struct af9033_ops *ops);
-
-#else
-static inline
-struct dvb_frontend *af9033_attach(const struct af9033_config *config,
-                                  struct i2c_adapter *i2c,
-                                  struct af9033_ops *ops)
-{
-       pr_warn("%s: driver disabled by Kconfig\n", __func__);
-       return NULL;
-}
-
-static inline int af9033_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
-{
-       pr_warn("%s: driver disabled by Kconfig\n", __func__);
-       return -ENODEV;
-}
-
-static inline int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid,
-       int onoff)
-{
-       pr_warn("%s: driver disabled by Kconfig\n", __func__);
-       return -ENODEV;
-}
-
-#endif
-
 #endif /* AF9033_H */
index ded7b67d7526cab8b9cd364dfe6ce19fdb3da36e..c12c92cb5855ae587f332f1d32673947ac90d912 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "dvb_frontend.h"
 #include "af9033.h"
+#include <linux/math64.h>
 
 struct reg_val {
        u32 reg;
diff --git a/drivers/media/dvb-frontends/as102_fe.c b/drivers/media/dvb-frontends/as102_fe.c
new file mode 100644 (file)
index 0000000..4936658
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.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, 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.
+ */
+
+#include <dvb_frontend.h>
+
+#include "as102_fe.h"
+
+struct as102_state {
+       struct dvb_frontend frontend;
+       struct as10x_demod_stats demod_stats;
+
+       const struct as102_fe_ops *ops;
+       void *priv;
+       uint8_t elna_cfg;
+
+       /* signal strength */
+       uint16_t signal_strength;
+       /* bit error rate */
+       uint32_t ber;
+};
+
+static uint8_t as102_fe_get_code_rate(fe_code_rate_t arg)
+{
+       uint8_t c;
+
+       switch (arg) {
+       case FEC_1_2:
+               c = CODE_RATE_1_2;
+               break;
+       case FEC_2_3:
+               c = CODE_RATE_2_3;
+               break;
+       case FEC_3_4:
+               c = CODE_RATE_3_4;
+               break;
+       case FEC_5_6:
+               c = CODE_RATE_5_6;
+               break;
+       case FEC_7_8:
+               c = CODE_RATE_7_8;
+               break;
+       default:
+               c = CODE_RATE_UNKNOWN;
+               break;
+       }
+
+       return c;
+}
+
+static int as102_fe_set_frontend(struct dvb_frontend *fe)
+{
+       struct as102_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       struct as10x_tune_args tune_args = { 0 };
+
+       /* set frequency */
+       tune_args.freq = c->frequency / 1000;
+
+       /* fix interleaving_mode */
+       tune_args.interleaving_mode = INTLV_NATIVE;
+
+       switch (c->bandwidth_hz) {
+       case 8000000:
+               tune_args.bandwidth = BW_8_MHZ;
+               break;
+       case 7000000:
+               tune_args.bandwidth = BW_7_MHZ;
+               break;
+       case 6000000:
+               tune_args.bandwidth = BW_6_MHZ;
+               break;
+       default:
+               tune_args.bandwidth = BW_8_MHZ;
+       }
+
+       switch (c->guard_interval) {
+       case GUARD_INTERVAL_1_32:
+               tune_args.guard_interval = GUARD_INT_1_32;
+               break;
+       case GUARD_INTERVAL_1_16:
+               tune_args.guard_interval = GUARD_INT_1_16;
+               break;
+       case GUARD_INTERVAL_1_8:
+               tune_args.guard_interval = GUARD_INT_1_8;
+               break;
+       case GUARD_INTERVAL_1_4:
+               tune_args.guard_interval = GUARD_INT_1_4;
+               break;
+       case GUARD_INTERVAL_AUTO:
+       default:
+               tune_args.guard_interval = GUARD_UNKNOWN;
+               break;
+       }
+
+       switch (c->modulation) {
+       case QPSK:
+               tune_args.modulation = CONST_QPSK;
+               break;
+       case QAM_16:
+               tune_args.modulation = CONST_QAM16;
+               break;
+       case QAM_64:
+               tune_args.modulation = CONST_QAM64;
+               break;
+       default:
+               tune_args.modulation = CONST_UNKNOWN;
+               break;
+       }
+
+       switch (c->transmission_mode) {
+       case TRANSMISSION_MODE_2K:
+               tune_args.transmission_mode = TRANS_MODE_2K;
+               break;
+       case TRANSMISSION_MODE_8K:
+               tune_args.transmission_mode = TRANS_MODE_8K;
+               break;
+       default:
+               tune_args.transmission_mode = TRANS_MODE_UNKNOWN;
+       }
+
+       switch (c->hierarchy) {
+       case HIERARCHY_NONE:
+               tune_args.hierarchy = HIER_NONE;
+               break;
+       case HIERARCHY_1:
+               tune_args.hierarchy = HIER_ALPHA_1;
+               break;
+       case HIERARCHY_2:
+               tune_args.hierarchy = HIER_ALPHA_2;
+               break;
+       case HIERARCHY_4:
+               tune_args.hierarchy = HIER_ALPHA_4;
+               break;
+       case HIERARCHY_AUTO:
+               tune_args.hierarchy = HIER_UNKNOWN;
+               break;
+       }
+
+       pr_debug("as102: tuner parameters: freq: %d  bw: 0x%02x  gi: 0x%02x\n",
+                       c->frequency,
+                       tune_args.bandwidth,
+                       tune_args.guard_interval);
+
+       /*
+        * Detect a hierarchy selection
+        * if HP/LP are both set to FEC_NONE, HP will be selected.
+        */
+       if ((tune_args.hierarchy != HIER_NONE) &&
+                      ((c->code_rate_LP == FEC_NONE) ||
+                       (c->code_rate_HP == FEC_NONE))) {
+
+               if (c->code_rate_LP == FEC_NONE) {
+                       tune_args.hier_select = HIER_HIGH_PRIORITY;
+                       tune_args.code_rate =
+                          as102_fe_get_code_rate(c->code_rate_HP);
+               }
+
+               if (c->code_rate_HP == FEC_NONE) {
+                       tune_args.hier_select = HIER_LOW_PRIORITY;
+                       tune_args.code_rate =
+                          as102_fe_get_code_rate(c->code_rate_LP);
+               }
+
+               pr_debug("as102: \thierarchy: 0x%02x  selected: %s  code_rate_%s: 0x%02x\n",
+                       tune_args.hierarchy,
+                       tune_args.hier_select == HIER_HIGH_PRIORITY ?
+                       "HP" : "LP",
+                       tune_args.hier_select == HIER_HIGH_PRIORITY ?
+                       "HP" : "LP",
+                       tune_args.code_rate);
+       } else {
+               tune_args.code_rate =
+                       as102_fe_get_code_rate(c->code_rate_HP);
+       }
+
+       /* Set frontend arguments */
+       return state->ops->set_tune(state->priv, &tune_args);
+}
+
+static int as102_fe_get_frontend(struct dvb_frontend *fe)
+{
+       struct as102_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int ret = 0;
+       struct as10x_tps tps = { 0 };
+
+       /* send abilis command: GET_TPS */
+       ret = state->ops->get_tps(state->priv, &tps);
+       if (ret < 0)
+               return ret;
+
+       /* extract constellation */
+       switch (tps.modulation) {
+       case CONST_QPSK:
+               c->modulation = QPSK;
+               break;
+       case CONST_QAM16:
+               c->modulation = QAM_16;
+               break;
+       case CONST_QAM64:
+               c->modulation = QAM_64;
+               break;
+       }
+
+       /* extract hierarchy */
+       switch (tps.hierarchy) {
+       case HIER_NONE:
+               c->hierarchy = HIERARCHY_NONE;
+               break;
+       case HIER_ALPHA_1:
+               c->hierarchy = HIERARCHY_1;
+               break;
+       case HIER_ALPHA_2:
+               c->hierarchy = HIERARCHY_2;
+               break;
+       case HIER_ALPHA_4:
+               c->hierarchy = HIERARCHY_4;
+               break;
+       }
+
+       /* extract code rate HP */
+       switch (tps.code_rate_HP) {
+       case CODE_RATE_1_2:
+               c->code_rate_HP = FEC_1_2;
+               break;
+       case CODE_RATE_2_3:
+               c->code_rate_HP = FEC_2_3;
+               break;
+       case CODE_RATE_3_4:
+               c->code_rate_HP = FEC_3_4;
+               break;
+       case CODE_RATE_5_6:
+               c->code_rate_HP = FEC_5_6;
+               break;
+       case CODE_RATE_7_8:
+               c->code_rate_HP = FEC_7_8;
+               break;
+       }
+
+       /* extract code rate LP */
+       switch (tps.code_rate_LP) {
+       case CODE_RATE_1_2:
+               c->code_rate_LP = FEC_1_2;
+               break;
+       case CODE_RATE_2_3:
+               c->code_rate_LP = FEC_2_3;
+               break;
+       case CODE_RATE_3_4:
+               c->code_rate_LP = FEC_3_4;
+               break;
+       case CODE_RATE_5_6:
+               c->code_rate_LP = FEC_5_6;
+               break;
+       case CODE_RATE_7_8:
+               c->code_rate_LP = FEC_7_8;
+               break;
+       }
+
+       /* extract guard interval */
+       switch (tps.guard_interval) {
+       case GUARD_INT_1_32:
+               c->guard_interval = GUARD_INTERVAL_1_32;
+               break;
+       case GUARD_INT_1_16:
+               c->guard_interval = GUARD_INTERVAL_1_16;
+               break;
+       case GUARD_INT_1_8:
+               c->guard_interval = GUARD_INTERVAL_1_8;
+               break;
+       case GUARD_INT_1_4:
+               c->guard_interval = GUARD_INTERVAL_1_4;
+               break;
+       }
+
+       /* extract transmission mode */
+       switch (tps.transmission_mode) {
+       case TRANS_MODE_2K:
+               c->transmission_mode = TRANSMISSION_MODE_2K;
+               break;
+       case TRANS_MODE_8K:
+               c->transmission_mode = TRANSMISSION_MODE_8K;
+               break;
+       }
+
+       return 0;
+}
+
+static int as102_fe_get_tune_settings(struct dvb_frontend *fe,
+                       struct dvb_frontend_tune_settings *settings) {
+
+       settings->min_delay_ms = 1000;
+
+       return 0;
+}
+
+static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+       int ret = 0;
+       struct as102_state *state = fe->demodulator_priv;
+       struct as10x_tune_status tstate = { 0 };
+
+       /* send abilis command: GET_TUNE_STATUS */
+       ret = state->ops->get_status(state->priv, &tstate);
+       if (ret < 0)
+               return ret;
+
+       state->signal_strength  = tstate.signal_strength;
+       state->ber  = tstate.BER;
+
+       switch (tstate.tune_state) {
+       case TUNE_STATUS_SIGNAL_DVB_OK:
+               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+               break;
+       case TUNE_STATUS_STREAM_DETECTED:
+               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
+                         FE_HAS_VITERBI;
+               break;
+       case TUNE_STATUS_STREAM_TUNED:
+               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
+                         FE_HAS_LOCK | FE_HAS_VITERBI;
+               break;
+       default:
+               *status = TUNE_STATUS_NOT_TUNED;
+       }
+
+       pr_debug("as102: tuner status: 0x%02x, strength %d, per: %d, ber: %d\n",
+                tstate.tune_state, tstate.signal_strength,
+                tstate.PER, tstate.BER);
+
+       if (!(*status & FE_HAS_LOCK)) {
+               memset(&state->demod_stats, 0, sizeof(state->demod_stats));
+               return 0;
+       }
+
+       ret = state->ops->get_stats(state->priv, &state->demod_stats);
+       if (ret < 0)
+               memset(&state->demod_stats, 0, sizeof(state->demod_stats));
+
+       return ret;
+}
+
+/*
+ * Note:
+ * - in AS102 SNR=MER
+ *   - the SNR will be returned in linear terms, i.e. not in dB
+ *   - the accuracy equals Â±2dB for a SNR range from 4dB to 30dB
+ *   - the accuracy is >2dB for SNR values outside this range
+ */
+static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       struct as102_state *state = fe->demodulator_priv;
+
+       *snr = state->demod_stats.mer;
+
+       return 0;
+}
+
+static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+       struct as102_state *state = fe->demodulator_priv;
+
+       *ber = state->ber;
+
+       return 0;
+}
+
+static int as102_fe_read_signal_strength(struct dvb_frontend *fe,
+                                        u16 *strength)
+{
+       struct as102_state *state = fe->demodulator_priv;
+
+       *strength = (((0xffff * 400) * state->signal_strength + 41000) * 2);
+
+       return 0;
+}
+
+static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+       struct as102_state *state = fe->demodulator_priv;
+
+       if (state->demod_stats.has_started)
+               *ucblocks = state->demod_stats.bad_frame_count;
+       else
+               *ucblocks = 0;
+
+       return 0;
+}
+
+static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+       struct as102_state *state = fe->demodulator_priv;
+
+       return state->ops->stream_ctrl(state->priv, acquire,
+                                     state->elna_cfg);
+}
+
+static void as102_fe_release(struct dvb_frontend *fe)
+{
+       struct as102_state *state = fe->demodulator_priv;
+
+       kfree(state);
+}
+
+
+static struct dvb_frontend_ops as102_fe_ops = {
+       .delsys = { SYS_DVBT },
+       .info = {
+               .name                   = "Abilis AS102 DVB-T",
+               .frequency_min          = 174000000,
+               .frequency_max          = 862000000,
+               .frequency_stepsize     = 166667,
+               .caps = FE_CAN_INVERSION_AUTO
+                       | 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_FEC_AUTO
+                       | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK
+                       | FE_CAN_QAM_AUTO
+                       | FE_CAN_TRANSMISSION_MODE_AUTO
+                       | FE_CAN_GUARD_INTERVAL_AUTO
+                       | FE_CAN_HIERARCHY_AUTO
+                       | FE_CAN_RECOVER
+                       | FE_CAN_MUTE_TS
+       },
+
+       .set_frontend           = as102_fe_set_frontend,
+       .get_frontend           = as102_fe_get_frontend,
+       .get_tune_settings      = as102_fe_get_tune_settings,
+
+       .read_status            = as102_fe_read_status,
+       .read_snr               = as102_fe_read_snr,
+       .read_ber               = as102_fe_read_ber,
+       .read_signal_strength   = as102_fe_read_signal_strength,
+       .read_ucblocks          = as102_fe_read_ucblocks,
+       .ts_bus_ctrl            = as102_fe_ts_bus_ctrl,
+       .release                = as102_fe_release,
+};
+
+struct dvb_frontend *as102_attach(const char *name,
+                                 const struct as102_fe_ops *ops,
+                                 void *priv,
+                                 uint8_t elna_cfg)
+{
+       struct as102_state *state;
+       struct dvb_frontend *fe;
+
+       state = kzalloc(sizeof(struct as102_state), GFP_KERNEL);
+       if (state == NULL) {
+               pr_err("%s: unable to allocate memory for state\n", __func__);
+               return NULL;
+       }
+       fe = &state->frontend;
+       fe->demodulator_priv = state;
+       state->ops = ops;
+       state->priv = priv;
+       state->elna_cfg = elna_cfg;
+
+       /* init frontend callback ops */
+       memcpy(&fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops));
+       strncpy(fe->ops.info.name, name, sizeof(fe->ops.info.name));
+
+       return fe;
+
+}
+EXPORT_SYMBOL_GPL(as102_attach);
+
+MODULE_DESCRIPTION("as102-fe");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");
diff --git a/drivers/media/dvb-frontends/as102_fe.h b/drivers/media/dvb-frontends/as102_fe.h
new file mode 100644 (file)
index 0000000..a7c9143
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2014 Mauro Carvalho Chehab <m.chehab@samsung.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, 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.
+ */
+
+#include "as102_fe_types.h"
+
+struct as102_fe_ops {
+       int (*set_tune)(void *priv, struct as10x_tune_args *tune_args);
+       int (*get_tps)(void *priv, struct as10x_tps *tps);
+       int (*get_status)(void *priv, struct as10x_tune_status *tstate);
+       int (*get_stats)(void *priv, struct as10x_demod_stats *demod_stats);
+       int (*stream_ctrl)(void *priv, int acquire, uint32_t elna_cfg);
+};
+
+struct dvb_frontend *as102_attach(const char *name,
+                                 const struct as102_fe_ops *ops,
+                                 void *priv,
+                                 uint8_t elna_cfg);
similarity index 95%
rename from drivers/staging/media/as102/as10x_types.h
rename to drivers/media/dvb-frontends/as102_fe_types.h
index af26e057d9a202c7735ead7de77f4a6c497ed9a0..80a5398b580fe87ade07a0479047f88324879c17 100644 (file)
  * 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 _AS10X_TYPES_H_
 #define _AS10X_TYPES_H_
 
-#include "as10x_handle.h"
-
 /*********************************/
 /*       MACRO DEFINITIONS       */
 /*********************************/
index 39a29dd29519cba8573fd7ce1837e46d7df1f73b..638c7aa0fb7e6ae90434d10d92c22879a89a860c 100644 (file)
@@ -639,12 +639,12 @@ static int bcm3510_download_firmware(struct dvb_frontend* fe)
                err("could not load firmware (%s): %d",BCM3510_DEFAULT_FIRMWARE,ret);
                return ret;
        }
-       deb_info("got firmware: %zd\n",fw->size);
+       deb_info("got firmware: %zu\n", fw->size);
 
        b = fw->data;
        for (i = 0; i < fw->size;) {
-               addr = le16_to_cpu( *( (u16 *)&b[i] ) );
-               len  = le16_to_cpu( *( (u16 *)&b[i+2] ) );
+               addr = le16_to_cpu(*((__le16 *)&b[i]));
+               len  = le16_to_cpu(*((__le16 *)&b[i+2]));
                deb_info("firmware chunk, addr: 0x%04x, len: 0x%04x, total length: 0x%04zx\n",addr,len,fw->size);
                if ((ret = bcm3510_write_ram(st,addr,&b[i+4],len)) < 0) {
                        err("firmware download failed: %d\n",ret);
index 0f4657e01cdeaa07d8f5b9b2c3e58b237e6058a1..149fdca3fb44e33fd60cda6c44de66041019c86c 100644 (file)
@@ -65,7 +65,7 @@ int cxd2820r_set_frontend_c(struct dvb_frontend *fe)
        }
 
        priv->delivery_system = SYS_DVBC_ANNEX_A;
-       priv->ber_running = 0; /* tune stops BER counter */
+       priv->ber_running = false; /* tune stops BER counter */
 
        /* program IF frequency */
        if (fe->ops.tuner_ops.get_if_frequency) {
@@ -168,7 +168,7 @@ int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber)
                        start_ber = 1;
                }
        } else {
-               priv->ber_running = 1;
+               priv->ber_running = true;
                start_ber = 1;
        }
 
index 03930d5e9fea4cbf2a5787aad71d08af809544fa..422e84bbb008ffe685fb149f529c27ce4957cc70 100644 (file)
@@ -564,10 +564,10 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe)
 
        /* check if we have a valid signal */
        if (status & FE_HAS_LOCK) {
-               priv->last_tune_failed = 0;
+               priv->last_tune_failed = false;
                return DVBFE_ALGO_SEARCH_SUCCESS;
        } else {
-               priv->last_tune_failed = 1;
+               priv->last_tune_failed = true;
                return DVBFE_ALGO_SEARCH_AGAIN;
        }
 
@@ -584,18 +584,14 @@ static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe)
 static void cxd2820r_release(struct dvb_frontend *fe)
 {
        struct cxd2820r_priv *priv = fe->demodulator_priv;
-       int uninitialized_var(ret); /* silence compiler warning */
 
        dev_dbg(&priv->i2c->dev, "%s\n", __func__);
 
 #ifdef CONFIG_GPIOLIB
        /* remove GPIOs */
-       if (priv->gpio_chip.label) {
-               ret = gpiochip_remove(&priv->gpio_chip);
-               if (ret)
-                       dev_err(&priv->i2c->dev, "%s: gpiochip_remove() " \
-                                       "failed=%d\n", KBUILD_MODNAME, ret);
-       }
+       if (priv->gpio_chip.label)
+               gpiochip_remove(&priv->gpio_chip);
+
 #endif
        kfree(priv);
        return;
index 9b5a45b907bcfca70f8e31ace191be9447da865c..51401d036530fbfd42d021c293a2a979b43af9c9 100644 (file)
@@ -89,7 +89,7 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe)
        }
 
        priv->delivery_system = SYS_DVBT;
-       priv->ber_running = 0; /* tune stops BER counter */
+       priv->ber_running = false; /* tune stops BER counter */
 
        /* program IF frequency */
        if (fe->ops.tuner_ops.get_if_frequency) {
@@ -272,7 +272,7 @@ int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber)
                        start_ber = 1;
                }
        } else {
-               priv->ber_running = 1;
+               priv->ber_running = true;
                start_ber = 1;
        }
 
index 661760d60232aa928015ad0cccbbd25c7a6d4948..589134e951758c1ab984be32e1becf88d3631ea5 100644 (file)
@@ -2559,7 +2559,7 @@ static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode)
        dib7000p_write_word(state, 1288, reg_1288);
 }
 
-int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff)
+static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff)
 {
        struct dib7000p_state *state = fe->demodulator_priv;
        u16 reg_1287;
index 7ca7a21df18330649ad23c255349423e33288871..5ec221ffdfcae3a05e88e971f9795785c680752a 100644 (file)
@@ -2174,7 +2174,7 @@ int drxj_dap_atomic_read_reg32(struct i2c_device_addr *dev_addr,
                                     u32 addr,
                                     u32 *data, u32 flags)
 {
-       u8 buf[sizeof(*data)];
+       u8 buf[sizeof(*data)] = { 0 };
        int rc = -EIO;
        u32 word = 0;
 
@@ -4193,7 +4193,7 @@ int drxj_dap_scu_atomic_read_reg16(struct i2c_device_addr *dev_addr,
                                         u32 addr,
                                         u16 *data, u32 flags)
 {
-       u8 buf[2];
+       u8 buf[2] = { 0 };
        int rc = -EIO;
        u16 word = 0;
 
@@ -10667,7 +10667,7 @@ ctrl_sig_quality(struct drx_demod_instance *demod,
        enum drx_standard standard = ext_attr->standard;
        int rc;
        u32 ber, cnt, err, pkt;
-       u16 mer, strength;
+       u16 mer, strength = 0;
 
        rc = get_sig_strength(demod, &strength);
        if (rc < 0) {
@@ -11602,7 +11602,7 @@ static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words)
        u32 carry = 0;
 
        while (i < nr_words) {
-               crc_word |= (u32)be16_to_cpu(*(u32 *)(block_data));
+               crc_word |= (u32)be16_to_cpu(*(__be16 *)(block_data));
                for (j = 0; j < 16; j++) {
                        crc_word <<= 1;
                        if (carry != 0)
@@ -11629,7 +11629,7 @@ static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data,
        int i;
        unsigned count = 2 * sizeof(u16);
        u32 mc_dev_type, mc_version, mc_base_version;
-       u16 mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data + sizeof(u16)));
+       u16 mc_nr_of_blks = be16_to_cpu(*(__be16 *)(mc_data + sizeof(u16)));
 
        /*
         * Scan microcode blocks first for version info
@@ -11647,13 +11647,13 @@ static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data,
                        goto eof;
 
                /* Process block header */
-               block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data + count));
+               block_hdr.addr = be32_to_cpu(*(__be32 *)(mc_data + count));
                count += sizeof(u32);
-               block_hdr.size = be16_to_cpu(*(u32 *)(mc_data + count));
+               block_hdr.size = be16_to_cpu(*(__be16 *)(mc_data + count));
                count += sizeof(u16);
-               block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data + count));
+               block_hdr.flags = be16_to_cpu(*(__be16 *)(mc_data + count));
                count += sizeof(u16);
-               block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data + count));
+               block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data + count));
                count += sizeof(u16);
 
                pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
@@ -11667,7 +11667,7 @@ static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data,
                        if (block_hdr.addr + sizeof(u16) > size)
                                goto eof;
 
-                       auxtype = be16_to_cpu(*(u32 *)(auxblk));
+                       auxtype = be16_to_cpu(*(__be16 *)(auxblk));
 
                        /* Aux block. Check type */
                        if (DRX_ISMCVERTYPE(auxtype)) {
@@ -11675,11 +11675,11 @@ static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data,
                                        goto eof;
 
                                auxblk += sizeof(u16);
-                               mc_dev_type = be32_to_cpu(*(u32 *)(auxblk));
+                               mc_dev_type = be32_to_cpu(*(__be32 *)(auxblk));
                                auxblk += sizeof(u32);
-                               mc_version = be32_to_cpu(*(u32 *)(auxblk));
+                               mc_version = be32_to_cpu(*(__be32 *)(auxblk));
                                auxblk += sizeof(u32);
-                               mc_base_version = be32_to_cpu(*(u32 *)(auxblk));
+                               mc_base_version = be32_to_cpu(*(__be32 *)(auxblk));
 
                                DRX_ATTR_MCRECORD(demod).aux_type = auxtype;
                                DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type;
@@ -11765,9 +11765,9 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
 
        mc_data = (void *)mc_data_init;
        /* Check data */
-       mc_magic_word = be16_to_cpu(*(u32 *)(mc_data));
+       mc_magic_word = be16_to_cpu(*(__be16 *)(mc_data));
        mc_data += sizeof(u16);
-       mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data));
+       mc_nr_of_blks = be16_to_cpu(*(__be16 *)(mc_data));
        mc_data += sizeof(u16);
 
        if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) {
@@ -11791,13 +11791,13 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod,
                u16 mc_block_nr_bytes = 0;
 
                /* Process block header */
-               block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data));
+               block_hdr.addr = be32_to_cpu(*(__be32 *)(mc_data));
                mc_data += sizeof(u32);
-               block_hdr.size = be16_to_cpu(*(u32 *)(mc_data));
+               block_hdr.size = be16_to_cpu(*(__be16 *)(mc_data));
                mc_data += sizeof(u16);
-               block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data));
+               block_hdr.flags = be16_to_cpu(*(__be16 *)(mc_data));
                mc_data += sizeof(u16);
-               block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data));
+               block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data));
                mc_data += sizeof(u16);
 
                pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
index ae2276db77bc8ff448aeb50a4314e1723619b470..687e893d29fec6e0429e603832bc26eab3b2cbeb 100644 (file)
@@ -2628,10 +2628,11 @@ static int DRXD_init(struct drxd_state *state, const u8 *fw, u32 fw_size)
                        break;
 
                /* Apply I2c address patch to B1 */
-               if (!state->type_A && state->m_HiI2cPatch != NULL)
+               if (!state->type_A && state->m_HiI2cPatch != NULL) {
                        status = WriteTable(state, state->m_HiI2cPatch);
                        if (status < 0)
                                break;
+               }
 
                if (state->type_A) {
                        /* HI firmware patch for UIO readout,
@@ -2830,14 +2831,8 @@ static int drxd_read_status(struct dvb_frontend *fe, fe_status_t * status)
 static int drxd_init(struct dvb_frontend *fe)
 {
        struct drxd_state *state = fe->demodulator_priv;
-       int err = 0;
 
-/*     if (request_firmware(&state->fw, "drxd.fw", state->dev)<0) */
        return DRXD_init(state, NULL, 0);
-
-       err = DRXD_init(state, state->fw->data, state->fw->size);
-       release_firmware(state->fw);
-       return err;
 }
 
 static int drxd_config_i2c(struct dvb_frontend *fe, int onoff)
index cce94a75b2e1f74c4dc05c0715f6320f130fe988..672195147d015778cefbfefd5fdbf1126e1e67be 100644 (file)
@@ -1028,7 +1028,7 @@ static int hi_command(struct drxk_state *state, u16 cmd, u16 *p_result)
                    ((state->m_hi_cfg_ctrl) &
                     SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) ==
                    SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ);
-       if (powerdown_cmd == false) {
+       if (!powerdown_cmd) {
                /* Wait until command rdy */
                u32 retry_count = 0;
                u16 wait_cmd;
@@ -1129,7 +1129,7 @@ static int mpegts_configure_pins(struct drxk_state *state, bool mpeg_enable)
        if (status < 0)
                goto error;
 
-       if (mpeg_enable == false) {
+       if (!mpeg_enable) {
                /*  Set MPEG TS pads to inputmode */
                status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000);
                if (status < 0)
@@ -1190,7 +1190,7 @@ static int mpegts_configure_pins(struct drxk_state *state, bool mpeg_enable)
                if (status < 0)
                        goto error;
 
-               if (state->m_enable_parallel == true) {
+               if (state->m_enable_parallel) {
                        /* parallel -> enable MD1 to MD7 */
                        status = write16(state, SIO_PDR_MD1_CFG__A,
                                         sio_pdr_mdx_cfg);
@@ -1392,7 +1392,7 @@ static int dvbt_enable_ofdm_token_ring(struct drxk_state *state, bool enable)
 
        dprintk(1, "\n");
 
-       if (enable == false) {
+       if (!enable) {
                desired_ctrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF;
                desired_status = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN;
        }
@@ -2012,7 +2012,7 @@ static int mpegts_dto_setup(struct drxk_state *state,
                goto error;
        fec_oc_reg_mode &= (~FEC_OC_MODE_PARITY__M);
        fec_oc_reg_ipr_mode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
-       if (state->m_insert_rs_byte == true) {
+       if (state->m_insert_rs_byte) {
                /* enable parity symbol forward */
                fec_oc_reg_mode |= FEC_OC_MODE_PARITY__M;
                /* MVAL disable during parity bytes */
@@ -2023,7 +2023,7 @@ static int mpegts_dto_setup(struct drxk_state *state,
 
        /* Check serial or parallel output */
        fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
-       if (state->m_enable_parallel == false) {
+       if (!state->m_enable_parallel) {
                /* MPEG data output is serial -> set ipr_mode[0] */
                fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_SERIAL__M;
        }
@@ -2136,19 +2136,19 @@ static int mpegts_configure_polarity(struct drxk_state *state)
 
        /* Control selective inversion of output bits */
        fec_oc_reg_ipr_invert &= (~(invert_data_mask));
-       if (state->m_invert_data == true)
+       if (state->m_invert_data)
                fec_oc_reg_ipr_invert |= invert_data_mask;
        fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MERR__M));
-       if (state->m_invert_err == true)
+       if (state->m_invert_err)
                fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MERR__M;
        fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
-       if (state->m_invert_str == true)
+       if (state->m_invert_str)
                fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MSTRT__M;
        fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
-       if (state->m_invert_val == true)
+       if (state->m_invert_val)
                fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MVAL__M;
        fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
-       if (state->m_invert_clk == true)
+       if (state->m_invert_clk)
                fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MCLK__M;
 
        return write16(state, FEC_OC_IPR_INVERT__A, fec_oc_reg_ipr_invert);
@@ -2220,12 +2220,13 @@ static int set_agc_rf(struct drxk_state *state,
                }
 
                /* Set TOP, only if IF-AGC is in AUTO mode */
-               if (p_if_agc_settings->ctrl_mode == DRXK_AGC_CTRL_AUTO)
+               if (p_if_agc_settings->ctrl_mode == DRXK_AGC_CTRL_AUTO) {
                        status = write16(state,
                                         SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A,
                                         p_agc_cfg->top);
                        if (status < 0)
                                goto error;
+               }
 
                /* Cut-Off current */
                status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A,
@@ -3352,7 +3353,7 @@ static int dvbt_ctrl_set_inc_enable(struct drxk_state *state, bool *enabled)
        int status;
 
        dprintk(1, "\n");
-       if (*enabled == true)
+       if (*enabled)
                status = write16(state, IQM_CF_BYPASSDET__A, 0);
        else
                status = write16(state, IQM_CF_BYPASSDET__A, 1);
@@ -3368,7 +3369,7 @@ static int dvbt_ctrl_set_fr_enable(struct drxk_state *state, bool *enabled)
        int status;
 
        dprintk(1, "\n");
-       if (*enabled == true) {
+       if (*enabled) {
                /* write mask to 1 */
                status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A,
                                   DEFAULT_FR_THRES_8K);
@@ -6794,11 +6795,11 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config,
        state->enable_merr_cfg = config->enable_merr_cfg;
 
        if (config->dynamic_clk) {
-               state->m_dvbt_static_clk = 0;
-               state->m_dvbc_static_clk = 0;
+               state->m_dvbt_static_clk = false;
+               state->m_dvbc_static_clk = false;
        } else {
-               state->m_dvbt_static_clk = 1;
-               state->m_dvbc_static_clk = 1;
+               state->m_dvbt_static_clk = true;
+               state->m_dvbc_static_clk = true;
        }
 
 
index dfe0c2f7f1efc749b7b409cbc7bb00262e9eca37..81657e94c5a44dccb4ccea6e3164f02c67db05b5 100644 (file)
@@ -159,6 +159,7 @@ static int m88ds3103_wr_reg_val_tab(struct m88ds3103_priv *priv,
 {
        int ret, i, j;
        u8 buf[83];
+
        dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len);
 
        if (tab_len > 83) {
@@ -247,8 +248,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
        u8 u8tmp, u8tmp1, u8tmp2;
        u8 buf[2];
        u16 u16tmp, divide_ratio;
-       u32 tuner_frequency, target_mclk, ts_clk;
+       u32 tuner_frequency, target_mclk;
        s32 s32tmp;
+
        dev_dbg(&priv->i2c->dev,
                        "%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n",
                        __func__, c->delivery_system,
@@ -316,9 +318,6 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
                                target_mclk = 144000;
                        break;
                case M88DS3103_TS_PARALLEL:
-               case M88DS3103_TS_PARALLEL_12:
-               case M88DS3103_TS_PARALLEL_16:
-               case M88DS3103_TS_PARALLEL_19_2:
                case M88DS3103_TS_CI:
                        if (c->symbol_rate < 18000000)
                                target_mclk = 96000;
@@ -352,33 +351,17 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
        switch (priv->cfg->ts_mode) {
        case M88DS3103_TS_SERIAL:
                u8tmp1 = 0x00;
-               ts_clk = 0;
-               u8tmp = 0x46;
+               u8tmp = 0x06;
                break;
        case M88DS3103_TS_SERIAL_D7:
                u8tmp1 = 0x20;
-               ts_clk = 0;
-               u8tmp = 0x46;
+               u8tmp = 0x06;
                break;
        case M88DS3103_TS_PARALLEL:
-               ts_clk = 24000;
-               u8tmp = 0x42;
-               break;
-       case M88DS3103_TS_PARALLEL_12:
-               ts_clk = 12000;
-               u8tmp = 0x42;
-               break;
-       case M88DS3103_TS_PARALLEL_16:
-               ts_clk = 16000;
-               u8tmp = 0x42;
-               break;
-       case M88DS3103_TS_PARALLEL_19_2:
-               ts_clk = 19200;
-               u8tmp = 0x42;
+               u8tmp = 0x02;
                break;
        case M88DS3103_TS_CI:
-               ts_clk = 6000;
-               u8tmp = 0x43;
+               u8tmp = 0x03;
                break;
        default:
                dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__);
@@ -386,6 +369,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
                goto err;
        }
 
+       if (priv->cfg->ts_clk_pol)
+               u8tmp |= 0x40;
+
        /* TS mode */
        ret = m88ds3103_wr_reg(priv, 0xfd, u8tmp);
        if (ret)
@@ -399,8 +385,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
                        goto err;
        }
 
-       if (ts_clk) {
-               divide_ratio = DIV_ROUND_UP(target_mclk, ts_clk);
+       if (priv->cfg->ts_clk) {
+               divide_ratio = DIV_ROUND_UP(target_mclk, priv->cfg->ts_clk);
                u8tmp1 = divide_ratio / 2;
                u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
        } else {
@@ -411,7 +397,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 
        dev_dbg(&priv->i2c->dev,
                        "%s: target_mclk=%d ts_clk=%d divide_ratio=%d\n",
-                       __func__, target_mclk, ts_clk, divide_ratio);
+                       __func__, target_mclk, priv->cfg->ts_clk, divide_ratio);
 
        u8tmp1--;
        u8tmp2--;
@@ -536,6 +522,7 @@ static int m88ds3103_init(struct dvb_frontend *fe)
        const struct firmware *fw = NULL;
        u8 *fw_file = M88DS3103_FIRMWARE;
        u8 u8tmp;
+
        dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
 
        /* set cold state by default */
@@ -648,6 +635,7 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
 {
        struct m88ds3103_priv *priv = fe->demodulator_priv;
        int ret;
+
        dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
 
        priv->delivery_system = SYS_UNDEFINED;
@@ -682,6 +670,7 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe)
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int ret;
        u8 buf[3];
+
        dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
 
        if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
@@ -857,6 +846,7 @@ static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr)
        u8 buf[3];
        u16 noise, signal;
        u32 noise_tot, signal_tot;
+
        dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
        /* reports SNR in resolution of 0.1 dB */
 
@@ -933,6 +923,7 @@ static int m88ds3103_read_ber(struct dvb_frontend *fe, u32 *ber)
        int ret;
        unsigned int utmp;
        u8 buf[3], u8tmp;
+
        dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
 
        switch (c->delivery_system) {
@@ -1013,6 +1004,7 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe,
        struct m88ds3103_priv *priv = fe->demodulator_priv;
        int ret;
        u8 u8tmp, tone, reg_a1_mask;
+
        dev_dbg(&priv->i2c->dev, "%s: fe_sec_tone_mode=%d\n", __func__,
                        fe_sec_tone_mode);
 
@@ -1053,12 +1045,64 @@ err:
        return ret;
 }
 
+static int m88ds3103_set_voltage(struct dvb_frontend *fe,
+       fe_sec_voltage_t fe_sec_voltage)
+{
+       struct m88ds3103_priv *priv = fe->demodulator_priv;
+       int ret;
+       u8 u8tmp;
+       bool voltage_sel, voltage_dis;
+
+       dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__,
+                       fe_sec_voltage);
+
+       if (!priv->warm) {
+               ret = -EAGAIN;
+               goto err;
+       }
+
+       switch (fe_sec_voltage) {
+       case SEC_VOLTAGE_18:
+               voltage_sel = true;
+               voltage_dis = false;
+               break;
+       case SEC_VOLTAGE_13:
+               voltage_sel = false;
+               voltage_dis = false;
+               break;
+       case SEC_VOLTAGE_OFF:
+               voltage_sel = false;
+               voltage_dis = true;
+               break;
+       default:
+               dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n",
+                               __func__);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* output pin polarity */
+       voltage_sel ^= priv->cfg->lnb_hv_pol;
+       voltage_dis ^= priv->cfg->lnb_en_pol;
+
+       u8tmp = voltage_dis << 1 | voltage_sel << 0;
+       ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0x03);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
 static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
                struct dvb_diseqc_master_cmd *diseqc_cmd)
 {
        struct m88ds3103_priv *priv = fe->demodulator_priv;
        int ret, i;
        u8 u8tmp;
+
        dev_dbg(&priv->i2c->dev, "%s: msg=%*ph\n", __func__,
                        diseqc_cmd->msg_len, diseqc_cmd->msg);
 
@@ -1130,6 +1174,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
        struct m88ds3103_priv *priv = fe->demodulator_priv;
        int ret, i;
        u8 u8tmp, burst;
+
        dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__,
                        fe_sec_mini_cmd);
 
@@ -1202,6 +1247,7 @@ static int m88ds3103_get_tune_settings(struct dvb_frontend *fe,
 static void m88ds3103_release(struct dvb_frontend *fe)
 {
        struct m88ds3103_priv *priv = fe->demodulator_priv;
+
        i2c_del_mux_adapter(priv->i2c_adapter);
        kfree(priv);
 }
@@ -1370,6 +1416,7 @@ static struct dvb_frontend_ops m88ds3103_ops = {
        .diseqc_send_burst = m88ds3103_diseqc_send_burst,
 
        .set_tone = m88ds3103_set_tone,
+       .set_voltage = m88ds3103_set_voltage,
 };
 
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
index bbb7e3aa56755f886db48ffb6e4c34897b31cb2e..9b3b4962da7c1653171c56d8f2f0dd6693a6c59d 100644 (file)
@@ -47,13 +47,22 @@ struct m88ds3103_config {
         */
 #define M88DS3103_TS_SERIAL             0 /* TS output pin D0, normal */
 #define M88DS3103_TS_SERIAL_D7          1 /* TS output pin D7 */
-#define M88DS3103_TS_PARALLEL           2 /* 24 MHz, normal */
-#define M88DS3103_TS_PARALLEL_12        3 /* 12 MHz */
-#define M88DS3103_TS_PARALLEL_16        4 /* 16 MHz */
-#define M88DS3103_TS_PARALLEL_19_2      5 /* 19.2 MHz */
-#define M88DS3103_TS_CI                 6 /* 6 MHz */
+#define M88DS3103_TS_PARALLEL           2 /* TS Parallel mode */
+#define M88DS3103_TS_CI                 3 /* TS CI Mode */
        u8 ts_mode;
 
+       /*
+        * TS clk in KHz
+        * Default: 0.
+        */
+       u32 ts_clk;
+
+       /*
+        * TS clk polarity.
+        * Default: 0. 1-active at falling edge; 0-active at rising edge.
+        */
+       u8 ts_clk_pol:1;
+
        /*
         * spectrum inversion
         * Default: 0
@@ -86,6 +95,22 @@ struct m88ds3103_config {
         * Default: none, must set
         */
        u8 agc;
+
+       /*
+        * LNB H/V pin polarity
+        * Default: 0.
+        * 1: pin high set to VOLTAGE_13, pin low to set VOLTAGE_18.
+        * 0: pin high set to VOLTAGE_18, pin low to set VOLTAGE_13.
+        */
+       u8 lnb_hv_pol:1;
+
+       /*
+        * LNB enable pin polarity
+        * Default: 0.
+        * 1: pin high to enable, pin low to disable.
+        * 0: pin high to disable, pin low to enable.
+        */
+       u8 lnb_en_pol:1;
 };
 
 /*
index 9ae40abfd71a170fe6940e372edefb1e0e558720..3ddea4471d2b6668f633a963115d02cdad067f24 100644 (file)
@@ -28,7 +28,7 @@
 #include "mb86a16.h"
 #include "mb86a16_priv.h"
 
-unsigned int verbose = 5;
+static unsigned int verbose = 5;
 module_param(verbose, int, 0644);
 
 #define ABS(x)         ((x) < 0 ? (-x) : (x))
@@ -115,9 +115,11 @@ static int mb86a16_read(struct mb86a16_state *state, u8 reg, u8 *val)
        };
        ret = i2c_transfer(state->i2c_adap, msg, 2);
        if (ret != 2) {
-               dprintk(verbose, MB86A16_ERROR, 1, "read error(reg=0x%02x, ret=0x%i)",
+               dprintk(verbose, MB86A16_ERROR, 1, "read error(reg=0x%02x, ret=%i)",
                        reg, ret);
 
+               if (ret < 0)
+                       return ret;
                return -EREMOTEIO;
        }
        *val = b1[0];
index b931179c70a48e767848f2fdc43e37ed2aaba5bf..e6f165a5b90d7630a3543966c2b679db4128cf5c 100644 (file)
@@ -33,7 +33,7 @@ enum mb86a20s_bandwidth {
        MB86A20S_3SEG = 3,
 };
 
-u8 mb86a20s_subchannel[] = {
+static u8 mb86a20s_subchannel[] = {
        0xb0, 0xc0, 0xd0, 0xe0,
        0xf0, 0x00, 0x10, 0x20,
 };
@@ -1228,7 +1228,7 @@ struct linear_segments {
  * All tables below return a dB/1000 measurement
  */
 
-static struct linear_segments cnr_to_db_table[] = {
+static const struct linear_segments cnr_to_db_table[] = {
        { 19648,     0},
        { 18187,  1000},
        { 16534,  2000},
@@ -1262,7 +1262,7 @@ static struct linear_segments cnr_to_db_table[] = {
        {   788, 30000},
 };
 
-static struct linear_segments cnr_64qam_table[] = {
+static const struct linear_segments cnr_64qam_table[] = {
        { 3922688,     0},
        { 3920384,  1000},
        { 3902720,  2000},
@@ -1296,7 +1296,7 @@ static struct linear_segments cnr_64qam_table[] = {
        {  388864, 30000},
 };
 
-static struct linear_segments cnr_16qam_table[] = {
+static const struct linear_segments cnr_16qam_table[] = {
        { 5314816,     0},
        { 5219072,  1000},
        { 5118720,  2000},
@@ -1330,7 +1330,7 @@ static struct linear_segments cnr_16qam_table[] = {
        {   95744, 30000},
 };
 
-struct linear_segments cnr_qpsk_table[] = {
+static const struct linear_segments cnr_qpsk_table[] = {
        { 2834176,     0},
        { 2683648,  1000},
        { 2536960,  2000},
@@ -1364,7 +1364,7 @@ struct linear_segments cnr_qpsk_table[] = {
        {   11520, 30000},
 };
 
-static u32 interpolate_value(u32 value, struct linear_segments *segments,
+static u32 interpolate_value(u32 value, const struct linear_segments *segments,
                             unsigned len)
 {
        u64 tmp64;
@@ -1448,7 +1448,7 @@ static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe)
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        u32 mer, cnr;
        int rc, val, layer;
-       struct linear_segments *segs;
+       const struct linear_segments *segs;
        unsigned segs_len;
 
        dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
index a74ac0ddb83310ab619870a0f465771ca451eb7a..2163490c1e6b23758cf8efcf3c14e60413e8ed45 100644 (file)
@@ -103,7 +103,7 @@ static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg,
 
        if (1 + count > sizeof(buf)) {
                printk(KERN_WARNING
-                      "mt312: write: len=%zd is too big!\n", count);
+                      "mt312: write: len=%zu is too big!\n", count);
                return -EINVAL;
        }
 
index 10cfc05791689323b0d4ab5b967eb22ff7e9e1db..873ea1da844b3b89940cff3a3082c9172b76b629 100644 (file)
@@ -111,7 +111,7 @@ static int or51211_load_firmware (struct dvb_frontend* fe,
        u8 tudata[585];
        int i;
 
-       dprintk("Firmware is %zd bytes\n",fw->size);
+       dprintk("Firmware is %zu bytes\n", fw->size);
 
        /* Get eprom data */
        tudata[0] = 17;
index fdbed35c87fa08472dbce4c2b016a7edd6231dd2..eb737cf29a36bf2487d97e6ae2462a15defe3e78 100644 (file)
@@ -936,7 +936,7 @@ static void rtl2832_i2c_gate_work(struct work_struct *work)
        if (ret != 1)
                goto err;
 
-       priv->i2c_gate_state = 0;
+       priv->i2c_gate_state = false;
 
        return;
 err:
index 023e0f49c786e732450b346ce50df9c7a1985e50..7bf98cf6bbe172715b3b2923fe771c35b5c5247b 100644 (file)
@@ -329,7 +329,7 @@ static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
 static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf(
                struct rtl2832_sdr_state *s)
 {
-       unsigned long flags = 0;
+       unsigned long flags;
        struct rtl2832_sdr_frame_buf *buf = NULL;
 
        spin_lock_irqsave(&s->queued_bufs_lock, flags);
@@ -365,17 +365,19 @@ static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s,
                dst_len = 0;
        }
 
-       /* calculate samping rate and output it in 10 seconds intervals */
+       /* calculate sample rate and output it in 10 seconds intervals */
        if (unlikely(time_is_before_jiffies(s->jiffies_next))) {
-#define MSECS 10000UL
+               #define MSECS 10000UL
+               unsigned int msecs = jiffies_to_msecs(jiffies -
+                               s->jiffies_next + msecs_to_jiffies(MSECS));
                unsigned int samples = s->sample - s->sample_measured;
 
                s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
                s->sample_measured = s->sample;
                dev_dbg(&s->udev->dev,
-                               "slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
-                               src_len, samples, MSECS,
-                               samples * 1000UL / MSECS);
+                               "slen=%u samples=%u msecs=%u sample rate=%lu\n",
+                               src_len, samples, msecs,
+                               samples * 1000UL / msecs);
        }
 
        /* total number of I+Q pairs */
@@ -394,8 +396,8 @@ static void rtl2832_sdr_urb_complete(struct urb *urb)
        struct rtl2832_sdr_frame_buf *fbuf;
 
        dev_dbg_ratelimited(&s->udev->dev,
-                       "%s: status=%d length=%d/%d errors=%d\n",
-                       __func__, urb->status, urb->actual_length,
+                       "status=%d length=%d/%d errors=%d\n",
+                       urb->status, urb->actual_length,
                        urb->transfer_buffer_length, urb->error_count);
 
        switch (urb->status) {
@@ -443,7 +445,7 @@ static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s)
        int i;
 
        for (i = s->urbs_submitted - 1; i >= 0; i--) {
-               dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i);
+               dev_dbg(&s->udev->dev, "kill urb=%d\n", i);
                /* stop the URB */
                usb_kill_urb(s->urb_list[i]);
        }
@@ -457,7 +459,7 @@ static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s)
        int i, ret;
 
        for (i = 0; i < s->urbs_initialized; i++) {
-               dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i);
+               dev_dbg(&s->udev->dev, "submit urb=%d\n", i);
                ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC);
                if (ret) {
                        dev_err(&s->udev->dev,
@@ -477,8 +479,7 @@ static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s)
        if (s->flags & USB_STATE_URB_BUF) {
                while (s->buf_num) {
                        s->buf_num--;
-                       dev_dbg(&s->udev->dev, "%s: free buf=%d\n",
-                                       __func__, s->buf_num);
+                       dev_dbg(&s->udev->dev, "free buf=%d\n", s->buf_num);
                        usb_free_coherent(s->udev, s->buf_size,
                                          s->buf_list[s->buf_num],
                                          s->dma_addr[s->buf_num]);
@@ -494,24 +495,22 @@ static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s)
        s->buf_num = 0;
        s->buf_size = BULK_BUFFER_SIZE;
 
-       dev_dbg(&s->udev->dev,
-                       "%s: all in all I will use %u bytes for streaming\n",
-                       __func__,  MAX_BULK_BUFS * BULK_BUFFER_SIZE);
+       dev_dbg(&s->udev->dev, "all in all I will use %u bytes for streaming\n",
+                       MAX_BULK_BUFS * BULK_BUFFER_SIZE);
 
        for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) {
                s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev,
                                BULK_BUFFER_SIZE, GFP_ATOMIC,
                                &s->dma_addr[s->buf_num]);
                if (!s->buf_list[s->buf_num]) {
-                       dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n",
-                                       __func__, s->buf_num);
+                       dev_dbg(&s->udev->dev, "alloc buf=%d failed\n",
+                                       s->buf_num);
                        rtl2832_sdr_free_stream_bufs(s);
                        return -ENOMEM;
                }
 
-               dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n",
-                               __func__, s->buf_num,
-                               s->buf_list[s->buf_num],
+               dev_dbg(&s->udev->dev, "alloc buf=%d %p (dma %llu)\n",
+                               s->buf_num, s->buf_list[s->buf_num],
                                (long long)s->dma_addr[s->buf_num]);
                s->flags |= USB_STATE_URB_BUF;
        }
@@ -527,8 +526,7 @@ static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s)
 
        for (i = s->urbs_initialized - 1; i >= 0; i--) {
                if (s->urb_list[i]) {
-                       dev_dbg(&s->udev->dev, "%s: free urb=%d\n",
-                                       __func__, i);
+                       dev_dbg(&s->udev->dev, "free urb=%d\n", i);
                        /* free the URBs */
                        usb_free_urb(s->urb_list[i]);
                }
@@ -544,10 +542,10 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s)
 
        /* allocate the URBs */
        for (i = 0; i < MAX_BULK_BUFS; i++) {
-               dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i);
+               dev_dbg(&s->udev->dev, "alloc urb=%d\n", i);
                s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
                if (!s->urb_list[i]) {
-                       dev_dbg(&s->udev->dev, "%s: failed\n", __func__);
+                       dev_dbg(&s->udev->dev, "failed\n");
                        for (j = 0; j < i; j++)
                                usb_free_urb(s->urb_list[j]);
                        return -ENOMEM;
@@ -570,9 +568,9 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s)
 /* Must be called with vb_queue_lock hold */
 static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s)
 {
-       unsigned long flags = 0;
+       unsigned long flags;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        spin_lock_irqsave(&s->queued_bufs_lock, flags);
        while (!list_empty(&s->queued_bufs)) {
@@ -591,7 +589,7 @@ static void rtl2832_sdr_release_sec(struct dvb_frontend *fe)
 {
        struct rtl2832_sdr_state *s = fe->sec_priv;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        mutex_lock(&s->vb_queue_lock);
        mutex_lock(&s->v4l2_lock);
@@ -613,7 +611,7 @@ static int rtl2832_sdr_querycap(struct file *file, void *fh,
 {
        struct rtl2832_sdr_state *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
        strlcpy(cap->card, s->vdev.name, sizeof(cap->card));
@@ -631,15 +629,15 @@ static int rtl2832_sdr_queue_setup(struct vb2_queue *vq,
 {
        struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
 
-       dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers);
+       dev_dbg(&s->udev->dev, "nbuffers=%d\n", *nbuffers);
 
        /* Need at least 8 buffers */
        if (vq->num_buffers + *nbuffers < 8)
                *nbuffers = 8 - vq->num_buffers;
        *nplanes = 1;
        sizes[0] = PAGE_ALIGN(s->buffersize);
-       dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
-                       __func__, *nbuffers, sizes[0]);
+       dev_dbg(&s->udev->dev, "nbuffers=%d sizes[0]=%d\n",
+                       *nbuffers, sizes[0]);
        return 0;
 }
 
@@ -659,7 +657,7 @@ static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb)
        struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
        struct rtl2832_sdr_frame_buf *buf =
                        container_of(vb, struct rtl2832_sdr_frame_buf, vb);
-       unsigned long flags = 0;
+       unsigned long flags;
 
        /* Check the device has not disconnected between prep and queuing */
        if (!s->udev) {
@@ -681,7 +679,7 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s)
        u64 u64tmp;
        u32 u32tmp;
 
-       dev_dbg(&s->udev->dev, "%s: f_adc=%u\n", __func__, s->f_adc);
+       dev_dbg(&s->udev->dev, "f_adc=%u\n", s->f_adc);
 
        if (!test_bit(POWER_ON, &s->flags))
                return 0;
@@ -715,8 +713,7 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s)
        u64tmp = -u64tmp;
        u32tmp = u64tmp & 0x3fffff;
 
-       dev_dbg(&s->udev->dev, "%s: f_if=%u if_ctl=%08x\n",
-                       __func__, f_if, u32tmp);
+       dev_dbg(&s->udev->dev, "f_if=%u if_ctl=%08x\n", f_if, u32tmp);
 
        buf[0] = (u32tmp >> 16) & 0xff;
        buf[1] = (u32tmp >>  8) & 0xff;
@@ -903,7 +900,7 @@ static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s)
 {
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        /* PID filter */
        ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1);
@@ -964,8 +961,8 @@ static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s)
        c->frequency = s->f_tuner;
        c->delivery_system = SYS_DVBT;
 
-       dev_dbg(&s->udev->dev, "%s: frequency=%u bandwidth=%d\n",
-                       __func__, c->frequency, c->bandwidth_hz);
+       dev_dbg(&s->udev->dev, "frequency=%u bandwidth=%d\n",
+                       c->frequency, c->bandwidth_hz);
 
        if (!test_bit(POWER_ON, &s->flags))
                return 0;
@@ -980,7 +977,7 @@ static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s)
 {
        struct dvb_frontend *fe = s->fe;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        if (fe->ops.tuner_ops.init)
                fe->ops.tuner_ops.init(fe);
@@ -992,7 +989,7 @@ static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s)
 {
        struct dvb_frontend *fe = s->fe;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        if (fe->ops.tuner_ops.sleep)
                fe->ops.tuner_ops.sleep(fe);
@@ -1005,7 +1002,7 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
        struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        if (!s->udev)
                return -ENODEV;
@@ -1054,7 +1051,7 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
 {
        struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        mutex_lock(&s->v4l2_lock);
 
@@ -1088,8 +1085,7 @@ static int rtl2832_sdr_g_tuner(struct file *file, void *priv,
 {
        struct rtl2832_sdr_state *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s: index=%d type=%d\n",
-                       __func__, v->index, v->type);
+       dev_dbg(&s->udev->dev, "index=%d type=%d\n", v->index, v->type);
 
        if (v->index == 0) {
                strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name));
@@ -1115,7 +1111,7 @@ static int rtl2832_sdr_s_tuner(struct file *file, void *priv,
 {
        struct rtl2832_sdr_state *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        if (v->index > 1)
                return -EINVAL;
@@ -1127,8 +1123,8 @@ static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
 {
        struct rtl2832_sdr_state *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n",
-                       __func__, band->tuner, band->type, band->index);
+       dev_dbg(&s->udev->dev, "tuner=%d type=%d index=%d\n",
+                       band->tuner, band->type, band->index);
 
        if (band->tuner == 0) {
                if (band->index >= ARRAY_SIZE(bands_adc))
@@ -1153,8 +1149,8 @@ static int rtl2832_sdr_g_frequency(struct file *file, void *priv,
        struct rtl2832_sdr_state *s = video_drvdata(file);
        int ret  = 0;
 
-       dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n",
-                       __func__, f->tuner, f->type);
+       dev_dbg(&s->udev->dev, "tuner=%d type=%d\n",
+                       f->tuner, f->type);
 
        if (f->tuner == 0) {
                f->frequency = s->f_adc;
@@ -1175,8 +1171,8 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
        struct rtl2832_sdr_state *s = video_drvdata(file);
        int ret, band;
 
-       dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n",
-                       __func__, f->tuner, f->type, f->frequency);
+       dev_dbg(&s->udev->dev, "tuner=%d type=%d frequency=%u\n",
+                       f->tuner, f->type, f->frequency);
 
        /* ADC band midpoints */
        #define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2)
@@ -1194,15 +1190,13 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
                                bands_adc[band].rangelow,
                                bands_adc[band].rangehigh);
 
-               dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n",
-                               __func__, s->f_adc);
+               dev_dbg(&s->udev->dev, "ADC frequency=%u Hz\n", s->f_adc);
                ret = rtl2832_sdr_set_adc(s);
        } else if (f->tuner == 1) {
                s->f_tuner = clamp_t(unsigned int, f->frequency,
                                bands_fm[0].rangelow,
                                bands_fm[0].rangehigh);
-               dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n",
-                               __func__, f->frequency);
+               dev_dbg(&s->udev->dev, "RF frequency=%u Hz\n", f->frequency);
 
                ret = rtl2832_sdr_set_tuner_freq(s);
        } else {
@@ -1217,7 +1211,7 @@ static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv,
 {
        struct rtl2832_sdr_state *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        if (f->index >= s->num_formats)
                return -EINVAL;
@@ -1233,7 +1227,7 @@ static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv,
 {
        struct rtl2832_sdr_state *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(&s->udev->dev, "\n");
 
        f->fmt.sdr.pixelformat = s->pixelformat;
        f->fmt.sdr.buffersize = s->buffersize;
@@ -1250,7 +1244,7 @@ static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv,
        struct vb2_queue *q = &s->vb_queue;
        int i;
 
-       dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+       dev_dbg(&s->udev->dev, "pixelformat fourcc %4.4s\n",
                        (char *)&f->fmt.sdr.pixelformat);
 
        if (vb2_is_busy(q))
@@ -1280,7 +1274,7 @@ static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv,
        struct rtl2832_sdr_state *s = video_drvdata(file);
        int i;
 
-       dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+       dev_dbg(&s->udev->dev, "pixelformat fourcc %4.4s\n",
                        (char *)&f->fmt.sdr.pixelformat);
 
        memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
@@ -1354,8 +1348,8 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl)
        int ret;
 
        dev_dbg(&s->udev->dev,
-                       "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n",
-                       __func__, ctrl->id, ctrl->name, ctrl->val,
+                       "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n",
+                       ctrl->id, ctrl->name, ctrl->val,
                        ctrl->minimum, ctrl->maximum, ctrl->step);
 
        switch (ctrl->id) {
@@ -1432,7 +1426,7 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
        s->pixelformat = formats[0].pixelformat;
        s->buffersize = formats[0].buffersize;
        s->num_formats = NUM_FORMATS;
-       if (rtl2832_sdr_emulated_fmt == false)
+       if (!rtl2832_sdr_emulated_fmt)
                s->num_formats -= 1;
 
        mutex_init(&s->v4l2_lock);
index 3a2d6c5aded6b450be22afe3e54443f254d78c95..98ddb49ad52b1a27a268ef6fabab3d3de97496e7 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Driver for Silicon Labs SI2165 DVB-C/-T Demodulator
+    Driver for Silicon Labs Si2161 DVB-T and Si2165 DVB-C/-T Demodulator
 
     Copyright (C) 2013-2014 Matthias Schwarzott <zzam@gentoo.org>
 
@@ -44,9 +44,7 @@ struct si2165_state {
 
        struct si2165_config config;
 
-       /* chip revision */
-       u8 revcode;
-       /* chip type */
+       u8 chip_revcode;
        u8 chip_type;
 
        /* calculated by xtal and div settings */
@@ -312,7 +310,7 @@ static u32 si2165_get_fe_clk(struct si2165_state *state)
        return state->adc_clk;
 }
 
-static bool si2165_wait_init_done(struct si2165_state *state)
+static int si2165_wait_init_done(struct si2165_state *state)
 {
        int ret = -EINVAL;
        u8 val = 0;
@@ -407,7 +405,7 @@ static int si2165_upload_firmware(struct si2165_state *state)
        int ret;
 
        const struct firmware *fw = NULL;
-       u8 *fw_file = SI2165_FIRMWARE;
+       u8 *fw_file;
        const u8 *data;
        u32 len;
        u32 offset;
@@ -415,10 +413,20 @@ static int si2165_upload_firmware(struct si2165_state *state)
        u8 block_count;
        u16 crc_expected;
 
+       switch (state->chip_revcode) {
+       case 0x03: /* revision D */
+               fw_file = SI2165_FIRMWARE_REV_D;
+               break;
+       default:
+               dev_info(&state->i2c->dev, "%s: no firmware file for revision=%d\n",
+                       KBUILD_MODNAME, state->chip_revcode);
+               return 0;
+       }
+
        /* request the firmware, this will block and timeout */
        ret = request_firmware(&fw, fw_file, state->i2c->dev.parent);
        if (ret) {
-               dev_warn(&state->i2c->dev, "%s: firmare file '%s' not found\n",
+               dev_warn(&state->i2c->dev, "%s: firmware file '%s' not found\n",
                                KBUILD_MODNAME, fw_file);
                goto error;
        }
@@ -908,7 +916,7 @@ static void si2165_release(struct dvb_frontend *fe)
 
 static struct dvb_frontend_ops si2165_ops = {
        .info = {
-               .name = "Silicon Labs Si2165",
+               .name = "Silicon Labs ",
                .caps = FE_CAN_FEC_1_2 |
                        FE_CAN_FEC_2_3 |
                        FE_CAN_FEC_3_4 |
@@ -948,6 +956,8 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config,
        int n;
        int io_ret;
        u8 val;
+       char rev_char;
+       const char *chip_name;
 
        if (config == NULL || i2c == NULL)
                goto error;
@@ -984,7 +994,7 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config,
        if (val != state->config.chip_mode)
                goto error;
 
-       io_ret = si2165_readreg8(state, 0x0023 , &state->revcode);
+       io_ret = si2165_readreg8(state, 0x0023, &state->chip_revcode);
        if (io_ret < 0)
                goto error;
 
@@ -997,22 +1007,35 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config,
        if (io_ret < 0)
                goto error;
 
-       dev_info(&state->i2c->dev, "%s: hardware revision 0x%02x, chip type 0x%02x\n",
-                KBUILD_MODNAME, state->revcode, state->chip_type);
+       if (state->chip_revcode < 26)
+               rev_char = 'A' + state->chip_revcode;
+       else
+               rev_char = '?';
 
-       /* It is a guess that register 0x0118 (chip type?) can be used to
-        * differ between si2161, si2163 and si2165
-        * Only si2165 has been tested.
-        */
-       if (state->revcode == 0x03 && state->chip_type == 0x07) {
+       switch (state->chip_type) {
+       case 0x06:
+               chip_name = "Si2161";
+               state->has_dvbt = true;
+               break;
+       case 0x07:
+               chip_name = "Si2165";
                state->has_dvbt = true;
                state->has_dvbc = true;
-       } else {
-               dev_err(&state->i2c->dev, "%s: Unsupported chip.\n",
-                       KBUILD_MODNAME);
+               break;
+       default:
+               dev_err(&state->i2c->dev, "%s: Unsupported Silicon Labs chip (type %d, rev %d)\n",
+                       KBUILD_MODNAME, state->chip_type, state->chip_revcode);
                goto error;
        }
 
+       dev_info(&state->i2c->dev,
+               "%s: Detected Silicon Labs %s-%c (type %d, rev %d)\n",
+               KBUILD_MODNAME, chip_name, rev_char, state->chip_type,
+               state->chip_revcode);
+
+       strlcat(state->frontend.ops.info.name, chip_name,
+                       sizeof(state->frontend.ops.info.name));
+
        n = 0;
        if (state->has_dvbt) {
                state->frontend.ops.delsys[n++] = SYS_DVBT;
@@ -1037,4 +1060,4 @@ MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 MODULE_DESCRIPTION("Silicon Labs Si2165 DVB-C/-T Demodulator driver");
 MODULE_AUTHOR("Matthias Schwarzott <zzam@gentoo.org>");
 MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(SI2165_FIRMWARE);
+MODULE_FIRMWARE(SI2165_FIRMWARE_REV_D);
index d4cc93fe1096ea31f5d67d7820c845986f4184c4..2b70cf12cd797799eaa1c7bfd7299428a562446a 100644 (file)
@@ -18,6 +18,6 @@
 #ifndef _DVB_SI2165_PRIV
 #define _DVB_SI2165_PRIV
 
-#define SI2165_FIRMWARE "dvb-demod-si2165.fw"
+#define SI2165_FIRMWARE_REV_D "dvb-demod-si2165.fw"
 
 #endif /* _DVB_SI2165_PRIV */
index 8f81d979de30541b31fb6f56559ef2c56aeb62ea..1cd93be281ed4d42194d15d2535bd86dd5eb638d 100644 (file)
@@ -55,8 +55,7 @@ static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd)
                                break;
                }
 
-               dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n",
-                               __func__,
+               dev_dbg(&s->client->dev, "cmd execution took %d ms\n",
                                jiffies_to_msecs(jiffies) -
                                (jiffies_to_msecs(timeout) - TIMEOUT));
 
@@ -75,7 +74,7 @@ err_mutex_unlock:
 
        return 0;
 err:
-       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&s->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -150,12 +149,12 @@ static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status)
                c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
        }
 
-       dev_dbg(&s->client->dev, "%s: status=%02x args=%*ph\n",
-                       __func__, *status, cmd.rlen, cmd.args);
+       dev_dbg(&s->client->dev, "status=%02x args=%*ph\n",
+                       *status, cmd.rlen, cmd.args);
 
        return 0;
 err:
-       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&s->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -168,10 +167,10 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
        u8 bandwidth, delivery_system;
 
        dev_dbg(&s->client->dev,
-                       "%s: delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u\n",
-                       __func__, c->delivery_system, c->modulation,
+                       "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u, stream_id=%d\n",
+                       c->delivery_system, c->modulation,
                        c->frequency, c->bandwidth_hz, c->symbol_rate,
-                       c->inversion);
+                       c->inversion, c->stream_id);
 
        if (!s->active) {
                ret = -EAGAIN;
@@ -235,6 +234,18 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
        if (ret)
                goto err;
 
+       if (c->delivery_system == SYS_DVBT2) {
+               /* select PLP */
+               cmd.args[0] = 0x52;
+               cmd.args[1] = c->stream_id & 0xff;
+               cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1;
+               cmd.wlen = 3;
+               cmd.rlen = 1;
+               ret = si2168_cmd_execute(s, &cmd);
+               if (ret)
+                       goto err;
+       }
+
        memcpy(cmd.args, "\x51\x03", 2);
        cmd.wlen = 2;
        cmd.rlen = 12;
@@ -297,13 +308,6 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
        if (ret)
                goto err;
 
-       memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6);
-       cmd.wlen = 6;
-       cmd.rlen = 4;
-       ret = si2168_cmd_execute(s, &cmd);
-       if (ret)
-               goto err;
-
        memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x18", 6);
        cmd.wlen = 6;
        cmd.rlen = 4;
@@ -343,7 +347,7 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
 
        return 0;
 err:
-       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&s->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -357,8 +361,9 @@ static int si2168_init(struct dvb_frontend *fe)
        struct si2168_cmd cmd;
        unsigned int chip_id;
 
-       dev_dbg(&s->client->dev, "%s:\n", __func__);
+       dev_dbg(&s->client->dev, "\n");
 
+       /* initialize */
        memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13);
        cmd.wlen = 13;
        cmd.rlen = 0;
@@ -366,6 +371,26 @@ static int si2168_init(struct dvb_frontend *fe)
        if (ret)
                goto err;
 
+       if (s->fw_loaded) {
+               /* resume */
+               memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8);
+               cmd.wlen = 8;
+               cmd.rlen = 1;
+               ret = si2168_cmd_execute(s, &cmd);
+               if (ret)
+                       goto err;
+
+               memcpy(cmd.args, "\x85", 1);
+               cmd.wlen = 1;
+               cmd.rlen = 1;
+               ret = si2168_cmd_execute(s, &cmd);
+               if (ret)
+                       goto err;
+
+               goto warm;
+       }
+
+       /* power up */
        memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8);
        cmd.wlen = 8;
        cmd.rlen = 1;
@@ -400,16 +425,16 @@ static int si2168_init(struct dvb_frontend *fe)
                break;
        default:
                dev_err(&s->client->dev,
-                               "%s: unkown chip version Si21%d-%c%c%c\n",
-                               KBUILD_MODNAME, cmd.args[2], cmd.args[1],
+                               "unknown chip version Si21%d-%c%c%c\n",
+                               cmd.args[2], cmd.args[1],
                                cmd.args[3], cmd.args[4]);
                ret = -EINVAL;
                goto err;
        }
 
        /* cold state - try to download firmware */
-       dev_info(&s->client->dev, "%s: found a '%s' in cold state\n",
-                       KBUILD_MODNAME, si2168_ops.info.name);
+       dev_info(&s->client->dev, "found a '%s' in cold state\n",
+                       si2168_ops.info.name);
 
        /* request the firmware, this will block and timeout */
        ret = request_firmware(&fw, fw_file, &s->client->dev);
@@ -422,18 +447,18 @@ static int si2168_init(struct dvb_frontend *fe)
 
                if (ret == 0) {
                        dev_notice(&s->client->dev,
-                                       "%s: please install firmware file '%s'\n",
-                                       KBUILD_MODNAME, SI2168_B40_FIRMWARE);
+                                       "please install firmware file '%s'\n",
+                                       SI2168_B40_FIRMWARE);
                } else {
                        dev_err(&s->client->dev,
-                                       "%s: firmware file '%s' not found\n",
-                                       KBUILD_MODNAME, fw_file);
+                                       "firmware file '%s' not found\n",
+                                       fw_file);
                        goto err;
                }
        }
 
-       dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n",
-                       KBUILD_MODNAME, fw_file);
+       dev_info(&s->client->dev, "downloading firmware from file '%s'\n",
+                       fw_file);
 
        for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) {
                len = remaining;
@@ -446,8 +471,8 @@ static int si2168_init(struct dvb_frontend *fe)
                ret = si2168_cmd_execute(s, &cmd);
                if (ret) {
                        dev_err(&s->client->dev,
-                                       "%s: firmware download failed=%d\n",
-                                       KBUILD_MODNAME, ret);
+                                       "firmware download failed=%d\n",
+                                       ret);
                        goto err;
                }
        }
@@ -462,8 +487,20 @@ static int si2168_init(struct dvb_frontend *fe)
        if (ret)
                goto err;
 
-       dev_info(&s->client->dev, "%s: found a '%s' in warm state\n",
-                       KBUILD_MODNAME, si2168_ops.info.name);
+       /* set ts mode */
+       memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6);
+       cmd.args[4] |= s->ts_mode;
+       cmd.wlen = 6;
+       cmd.rlen = 4;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       s->fw_loaded = true;
+
+warm:
+       dev_info(&s->client->dev, "found a '%s' in warm state\n",
+                       si2168_ops.info.name);
 
        s->active = true;
 
@@ -472,7 +509,7 @@ err:
        if (fw)
                release_firmware(fw);
 
-       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&s->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -482,7 +519,7 @@ static int si2168_sleep(struct dvb_frontend *fe)
        int ret;
        struct si2168_cmd cmd;
 
-       dev_dbg(&s->client->dev, "%s:\n", __func__);
+       dev_dbg(&s->client->dev, "\n");
 
        s->active = false;
 
@@ -495,7 +532,7 @@ static int si2168_sleep(struct dvb_frontend *fe)
 
        return 0;
 err:
-       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&s->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -528,8 +565,7 @@ static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
        /* open tuner I2C gate */
        ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1);
        if (ret != 1) {
-               dev_warn(&s->client->dev, "%s: i2c write failed=%d\n",
-                               KBUILD_MODNAME, ret);
+               dev_warn(&s->client->dev, "i2c write failed=%d\n", ret);
                if (ret >= 0)
                        ret = -EREMOTEIO;
        } else {
@@ -553,8 +589,7 @@ static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
        /* close tuner I2C gate */
        ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1);
        if (ret != 1) {
-               dev_warn(&s->client->dev, "%s: i2c write failed=%d\n",
-                               KBUILD_MODNAME, ret);
+               dev_warn(&s->client->dev, "i2c write failed=%d\n", ret);
                if (ret >= 0)
                        ret = -EREMOTEIO;
        } else {
@@ -587,7 +622,8 @@ static const struct dvb_frontend_ops si2168_ops = {
                        FE_CAN_GUARD_INTERVAL_AUTO |
                        FE_CAN_HIERARCHY_AUTO |
                        FE_CAN_MUTE_TS |
-                       FE_CAN_2G_MODULATION
+                       FE_CAN_2G_MODULATION |
+                       FE_CAN_MULTISTREAM
        },
 
        .get_tune_settings = si2168_get_tune_settings,
@@ -607,12 +643,12 @@ static int si2168_probe(struct i2c_client *client,
        struct si2168 *s;
        int ret;
 
-       dev_dbg(&client->dev, "%s:\n", __func__);
+       dev_dbg(&client->dev, "\n");
 
        s = kzalloc(sizeof(struct si2168), GFP_KERNEL);
        if (!s) {
                ret = -ENOMEM;
-               dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+               dev_err(&client->dev, "kzalloc() failed\n");
                goto err;
        }
 
@@ -633,16 +669,17 @@ static int si2168_probe(struct i2c_client *client,
 
        *config->i2c_adapter = s->adapter;
        *config->fe = &s->fe;
+       s->ts_mode = config->ts_mode;
+       s->fw_loaded = false;
 
        i2c_set_clientdata(client, s);
 
        dev_info(&s->client->dev,
-                       "%s: Silicon Labs Si2168 successfully attached\n",
-                       KBUILD_MODNAME);
+                       "Silicon Labs Si2168 successfully attached\n");
        return 0;
 err:
        kfree(s);
-       dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -650,7 +687,7 @@ static int si2168_remove(struct i2c_client *client)
 {
        struct si2168 *s = i2c_get_clientdata(client);
 
-       dev_dbg(&client->dev, "%s:\n", __func__);
+       dev_dbg(&client->dev, "\n");
 
        i2c_del_mux_adapter(s->adapter);
 
index 3c5b5ab01796c69c2cf955e81756ccda08a3108e..e086d671945167046f18a62e4f6cac6131950bda 100644 (file)
@@ -34,6 +34,12 @@ struct si2168_config {
         * returned by driver
         */
        struct i2c_adapter **i2c_adapter;
+
+       /* TS mode */
+       u8 ts_mode;
 };
 
+#define SI2168_TS_PARALLEL     0x06
+#define SI2168_TS_SERIAL       0x03
+
 #endif
index ebbf502ec313cff28deecb7c2531ac8a04cbc75c..e13983ed4be1a077e3b04b896c46870e8aad7a60 100644 (file)
@@ -36,6 +36,8 @@ struct si2168 {
        fe_delivery_system_t delivery_system;
        fe_status_t fe_status;
        bool active;
+       bool fw_loaded;
+       u8 ts_mode;
 };
 
 /* firmare command struct */
index 73b47cc6a13b3b8c2f6ca85d0ee1d9eee71120c1..16850e2bf02fe37bccc4c54af466a43f5dd61b85 100644 (file)
@@ -236,6 +236,9 @@ static int si21_writeregs(struct si21xx_state *state, u8 reg1,
                                .len = len + 1
        };
 
+       if (len > sizeof(buf) - 1)
+               return -EINVAL;
+
        msg.buf[0] =  reg1;
        memcpy(msg.buf + 1, data, len);
 
diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c
new file mode 100644 (file)
index 0000000..9b684d5
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * CIMaX SP2/SP2HF (Atmel T90FJR) CI driver
+ *
+ * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi>
+ *
+ * Heavily based on CIMax2(R) SP2 driver in conjunction with NetUp Dual
+ * DVB-S2 CI card (cimax2) with following copyrights:
+ *
+ *  Copyright (C) 2009 NetUP Inc.
+ *  Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ *  Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ */
+
+#include "sp2_priv.h"
+
+static int sp2_read_i2c(struct sp2 *s, u8 reg, u8 *buf, int len)
+{
+       int ret;
+       struct i2c_client *client = s->client;
+       struct i2c_adapter *adap = client->adapter;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = client->addr,
+                       .flags = 0,
+                       .buf = &reg,
+                       .len = 1
+               }, {
+                       .addr = client->addr,
+                       .flags  = I2C_M_RD,
+                       .buf = buf,
+                       .len = len
+               }
+       };
+
+       ret = i2c_transfer(adap, msg, 2);
+
+       if (ret != 2) {
+               dev_err(&client->dev, "i2c read error, reg = 0x%02x, status = %d\n",
+                               reg, ret);
+               if (ret < 0)
+                       return ret;
+               else
+                       return -EIO;
+       }
+
+       dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %02x\n",
+                               client->addr, reg, buf[0]);
+
+       return 0;
+}
+
+static int sp2_write_i2c(struct sp2 *s, u8 reg, u8 *buf, int len)
+{
+       int ret;
+       u8 buffer[35];
+       struct i2c_client *client = s->client;
+       struct i2c_adapter *adap = client->adapter;
+       struct i2c_msg msg = {
+               .addr = client->addr,
+               .flags = 0,
+               .buf = &buffer[0],
+               .len = len + 1
+       };
+
+       if ((len + 1) > sizeof(buffer)) {
+               dev_err(&client->dev, "i2c wr reg=%02x: len=%d is too big!\n",
+                               reg, len);
+               return -EINVAL;
+       }
+
+       buffer[0] = reg;
+       memcpy(&buffer[1], buf, len);
+
+       ret = i2c_transfer(adap, &msg, 1);
+
+       if (ret != 1) {
+               dev_err(&client->dev, "i2c write error, reg = 0x%02x, status = %d\n",
+                               reg, ret);
+               if (ret < 0)
+                       return ret;
+               else
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+static int sp2_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, u8 acs,
+                       u8 read, int addr, u8 data)
+{
+       struct sp2 *s = en50221->data;
+       u8 store;
+       int mem, ret;
+       int (*ci_op_cam)(void*, u8, int, u8, int*) = s->ci_control;
+
+       dev_dbg(&s->client->dev, "slot=%d, acs=0x%02x, addr=0x%04x, data = 0x%02x",
+                       slot, acs, addr, data);
+
+       if (slot != 0)
+               return -EINVAL;
+
+       /*
+        * change module access type between IO space and attribute memory
+        * when needed
+        */
+       if (s->module_access_type != acs) {
+               ret = sp2_read_i2c(s, 0x00, &store, 1);
+
+               if (ret)
+                       return ret;
+
+               store &= ~(SP2_MOD_CTL_ACS1 | SP2_MOD_CTL_ACS0);
+               store |= acs;
+
+               ret = sp2_write_i2c(s, 0x00, &store, 1);
+               if (ret)
+                       return ret;
+       }
+
+       s->module_access_type = acs;
+
+       /* implementation of ci_op_cam is device specific */
+       if (ci_op_cam) {
+               ret = ci_op_cam(s->priv, read, addr, data, &mem);
+       } else {
+               dev_err(&s->client->dev, "callback not defined");
+               return -EINVAL;
+       }
+
+       if (ret)
+               return ret;
+
+       if (read) {
+               dev_dbg(&s->client->dev, "cam read, addr=0x%04x, data = 0x%04x",
+                               addr, mem);
+               return mem;
+       } else {
+               return 0;
+       }
+}
+
+int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+                               int slot, int addr)
+{
+       return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS,
+                       SP2_CI_RD, addr, 0);
+}
+
+int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+                               int slot, int addr, u8 data)
+{
+       return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS,
+                       SP2_CI_WR, addr, data);
+}
+
+int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221,
+                               int slot, u8 addr)
+{
+       return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS,
+                       SP2_CI_RD, addr, 0);
+}
+
+int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221,
+                               int slot, u8 addr, u8 data)
+{
+       return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS,
+                       SP2_CI_WR, addr, data);
+}
+
+int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
+{
+       struct sp2 *s = en50221->data;
+       u8 buf;
+       int ret;
+
+       dev_dbg(&s->client->dev, "slot: %d\n", slot);
+
+       if (slot != 0)
+               return -EINVAL;
+
+       /* RST on */
+       buf = SP2_MOD_CTL_RST;
+       ret = sp2_write_i2c(s, 0x00, &buf, 1);
+
+       if (ret)
+               return ret;
+
+       usleep_range(500, 600);
+
+       /* RST off */
+       buf = 0x00;
+       ret = sp2_write_i2c(s, 0x00, &buf, 1);
+
+       if (ret)
+               return ret;
+
+       msleep(1000);
+
+       return 0;
+}
+
+int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
+{
+       struct sp2 *s = en50221->data;
+
+       dev_dbg(&s->client->dev, "slot:%d\n", slot);
+
+       /* not implemented */
+       return 0;
+}
+
+int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot)
+{
+       struct sp2 *s = en50221->data;
+       u8 buf;
+
+       dev_dbg(&s->client->dev, "slot:%d\n", slot);
+
+       if (slot != 0)
+               return -EINVAL;
+
+       sp2_read_i2c(s, 0x00, &buf, 1);
+
+       /* disable bypass and enable TS */
+       buf |= (SP2_MOD_CTL_TSOEN | SP2_MOD_CTL_TSIEN);
+       return sp2_write_i2c(s, 0, &buf, 1);
+}
+
+int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221,
+                               int slot, int open)
+{
+       struct sp2 *s = en50221->data;
+       u8 buf[2];
+       int ret;
+
+       dev_dbg(&s->client->dev, "slot:%d open:%d\n", slot, open);
+
+       /*
+        * CAM module INSERT/REMOVE processing. Slow operation because of i2c
+        * transfers. Throttle read to one per sec.
+        */
+       if (time_after(jiffies, s->next_status_checked_time)) {
+               ret = sp2_read_i2c(s, 0x00, buf, 1);
+               s->next_status_checked_time = jiffies + msecs_to_jiffies(1000);
+
+               if (ret)
+                       return 0;
+
+               if (buf[0] & SP2_MOD_CTL_DET)
+                       s->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
+                                       DVB_CA_EN50221_POLL_CAM_READY;
+               else
+                       s->status = 0;
+       }
+
+       return s->status;
+}
+
+int sp2_init(struct sp2 *s)
+{
+       int ret = 0;
+       u8 buf;
+       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, 600 ns */
+               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, 600 ns */
+               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 control reg, VCC power on */
+               0x00, /* RFU */
+               0x00, /* int status read only */
+               0x00, /* Interrupt Mask Register */
+               0x05, /* EXTINT=active-high, INT=push-pull */
+               0x00, /* USCG1 */
+               0x04, /* ack active low */
+               0x00, /* LOCK = 0 */
+               0x22, /* unknown */
+               0x00, /* synchronization? */
+       };
+
+       dev_dbg(&s->client->dev, "\n");
+
+       s->ca.owner = THIS_MODULE;
+       s->ca.read_attribute_mem = sp2_ci_read_attribute_mem;
+       s->ca.write_attribute_mem = sp2_ci_write_attribute_mem;
+       s->ca.read_cam_control = sp2_ci_read_cam_control;
+       s->ca.write_cam_control = sp2_ci_write_cam_control;
+       s->ca.slot_reset = sp2_ci_slot_reset;
+       s->ca.slot_shutdown = sp2_ci_slot_shutdown;
+       s->ca.slot_ts_enable = sp2_ci_slot_ts_enable;
+       s->ca.poll_slot_status = sp2_ci_poll_slot_status;
+       s->ca.data = s;
+       s->module_access_type = 0;
+
+       /* initialize all regs */
+       ret = sp2_write_i2c(s, 0x00, &cimax_init[0], 34);
+       if (ret)
+               goto err;
+
+       /* lock registers */
+       buf = 1;
+       ret = sp2_write_i2c(s, 0x1f, &buf, 1);
+       if (ret)
+               goto err;
+
+       /* power on slots */
+       ret = sp2_write_i2c(s, 0x18, &buf, 1);
+       if (ret)
+               goto err;
+
+       ret = dvb_ca_en50221_init(s->dvb_adap, &s->ca, 0, 1);
+       if (ret)
+               goto err;
+
+       return 0;
+
+err:
+       dev_dbg(&s->client->dev, "init failed=%d\n", ret);
+       return ret;
+}
+
+int sp2_exit(struct i2c_client *client)
+{
+       struct sp2 *s;
+
+       dev_dbg(&client->dev, "\n");
+
+       if (client == NULL)
+               return 0;
+
+       s = i2c_get_clientdata(client);
+       if (s == NULL)
+               return 0;
+
+       if (s->ca.data == NULL)
+               return 0;
+
+       dvb_ca_en50221_release(&s->ca);
+
+       return 0;
+}
+
+static int sp2_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct sp2_config *cfg = client->dev.platform_data;
+       struct sp2 *s;
+       int ret;
+
+       dev_dbg(&client->dev, "\n");
+
+       s = kzalloc(sizeof(struct sp2), GFP_KERNEL);
+       if (!s) {
+               ret = -ENOMEM;
+               dev_err(&client->dev, "kzalloc() failed\n");
+               goto err;
+       }
+
+       s->client = client;
+       s->dvb_adap = cfg->dvb_adap;
+       s->priv = cfg->priv;
+       s->ci_control = cfg->ci_control;
+
+       i2c_set_clientdata(client, s);
+
+       ret = sp2_init(s);
+       if (ret)
+               goto err;
+
+       dev_info(&s->client->dev, "CIMaX SP2 successfully attached\n");
+       return 0;
+err:
+       dev_dbg(&client->dev, "init failed=%d\n", ret);
+       kfree(s);
+
+       return ret;
+}
+
+static int sp2_remove(struct i2c_client *client)
+{
+       struct si2157 *s = i2c_get_clientdata(client);
+
+       dev_dbg(&client->dev, "\n");
+
+       sp2_exit(client);
+       if (s != NULL)
+               kfree(s);
+
+       return 0;
+}
+
+static const struct i2c_device_id sp2_id[] = {
+       {"sp2", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, sp2_id);
+
+static struct i2c_driver sp2_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "sp2",
+       },
+       .probe          = sp2_probe,
+       .remove         = sp2_remove,
+       .id_table       = sp2_id,
+};
+
+module_i2c_driver(sp2_driver);
+
+MODULE_DESCRIPTION("CIMaX SP2/HF CI driver");
+MODULE_AUTHOR("Olli Salonen <olli.salonen@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/sp2.h b/drivers/media/dvb-frontends/sp2.h
new file mode 100644 (file)
index 0000000..6cceea0
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * CIMaX SP2/HF CI driver
+ *
+ * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ */
+
+#ifndef SP2_H
+#define SP2_H
+
+#include <linux/kconfig.h>
+#include "dvb_ca_en50221.h"
+
+/*
+ * I2C address
+ * 0x40 (port 0)
+ * 0x41 (port 1)
+ */
+struct sp2_config {
+       /* dvb_adapter to attach the ci to */
+       struct dvb_adapter *dvb_adap;
+
+       /* function ci_control handles the device specific ci ops */
+       void *ci_control;
+
+       /* priv is passed back to function ci_control */
+       void *priv;
+};
+
+extern int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+                                       int slot, int addr);
+extern int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+                                       int slot, int addr, u8 data);
+extern int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221,
+                                       int slot, u8 addr);
+extern int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221,
+                                       int slot, u8 addr, u8 data);
+extern int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot);
+extern int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot);
+extern int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot);
+extern int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221,
+                                       int slot, int open);
+
+#endif
diff --git a/drivers/media/dvb-frontends/sp2_priv.h b/drivers/media/dvb-frontends/sp2_priv.h
new file mode 100644 (file)
index 0000000..37fef7b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * CIMaX SP2/HF CI driver
+ *
+ * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ */
+
+#ifndef SP2_PRIV_H
+#define SP2_PRIV_H
+
+#include "sp2.h"
+#include "dvb_frontend.h"
+
+/* state struct */
+struct sp2 {
+       int status;
+       struct i2c_client *client;
+       struct dvb_adapter *dvb_adap;
+       struct dvb_ca_en50221 ca;
+       int module_access_type;
+       unsigned long next_status_checked_time;
+       void *priv;
+       void *ci_control;
+};
+
+#define SP2_CI_ATTR_ACS                0x00
+#define SP2_CI_IO_ACS          0x04
+#define SP2_CI_WR              0
+#define SP2_CI_RD              1
+
+/* Module control register (0x00 module A, 0x09 module B) bits */
+#define SP2_MOD_CTL_DET                0x01
+#define SP2_MOD_CTL_AUTO       0x02
+#define SP2_MOD_CTL_ACS0       0x04
+#define SP2_MOD_CTL_ACS1       0x08
+#define SP2_MOD_CTL_HAD                0x10
+#define SP2_MOD_CTL_TSIEN      0x20
+#define SP2_MOD_CTL_TSOEN      0x40
+#define SP2_MOD_CTL_RST                0x80
+
+#endif
index 2aa8ef76eba259efba6490590f5201f0882e5805..57dc2abaa87bc7d5e0cf881d3b30e3065a6dfc26 100644 (file)
@@ -394,8 +394,7 @@ static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber)
        if (ret < 0)
                return -EIO;
 
-        tmp = ret << 6;
-
+       tmp = ret << 6;
        if (tmp >= 0x3FFF0)
                tmp = ~0;
 
index 59b6e661acc085bd99e6c11ff8dc8f58c88de35f..b31ff265ff248996507a0766739bb5e10392a239 100644 (file)
@@ -59,7 +59,6 @@ struct stv0367cab_state {
        int locked;                     /* channel found                */
        u32 freq_khz;                   /* found frequency (in kHz)     */
        u32 symbol_rate;                /* found symbol rate (in Bds)   */
-       enum stv0367cab_mod modulation; /* modulation                   */
        fe_spectral_inversion_t spect_inv; /* Spectrum Inversion        */
 };
 
@@ -554,7 +553,7 @@ static struct st_register def0367ter[STV0367TER_NBREGS] = {
 #define RF_LOOKUP_TABLE_SIZE  31
 #define RF_LOOKUP_TABLE2_SIZE 16
 /* RF Level (for RF AGC->AGC1) Lookup Table, depends on the board and tuner.*/
-s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = {
+static const s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = {
        {/*AGC1*/
                48, 50, 51, 53, 54, 56, 57, 58, 60, 61, 62, 63,
                64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
@@ -566,7 +565,7 @@ s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = {
        }
 };
 /* RF Level (for IF AGC->AGC2) Lookup Table, depends on the board and tuner.*/
-s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_SIZE] = {
+static const s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_SIZE] = {
        {/*AGC2*/
                28, 29, 31, 32, 34, 35, 36, 37,
                38, 39, 40, 41, 42, 43, 44, 45,
@@ -1935,8 +1934,6 @@ static int stv0367ter_get_frontend(struct dvb_frontend *fe)
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
        struct stv0367_state *state = fe->demodulator_priv;
        struct stv0367ter_state *ter_state = state->ter_state;
-
-       int error = 0;
        enum stv0367_ter_mode mode;
        int constell = 0,/* snr = 0,*/ Data = 0;
 
@@ -2020,7 +2017,7 @@ static int stv0367ter_get_frontend(struct dvb_frontend *fe)
 
        p->guard_interval = stv0367_readbits(state, F367TER_SYR_GUARD);
 
-       return error;
+       return 0;
 }
 
 static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr)
@@ -2999,7 +2996,6 @@ enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state,
 
        if (QAMFEC_Lock) {
                signalType = FE_CAB_DATAOK;
-               cab_state->modulation = p->modulation;
                cab_state->spect_inv = stv0367_readbits(state,
                                                        F367CAB_QUAD_INV);
 #if 0
@@ -3165,7 +3161,7 @@ static int stv0367cab_get_frontend(struct dvb_frontend *fe)
        case FE_CAB_MOD_QAM128:
                p->modulation = QAM_128;
                break;
-       case QAM_256:
+       case FE_CAB_MOD_QAM256:
                p->modulation = QAM_256;
                break;
        default:
index e5a87b57d8550c39d7519282d1e6469a3c939b84..2c88abfab5313b5976b14f31637794ffa1017ba0 100644 (file)
@@ -1270,7 +1270,6 @@ enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_mode LDPC_Mode,
                                        enum fe_stv0900_demod_num demod)
 {
-       enum fe_stv0900_error error = STV0900_NO_ERROR;
        s32 reg_ind;
 
        dprintk("%s\n", __func__);
@@ -1337,7 +1336,7 @@ enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp,
                break;
        }
 
-       return error;
+       return STV0900_NO_ERROR;
 }
 
 static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
@@ -1555,8 +1554,6 @@ static int stv0900_status(struct stv0900_internal *intp,
 static int stv0900_set_mis(struct stv0900_internal *intp,
                                enum fe_stv0900_demod_num demod, int mis)
 {
-       enum fe_stv0900_error error = STV0900_NO_ERROR;
-
        dprintk("%s\n", __func__);
 
        if (mis < 0 || mis > 255) {
@@ -1569,7 +1566,7 @@ static int stv0900_set_mis(struct stv0900_internal *intp,
                stv0900_write_reg(intp, ISIBITENA, 0xff);
        }
 
-       return error;
+       return STV0900_NO_ERROR;
 }
 
 
index 4ce1d260b3eba1b7e4acdce23345df8b2108e0f3..a0a7b1664c5339f36e97a296230dced6c4500dfb 100644 (file)
@@ -1733,9 +1733,10 @@ static void stv0900_set_search_standard(struct stv0900_internal *intp,
                break;
        case STV0900_SEARCH_DSS:
                dprintk("Search Standard = DSS\n");
-       case STV0900_SEARCH_DVBS2:
                break;
+       case STV0900_SEARCH_DVBS2:
                dprintk("Search Standard = DVBS2\n");
+               break;
        case STV0900_AUTO_SEARCH:
        default:
                dprintk("Search Standard = AUTO\n");
diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c
new file mode 100644 (file)
index 0000000..d9905fb
--- /dev/null
@@ -0,0 +1,840 @@
+/*
+ * Toshiba TC90522 Demodulator
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+
+/*
+ * NOTICE:
+ * This driver is incomplete and lacks init/config of the chips,
+ * as the necessary info is not disclosed.
+ * It assumes that users of this driver (such as a PCI bridge of
+ * DTV receiver cards) properly init and configure the chip
+ * via I2C *before* calling this driver's init() function.
+ *
+ * Currently, PT3 driver is the only one that uses this driver,
+ * and contains init/config code in its firmware.
+ * Thus some part of the code might be dependent on PT3 specific config.
+ */
+
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/dvb/frontend.h>
+#include "dvb_math.h"
+#include "tc90522.h"
+
+#define TC90522_I2C_THRU_REG 0xfe
+
+#define TC90522_MODULE_IDX(addr) (((u8)(addr) & 0x02U) >> 1)
+
+struct tc90522_state {
+       struct tc90522_config cfg;
+       struct dvb_frontend fe;
+       struct i2c_client *i2c_client;
+       struct i2c_adapter tuner_i2c;
+
+       bool lna;
+};
+
+struct reg_val {
+       u8 reg;
+       u8 val;
+};
+
+static int
+reg_write(struct tc90522_state *state, const struct reg_val *regs, int num)
+{
+       int i, ret;
+       struct i2c_msg msg;
+
+       ret = 0;
+       msg.addr = state->i2c_client->addr;
+       msg.flags = 0;
+       msg.len = 2;
+       for (i = 0; i < num; i++) {
+               msg.buf = (u8 *)&regs[i];
+               ret = i2c_transfer(state->i2c_client->adapter, &msg, 1);
+               if (ret == 0)
+                       ret = -EIO;
+               if (ret < 0)
+                       return ret;
+       }
+       return 0;
+}
+
+static int reg_read(struct tc90522_state *state, u8 reg, u8 *val, u8 len)
+{
+       struct i2c_msg msgs[2] = {
+               {
+                       .addr = state->i2c_client->addr,
+                       .flags = 0,
+                       .buf = &reg,
+                       .len = 1,
+               },
+               {
+                       .addr = state->i2c_client->addr,
+                       .flags = I2C_M_RD,
+                       .buf = val,
+                       .len = len,
+               },
+       };
+       int ret;
+
+       ret = i2c_transfer(state->i2c_client->adapter, msgs, ARRAY_SIZE(msgs));
+       if (ret == ARRAY_SIZE(msgs))
+               ret = 0;
+       else if (ret >= 0)
+               ret = -EIO;
+       return ret;
+}
+
+static struct tc90522_state *cfg_to_state(struct tc90522_config *c)
+{
+       return container_of(c, struct tc90522_state, cfg);
+}
+
+
+static int tc90522s_set_tsid(struct dvb_frontend *fe)
+{
+       struct reg_val set_tsid[] = {
+               { 0x8f, 00 },
+               { 0x90, 00 }
+       };
+
+       set_tsid[0].val = (fe->dtv_property_cache.stream_id & 0xff00) >> 8;
+       set_tsid[1].val = fe->dtv_property_cache.stream_id & 0xff;
+       return reg_write(fe->demodulator_priv, set_tsid, ARRAY_SIZE(set_tsid));
+}
+
+static int tc90522t_set_layers(struct dvb_frontend *fe)
+{
+       struct reg_val rv;
+       u8 laysel;
+
+       laysel = ~fe->dtv_property_cache.isdbt_layer_enabled & 0x07;
+       laysel = (laysel & 0x01) << 2 | (laysel & 0x02) | (laysel & 0x04) >> 2;
+       rv.reg = 0x71;
+       rv.val = laysel;
+       return reg_write(fe->demodulator_priv, &rv, 1);
+}
+
+/* frontend ops */
+
+static int tc90522s_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+       struct tc90522_state *state;
+       int ret;
+       u8 reg;
+
+       state = fe->demodulator_priv;
+       ret = reg_read(state, 0xc3, &reg, 1);
+       if (ret < 0)
+               return ret;
+
+       *status = 0;
+       if (reg & 0x80) /* input level under min ? */
+               return 0;
+       *status |= FE_HAS_SIGNAL;
+
+       if (reg & 0x60) /* carrier? */
+               return 0;
+       *status |= FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC;
+
+       if (reg & 0x10)
+               return 0;
+       if (reg_read(state, 0xc5, &reg, 1) < 0 || !(reg & 0x03))
+               return 0;
+       *status |= FE_HAS_LOCK;
+       return 0;
+}
+
+static int tc90522t_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+       struct tc90522_state *state;
+       int ret;
+       u8 reg;
+
+       state = fe->demodulator_priv;
+       ret = reg_read(state, 0x96, &reg, 1);
+       if (ret < 0)
+               return ret;
+
+       *status = 0;
+       if (reg & 0xe0) {
+               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI
+                               | FE_HAS_SYNC | FE_HAS_LOCK;
+               return 0;
+       }
+
+       ret = reg_read(state, 0x80, &reg, 1);
+       if (ret < 0)
+               return ret;
+
+       if (reg & 0xf0)
+               return 0;
+       *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+
+       if (reg & 0x0c)
+               return 0;
+       *status |= FE_HAS_SYNC | FE_HAS_VITERBI;
+
+       if (reg & 0x02)
+               return 0;
+       *status |= FE_HAS_LOCK;
+       return 0;
+}
+
+static const fe_code_rate_t fec_conv_sat[] = {
+       FEC_NONE, /* unused */
+       FEC_1_2, /* for BPSK */
+       FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, /* for QPSK */
+       FEC_2_3, /* for 8PSK. (trellis code) */
+};
+
+static int tc90522s_get_frontend(struct dvb_frontend *fe)
+{
+       struct tc90522_state *state;
+       struct dtv_frontend_properties *c;
+       struct dtv_fe_stats *stats;
+       int ret, i;
+       int layers;
+       u8 val[10];
+       u32 cndat;
+
+       state = fe->demodulator_priv;
+       c = &fe->dtv_property_cache;
+       c->delivery_system = SYS_ISDBS;
+
+       layers = 0;
+       ret = reg_read(state, 0xe8, val, 3);
+       if (ret == 0) {
+               int slots;
+               u8 v;
+
+               /* high/single layer */
+               v = (val[0] & 0x70) >> 4;
+               c->modulation = (v == 7) ? PSK_8 : QPSK;
+               c->fec_inner = fec_conv_sat[v];
+               c->layer[0].fec = c->fec_inner;
+               c->layer[0].modulation = c->modulation;
+               c->layer[0].segment_count = val[1] & 0x3f; /* slots */
+
+               /* low layer */
+               v = (val[0] & 0x07);
+               c->layer[1].fec = fec_conv_sat[v];
+               if (v == 0)  /* no low layer */
+                       c->layer[1].segment_count = 0;
+               else
+                       c->layer[1].segment_count = val[2] & 0x3f; /* slots */
+               /* actually, BPSK if v==1, but not defined in fe_modulation_t */
+               c->layer[1].modulation = QPSK;
+               layers = (v > 0) ? 2 : 1;
+
+               slots =  c->layer[0].segment_count +  c->layer[1].segment_count;
+               c->symbol_rate = 28860000 * slots / 48;
+       }
+
+       /* statistics */
+
+       stats = &c->strength;
+       stats->len = 0;
+       /* let the connected tuner set RSSI property cache */
+       if (fe->ops.tuner_ops.get_rf_strength) {
+               u16 dummy;
+
+               fe->ops.tuner_ops.get_rf_strength(fe, &dummy);
+       }
+
+       stats = &c->cnr;
+       stats->len = 1;
+       stats->stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       cndat = 0;
+       ret = reg_read(state, 0xbc, val, 2);
+       if (ret == 0)
+               cndat = val[0] << 8 | val[1];
+       if (cndat >= 3000) {
+               u32 p, p4;
+               s64 cn;
+
+               cndat -= 3000;  /* cndat: 4.12 fixed point float */
+               /*
+                * cnr[mdB] = -1634.6 * P^5 + 14341 * P^4 - 50259 * P^3
+                *                 + 88977 * P^2 - 89565 * P + 58857
+                *  (P = sqrt(cndat) / 64)
+                */
+               /* p := sqrt(cndat) << 8 = P << 14, 2.14 fixed  point float */
+               /* cn = cnr << 3 */
+               p = int_sqrt(cndat << 16);
+               p4 = cndat * cndat;
+               cn = div64_s64(-16346LL * p4 * p, 10) >> 35;
+               cn += (14341LL * p4) >> 21;
+               cn -= (50259LL * cndat * p) >> 23;
+               cn += (88977LL * cndat) >> 9;
+               cn -= (89565LL * p) >> 11;
+               cn += 58857  << 3;
+               stats->stat[0].svalue = cn >> 3;
+               stats->stat[0].scale = FE_SCALE_DECIBEL;
+       }
+
+       /* per-layer post viterbi BER (or PER? config dependent?) */
+       stats = &c->post_bit_error;
+       memset(stats, 0, sizeof(*stats));
+       stats->len = layers;
+       ret = reg_read(state, 0xeb, val, 10);
+       if (ret < 0)
+               for (i = 0; i < layers; i++)
+                       stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+       else {
+               for (i = 0; i < layers; i++) {
+                       stats->stat[i].scale = FE_SCALE_COUNTER;
+                       stats->stat[i].uvalue = val[i * 5] << 16
+                               | val[i * 5 + 1] << 8 | val[i * 5 + 2];
+               }
+       }
+       stats = &c->post_bit_count;
+       memset(stats, 0, sizeof(*stats));
+       stats->len = layers;
+       if (ret < 0)
+               for (i = 0; i < layers; i++)
+                       stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+       else {
+               for (i = 0; i < layers; i++) {
+                       stats->stat[i].scale = FE_SCALE_COUNTER;
+                       stats->stat[i].uvalue =
+                               val[i * 5 + 3] << 8 | val[i * 5 + 4];
+                       stats->stat[i].uvalue *= 204 * 8;
+               }
+       }
+
+       return 0;
+}
+
+
+static const fe_transmit_mode_t tm_conv[] = {
+       TRANSMISSION_MODE_2K,
+       TRANSMISSION_MODE_4K,
+       TRANSMISSION_MODE_8K,
+       0
+};
+
+static const fe_code_rate_t fec_conv_ter[] = {
+       FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, 0, 0, 0
+};
+
+static const fe_modulation_t mod_conv[] = {
+       DQPSK, QPSK, QAM_16, QAM_64, 0, 0, 0, 0
+};
+
+static int tc90522t_get_frontend(struct dvb_frontend *fe)
+{
+       struct tc90522_state *state;
+       struct dtv_frontend_properties *c;
+       struct dtv_fe_stats *stats;
+       int ret, i;
+       int layers;
+       u8 val[15], mode;
+       u32 cndat;
+
+       state = fe->demodulator_priv;
+       c = &fe->dtv_property_cache;
+       c->delivery_system = SYS_ISDBT;
+       c->bandwidth_hz = 6000000;
+       mode = 1;
+       ret = reg_read(state, 0xb0, val, 1);
+       if (ret == 0) {
+               mode = (val[0] & 0xc0) >> 2;
+               c->transmission_mode = tm_conv[mode];
+               c->guard_interval = (val[0] & 0x30) >> 4;
+       }
+
+       ret = reg_read(state, 0xb2, val, 6);
+       layers = 0;
+       if (ret == 0) {
+               u8 v;
+
+               c->isdbt_partial_reception = val[0] & 0x01;
+               c->isdbt_sb_mode = (val[0] & 0xc0) == 0x01;
+
+               /* layer A */
+               v = (val[2] & 0x78) >> 3;
+               if (v == 0x0f)
+                       c->layer[0].segment_count = 0;
+               else {
+                       layers++;
+                       c->layer[0].segment_count = v;
+                       c->layer[0].fec = fec_conv_ter[(val[1] & 0x1c) >> 2];
+                       c->layer[0].modulation = mod_conv[(val[1] & 0xe0) >> 5];
+                       v = (val[1] & 0x03) << 1 | (val[2] & 0x80) >> 7;
+                       c->layer[0].interleaving = v;
+               }
+
+               /* layer B */
+               v = (val[3] & 0x03) << 1 | (val[4] & 0xc0) >> 6;
+               if (v == 0x0f)
+                       c->layer[1].segment_count = 0;
+               else {
+                       layers++;
+                       c->layer[1].segment_count = v;
+                       c->layer[1].fec = fec_conv_ter[(val[3] & 0xe0) >> 5];
+                       c->layer[1].modulation = mod_conv[(val[2] & 0x07)];
+                       c->layer[1].interleaving = (val[3] & 0x1c) >> 2;
+               }
+
+               /* layer C */
+               v = (val[5] & 0x1e) >> 1;
+               if (v == 0x0f)
+                       c->layer[2].segment_count = 0;
+               else {
+                       layers++;
+                       c->layer[2].segment_count = v;
+                       c->layer[2].fec = fec_conv_ter[(val[4] & 0x07)];
+                       c->layer[2].modulation = mod_conv[(val[4] & 0x38) >> 3];
+                       c->layer[2].interleaving = (val[5] & 0xe0) >> 5;
+               }
+       }
+
+       /* statistics */
+
+       stats = &c->strength;
+       stats->len = 0;
+       /* let the connected tuner set RSSI property cache */
+       if (fe->ops.tuner_ops.get_rf_strength) {
+               u16 dummy;
+
+               fe->ops.tuner_ops.get_rf_strength(fe, &dummy);
+       }
+
+       stats = &c->cnr;
+       stats->len = 1;
+       stats->stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       cndat = 0;
+       ret = reg_read(state, 0x8b, val, 3);
+       if (ret == 0)
+               cndat = val[0] << 16 | val[1] << 8 | val[2];
+       if (cndat != 0) {
+               u32 p, tmp;
+               s64 cn;
+
+               /*
+                * cnr[mdB] = 0.024 P^4 - 1.6 P^3 + 39.8 P^2 + 549.1 P + 3096.5
+                * (P = 10log10(5505024/cndat))
+                */
+               /* cn = cnr << 3 (61.3 fixed point float */
+               /* p = 10log10(5505024/cndat) << 24  (8.24 fixed point float)*/
+               p = intlog10(5505024) - intlog10(cndat);
+               p *= 10;
+
+               cn = 24772;
+               cn += div64_s64(43827LL * p, 10) >> 24;
+               tmp = p >> 8;
+               cn += div64_s64(3184LL * tmp * tmp, 10) >> 32;
+               tmp = p >> 13;
+               cn -= div64_s64(128LL * tmp * tmp * tmp, 10) >> 33;
+               tmp = p >> 18;
+               cn += div64_s64(192LL * tmp * tmp * tmp * tmp, 1000) >> 24;
+
+               stats->stat[0].svalue = cn >> 3;
+               stats->stat[0].scale = FE_SCALE_DECIBEL;
+       }
+
+       /* per-layer post viterbi BER (or PER? config dependent?) */
+       stats = &c->post_bit_error;
+       memset(stats, 0, sizeof(*stats));
+       stats->len = layers;
+       ret = reg_read(state, 0x9d, val, 15);
+       if (ret < 0)
+               for (i = 0; i < layers; i++)
+                       stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+       else {
+               for (i = 0; i < layers; i++) {
+                       stats->stat[i].scale = FE_SCALE_COUNTER;
+                       stats->stat[i].uvalue = val[i * 3] << 16
+                               | val[i * 3 + 1] << 8 | val[i * 3 + 2];
+               }
+       }
+       stats = &c->post_bit_count;
+       memset(stats, 0, sizeof(*stats));
+       stats->len = layers;
+       if (ret < 0)
+               for (i = 0; i < layers; i++)
+                       stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+       else {
+               for (i = 0; i < layers; i++) {
+                       stats->stat[i].scale = FE_SCALE_COUNTER;
+                       stats->stat[i].uvalue =
+                               val[9 + i * 2] << 8 | val[9 + i * 2 + 1];
+                       stats->stat[i].uvalue *= 204 * 8;
+               }
+       }
+
+       return 0;
+}
+
+static const struct reg_val reset_sat = { 0x03, 0x01 };
+static const struct reg_val reset_ter = { 0x01, 0x40 };
+
+static int tc90522_set_frontend(struct dvb_frontend *fe)
+{
+       struct tc90522_state *state;
+       int ret;
+
+       state = fe->demodulator_priv;
+
+       if (fe->ops.tuner_ops.set_params)
+               ret = fe->ops.tuner_ops.set_params(fe);
+       else
+               ret = -ENODEV;
+       if (ret < 0)
+               goto failed;
+
+       if (fe->ops.delsys[0] == SYS_ISDBS) {
+               ret = tc90522s_set_tsid(fe);
+               if (ret < 0)
+                       goto failed;
+               ret = reg_write(state, &reset_sat, 1);
+       } else {
+               ret = tc90522t_set_layers(fe);
+               if (ret < 0)
+                       goto failed;
+               ret = reg_write(state, &reset_ter, 1);
+       }
+       if (ret < 0)
+               goto failed;
+
+       return 0;
+
+failed:
+       dev_warn(&state->tuner_i2c.dev, "(%s) failed. [adap%d-fe%d]\n",
+                       __func__, fe->dvb->num, fe->id);
+       return ret;
+}
+
+static int tc90522_get_tune_settings(struct dvb_frontend *fe,
+       struct dvb_frontend_tune_settings *settings)
+{
+       if (fe->ops.delsys[0] == SYS_ISDBS) {
+               settings->min_delay_ms = 250;
+               settings->step_size = 1000;
+               settings->max_drift = settings->step_size * 2;
+       } else {
+               settings->min_delay_ms = 400;
+               settings->step_size = 142857;
+               settings->max_drift = settings->step_size;
+       }
+       return 0;
+}
+
+static int tc90522_set_if_agc(struct dvb_frontend *fe, bool on)
+{
+       struct reg_val agc_sat[] = {
+               { 0x0a, 0x00 },
+               { 0x10, 0x30 },
+               { 0x11, 0x00 },
+               { 0x03, 0x01 },
+       };
+       struct reg_val agc_ter[] = {
+               { 0x25, 0x00 },
+               { 0x23, 0x4c },
+               { 0x01, 0x40 },
+       };
+       struct tc90522_state *state;
+       struct reg_val *rv;
+       int num;
+
+       state = fe->demodulator_priv;
+       if (fe->ops.delsys[0] == SYS_ISDBS) {
+               agc_sat[0].val = on ? 0xff : 0x00;
+               agc_sat[1].val |= 0x80;
+               agc_sat[1].val |= on ? 0x01 : 0x00;
+               agc_sat[2].val |= on ? 0x40 : 0x00;
+               rv = agc_sat;
+               num = ARRAY_SIZE(agc_sat);
+       } else {
+               agc_ter[0].val = on ? 0x40 : 0x00;
+               agc_ter[1].val |= on ? 0x00 : 0x01;
+               rv = agc_ter;
+               num = ARRAY_SIZE(agc_ter);
+       }
+       return reg_write(state, rv, num);
+}
+
+static const struct reg_val sleep_sat = { 0x17, 0x01 };
+static const struct reg_val sleep_ter = { 0x03, 0x90 };
+
+static int tc90522_sleep(struct dvb_frontend *fe)
+{
+       struct tc90522_state *state;
+       int ret;
+
+       state = fe->demodulator_priv;
+       if (fe->ops.delsys[0] == SYS_ISDBS)
+               ret = reg_write(state, &sleep_sat, 1);
+       else {
+               ret = reg_write(state, &sleep_ter, 1);
+               if (ret == 0 && fe->ops.set_lna &&
+                   fe->dtv_property_cache.lna == LNA_AUTO) {
+                       fe->dtv_property_cache.lna = 0;
+                       ret = fe->ops.set_lna(fe);
+                       fe->dtv_property_cache.lna = LNA_AUTO;
+               }
+       }
+       if (ret < 0)
+               dev_warn(&state->tuner_i2c.dev,
+                       "(%s) failed. [adap%d-fe%d]\n",
+                       __func__, fe->dvb->num, fe->id);
+       return ret;
+}
+
+static const struct reg_val wakeup_sat = { 0x17, 0x00 };
+static const struct reg_val wakeup_ter = { 0x03, 0x80 };
+
+static int tc90522_init(struct dvb_frontend *fe)
+{
+       struct tc90522_state *state;
+       int ret;
+
+       /*
+        * Because the init sequence is not public,
+        * the parent device/driver should have init'ed the device before.
+        * just wake up the device here.
+        */
+
+       state = fe->demodulator_priv;
+       if (fe->ops.delsys[0] == SYS_ISDBS)
+               ret = reg_write(state, &wakeup_sat, 1);
+       else {
+               ret = reg_write(state, &wakeup_ter, 1);
+               if (ret == 0 && fe->ops.set_lna &&
+                   fe->dtv_property_cache.lna == LNA_AUTO) {
+                       fe->dtv_property_cache.lna = 1;
+                       ret = fe->ops.set_lna(fe);
+                       fe->dtv_property_cache.lna = LNA_AUTO;
+               }
+       }
+       if (ret < 0) {
+               dev_warn(&state->tuner_i2c.dev,
+                       "(%s) failed. [adap%d-fe%d]\n",
+                       __func__, fe->dvb->num, fe->id);
+               return ret;
+       }
+
+       /* prefer 'all-layers' to 'none' as a default */
+       if (fe->dtv_property_cache.isdbt_layer_enabled == 0)
+               fe->dtv_property_cache.isdbt_layer_enabled = 7;
+       return tc90522_set_if_agc(fe, true);
+}
+
+
+/*
+ * tuner I2C adapter functions
+ */
+
+static int
+tc90522_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       struct tc90522_state *state;
+       struct i2c_msg *new_msgs;
+       int i, j;
+       int ret, rd_num;
+       u8 wbuf[256];
+       u8 *p, *bufend;
+
+       if (num <= 0)
+               return -EINVAL;
+
+       rd_num = 0;
+       for (i = 0; i < num; i++)
+               if (msgs[i].flags & I2C_M_RD)
+                       rd_num++;
+       new_msgs = kmalloc(sizeof(*new_msgs) * (num + rd_num), GFP_KERNEL);
+       if (!new_msgs)
+               return -ENOMEM;
+
+       state = i2c_get_adapdata(adap);
+       p = wbuf;
+       bufend = wbuf + sizeof(wbuf);
+       for (i = 0, j = 0; i < num; i++, j++) {
+               new_msgs[j].addr = state->i2c_client->addr;
+               new_msgs[j].flags = msgs[i].flags;
+
+               if (msgs[i].flags & I2C_M_RD) {
+                       new_msgs[j].flags &= ~I2C_M_RD;
+                       if (p + 2 > bufend)
+                               break;
+                       p[0] = TC90522_I2C_THRU_REG;
+                       p[1] = msgs[i].addr << 1 | 0x01;
+                       new_msgs[j].buf = p;
+                       new_msgs[j].len = 2;
+                       p += 2;
+                       j++;
+                       new_msgs[j].addr = state->i2c_client->addr;
+                       new_msgs[j].flags = msgs[i].flags;
+                       new_msgs[j].buf = msgs[i].buf;
+                       new_msgs[j].len = msgs[i].len;
+                       continue;
+               }
+
+               if (p + msgs[i].len + 2 > bufend)
+                       break;
+               p[0] = TC90522_I2C_THRU_REG;
+               p[1] = msgs[i].addr << 1;
+               memcpy(p + 2, msgs[i].buf, msgs[i].len);
+               new_msgs[j].buf = p;
+               new_msgs[j].len = msgs[i].len + 2;
+               p += new_msgs[j].len;
+       }
+
+       if (i < num)
+               ret = -ENOMEM;
+       else
+               ret = i2c_transfer(state->i2c_client->adapter, new_msgs, j);
+       if (ret >= 0 && ret < j)
+               ret = -EIO;
+       kfree(new_msgs);
+       return (ret == j) ? num : ret;
+}
+
+static u32 tc90522_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm tc90522_tuner_i2c_algo = {
+       .master_xfer   = &tc90522_master_xfer,
+       .functionality = &tc90522_functionality,
+};
+
+
+/*
+ * I2C driver functions
+ */
+
+static const struct dvb_frontend_ops tc90522_ops_sat = {
+       .delsys = { SYS_ISDBS },
+       .info = {
+               .name = "Toshiba TC90522 ISDB-S module",
+               .frequency_min =  950000,
+               .frequency_max = 2150000,
+               .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
+                       FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+                       FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
+       },
+
+       .init = tc90522_init,
+       .sleep = tc90522_sleep,
+       .set_frontend = tc90522_set_frontend,
+       .get_tune_settings = tc90522_get_tune_settings,
+
+       .get_frontend = tc90522s_get_frontend,
+       .read_status = tc90522s_read_status,
+};
+
+static const struct dvb_frontend_ops tc90522_ops_ter = {
+       .delsys = { SYS_ISDBT },
+       .info = {
+               .name = "Toshiba TC90522 ISDB-T module",
+               .frequency_min = 470000000,
+               .frequency_max = 770000000,
+               .frequency_stepsize = 142857,
+               .caps = FE_CAN_INVERSION_AUTO |
+                       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_FEC_AUTO |
+                       FE_CAN_QPSK     | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+                       FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+                       FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER |
+                       FE_CAN_HIERARCHY_AUTO,
+       },
+
+       .init = tc90522_init,
+       .sleep = tc90522_sleep,
+       .set_frontend = tc90522_set_frontend,
+       .get_tune_settings = tc90522_get_tune_settings,
+
+       .get_frontend = tc90522t_get_frontend,
+       .read_status = tc90522t_read_status,
+};
+
+
+static int tc90522_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct tc90522_state *state;
+       struct tc90522_config *cfg;
+       const struct dvb_frontend_ops *ops;
+       struct i2c_adapter *adap;
+       int ret;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+       state->i2c_client = client;
+
+       cfg = client->dev.platform_data;
+       memcpy(&state->cfg, cfg, sizeof(state->cfg));
+       cfg->fe = state->cfg.fe = &state->fe;
+       ops =  id->driver_data == 0 ? &tc90522_ops_sat : &tc90522_ops_ter;
+       memcpy(&state->fe.ops, ops, sizeof(*ops));
+       state->fe.demodulator_priv = state;
+
+       adap = &state->tuner_i2c;
+       adap->owner = THIS_MODULE;
+       adap->algo = &tc90522_tuner_i2c_algo;
+       adap->dev.parent = &client->dev;
+       strlcpy(adap->name, "tc90522_sub", sizeof(adap->name));
+       i2c_set_adapdata(adap, state);
+       ret = i2c_add_adapter(adap);
+       if (ret < 0)
+               goto err;
+       cfg->tuner_i2c = state->cfg.tuner_i2c = adap;
+
+       i2c_set_clientdata(client, &state->cfg);
+       dev_info(&client->dev, "Toshiba TC90522 attached.\n");
+       return 0;
+
+err:
+       kfree(state);
+       return ret;
+}
+
+static int tc90522_remove(struct i2c_client *client)
+{
+       struct tc90522_state *state;
+
+       state = cfg_to_state(i2c_get_clientdata(client));
+       i2c_del_adapter(&state->tuner_i2c);
+       kfree(state);
+       return 0;
+}
+
+
+static const struct i2c_device_id tc90522_id[] = {
+       { TC90522_I2C_DEV_SAT, 0 },
+       { TC90522_I2C_DEV_TER, 1 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, tc90522_id);
+
+static struct i2c_driver tc90522_driver = {
+       .driver = {
+               .name   = "tc90522",
+       },
+       .probe          = tc90522_probe,
+       .remove         = tc90522_remove,
+       .id_table       = tc90522_id,
+};
+
+module_i2c_driver(tc90522_driver);
+
+MODULE_DESCRIPTION("Toshiba TC90522 frontend");
+MODULE_AUTHOR("Akihiro TSUKADA");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/tc90522.h b/drivers/media/dvb-frontends/tc90522.h
new file mode 100644 (file)
index 0000000..b1cbddf
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Toshiba TC90522 Demodulator
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+
+/*
+ * The demod has 4 input (2xISDB-T and 2xISDB-S),
+ * and provides independent sub modules for each input.
+ * As the sub modules work in parallel and have the separate i2c addr's,
+ * this driver treats each sub module as one demod device.
+ */
+
+#ifndef TC90522_H
+#define TC90522_H
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+/* I2C device types */
+#define TC90522_I2C_DEV_SAT "tc90522sat"
+#define TC90522_I2C_DEV_TER "tc90522ter"
+
+struct tc90522_config {
+       /* [OUT] frontend returned by driver */
+       struct dvb_frontend *fe;
+
+       /* [OUT] tuner I2C adapter returned by driver */
+       struct i2c_adapter *tuner_i2c;
+};
+
+#endif /* TC90522_H */
index 9619be5d48271827b28052c080f01fc5631224b7..4a19b85995f17f47018732e8c82a67847c15e0d6 100644 (file)
@@ -1037,7 +1037,7 @@ static int tda10071_init(struct dvb_frontend *fe)
                        ret = -EFAULT;
                        goto error;
                } else {
-                       priv->warm = 1;
+                       priv->warm = true;
                }
 
                cmd.args[0] = CMD_GET_FW_VERSION;
index 91b6b2e9b79228b6c497f7c7aba65dd36415f9da..ee09ec26c553ef7aa4e8387f4ba7bcab3948db37 100644 (file)
@@ -111,7 +111,7 @@ static int zl10039_write(struct zl10039_state *state,
 
        if (1 + count > sizeof(buf)) {
                printk(KERN_WARNING
-                      "%s: i2c wr reg=%04x: len=%zd is too big!\n",
+                      "%s: i2c wr reg=%04x: len=%zu is too big!\n",
                       KBUILD_MODNAME, reg, count);
                return -EINVAL;
        }
index d1a1a1324ef87018e030ba0aa149de3c79856085..251a556112a99586288f680e018103c0b9eef90c 100644 (file)
@@ -1157,6 +1157,10 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
                if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
                        dev_err(fdtv->device,
                                "invalid pmt_cmd_id %d\n", pmt_cmd_id);
+               if (program_info_length > sizeof(c->operand) - 4 - write_pos) {
+                       ret = -EINVAL;
+                       goto out;
+               }
 
                memcpy(&c->operand[write_pos], &msg[read_pos],
                       program_info_length);
@@ -1180,6 +1184,12 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
                                dev_err(fdtv->device, "invalid pmt_cmd_id %d "
                                        "at stream level\n", pmt_cmd_id);
 
+                       if (es_info_length > sizeof(c->operand) - 4 -
+                                            write_pos) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
                        memcpy(&c->operand[write_pos], &msg[read_pos],
                               es_info_length);
                        read_pos += es_info_length;
index 4466067643465563dfbe0db7057b2f50c4e9d2e3..2f04ce4b9118a43c39d2d8c8c5f6a7e1eb4ce6c7 100644 (file)
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  */
 
-#ifndef ADV7343_REG_H
+#ifndef ADV7343_REGS_H
 #define ADV7343_REGS_H
 
 struct adv7343_std_info {
index de88b980a8370bea73e380da85eb33fe50610761..47795ff716882ba5fddb929529c94d0acf5a84bc 100644 (file)
@@ -1593,7 +1593,7 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
                        bt->height += hdmi_read16(sd, 0x0b, 0xfff);
                        bt->il_vfrontporch = hdmi_read16(sd, 0x2c, 0x1fff) / 2;
                        bt->il_vsync = hdmi_read16(sd, 0x30, 0x1fff) / 2;
-                       bt->vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2;
+                       bt->il_vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2;
                }
                adv7604_fill_optional_dv_timings_fields(sd, timings);
        } else {
index 0d554919cdd52726f52deda278414aa62bb6e47c..48b628bc6714ecae53144f4bf90fa64d0454c90e 100644 (file)
@@ -1435,6 +1435,8 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
 
        v4l2_dbg(1, debug, sd, "%s:\n", __func__);
 
+       memset(timings, 0, sizeof(struct v4l2_dv_timings));
+
        /* SDP block */
        if (state->mode == ADV7842_MODE_SDP)
                return -ENODATA;
@@ -1483,7 +1485,7 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
                                        hdmi_read(sd, 0x2d)) / 2;
                        bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 +
                                        hdmi_read(sd, 0x31)) / 2;
-                       bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 +
+                       bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 +
                                        hdmi_read(sd, 0x35)) / 2;
                }
                adv7842_fill_optional_dv_timings_fields(sd, timings);
index c23de593c17d5572ff2123403ad6e4e006d21e55..d9ece4b2d047067dc19921e1e79a52fd80ad130e 100644 (file)
@@ -100,14 +100,14 @@ static int lm3560_enable_ctrl(struct lm3560_flash *flash,
        int rval;
 
        if (led_no == LM3560_LED0) {
-               if (on == true)
+               if (on)
                        rval = regmap_update_bits(flash->regmap,
                                                  REG_ENABLE, 0x08, 0x08);
                else
                        rval = regmap_update_bits(flash->regmap,
                                                  REG_ENABLE, 0x08, 0x00);
        } else {
-               if (on == true)
+               if (on)
                        rval = regmap_update_bits(flash->regmap,
                                                  REG_ENABLE, 0x10, 0x10);
                else
index cdd7c1b7259b008e873cc711294a3f0cecba422a..dd3db2458a4fc8fff9512b695c6b2b40651d5921 100644 (file)
@@ -19,6 +19,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-mediabus.h>
+#include <media/v4l2-image-sizes.h>
 #include <media/ov7670.h>
 
 MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
@@ -29,19 +30,6 @@ static bool debug;
 module_param(debug, bool, 0644);
 MODULE_PARM_DESC(debug, "Debug level (0-1)");
 
-/*
- * Basic window sizes.  These probably belong somewhere more globally
- * useful.
- */
-#define VGA_WIDTH      640
-#define VGA_HEIGHT     480
-#define QVGA_WIDTH     320
-#define QVGA_HEIGHT    240
-#define CIF_WIDTH      352
-#define CIF_HEIGHT     288
-#define QCIF_WIDTH     176
-#define        QCIF_HEIGHT     144
-
 /*
  * The 7670 sits on i2c with ID 0x42
  */
index 564f05f2c9efbe54d569c5030c7152ddc2f0390f..0e461a6fd0654cb3d97ef52d72e9b7c586f044b2 100644 (file)
@@ -816,7 +816,7 @@ static void s5k5baf_hw_find_min_fiv(struct s5k5baf *state)
                                 "error setting frame interval: %d\n", err);
                        state->error = -EINVAL;
                }
-       };
+       }
        v4l2_err(&state->sd, "cannot find correct frame interval\n");
        state->error = -ERANGE;
 }
index 04e9e55018a5044ff194f4d72f3bc01314ff75c4..4024ea6f1371ad48c1bd744a8a662bd545c53eb1 100644 (file)
@@ -660,7 +660,7 @@ static const struct v4l2_subdev_ops saa6752hs_ops = {
 static int saa6752hs_probe(struct i2c_client *client,
                const struct i2c_device_id *id)
 {
-       struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL);
+       struct saa6752hs_state *h;
        struct v4l2_subdev *sd;
        struct v4l2_ctrl_handler *hdl;
        u8 addr = 0x13;
@@ -668,6 +668,8 @@ static int saa6752hs_probe(struct i2c_client *client,
 
        v4l_info(client, "chip found @ 0x%x (%s)\n",
                        client->addr << 1, client->adapter->name);
+
+       h = devm_kzalloc(&client->dev, sizeof(*h), GFP_KERNEL);
        if (h == NULL)
                return -ENOMEM;
        sd = &h->sd;
@@ -752,7 +754,6 @@ static int saa6752hs_probe(struct i2c_client *client,
                int err = hdl->error;
 
                v4l2_ctrl_handler_free(hdl);
-               kfree(h);
                return err;
        }
        v4l2_ctrl_cluster(3, &h->video_bitrate_mode);
@@ -767,7 +768,6 @@ static int saa6752hs_remove(struct i2c_client *client)
 
        v4l2_device_unregister_subdev(sd);
        v4l2_ctrl_handler_free(&to_state(sd)->hdl);
-       kfree(to_state(sd));
        return 0;
 }
 
index 62acb10630f9b2a1b12c114eddad10995308389e..932ed9be9ff3d756fb35948e8183e09faaa60c34 100644 (file)
@@ -31,8 +31,9 @@
 #include <linux/device.h>
 #include <linux/gpio.h>
 #include <linux/module.h>
-#include <linux/slab.h>
 #include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/smiapp.h>
 #include <linux/v4l2-mediabus.h>
 #include <media/v4l2-device.h>
 
@@ -297,8 +298,9 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor)
        if (rval < 0)
                return rval;
 
-       *sensor->pixel_rate_parray->p_cur.p_s64 = pll->vt_pix_clk_freq_hz;
-       *sensor->pixel_rate_csi->p_cur.p_s64 = pll->pixel_rate_csi;
+       __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_parray,
+                                pll->vt_pix_clk_freq_hz);
+       __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_csi, pll->pixel_rate_csi);
 
        return 0;
 }
@@ -319,13 +321,7 @@ static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor)
                + sensor->vblank->val
                - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN];
 
-       ctrl->maximum = max;
-       if (ctrl->default_value > max)
-               ctrl->default_value = max;
-       if (ctrl->val > max)
-               ctrl->val = max;
-       if (ctrl->cur.val > max)
-               ctrl->cur.val = max;
+       __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max);
 }
 
 /*
@@ -404,6 +400,14 @@ static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor)
                pixel_order_str[pixel_order]);
 }
 
+static const char * const smiapp_test_patterns[] = {
+       "Disabled",
+       "Solid Colour",
+       "Eight Vertical Colour Bars",
+       "Colour Bars With Fade to Grey",
+       "Pseudorandom Sequence (PN9)",
+};
+
 static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl)
 {
        struct smiapp_sensor *sensor =
@@ -477,6 +481,39 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl)
 
                return smiapp_pll_update(sensor);
 
+       case V4L2_CID_TEST_PATTERN: {
+               unsigned int i;
+
+               for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++)
+                       v4l2_ctrl_activate(
+                               sensor->test_data[i],
+                               ctrl->val ==
+                               V4L2_SMIAPP_TEST_PATTERN_MODE_SOLID_COLOUR);
+
+               return smiapp_write(
+                       sensor, SMIAPP_REG_U16_TEST_PATTERN_MODE, ctrl->val);
+       }
+
+       case V4L2_CID_TEST_PATTERN_RED:
+               return smiapp_write(
+                       sensor, SMIAPP_REG_U16_TEST_DATA_RED, ctrl->val);
+
+       case V4L2_CID_TEST_PATTERN_GREENR:
+               return smiapp_write(
+                       sensor, SMIAPP_REG_U16_TEST_DATA_GREENR, ctrl->val);
+
+       case V4L2_CID_TEST_PATTERN_BLUE:
+               return smiapp_write(
+                       sensor, SMIAPP_REG_U16_TEST_DATA_BLUE, ctrl->val);
+
+       case V4L2_CID_TEST_PATTERN_GREENB:
+               return smiapp_write(
+                       sensor, SMIAPP_REG_U16_TEST_DATA_GREENB, ctrl->val);
+
+       case V4L2_CID_PIXEL_RATE:
+               /* For v4l2_ctrl_s_ctrl_int64() used internally. */
+               return 0;
+
        default:
                return -EINVAL;
        }
@@ -489,10 +526,10 @@ static const struct v4l2_ctrl_ops smiapp_ctrl_ops = {
 static int smiapp_init_controls(struct smiapp_sensor *sensor)
 {
        struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
-       unsigned int max;
+       unsigned int max, i;
        int rval;
 
-       rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7);
+       rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 12);
        if (rval)
                return rval;
        sensor->pixel_array->ctrl_handler.lock = &sensor->mutex;
@@ -535,6 +572,20 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor)
                &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
                V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
 
+       v4l2_ctrl_new_std_menu_items(&sensor->pixel_array->ctrl_handler,
+                                    &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN,
+                                    ARRAY_SIZE(smiapp_test_patterns) - 1,
+                                    0, 0, smiapp_test_patterns);
+
+       for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) {
+               int max_value = (1 << sensor->csi_format->width) - 1;
+               sensor->test_data[i] =
+                       v4l2_ctrl_new_std(
+                               &sensor->pixel_array->ctrl_handler,
+                               &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i,
+                               0, max_value, 1, max_value);
+       }
+
        if (sensor->pixel_array->ctrl_handler.error) {
                dev_err(&client->dev,
                        "pixel array controls initialization failed (%d)\n",
@@ -782,36 +833,25 @@ static void smiapp_update_blanking(struct smiapp_sensor *sensor)
 {
        struct v4l2_ctrl *vblank = sensor->vblank;
        struct v4l2_ctrl *hblank = sensor->hblank;
+       int min, max;
 
-       vblank->minimum =
-               max_t(int,
-                     sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES],
-                     sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] -
-                     sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height);
-       vblank->maximum =
-               sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] -
+       min = max_t(int,
+                   sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES],
+                   sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] -
+                   sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height);
+       max = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] -
                sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height;
 
-       vblank->val = clamp_t(int, vblank->val,
-                             vblank->minimum, vblank->maximum);
-       vblank->default_value = vblank->minimum;
-       vblank->val = vblank->val;
-       vblank->cur.val = vblank->val;
-
-       hblank->minimum =
-               max_t(int,
-                     sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] -
-                     sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width,
-                     sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]);
-       hblank->maximum =
-               sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] -
+       __v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min);
+
+       min = max_t(int,
+                   sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] -
+                   sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width,
+                   sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]);
+       max = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] -
                sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width;
 
-       hblank->val = clamp_t(int, hblank->val,
-                             hblank->minimum, hblank->maximum);
-       hblank->default_value = hblank->minimum;
-       hblank->val = hblank->val;
-       hblank->cur.val = hblank->val;
+       __v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min);
 
        __smiapp_update_exposure_limits(sensor);
 }
@@ -1272,7 +1312,7 @@ static void smiapp_power_off(struct smiapp_sensor *sensor)
                clk_disable_unprepare(sensor->ext_clk);
        usleep_range(5000, 5000);
        regulator_disable(sensor->vana);
-       sensor->streaming = 0;
+       sensor->streaming = false;
 }
 
 static int smiapp_set_power(struct v4l2_subdev *subdev, int on)
@@ -1462,13 +1502,13 @@ static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable)
                return 0;
 
        if (enable) {
-               sensor->streaming = 1;
+               sensor->streaming = true;
                rval = smiapp_start_streaming(sensor);
                if (rval < 0)
-                       sensor->streaming = 0;
+                       sensor->streaming = false;
        } else {
                rval = smiapp_stop_streaming(sensor);
-               sensor->streaming = 0;
+               sensor->streaming = false;
        }
 
        return rval;
@@ -1664,17 +1704,34 @@ static int smiapp_set_format(struct v4l2_subdev *subdev,
        if (fmt->pad == ssd->source_pad) {
                u32 code = fmt->format.code;
                int rval = __smiapp_get_format(subdev, fh, fmt);
+               bool range_changed = false;
+               unsigned int i;
 
                if (!rval && subdev == &sensor->src->sd) {
                        const struct smiapp_csi_data_format *csi_format =
                                smiapp_validate_csi_data_format(sensor, code);
-                       if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+
+                       if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+                               if (csi_format->width !=
+                                   sensor->csi_format->width)
+                                       range_changed = true;
+
                                sensor->csi_format = csi_format;
+                       }
+
                        fmt->format.code = csi_format->code;
                }
 
                mutex_unlock(&sensor->mutex);
-               return rval;
+               if (rval || !range_changed)
+                       return rval;
+
+               for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++)
+                       v4l2_ctrl_modify_range(
+                               sensor->test_data[i],
+                               0, (1 << sensor->csi_format->width) - 1, 1, 0);
+
+               return 0;
        }
 
        /* Sink pad. Width and height are changeable here. */
index 7cc5aae662fda5e0ef1ed6ba0b3534fafb4378c4..874b49ffd88f020e1e78ec2d39f7b8041559265e 100644 (file)
@@ -54,6 +54,8 @@
        (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000       \
                 + (clk) / 1000 - 1) / ((clk) / 1000))
 
+#define SMIAPP_COLOUR_COMPONENTS       4
+
 #include "smiapp-limits.h"
 
 struct smiapp_quirk;
@@ -241,6 +243,8 @@ struct smiapp_sensor {
        /* src controls */
        struct v4l2_ctrl *link_freq;
        struct v4l2_ctrl *pixel_rate_csi;
+       /* test pattern colour components */
+       struct v4l2_ctrl *test_data[SMIAPP_COLOUR_COMPONENTS];
 };
 
 #define to_smiapp_subdev(_sd)                          \
index 46f431a13782800c04198547ebf906394086e20c..996d7b4007a52a9c3360d8238ada92c08b3a750c 100644 (file)
@@ -29,6 +29,7 @@
 #include <media/soc_camera.h>
 #include <media/v4l2-clk.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-image-sizes.h>
 
 /* you can check PLL/clock info */
 /* #define EXT_CLOCK 24000000 */
@@ -42,9 +43,6 @@
 #define MAX_WIDTH   2048
 #define MAX_HEIGHT  1536
 
-#define VGA_WIDTH   640
-#define VGA_HEIGHT  480
-
 /*
  * macro of read/write
  */
index 7f2b3c8926afbfade0fca5c58bea1e9356dc9d3b..970a04e1e56e694602f4a87261079646c517875f 100644 (file)
@@ -29,6 +29,7 @@
 #include <media/v4l2-clk.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-subdev.h>
+#include <media/v4l2-image-sizes.h>
 
 /*
  * register offset
 #define SCAL0_ACTRL     0x08 /* Auto scaling factor control */
 #define SCAL1_2_ACTRL   0x04 /* Auto scaling factor control */
 
-#define VGA_WIDTH              640
-#define VGA_HEIGHT             480
-#define QVGA_WIDTH             320
-#define QVGA_HEIGHT            240
 #define OV772X_MAX_WIDTH       VGA_WIDTH
 #define OV772X_MAX_HEIGHT      VGA_HEIGHT
 
index ea76863dfdb44e22bb265d3ee6928a0611e60f63..ee9eb635d5403acbf88fb116b939e3f5cbc77152 100644 (file)
@@ -564,13 +564,13 @@ static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height)
        u32 y_start;
        u32 x_end;
        u32 y_end;
-       bool scaling = 0;
+       bool scaling = false;
        u32 scale_input_x;
        u32 scale_input_y;
        int ret;
 
        if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT))
-               scaling = 1;
+               scaling = true;
 
        /*
         * Try to use as much of the sensor area as possible when supporting
index 72af644fa05127f89548b9782cee62367823d445..cf93021a650092d8dee0710c592ba6b00611769a 100644 (file)
@@ -293,7 +293,7 @@ static int tda7432_s_ctrl(struct v4l2_ctrl *ctrl)
                if (t->mute->val) {
                        lf |= TDA7432_MUTE;
                        lr |= TDA7432_MUTE;
-                       lf |= TDA7432_MUTE;
+                       rf |= TDA7432_MUTE;
                        rr |= TDA7432_MUTE;
                }
                /* Mute & update balance*/
index 11f2387e1dabe8235ec1857352068ca1a10848bc..51bac762638b7aca70ba204d86588a38eae997c4 100644 (file)
@@ -775,25 +775,20 @@ static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
 static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable)
 {
        struct tvp7002 *device = to_tvp7002(sd);
-       int error = 0;
+       int error;
 
        if (device->streaming == enable)
                return 0;
 
-       if (enable) {
-               /* Set output state on (low impedance means stream on) */
-               error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
-               device->streaming = enable;
-       } else {
-               /* Set output state off (high impedance means stream off) */
-               error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03);
-               if (error)
-                       v4l2_dbg(1, debug, sd, "Unable to stop streaming\n");
-
-               device->streaming = enable;
+       /* low impedance: on, high impedance: off */
+       error = tvp7002_write(sd, TVP7002_MISC_CTL_2, enable ? 0x00 : 0x03);
+       if (error) {
+               v4l2_dbg(1, debug, sd, "Fail to set streaming\n");
+               return error;
        }
 
-       return error;
+       device->streaming = enable;
+       return 0;
 }
 
 /*
index 23f4f65fccd7ac19b43bc33fb377bdf0cc9bd8af..373f2df524924aa8b754b428a57cc5915c4b7bb4 100644 (file)
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-mediabus.h>
+#include <media/v4l2-image-sizes.h>
 
 #include "vs6624_regs.h"
 
-#define VGA_WIDTH       640
-#define VGA_HEIGHT      480
-#define QVGA_WIDTH      320
-#define QVGA_HEIGHT     240
-#define QQVGA_WIDTH     160
-#define QQVGA_HEIGHT    120
-#define CIF_WIDTH       352
-#define CIF_HEIGHT      288
-#define QCIF_WIDTH      176
-#define QCIF_HEIGHT     144
-#define QQCIF_WIDTH     88
-#define QQCIF_HEIGHT    72
-
 #define MAX_FRAME_RATE  30
 
 struct vs6624 {
index 73a432934bd8d1ee8bad5c2e07689cca9ed1a1d3..7b39440192d61a7f91b0896c406beea751d8d515 100644 (file)
@@ -103,10 +103,8 @@ static long media_device_enum_entities(struct media_device *mdev,
                return -EINVAL;
 
        u_ent.id = ent->id;
-       if (ent->name) {
-               strncpy(u_ent.name, ent->name, sizeof(u_ent.name));
-               u_ent.name[sizeof(u_ent.name) - 1] = '\0';
-       }
+       if (ent->name)
+               strlcpy(u_ent.name, ent->name, sizeof(u_ent.name));
        u_ent.type = ent->type;
        u_ent.revision = ent->revision;
        u_ent.flags = ent->flags;
index 7acd19c881debe4de5e98e9658108489d4c98c10..ebf9626e5ae5a709f2cb76b3a1c173d7ddb106ba 100644 (file)
@@ -192,7 +192,6 @@ static int media_open(struct inode *inode, struct file *filp)
 static int media_release(struct inode *inode, struct file *filp)
 {
        struct media_devnode *mdev = media_devnode_data(filp);
-       int ret = 0;
 
        if (mdev->fops->release)
                mdev->fops->release(filp);
@@ -201,7 +200,7 @@ static int media_release(struct inode *inode, struct file *filp)
           return value is ignored. */
        put_device(&mdev->dev);
        filp->private_data = NULL;
-       return ret;
+       return 0;
 }
 
 static const struct file_operations media_devnode_fops = {
index 9bc105b3db1bcec5dbb775f591188d7ec34e1adf..e6b497528ceaca5dcc671ad7a92d080936a2a316 100644 (file)
@@ -629,11 +629,15 @@ static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
 {
        int y;
        int dw = 2 * dev->width;
-       char tmp[dw + 32]; /* using a temp buffer is faster than direct  */
+       char *tmp; /* using a temp buffer is faster than direct  */
        int cnt = 0;
        int len = 0;
        unsigned char r8 = 0x5;  /* value for reg8  */
 
+       tmp = kmalloc(dw + 32, GFP_KERNEL);
+       if (!tmp)
+               return 0;
+
        if (rgb555)
                r8 |= 0x20; /* else use untranslated rgb = 565 */
        mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
@@ -664,6 +668,7 @@ static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
                        len += dt;
                }
        }
+       kfree(tmp);
        return len;
 }
 
index 5c16c9c2203ef4e1a9fdd931ec3ecff5c2dc498a..f8cec8e8cf8243902db375d52ae5745efafbbccb 100644 (file)
@@ -20,6 +20,7 @@ source "drivers/media/pci/ivtv/Kconfig"
 source "drivers/media/pci/zoran/Kconfig"
 source "drivers/media/pci/saa7146/Kconfig"
 source "drivers/media/pci/solo6x10/Kconfig"
+source "drivers/media/pci/tw68/Kconfig"
 endif
 
 if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT
@@ -41,6 +42,7 @@ source "drivers/media/pci/b2c2/Kconfig"
 source "drivers/media/pci/pluto2/Kconfig"
 source "drivers/media/pci/dm1105/Kconfig"
 source "drivers/media/pci/pt1/Kconfig"
+source "drivers/media/pci/pt3/Kconfig"
 source "drivers/media/pci/mantis/Kconfig"
 source "drivers/media/pci/ngene/Kconfig"
 source "drivers/media/pci/ddbridge/Kconfig"
index e5b53fb569efaf038d4f86a010ec83e90c06d5e6..a12926e4b51f3afc44cba9a9199886d5a52e1fae 100644 (file)
@@ -7,10 +7,10 @@ obj-y        +=       ttpci/          \
                pluto2/         \
                dm1105/         \
                pt1/            \
+               pt3/            \
                mantis/         \
                ngene/          \
                ddbridge/       \
-               b2c2/           \
                saa7146/
 
 obj-$(CONFIG_VIDEO_IVTV) += ivtv/
@@ -22,6 +22,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
 obj-$(CONFIG_VIDEO_BT848) += bt8xx/
 obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
 obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
+obj-$(CONFIG_VIDEO_TW68) += tw68/
 obj-$(CONFIG_VIDEO_MEYE) += meye/
 obj-$(CONFIG_STA2X11_VIP) += sta2x11/
 obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/
index 970e542d3a51fabcc17f2be4e371b9ccdd55cc36..4a8176c09fc9429eea49c2e49e811618354c78b0 100644 (file)
@@ -1531,7 +1531,6 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
 {
        struct bttv_buffer *old;
        unsigned long flags;
-       int retval = 0;
 
        dprintk("switch_overlay: enter [new=%p]\n", new);
        if (new)
@@ -1551,7 +1550,7 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
        if (NULL == new)
                free_btres_lock(btv,fh,RESOURCE_OVERLAY);
        dprintk("switch_overlay: done\n");
-       return retval;
+       return 0;
 }
 
 /* ----------------------------------------------------------------------- */
@@ -3856,7 +3855,7 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
 
                                btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
                                                BT848_INT_MASK);
-                       };
+                       }
 
                        bttv_print_irqbits(stat,astat);
 
index 0e788fca992cf1e27579dd689a12cfc2b0da2f38..c22c4ae0684491266406fd0dea3001b992ae101f 100644 (file)
@@ -674,11 +674,9 @@ static int dst_ca_release(struct inode *inode, struct file *file)
 
 static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
 {
-       ssize_t bytes_read = 0;
-
        dprintk(verbose, DST_CA_DEBUG, 1, " Device read.");
 
-       return bytes_read;
+       return 0;
 }
 
 static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)
index 180077c49123fe11159ec3cd8480995586cf7fbf..ffb6acdc575f65993f231d9f600f3ab0f7682883 100644 (file)
@@ -80,7 +80,7 @@ void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data,
        int period_elapsed = 0;
        int length;
 
-       dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc,
+       dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zu\n", cxsc,
                pcm_data, num_bytes);
 
        substream = cxsc->capture_pcm_substream;
index a1c1cec05f98e4693bdc3dacfa073a64adec1d32..c6c83445f8bfb93605990d6726b9d2755d0d4f24 100644 (file)
@@ -130,7 +130,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
                }
        }
        if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
-               CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
+               CX18_INFO("loaded %s firmware (%zu bytes)\n", fn, fw->size);
        size = fw->size;
        release_firmware(fw);
        cx18_setup_page(cx, SCB_OFFSET);
@@ -164,7 +164,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
 
        apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32];
        while (offset + sizeof(seghdr) < fw->size) {
-               const u32 *shptr = src + offset / 4;
+               const __le32 *shptr = (__force __le32 *)src + offset / 4;
 
                seghdr.sync1 = le32_to_cpu(shptr[0]);
                seghdr.sync2 = le32_to_cpu(shptr[1]);
@@ -202,7 +202,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
                offset += seghdr.size;
        }
        if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
-               CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n",
+               CX18_INFO("loaded %s firmware V%08x (%zu bytes)\n",
                                fn, apu_version, fw->size);
        size = fw->size;
        release_firmware(fw);
index 8884537bd62f9bf4685ea1a48c89f970cbb6f8b7..2a247d264b87740f6af9bb23ee411a799beb1020 100644 (file)
@@ -364,7 +364,7 @@ int cx18_stream_alloc(struct cx18_stream *s)
                                        ((char __iomem *)cx->scb->cpu_mdl));
 
                CX18_ERR("Too many buffers, cannot fit in SCB area\n");
-               CX18_ERR("Max buffers = %zd\n",
+               CX18_ERR("Max buffers = %zu\n",
                        bufsz / sizeof(struct cx18_mdl_ent));
                return -ENOMEM;
        }
index e12c006e6e2dc3be8470e454f64c5a055258791c..f613314b360ba735a048cfec56cb3324075f3f69 100644 (file)
@@ -3,12 +3,11 @@ config VIDEO_CX23885
        depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND
        select SND_PCM
        select I2C_ALGOBIT
-       select VIDEO_BTCX
        select VIDEO_TUNER
        select VIDEO_TVEEPROM
        depends on RC_CORE
-       select VIDEOBUF_DVB
-       select VIDEOBUF_DMA_SG
+       select VIDEOBUF2_DVB
+       select VIDEOBUF2_DMA_SG
        select VIDEO_CX25840
        select VIDEO_CX2341X
        select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT
@@ -32,12 +31,16 @@ config VIDEO_CX23885
        select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
        select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT
+       select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
+       select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT
        ---help---
          This is a video4linux driver for Conexant 23885 based
index 2a2cafb8cf5be31b48daf3c881aa50871e9a90fa..a2cbdcf15a8c48fde4cd79c43b33dd92c81a8def 100644 (file)
@@ -8,7 +8,6 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
 obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o
 
 ccflags-y += -Idrivers/media/i2c
-ccflags-y += -Idrivers/media/common
 ccflags-y += -Idrivers/media/tuners
 ccflags-y += -Idrivers/media/dvb-core
 ccflags-y += -Idrivers/media/dvb-frontends
index 2926f7fadccdcdeba7ca6b3a5cd5e99ba84e4bb7..2bbbf545b0422d0eca0548e4e30989c22bc5a0dc 100644 (file)
  * 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.
  */
 
 /*
@@ -52,8 +48,8 @@
  * |  DATA7|  DATA6|  DATA5|  DATA4|  DATA3|  DATA2|  DATA1|  DATA0|
  * +-------+-------+-------+-------+-------+-------+-------+-------+
  */
-#include <media/videobuf-dma-sg.h>
-#include <media/videobuf-dvb.h>
+#include <dvb_demux.h>
+#include <dvb_frontend.h>
 #include "altera-ci.h"
 #include "dvb_ca_en50221.h"
 
index 4998c96caebee5cb8e3d38fa2962b7b8ec642839..5028f0cf83f43a87b7e93270c0d6fb8c277bb584 100644 (file)
  * 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 __ALTERA_CI_H
 #define __ALTERA_CI_H
index 16fa7ea4d4aa7d472ff9f59f6b0131ead21c2902..631e4f24aea6424ef265fa1dcb8b4d81a5966eb3 100644 (file)
  * 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"
index 518744a4c8a5e41c82b8091bbee2f2d71166e6b6..565e958f6f8d2de35bfc15701961d387e8a8c68e 100644 (file)
  * 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
index bf89fc88692eb1ac081b34fc24691737e08c3951..3948db386fb5624c7a67e73a08bc5454fcd05e88 100644 (file)
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
@@ -865,6 +861,11 @@ static int cx23885_api_cmd(struct cx23885_dev *dev,
        return err;
 }
 
+static int cx23885_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+       return cx23885_mbox_func(priv, cmd, in, out, data);
+}
+
 static int cx23885_find_mailbox(struct cx23885_dev *dev)
 {
        u32 signature[4] = {
@@ -941,7 +942,7 @@ static int cx23885_load_firmware(struct cx23885_dev *dev)
 
        if (firmware->size != CX23885_FIRM_IMAGE_SIZE) {
                printk(KERN_ERR "ERROR: Firmware size mismatch "
-                       "(have %zd, expected %d)\n",
+                       "(have %zu, expected %d)\n",
                        firmware->size, CX23885_FIRM_IMAGE_SIZE);
                release_firmware(firmware);
                return -1;
@@ -1033,12 +1034,12 @@ static void cx23885_codec_settings(struct cx23885_dev *dev)
        cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
                                dev->ts1.height, dev->ts1.width);
 
-       dev->mpeg_params.width = dev->ts1.width;
-       dev->mpeg_params.height = dev->ts1.height;
-       dev->mpeg_params.is_50hz =
+       dev->cxhdl.width = dev->ts1.width;
+       dev->cxhdl.height = dev->ts1.height;
+       dev->cxhdl.is_50hz =
                (dev->encodernorm.id & V4L2_STD_625_50) != 0;
 
-       cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params);
+       cx2341x_handler_setup(&dev->cxhdl);
 
        cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1);
        cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1);
@@ -1137,85 +1138,107 @@ static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder)
 
 /* ------------------------------------------------------------------ */
 
-static int bb_buf_setup(struct videobuf_queue *q,
-       unsigned int *count, unsigned int *size)
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *num_buffers, unsigned int *num_planes,
+                          unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct cx23885_fh *fh = q->priv_data;
-
-       fh->dev->ts1.ts_packet_size  = mpeglinesize;
-       fh->dev->ts1.ts_packet_count = mpeglines;
-
-       *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
-       *count = mpegbufs;
+       struct cx23885_dev *dev = q->drv_priv;
 
+       dev->ts1.ts_packet_size  = mpeglinesize;
+       dev->ts1.ts_packet_count = mpeglines;
+       *num_planes = 1;
+       sizes[0] = mpeglinesize * mpeglines;
+       *num_buffers = mpegbufs;
        return 0;
 }
 
-static int bb_buf_prepare(struct videobuf_queue *q,
-       struct videobuf_buffer *vb, enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb)
 {
-       struct cx23885_fh *fh = q->priv_data;
-       return cx23885_buf_prepare(q, &fh->dev->ts1,
-               (struct cx23885_buffer *)vb,
-               field);
+       struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
+       struct cx23885_buffer *buf =
+               container_of(vb, struct cx23885_buffer, vb);
+
+       return cx23885_buf_prepare(buf, &dev->ts1);
 }
 
-static void bb_buf_queue(struct videobuf_queue *q,
-       struct videobuf_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
 {
-       struct cx23885_fh *fh = q->priv_data;
-       cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb);
+       struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
+       struct cx23885_buffer *buf = container_of(vb,
+               struct cx23885_buffer, vb);
+       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
+
+       cx23885_free_buffer(dev, buf);
+
+       dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
 }
 
-static void bb_buf_release(struct videobuf_queue *q,
-       struct videobuf_buffer *vb)
+static void buffer_queue(struct vb2_buffer *vb)
 {
-       cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
-}
+       struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
+       struct cx23885_buffer   *buf = container_of(vb,
+               struct cx23885_buffer, vb);
 
-static struct videobuf_queue_ops cx23885_qops = {
-       .buf_setup    = bb_buf_setup,
-       .buf_prepare  = bb_buf_prepare,
-       .buf_queue    = bb_buf_queue,
-       .buf_release  = bb_buf_release,
-};
+       cx23885_buf_queue(&dev->ts1, buf);
+}
 
-/* ------------------------------------------------------------------ */
+static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct cx23885_dev *dev = q->drv_priv;
+       struct cx23885_dmaqueue *dmaq = &dev->ts1.mpegq;
+       unsigned long flags;
+       int ret;
+
+       ret = cx23885_initialize_codec(dev, 1);
+       if (ret == 0) {
+               struct cx23885_buffer *buf = list_entry(dmaq->active.next,
+                       struct cx23885_buffer, queue);
+
+               cx23885_start_dma(&dev->ts1, dmaq, buf);
+               return 0;
+       }
+       spin_lock_irqsave(&dev->slock, flags);
+       while (!list_empty(&dmaq->active)) {
+               struct cx23885_buffer *buf = list_entry(dmaq->active.next,
+                       struct cx23885_buffer, queue);
 
-static const u32 *ctrl_classes[] = {
-       cx2341x_mpeg_ctrls,
-       NULL
-};
+               list_del(&buf->queue);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+       }
+       spin_unlock_irqrestore(&dev->slock, flags);
+       return ret;
+}
 
-static int cx23885_queryctrl(struct cx23885_dev *dev,
-       struct v4l2_queryctrl *qctrl)
+static void cx23885_stop_streaming(struct vb2_queue *q)
 {
-       qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
-       if (qctrl->id == 0)
-               return -EINVAL;
+       struct cx23885_dev *dev = q->drv_priv;
 
-       /* MPEG V4L2 controls */
-       if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl))
-               qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+       /* stop mpeg capture */
+       cx23885_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+                       CX23885_END_NOW, CX23885_MPEG_CAPTURE,
+                       CX23885_RAW_BITS_NONE);
 
-       return 0;
+       msleep(500);
+       cx23885_417_check_encoder(dev);
+       cx23885_cancel_buffers(&dev->ts1);
 }
 
-static int cx23885_querymenu(struct cx23885_dev *dev,
-       struct v4l2_querymenu *qmenu)
-{
-       struct v4l2_queryctrl qctrl;
+static struct vb2_ops cx23885_qops = {
+       .queue_setup    = queue_setup,
+       .buf_prepare  = buffer_prepare,
+       .buf_finish = buffer_finish,
+       .buf_queue    = buffer_queue,
+       .wait_prepare = vb2_ops_wait_prepare,
+       .wait_finish = vb2_ops_wait_finish,
+       .start_streaming = cx23885_start_streaming,
+       .stop_streaming = cx23885_stop_streaming,
+};
 
-       qctrl.id = qmenu->id;
-       cx23885_queryctrl(dev, &qctrl);
-       return v4l2_ctrl_query_menu(qmenu, &qctrl,
-               cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
-}
+/* ------------------------------------------------------------------ */
 
 static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
 {
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        *id = dev->tvnorm;
        return 0;
@@ -1223,29 +1246,26 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
 
 static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
 {
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        unsigned int i;
+       int ret;
 
        for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++)
                if (id & cx23885_tvnorms[i].id)
                        break;
        if (i == ARRAY_SIZE(cx23885_tvnorms))
                return -EINVAL;
-       dev->encodernorm = cx23885_tvnorms[i];
 
-       /* Have the drier core notify the subdevices */
-       mutex_lock(&dev->lock);
-       cx23885_set_tvnorm(dev, id);
-       mutex_unlock(&dev->lock);
-
-       return 0;
+       ret = cx23885_set_tvnorm(dev, id);
+       if (!ret)
+               dev->encodernorm = cx23885_tvnorms[i];
+       return ret;
 }
 
 static int vidioc_enum_input(struct file *file, void *priv,
        struct v4l2_input *i)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        dprintk(1, "%s()\n", __func__);
        return cx23885_enum_input(dev, i);
 }
@@ -1263,8 +1283,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
 static int vidioc_g_tuner(struct file *file, void *priv,
                                struct v4l2_tuner *t)
 {
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        if (dev->tuner_type == TUNER_ABSENT)
                return -EINVAL;
@@ -1281,8 +1300,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
 static int vidioc_s_tuner(struct file *file, void *priv,
                                const struct v4l2_tuner *t)
 {
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        if (dev->tuner_type == TUNER_ABSENT)
                return -EINVAL;
@@ -1296,8 +1314,7 @@ static int vidioc_s_tuner(struct file *file, void *priv,
 static int vidioc_g_frequency(struct file *file, void *priv,
                                struct v4l2_frequency *f)
 {
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        if (dev->tuner_type == TUNER_ABSENT)
                return -EINVAL;
@@ -1315,27 +1332,10 @@ static int vidioc_s_frequency(struct file *file, void *priv,
        return cx23885_set_frequency(file, priv, f);
 }
 
-static int vidioc_g_ctrl(struct file *file, void *priv,
-       struct v4l2_control *ctl)
-{
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
-       return cx23885_get_control(dev, ctl);
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-       struct v4l2_control *ctl)
-{
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
-       return cx23885_set_control(dev, ctl);
-}
-
 static int vidioc_querycap(struct file *file, void  *priv,
                                struct v4l2_capability *cap)
 {
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        struct cx23885_tsport  *tsport = &dev->ts1;
 
        strlcpy(cap->driver, dev->name, sizeof(cap->driver));
@@ -1368,8 +1368,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
 static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
        f->fmt.pix.bytesperline = 0;
@@ -1378,285 +1377,63 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
        f->fmt.pix.colorspace   = 0;
        f->fmt.pix.width        = dev->ts1.width;
        f->fmt.pix.height       = dev->ts1.height;
-       f->fmt.pix.field        = fh->mpegq.field;
-       dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
-               dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+       f->fmt.pix.field        = V4L2_FIELD_INTERLACED;
+       dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d\n",
+               dev->ts1.width, dev->ts1.height);
        return 0;
 }
 
 static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        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.colorspace   = 0;
-       dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
-               dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+       f->fmt.pix.field        = V4L2_FIELD_INTERLACED;
+       dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d\n",
+               dev->ts1.width, dev->ts1.height);
        return 0;
 }
 
 static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        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.colorspace   = 0;
+       f->fmt.pix.field        = V4L2_FIELD_INTERLACED;
        dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
                f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
        return 0;
 }
 
-static int vidioc_reqbufs(struct file *file, void *priv,
-                               struct v4l2_requestbuffers *p)
-{
-       struct cx23885_fh  *fh  = file->private_data;
-
-       return videobuf_reqbufs(&fh->mpegq, p);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
-                               struct v4l2_buffer *p)
-{
-       struct cx23885_fh  *fh  = file->private_data;
-
-       return videobuf_querybuf(&fh->mpegq, p);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv,
-                               struct v4l2_buffer *p)
-{
-       struct cx23885_fh  *fh  = file->private_data;
-
-       return videobuf_qbuf(&fh->mpegq, p);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
-       struct cx23885_fh  *fh  = priv;
-
-       return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK);
-}
-
-
-static int vidioc_streamon(struct file *file, void *priv,
-                               enum v4l2_buf_type i)
-{
-       struct cx23885_fh  *fh  = file->private_data;
-
-       return videobuf_streamon(&fh->mpegq);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
-       struct cx23885_fh  *fh  = file->private_data;
-
-       return videobuf_streamoff(&fh->mpegq);
-}
-
-static int vidioc_g_ext_ctrls(struct file *file, void *priv,
-                               struct v4l2_ext_controls *f)
-{
-       struct cx23885_fh  *fh  = priv;
-       struct cx23885_dev *dev = fh->dev;
-
-       if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
-               return -EINVAL;
-       return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
-}
-
-static int vidioc_s_ext_ctrls(struct file *file, void *priv,
-                               struct v4l2_ext_controls *f)
-{
-       struct cx23885_fh  *fh  = priv;
-       struct cx23885_dev *dev = fh->dev;
-       struct cx2341x_mpeg_params p;
-       int err;
-
-       if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
-               return -EINVAL;
-
-       p = dev->mpeg_params;
-       err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
-
-       if (err == 0) {
-               err = cx2341x_update(dev, cx23885_mbox_func,
-                       &dev->mpeg_params, &p);
-               dev->mpeg_params = p;
-       }
-       return err;
-}
-
-static int vidioc_try_ext_ctrls(struct file *file, void *priv,
-                               struct v4l2_ext_controls *f)
-{
-       struct cx23885_fh  *fh  = priv;
-       struct cx23885_dev *dev = fh->dev;
-       struct cx2341x_mpeg_params p;
-       int err;
-
-       if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
-               return -EINVAL;
-
-       p = dev->mpeg_params;
-       err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
-       return err;
-}
-
 static int vidioc_log_status(struct file *file, void *priv)
 {
-       struct cx23885_fh  *fh  = priv;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        char name[32 + 2];
 
        snprintf(name, sizeof(name), "%s/2", dev->name);
-       printk(KERN_INFO
-               "%s/2: ============  START LOG STATUS  ============\n",
-              dev->name);
        call_all(dev, core, log_status);
-       cx2341x_log_status(&dev->mpeg_params, name);
-       printk(KERN_INFO
-               "%s/2: =============  END LOG STATUS  =============\n",
-              dev->name);
+       v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name);
        return 0;
 }
 
-static int vidioc_querymenu(struct file *file, void *priv,
-                               struct v4l2_querymenu *a)
-{
-       struct cx23885_fh  *fh  = priv;
-       struct cx23885_dev *dev = fh->dev;
-
-       return cx23885_querymenu(dev, a);
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
-                               struct v4l2_queryctrl *c)
-{
-       struct cx23885_fh  *fh  = priv;
-       struct cx23885_dev *dev = fh->dev;
-
-       return cx23885_queryctrl(dev, c);
-}
-
-static int mpeg_open(struct file *file)
-{
-       struct cx23885_dev *dev = video_drvdata(file);
-       struct cx23885_fh *fh;
-
-       dprintk(2, "%s()\n", __func__);
-
-       /* allocate + initialize per filehandle data */
-       fh = kzalloc(sizeof(*fh), GFP_KERNEL);
-       if (!fh)
-               return -ENOMEM;
-
-       file->private_data = fh;
-       fh->dev      = dev;
-
-       videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops,
-                           &dev->pci->dev, &dev->ts1.slock,
-                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                           V4L2_FIELD_INTERLACED,
-                           sizeof(struct cx23885_buffer),
-                           fh, NULL);
-       return 0;
-}
-
-static int mpeg_release(struct file *file)
-{
-       struct cx23885_fh  *fh  = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
-
-       dprintk(2, "%s()\n", __func__);
-
-       /* FIXME: Review this crap */
-       /* Shut device down on last close */
-       if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
-               if (atomic_dec_return(&dev->v4l_reader_count) == 0) {
-                       /* stop mpeg capture */
-                       cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
-                               CX23885_END_NOW, CX23885_MPEG_CAPTURE,
-                               CX23885_RAW_BITS_NONE);
-
-                       msleep(500);
-                       cx23885_417_check_encoder(dev);
-
-                       cx23885_cancel_buffers(&fh->dev->ts1);
-               }
-       }
-
-       if (fh->mpegq.streaming)
-               videobuf_streamoff(&fh->mpegq);
-       if (fh->mpegq.reading)
-               videobuf_read_stop(&fh->mpegq);
-
-       videobuf_mmap_free(&fh->mpegq);
-       file->private_data = NULL;
-       kfree(fh);
-
-       return 0;
-}
-
-static ssize_t mpeg_read(struct file *file, char __user *data,
-       size_t count, loff_t *ppos)
-{
-       struct cx23885_fh *fh = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
-
-       dprintk(2, "%s()\n", __func__);
-
-       /* Deal w/ A/V decoder * and mpeg encoder sync issues. */
-       /* Start mpeg encoder on first read. */
-       if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
-               if (atomic_inc_return(&dev->v4l_reader_count) == 1) {
-                       if (cx23885_initialize_codec(dev, 1) < 0)
-                               return -EINVAL;
-               }
-       }
-
-       return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
-                                   file->f_flags & O_NONBLOCK);
-}
-
-static unsigned int mpeg_poll(struct file *file,
-       struct poll_table_struct *wait)
-{
-       struct cx23885_fh *fh = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
-
-       dprintk(2, "%s\n", __func__);
-
-       return videobuf_poll_stream(file, &fh->mpegq, wait);
-}
-
-static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct cx23885_fh *fh = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
-
-       dprintk(2, "%s()\n", __func__);
-
-       return videobuf_mmap_mapper(&fh->mpegq, vma);
-}
-
 static struct v4l2_file_operations mpeg_fops = {
        .owner         = THIS_MODULE,
-       .open          = mpeg_open,
-       .release       = mpeg_release,
-       .read          = mpeg_read,
-       .poll          = mpeg_poll,
-       .mmap          = mpeg_mmap,
-       .ioctl         = video_ioctl2,
+       .open           = v4l2_fh_open,
+       .release        = vb2_fop_release,
+       .read           = vb2_fop_read,
+       .poll           = vb2_fop_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = vb2_fop_mmap,
 };
 
 static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
@@ -1669,25 +1446,19 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
        .vidioc_s_tuner          = vidioc_s_tuner,
        .vidioc_g_frequency      = vidioc_g_frequency,
        .vidioc_s_frequency      = vidioc_s_frequency,
-       .vidioc_s_ctrl           = vidioc_s_ctrl,
-       .vidioc_g_ctrl           = vidioc_g_ctrl,
        .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_reqbufs          = vidioc_reqbufs,
-       .vidioc_querybuf         = vidioc_querybuf,
-       .vidioc_qbuf             = vidioc_qbuf,
-       .vidioc_dqbuf            = vidioc_dqbuf,
-       .vidioc_streamon         = vidioc_streamon,
-       .vidioc_streamoff        = vidioc_streamoff,
-       .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_reqbufs       = vb2_ioctl_reqbufs,
+       .vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf      = vb2_ioctl_querybuf,
+       .vidioc_qbuf          = vb2_ioctl_qbuf,
+       .vidioc_dqbuf         = vb2_ioctl_dqbuf,
+       .vidioc_streamon      = vb2_ioctl_streamon,
+       .vidioc_streamoff     = vb2_ioctl_streamoff,
        .vidioc_log_status       = vidioc_log_status,
-       .vidioc_querymenu        = vidioc_querymenu,
-       .vidioc_queryctrl        = vidioc_queryctrl,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .vidioc_g_chip_info      = cx23885_g_chip_info,
        .vidioc_g_register       = cx23885_g_register,
@@ -1711,6 +1482,7 @@ void cx23885_417_unregister(struct cx23885_dev *dev)
                        video_unregister_device(dev->v4l_device);
                else
                        video_device_release(dev->v4l_device);
+               v4l2_ctrl_handler_free(&dev->cxhdl.hdl);
                dev->v4l_device = NULL;
        }
 }
@@ -1742,6 +1514,7 @@ int cx23885_417_register(struct cx23885_dev *dev)
        /* FIXME: Port1 hardcoded here */
        int err = -ENODEV;
        struct cx23885_tsport *tsport = &dev->ts1;
+       struct vb2_queue *q;
 
        dprintk(1, "%s()\n", __func__);
 
@@ -1757,14 +1530,36 @@ int cx23885_417_register(struct cx23885_dev *dev)
                tsport->height = 576;
 
        tsport->width = 720;
-       cx2341x_fill_defaults(&dev->mpeg_params);
-
-       dev->mpeg_params.port = CX2341X_PORT_SERIAL;
+       dev->cxhdl.port = CX2341X_PORT_SERIAL;
+       err = cx2341x_handler_init(&dev->cxhdl, 50);
+       if (err)
+               return err;
+       dev->cxhdl.priv = dev;
+       dev->cxhdl.func = cx23885_api_func;
+       cx2341x_handler_set_50hz(&dev->cxhdl, tsport->height == 576);
+       v4l2_ctrl_add_handler(&dev->ctrl_handler, &dev->cxhdl.hdl, NULL);
 
        /* Allocate and initialize V4L video device */
        dev->v4l_device = cx23885_video_dev_alloc(tsport,
                dev->pci, &cx23885_mpeg_template, "mpeg");
+       q = &dev->vb2_mpegq;
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+       q->gfp_flags = GFP_DMA32;
+       q->min_buffers_needed = 2;
+       q->drv_priv = dev;
+       q->buf_struct_size = sizeof(struct cx23885_buffer);
+       q->ops = &cx23885_qops;
+       q->mem_ops = &vb2_dma_sg_memops;
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->lock = &dev->lock;
+
+       err = vb2_queue_init(q);
+       if (err < 0)
+               return err;
        video_set_drvdata(dev->v4l_device, dev);
+       dev->v4l_device->lock = &dev->lock;
+       dev->v4l_device->queue = q;
        err = video_register_device(dev->v4l_device,
                VFL_TYPE_GRABBER, -1);
        if (err < 0) {
index 554798dcedd09b3a1ad86961b2fd5fde691df254..ae7c2e89ad1cd6b9aebe8f0baea24d76e9065b79 100644 (file)
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
@@ -84,6 +80,82 @@ MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]");
 #define AUD_INT_MCHG_IRQ        (1 << 21)
 #define GP_COUNT_CONTROL_RESET 0x3
 
+static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip, int nr_pages)
+{
+       struct cx23885_audio_buffer *buf = chip->buf;
+       struct page *pg;
+       int i;
+
+       buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
+       if (NULL == buf->vaddr) {
+               dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
+               return -ENOMEM;
+       }
+
+       dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
+                               (unsigned long)buf->vaddr,
+                               nr_pages << PAGE_SHIFT);
+
+       memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
+       buf->nr_pages = nr_pages;
+
+       buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist));
+       if (NULL == buf->sglist)
+               goto vzalloc_err;
+
+       sg_init_table(buf->sglist, buf->nr_pages);
+       for (i = 0; i < buf->nr_pages; i++) {
+               pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE);
+               if (NULL == pg)
+                       goto vmalloc_to_page_err;
+               sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0);
+       }
+       return 0;
+
+vmalloc_to_page_err:
+       vfree(buf->sglist);
+       buf->sglist = NULL;
+vzalloc_err:
+       vfree(buf->vaddr);
+       buf->vaddr = NULL;
+       return -ENOMEM;
+}
+
+static int cx23885_alsa_dma_map(struct cx23885_audio_dev *dev)
+{
+       struct cx23885_audio_buffer *buf = dev->buf;
+
+       buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist,
+                       buf->nr_pages, PCI_DMA_FROMDEVICE);
+
+       if (0 == buf->sglen) {
+               pr_warn("%s: cx23885_alsa_map_sg failed\n", __func__);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static int cx23885_alsa_dma_unmap(struct cx23885_audio_dev *dev)
+{
+       struct cx23885_audio_buffer *buf = dev->buf;
+
+       if (!buf->sglen)
+               return 0;
+
+       dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, PCI_DMA_FROMDEVICE);
+       buf->sglen = 0;
+       return 0;
+}
+
+static int cx23885_alsa_dma_free(struct cx23885_audio_buffer *buf)
+{
+       vfree(buf->sglist);
+       buf->sglist = NULL;
+       vfree(buf->vaddr);
+       buf->vaddr = NULL;
+       return 0;
+}
+
 /*
  * BOARD Specific: Sets audio DMA
  */
@@ -198,15 +270,18 @@ int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask)
 
 static int dsp_buffer_free(struct cx23885_audio_dev *chip)
 {
+       struct cx23885_riscmem *risc;
+
        BUG_ON(!chip->dma_size);
 
        dprintk(2, "Freeing buffer\n");
-       videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
-       videobuf_dma_free(chip->dma_risc);
-       btcx_riscmem_free(chip->pci, &chip->buf->risc);
+       cx23885_alsa_dma_unmap(chip);
+       cx23885_alsa_dma_free(chip->buf);
+       risc = &chip->buf->risc;
+       pci_free_consistent(chip->pci, risc->size, risc->cpu, risc->dma);
        kfree(chip->buf);
 
-       chip->dma_risc = NULL;
+       chip->buf = NULL;
        chip->dma_size = 0;
 
        return 0;
@@ -289,6 +364,7 @@ static int snd_cx23885_close(struct snd_pcm_substream *substream)
        return 0;
 }
 
+
 /*
  * hw_params callback
  */
@@ -296,8 +372,6 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
                              struct snd_pcm_hw_params *hw_params)
 {
        struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
-       struct videobuf_dmabuf *dma;
-
        struct cx23885_audio_buffer *buf;
        int ret;
 
@@ -318,19 +392,18 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
                return -ENOMEM;
 
        buf->bpl = chip->period_size;
+       chip->buf = buf;
 
-       dma = &buf->dma;
-       videobuf_dma_init(dma);
-       ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+       ret = cx23885_alsa_dma_init(chip,
                        (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
        if (ret < 0)
                goto error;
 
-       ret = videobuf_dma_map(&chip->pci->dev, dma);
+       ret = cx23885_alsa_dma_map(chip);
        if (ret < 0)
                goto error;
 
-       ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
+       ret = cx23885_risc_databuffer(chip->pci, &buf->risc, buf->sglist,
                                   chip->period_size, chip->num_periods, 1);
        if (ret < 0)
                goto error;
@@ -340,10 +413,7 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
        buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
        buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
 
-       chip->buf = buf;
-       chip->dma_risc = dma;
-
-       substream->runtime->dma_area = chip->dma_risc->vaddr;
+       substream->runtime->dma_area = chip->buf->vaddr;
        substream->runtime->dma_bytes = chip->dma_size;
        substream->runtime->dma_addr = 0;
 
@@ -351,6 +421,7 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
 
 error:
        kfree(buf);
+       chip->buf = NULL;
        return ret;
 }
 
index c443b7ac5adfeb29806fd0cc345ac9b8d64c0c16..877dad89107ec5348cb0e21323122af8202f8365 100644 (file)
  *  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 "cx23885.h"
index d2915c3e53a29d87ed9ea220c482d4434e9e33e7..97f232f8efb918fdb238c36ddff56c3a85ff0634 100644 (file)
  *  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.
  */
 
 #ifndef _CX23885_AV_H_
index c2b6080071908722706cd335917e6c99b4064dee..88c257d1161b1629294150e9bb9bc49bf887f52e 100644 (file)
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/init.h>
@@ -679,6 +675,11 @@ struct cx23885_board cx23885_boards[] = {
                        .amux   = CX25840_AUDIO7,
                } },
        },
+       [CX23885_BOARD_DVBSKY_T9580] = {
+               .name           = "DVBSky T9580",
+               .portb          = CX23885_MPEG_DVB,
+               .portc          = CX23885_MPEG_DVB,
+       },
 };
 const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
 
@@ -934,6 +935,10 @@ struct cx23885_subid cx23885_subids[] = {
                .subvendor = 0x18ac,
                .subdevice = 0xdb98,
                .card      = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2,
+       }, {
+               .subvendor = 0x4254,
+               .subdevice = 0x9580,
+               .card      = CX23885_BOARD_DVBSKY_T9580,
        },
 };
 const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -1528,6 +1533,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
                cx_set(GP0_IO, 0x00040004);
                mdelay(60);
                break;
+       case CX23885_BOARD_DVBSKY_T9580:
+               /* enable GPIO3-18 pins */
+               cx_write(MC417_CTL, 0x00000037);
+               cx23885_gpio_enable(dev, GPIO_2 | GPIO_11, 1);
+               cx23885_gpio_clear(dev, GPIO_2 | GPIO_11);
+               mdelay(100);
+               cx23885_gpio_set(dev, GPIO_2 | GPIO_11);
+               break;
        }
 }
 
@@ -1851,6 +1864,14 @@ 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_DVBSKY_T9580:
+               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;
+               ts2->gen_ctrl_val  = 0x8; /* Serial bus */
+               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:
@@ -1913,6 +1934,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        case CX23885_BOARD_AVERMEDIA_HC81R:
        case CX23885_BOARD_TBS_6980:
        case CX23885_BOARD_TBS_6981:
+       case CX23885_BOARD_DVBSKY_T9580:
                dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
                                &dev->i2c_bus[2].i2c_adap,
                                "cx25840", 0x88 >> 1, NULL);
@@ -1970,5 +1992,3 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        }
        }
 }
-
-/* ------------------------------------------------------------------ */
index edcd79db1e4ebc5d8ba6319b2ccb3c900a3024b4..331eddac7222ae7f62fb757148f90c8881b5ef4c 100644 (file)
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/init.h>
@@ -420,39 +416,23 @@ static int cx23885_risc_decode(u32 risc)
        return incr[risc >> 28] ? incr[risc >> 28] : 1;
 }
 
-void cx23885_wakeup(struct cx23885_tsport *port,
+static void cx23885_wakeup(struct cx23885_tsport *port,
                           struct cx23885_dmaqueue *q, u32 count)
 {
        struct cx23885_dev *dev = port->dev;
        struct cx23885_buffer *buf;
-       int bc;
-
-       for (bc = 0;; bc++) {
-               if (list_empty(&q->active))
-                       break;
-               buf = list_entry(q->active.next,
-                                struct cx23885_buffer, vb.queue);
-
-               /* count comes from the hw and is is 16bit wide --
-                * this trick handles wrap-arounds correctly for
-                * up to 32767 buffers in flight... */
-               if ((s16) (count - buf->count) < 0)
-                       break;
 
-               v4l2_get_timestamp(&buf->vb.ts);
-               dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
-                       count, buf->count);
-               buf->vb.state = VIDEOBUF_DONE;
-               list_del(&buf->vb.queue);
-               wake_up(&buf->vb.done);
-       }
        if (list_empty(&q->active))
-               del_timer(&q->timeout);
-       else
-               mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
-       if (bc != 1)
-               printk(KERN_WARNING "%s: %d buffers handled (should be 1)\n",
-                      __func__, bc);
+               return;
+       buf = list_entry(q->active.next,
+                        struct cx23885_buffer, queue);
+
+       v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+       buf->vb.v4l2_buf.sequence = q->count++;
+       dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.v4l2_buf.index,
+               count, q->count);
+       list_del(&buf->queue);
+       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
 }
 
 int cx23885_sram_channel_setup(struct cx23885_dev *dev,
@@ -482,8 +462,8 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev,
                lines = 6;
        BUG_ON(lines < 2);
 
-       cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
-       cx_write(8 + 4, 8);
+       cx_write(8 + 0, RISC_JUMP | RISC_CNT_RESET);
+       cx_write(8 + 4, 12);
        cx_write(8 + 8, 0);
 
        /* write CDT */
@@ -590,7 +570,7 @@ void cx23885_sram_channel_dump(struct cx23885_dev *dev,
 }
 
 static void cx23885_risc_disasm(struct cx23885_tsport *port,
-                               struct btcx_riscmem *risc)
+                               struct cx23885_riscmem *risc)
 {
        struct cx23885_dev *dev = port->dev;
        unsigned int i, j, n;
@@ -699,10 +679,6 @@ static int get_resources(struct cx23885_dev *dev)
        return -EBUSY;
 }
 
-static void cx23885_timeout(unsigned long data);
-int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
-                               u32 reg, u32 mask, u32 value);
-
 static int cx23885_init_tsport(struct cx23885_dev *dev,
        struct cx23885_tsport *port, int portno)
 {
@@ -719,11 +695,6 @@ static int cx23885_init_tsport(struct cx23885_dev *dev,
        port->nr = portno;
 
        INIT_LIST_HEAD(&port->mpegq.active);
-       INIT_LIST_HEAD(&port->mpegq.queued);
-       port->mpegq.timeout.function = cx23885_timeout;
-       port->mpegq.timeout.data = (unsigned long)port;
-       init_timer(&port->mpegq.timeout);
-
        mutex_init(&port->frontends.lock);
        INIT_LIST_HEAD(&port->frontends.felist);
        port->frontends.active_fe_id = 0;
@@ -776,9 +747,6 @@ static int cx23885_init_tsport(struct cx23885_dev *dev,
                BUG();
        }
 
-       cx23885_risc_stopper(dev->pci, &port->mpegq.stopper,
-                    port->reg_dma_ctl, port->dma_ctl_val, 0x00);
-
        return 0;
 }
 
@@ -1089,11 +1057,18 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev)
 static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
                               unsigned int offset, u32 sync_line,
                               unsigned int bpl, unsigned int padding,
-                              unsigned int lines,  unsigned int lpi)
+                              unsigned int lines,  unsigned int lpi, bool jump)
 {
        struct scatterlist *sg;
        unsigned int line, todo, sol;
 
+
+       if (jump) {
+               *(rp++) = cpu_to_le32(RISC_JUMP);
+               *(rp++) = cpu_to_le32(0);
+               *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+       }
+
        /* sync instruction */
        if (sync_line != NO_SYNC_LINE)
                *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
@@ -1146,14 +1121,13 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
        return rp;
 }
 
-int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+int cx23885_risc_buffer(struct pci_dev *pci, struct cx23885_riscmem *risc,
                        struct scatterlist *sglist, unsigned int top_offset,
                        unsigned int bottom_offset, unsigned int bpl,
                        unsigned int padding, unsigned int lines)
 {
        u32 instructions, fields;
        __le32 *rp;
-       int rc;
 
        fields = 0;
        if (UNSET != top_offset)
@@ -1168,19 +1142,20 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
        /* write and jump need and extra dword */
        instructions  = fields * (1 + ((bpl + padding) * lines)
                / PAGE_SIZE + lines);
-       instructions += 2;
-       rc = btcx_riscmem_alloc(pci, risc, instructions*12);
-       if (rc < 0)
-               return rc;
+       instructions += 5;
+       risc->size = instructions * 12;
+       risc->cpu = pci_alloc_consistent(pci, risc->size, &risc->dma);
+       if (risc->cpu == NULL)
+               return -ENOMEM;
 
        /* write risc instructions */
        rp = risc->cpu;
        if (UNSET != top_offset)
                rp = cx23885_risc_field(rp, sglist, top_offset, 0,
-                                       bpl, padding, lines, 0);
+                                       bpl, padding, lines, 0, true);
        if (UNSET != bottom_offset)
                rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200,
-                                       bpl, padding, lines, 0);
+                                       bpl, padding, lines, 0, UNSET == top_offset);
 
        /* save pointer to jmp instruction address */
        risc->jmp = rp;
@@ -1189,14 +1164,13 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
 }
 
 int cx23885_risc_databuffer(struct pci_dev *pci,
-                                  struct btcx_riscmem *risc,
+                                  struct cx23885_riscmem *risc,
                                   struct scatterlist *sglist,
                                   unsigned int bpl,
                                   unsigned int lines, unsigned int lpi)
 {
        u32 instructions;
        __le32 *rp;
-       int rc;
 
        /* estimate risc mem: worst case is one write per page border +
           one write per scan line + syncs + jump (all 2 dwords).  Here
@@ -1204,16 +1178,17 @@ int cx23885_risc_databuffer(struct pci_dev *pci,
           than PAGE_SIZE */
        /* Jump and write need an extra dword */
        instructions  = 1 + (bpl * lines) / PAGE_SIZE + lines;
-       instructions += 1;
+       instructions += 4;
 
-       rc = btcx_riscmem_alloc(pci, risc, instructions*12);
-       if (rc < 0)
-               return rc;
+       risc->size = instructions * 12;
+       risc->cpu = pci_alloc_consistent(pci, risc->size, &risc->dma);
+       if (risc->cpu == NULL)
+               return -ENOMEM;
 
        /* write risc instructions */
        rp = risc->cpu;
        rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE,
-                               bpl, 0, lines, lpi);
+                               bpl, 0, lines, lpi, lpi == 0);
 
        /* save pointer to jmp instruction address */
        risc->jmp = rp;
@@ -1221,14 +1196,13 @@ int cx23885_risc_databuffer(struct pci_dev *pci,
        return 0;
 }
 
-int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+int cx23885_risc_vbibuffer(struct pci_dev *pci, struct cx23885_riscmem *risc,
                        struct scatterlist *sglist, unsigned int top_offset,
                        unsigned int bottom_offset, unsigned int bpl,
                        unsigned int padding, unsigned int lines)
 {
        u32 instructions, fields;
        __le32 *rp;
-       int rc;
 
        fields = 0;
        if (UNSET != top_offset)
@@ -1243,22 +1217,23 @@ int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
        /* write and jump need and extra dword */
        instructions  = fields * (1 + ((bpl + padding) * lines)
                / PAGE_SIZE + lines);
-       instructions += 2;
-       rc = btcx_riscmem_alloc(pci, risc, instructions*12);
-       if (rc < 0)
-               return rc;
+       instructions += 5;
+       risc->size = instructions * 12;
+       risc->cpu = pci_alloc_consistent(pci, risc->size, &risc->dma);
+       if (risc->cpu == NULL)
+               return -ENOMEM;
        /* write risc instructions */
        rp = risc->cpu;
 
        /* Sync to line 6, so US CC line 21 will appear in line '12'
         * in the userland vbi payload */
        if (UNSET != top_offset)
-               rp = cx23885_risc_field(rp, sglist, top_offset, 6,
-                                       bpl, padding, lines, 0);
+               rp = cx23885_risc_field(rp, sglist, top_offset, 0,
+                                       bpl, padding, lines, 0, true);
 
        if (UNSET != bottom_offset)
-               rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x207,
-                                       bpl, padding, lines, 0);
+               rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200,
+                                       bpl, padding, lines, 0, UNSET == top_offset);
 
 
 
@@ -1269,38 +1244,12 @@ int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
 }
 
 
-int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
-                               u32 reg, u32 mask, u32 value)
+void cx23885_free_buffer(struct cx23885_dev *dev, struct cx23885_buffer *buf)
 {
-       __le32 *rp;
-       int rc;
-
-       rc = btcx_riscmem_alloc(pci, risc, 4*16);
-       if (rc < 0)
-               return rc;
-
-       /* write risc instructions */
-       rp = risc->cpu;
-       *(rp++) = cpu_to_le32(RISC_WRITECR  | RISC_IRQ2);
-       *(rp++) = cpu_to_le32(reg);
-       *(rp++) = cpu_to_le32(value);
-       *(rp++) = cpu_to_le32(mask);
-       *(rp++) = cpu_to_le32(RISC_JUMP);
-       *(rp++) = cpu_to_le32(risc->dma);
-       *(rp++) = cpu_to_le32(0); /* bits 63-32 */
-       return 0;
-}
-
-void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf)
-{
-       struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+       struct cx23885_riscmem *risc = &buf->risc;
 
        BUG_ON(in_interrupt());
-       videobuf_waiton(q, &buf->vb, 0, 0);
-       videobuf_dma_unmap(q->dev, dma);
-       videobuf_dma_free(dma);
-       btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
-       buf->vb.state = VIDEOBUF_NEEDS_INIT;
+       pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
 }
 
 static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
@@ -1355,7 +1304,7 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
                port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk));
 }
 
-static int cx23885_start_dma(struct cx23885_tsport *port,
+int cx23885_start_dma(struct cx23885_tsport *port,
                             struct cx23885_dmaqueue *q,
                             struct cx23885_buffer   *buf)
 {
@@ -1363,7 +1312,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
        u32 reg;
 
        dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__,
-               buf->vb.width, buf->vb.height, buf->vb.field);
+               dev->width, dev->height, dev->field);
 
        /* Stop the fifo and risc engine for this port */
        cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
@@ -1379,7 +1328,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
        }
 
        /* write TS length to chip */
-       cx_write(port->reg_lngth, buf->vb.width);
+       cx_write(port->reg_lngth, port->ts_packet_size);
 
        if ((!(cx23885_boards[dev->board].portb & CX23885_MPEG_DVB)) &&
                (!(cx23885_boards[dev->board].portc & CX23885_MPEG_DVB))) {
@@ -1408,7 +1357,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
        /* NOTE: this is 2 (reserved) for portb, does it matter? */
        /* reset counter to zero */
        cx_write(port->reg_gpcnt_ctl, 3);
-       q->count = 1;
+       q->count = 0;
 
        /* Set VIDB pins to input */
        if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
@@ -1497,134 +1446,83 @@ static int cx23885_stop_dma(struct cx23885_tsport *port)
        return 0;
 }
 
-int cx23885_restart_queue(struct cx23885_tsport *port,
-                               struct cx23885_dmaqueue *q)
-{
-       struct cx23885_dev *dev = port->dev;
-       struct cx23885_buffer *buf;
-
-       dprintk(5, "%s()\n", __func__);
-       if (list_empty(&q->active)) {
-               struct cx23885_buffer *prev;
-               prev = NULL;
-
-               dprintk(5, "%s() queue is empty\n", __func__);
-
-               for (;;) {
-                       if (list_empty(&q->queued))
-                               return 0;
-                       buf = list_entry(q->queued.next, struct cx23885_buffer,
-                                        vb.queue);
-                       if (NULL == prev) {
-                               list_move_tail(&buf->vb.queue, &q->active);
-                               cx23885_start_dma(port, q, buf);
-                               buf->vb.state = VIDEOBUF_ACTIVE;
-                               buf->count    = q->count++;
-                               mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
-                               dprintk(5, "[%p/%d] restart_queue - f/active\n",
-                                       buf, buf->vb.i);
-
-                       } else if (prev->vb.width  == buf->vb.width  &&
-                                  prev->vb.height == buf->vb.height &&
-                                  prev->fmt       == buf->fmt) {
-                               list_move_tail(&buf->vb.queue, &q->active);
-                               buf->vb.state = VIDEOBUF_ACTIVE;
-                               buf->count    = q->count++;
-                               prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-                               /* 64 bit bits 63-32 */
-                               prev->risc.jmp[2] = cpu_to_le32(0);
-                               dprintk(5, "[%p/%d] restart_queue - m/active\n",
-                                       buf, buf->vb.i);
-                       } else {
-                               return 0;
-                       }
-                       prev = buf;
-               }
-               return 0;
-       }
-
-       buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue);
-       dprintk(2, "restart_queue [%p/%d]: restart dma\n",
-               buf, buf->vb.i);
-       cx23885_start_dma(port, q, buf);
-       list_for_each_entry(buf, &q->active, vb.queue)
-               buf->count = q->count++;
-       mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
-       return 0;
-}
-
 /* ------------------------------------------------------------------ */
 
-int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
-                       struct cx23885_buffer *buf, enum v4l2_field field)
+int cx23885_buf_prepare(struct cx23885_buffer *buf, struct cx23885_tsport *port)
 {
        struct cx23885_dev *dev = port->dev;
        int size = port->ts_packet_size * port->ts_packet_count;
+       struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb, 0);
        int rc;
 
        dprintk(1, "%s: %p\n", __func__, buf);
-       if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+       if (vb2_plane_size(&buf->vb, 0) < size)
                return -EINVAL;
+       vb2_set_plane_payload(&buf->vb, 0, size);
 
-       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-               buf->vb.width  = port->ts_packet_size;
-               buf->vb.height = port->ts_packet_count;
-               buf->vb.size   = size;
-               buf->vb.field  = field /*V4L2_FIELD_TOP*/;
-
-               rc = videobuf_iolock(q, &buf->vb, NULL);
-               if (0 != rc)
-                       goto fail;
-               cx23885_risc_databuffer(dev->pci, &buf->risc,
-                                       videobuf_to_dma(&buf->vb)->sglist,
-                                       buf->vb.width, buf->vb.height, 0);
-       }
-       buf->vb.state = VIDEOBUF_PREPARED;
-       return 0;
+       rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
+       if (!rc)
+               return -EIO;
 
- fail:
-       cx23885_free_buffer(q, buf);
-       return rc;
+       cx23885_risc_databuffer(dev->pci, &buf->risc,
+                               sgt->sgl,
+                               port->ts_packet_size, port->ts_packet_count, 0);
+       return 0;
 }
 
+/*
+ * The risc program for each buffer works as follows: it starts with a simple
+ * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the
+ * buffer follows and at the end we have a JUMP back to the start + 12 (skipping
+ * the initial JUMP).
+ *
+ * This is the risc program of the first buffer to be queued if the active list
+ * is empty and it just keeps DMAing this buffer without generating any
+ * interrupts.
+ *
+ * If a new buffer is added then the initial JUMP in the code for that buffer
+ * will generate an interrupt which signals that the previous buffer has been
+ * DMAed successfully and that it can be returned to userspace.
+ *
+ * It also sets the final jump of the previous buffer to the start of the new
+ * buffer, thus chaining the new buffer into the DMA chain. This is a single
+ * atomic u32 write, so there is no race condition.
+ *
+ * The end-result of all this that you only get an interrupt when a buffer
+ * is ready, so the control flow is very easy.
+ */
 void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf)
 {
        struct cx23885_buffer    *prev;
        struct cx23885_dev *dev = port->dev;
        struct cx23885_dmaqueue  *cx88q = &port->mpegq;
+       unsigned long flags;
 
-       /* add jump to stopper */
-       buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
-       buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma);
+       buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12);
+       buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
+       buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12);
        buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
 
+       spin_lock_irqsave(&dev->slock, flags);
        if (list_empty(&cx88q->active)) {
-               dprintk(1, "queue is empty - first active\n");
-               list_add_tail(&buf->vb.queue, &cx88q->active);
-               cx23885_start_dma(port, cx88q, buf);
-               buf->vb.state = VIDEOBUF_ACTIVE;
-               buf->count    = cx88q->count++;
-               mod_timer(&cx88q->timeout, jiffies + BUFFER_TIMEOUT);
+               list_add_tail(&buf->queue, &cx88q->active);
                dprintk(1, "[%p/%d] %s - first active\n",
-                       buf, buf->vb.i, __func__);
+                       buf, buf->vb.v4l2_buf.index, __func__);
        } else {
-               dprintk(1, "queue is not empty - append to active\n");
+               buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
                prev = list_entry(cx88q->active.prev, struct cx23885_buffer,
-                                 vb.queue);
-               list_add_tail(&buf->vb.queue, &cx88q->active);
-               buf->vb.state = VIDEOBUF_ACTIVE;
-               buf->count    = cx88q->count++;
+                                 queue);
+               list_add_tail(&buf->queue, &cx88q->active);
                prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-               prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */
                dprintk(1, "[%p/%d] %s - append to active\n",
-                        buf, buf->vb.i, __func__);
+                        buf, buf->vb.v4l2_buf.index, __func__);
        }
+       spin_unlock_irqrestore(&dev->slock, flags);
 }
 
 /* ----------------------------------------------------------- */
 
-static void do_cancel_buffers(struct cx23885_tsport *port, char *reason,
-                             int restart)
+static void do_cancel_buffers(struct cx23885_tsport *port, char *reason)
 {
        struct cx23885_dev *dev = port->dev;
        struct cx23885_dmaqueue *q = &port->mpegq;
@@ -1634,16 +1532,11 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason,
        spin_lock_irqsave(&port->slock, flags);
        while (!list_empty(&q->active)) {
                buf = list_entry(q->active.next, struct cx23885_buffer,
-                                vb.queue);
-               list_del(&buf->vb.queue);
-               buf->vb.state = VIDEOBUF_ERROR;
-               wake_up(&buf->vb.done);
+                                queue);
+               list_del(&buf->queue);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
                dprintk(1, "[%p/%d] %s - dma=0x%08lx\n",
-                       buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
-       }
-       if (restart) {
-               dprintk(1, "restarting queue\n");
-               cx23885_restart_queue(port, q);
+                       buf, buf->vb.v4l2_buf.index, reason, (unsigned long)buf->risc.dma);
        }
        spin_unlock_irqrestore(&port->slock, flags);
 }
@@ -1651,27 +1544,10 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason,
 void cx23885_cancel_buffers(struct cx23885_tsport *port)
 {
        struct cx23885_dev *dev = port->dev;
-       struct cx23885_dmaqueue *q = &port->mpegq;
-
-       dprintk(1, "%s()\n", __func__);
-       del_timer_sync(&q->timeout);
-       cx23885_stop_dma(port);
-       do_cancel_buffers(port, "cancel", 0);
-}
-
-static void cx23885_timeout(unsigned long data)
-{
-       struct cx23885_tsport *port = (struct cx23885_tsport *)data;
-       struct cx23885_dev *dev = port->dev;
 
        dprintk(1, "%s()\n", __func__);
-
-       if (debug > 5)
-               cx23885_sram_channel_dump(dev,
-                       &dev->sram_channels[port->sram_chno]);
-
        cx23885_stop_dma(port);
-       do_cancel_buffers(port, "timeout", 1);
+       do_cancel_buffers(port, "cancel");
 }
 
 int cx23885_irq_417(struct cx23885_dev *dev, u32 status)
@@ -1721,11 +1597,6 @@ int cx23885_irq_417(struct cx23885_dev *dev, u32 status)
                spin_lock(&port->slock);
                cx23885_wakeup(port, &port->mpegq, count);
                spin_unlock(&port->slock);
-       } else if (status & VID_B_MSK_RISCI2) {
-               dprintk(7, "        VID_B_MSK_RISCI2\n");
-               spin_lock(&port->slock);
-               cx23885_restart_queue(port, &port->mpegq);
-               spin_unlock(&port->slock);
        }
        if (status) {
                cx_write(port->reg_ts_int_stat, status);
@@ -1777,14 +1648,6 @@ static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status)
                cx23885_wakeup(port, &port->mpegq, count);
                spin_unlock(&port->slock);
 
-       } else if (status & VID_BC_MSK_RISCI2) {
-
-               dprintk(7, " (RISCI2            0x%08x)\n", VID_BC_MSK_RISCI2);
-
-               spin_lock(&port->slock);
-               cx23885_restart_queue(port, &port->mpegq);
-               spin_unlock(&port->slock);
-
        }
        if (status) {
                cx_write(port->reg_ts_int_stat, status);
@@ -2087,6 +1950,7 @@ static int cx23885_initdev(struct pci_dev *pci_dev,
                           const struct pci_device_id *pci_id)
 {
        struct cx23885_dev *dev;
+       struct v4l2_ctrl_handler *hdl;
        int err;
 
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -2097,6 +1961,14 @@ static int cx23885_initdev(struct pci_dev *pci_dev,
        if (err < 0)
                goto fail_free;
 
+       hdl = &dev->ctrl_handler;
+       v4l2_ctrl_handler_init(hdl, 6);
+       if (hdl->error) {
+               err = hdl->error;
+               goto fail_ctrl;
+       }
+       dev->v4l2_dev.ctrl_handler = hdl;
+
        /* Prepare to handle notifications from subdevices */
        cx23885_v4l2_dev_notify_init(dev);
 
@@ -2104,12 +1976,12 @@ static int cx23885_initdev(struct pci_dev *pci_dev,
        dev->pci = pci_dev;
        if (pci_enable_device(pci_dev)) {
                err = -EIO;
-               goto fail_unreg;
+               goto fail_ctrl;
        }
 
        if (cx23885_dev_setup(dev) < 0) {
                err = -EINVAL;
-               goto fail_unreg;
+               goto fail_ctrl;
        }
 
        /* print pci info */
@@ -2157,7 +2029,8 @@ static int cx23885_initdev(struct pci_dev *pci_dev,
 
 fail_irq:
        cx23885_dev_unregister(dev);
-fail_unreg:
+fail_ctrl:
+       v4l2_ctrl_handler_free(hdl);
        v4l2_device_unregister(&dev->v4l2_dev);
 fail_free:
        kfree(dev);
@@ -2180,6 +2053,7 @@ static void cx23885_finidev(struct pci_dev *pci_dev)
        free_irq(pci_dev->irq, dev);
 
        cx23885_dev_unregister(dev);
+       v4l2_ctrl_handler_free(&dev->ctrl_handler);
        v4l2_device_unregister(v4l2_dev);
        kfree(dev);
 }
index 968fecc32f9cad35413945e0af9f7ce9edbde5bb..13734b8c791724e8a84dc27cd5949aeaf6a3b1ca 100644 (file)
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
 #include "a8293.h"
 #include "mb86a20s.h"
 #include "si2165.h"
+#include "si2168.h"
+#include "si2157.h"
+#include "m88ds3103.h"
+#include "m88ts2022.h"
 
 static unsigned int debug;
 
@@ -91,59 +91,95 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 /* ------------------------------------------------------------------ */
 
-static int dvb_buf_setup(struct videobuf_queue *q,
-                        unsigned int *count, unsigned int *size)
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *num_buffers, unsigned int *num_planes,
+                          unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct cx23885_tsport *port = q->priv_data;
+       struct cx23885_tsport *port = q->drv_priv;
 
        port->ts_packet_size  = 188 * 4;
        port->ts_packet_count = 32;
-
-       *size  = port->ts_packet_size * port->ts_packet_count;
-       *count = 32;
+       *num_planes = 1;
+       sizes[0] = port->ts_packet_size * port->ts_packet_count;
+       *num_buffers = 32;
        return 0;
 }
 
-static int dvb_buf_prepare(struct videobuf_queue *q,
-                          struct videobuf_buffer *vb, enum v4l2_field field)
+
+static int buffer_prepare(struct vb2_buffer *vb)
 {
-       struct cx23885_tsport *port = q->priv_data;
-       return cx23885_buf_prepare(q, port, (struct cx23885_buffer *)vb, field);
+       struct cx23885_tsport *port = vb->vb2_queue->drv_priv;
+       struct cx23885_buffer *buf =
+               container_of(vb, struct cx23885_buffer, vb);
+
+       return cx23885_buf_prepare(buf, port);
 }
 
-static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
 {
-       struct cx23885_tsport *port = q->priv_data;
-       cx23885_buf_queue(port, (struct cx23885_buffer *)vb);
+       struct cx23885_tsport *port = vb->vb2_queue->drv_priv;
+       struct cx23885_dev *dev = port->dev;
+       struct cx23885_buffer *buf = container_of(vb,
+               struct cx23885_buffer, vb);
+       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
+
+       cx23885_free_buffer(dev, buf);
+
+       dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
 }
 
-static void dvb_buf_release(struct videobuf_queue *q,
-                           struct videobuf_buffer *vb)
+static void buffer_queue(struct vb2_buffer *vb)
 {
-       cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
+       struct cx23885_tsport *port = vb->vb2_queue->drv_priv;
+       struct cx23885_buffer   *buf = container_of(vb,
+               struct cx23885_buffer, vb);
+
+       cx23885_buf_queue(port, buf);
 }
 
 static void cx23885_dvb_gate_ctrl(struct cx23885_tsport  *port, int open)
 {
-       struct videobuf_dvb_frontends *f;
-       struct videobuf_dvb_frontend *fe;
+       struct vb2_dvb_frontends *f;
+       struct vb2_dvb_frontend *fe;
 
        f = &port->frontends;
 
        if (f->gate <= 1) /* undefined or fe0 */
-               fe = videobuf_dvb_get_frontend(f, 1);
+               fe = vb2_dvb_get_frontend(f, 1);
        else
-               fe = videobuf_dvb_get_frontend(f, f->gate);
+               fe = vb2_dvb_get_frontend(f, f->gate);
 
        if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
                fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
 }
 
-static struct videobuf_queue_ops dvb_qops = {
-       .buf_setup    = dvb_buf_setup,
-       .buf_prepare  = dvb_buf_prepare,
-       .buf_queue    = dvb_buf_queue,
-       .buf_release  = dvb_buf_release,
+static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct cx23885_tsport *port = q->drv_priv;
+       struct cx23885_dmaqueue *dmaq = &port->mpegq;
+       struct cx23885_buffer *buf = list_entry(dmaq->active.next,
+                       struct cx23885_buffer, queue);
+
+       cx23885_start_dma(port, dmaq, buf);
+       return 0;
+}
+
+static void cx23885_stop_streaming(struct vb2_queue *q)
+{
+       struct cx23885_tsport *port = q->drv_priv;
+
+       cx23885_cancel_buffers(port);
+}
+
+static struct vb2_ops dvb_qops = {
+       .queue_setup    = queue_setup,
+       .buf_prepare  = buffer_prepare,
+       .buf_finish = buffer_finish,
+       .buf_queue    = buffer_queue,
+       .wait_prepare = vb2_ops_wait_prepare,
+       .wait_finish = vb2_ops_wait_finish,
+       .start_streaming = cx23885_start_streaming,
+       .stop_streaming = cx23885_stop_streaming,
 };
 
 static struct s5h1409_config hauppauge_generic_config = {
@@ -551,6 +587,35 @@ static int p8000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
        return 0;
 }
 
+static int dvbsky_t9580_set_voltage(struct dvb_frontend *fe,
+                                       fe_sec_voltage_t voltage)
+{
+       struct cx23885_tsport *port = fe->dvb->priv;
+       struct cx23885_dev *dev = port->dev;
+
+       cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1);
+
+       switch (voltage) {
+       case SEC_VOLTAGE_13:
+               cx23885_gpio_set(dev, GPIO_1);
+               cx23885_gpio_clear(dev, GPIO_0);
+               break;
+       case SEC_VOLTAGE_18:
+               cx23885_gpio_set(dev, GPIO_1);
+               cx23885_gpio_set(dev, GPIO_0);
+               break;
+       case SEC_VOLTAGE_OFF:
+               cx23885_gpio_clear(dev, GPIO_1);
+               cx23885_gpio_clear(dev, GPIO_0);
+               break;
+       }
+
+       /* call the frontend set_voltage function */
+       port->fe_set_voltage(fe, voltage);
+
+       return 0;
+}
+
 static int cx23885_dvb_set_frontend(struct dvb_frontend *fe)
 {
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
@@ -715,6 +780,19 @@ static const struct si2165_config hauppauge_hvr4400_si2165_config = {
        .ref_freq_Hz    = 16000000,
 };
 
+static const struct m88ds3103_config dvbsky_t9580_m88ds3103_config = {
+       .i2c_addr = 0x68,
+       .clock = 27000000,
+       .i2c_wr_max = 33,
+       .clock_out = 0,
+       .ts_mode = M88DS3103_TS_PARALLEL,
+       .ts_clk = 16000,
+       .ts_clk_pol = 1,
+       .lnb_en_pol = 1,
+       .lnb_hv_pol = 0,
+       .agc = 0x99,
+};
+
 static int netup_altera_fpga_rw(void *device, int flag, int data, int read)
 {
        struct cx23885_dev *dev = (struct cx23885_dev *)device;
@@ -863,16 +941,23 @@ static int dvb_register(struct cx23885_tsport *port)
        struct dib7000p_ops dib7000p_ops;
        struct cx23885_dev *dev = port->dev;
        struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL;
-       struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+       struct vb2_dvb_frontend *fe0, *fe1 = NULL;
+       struct si2168_config si2168_config;
+       struct si2157_config si2157_config;
+       struct m88ts2022_config m88ts2022_config;
+       struct i2c_board_info info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *client_demod;
+       struct i2c_client *client_tuner;
        int mfe_shared = 0; /* bus not shared by default */
        int ret;
 
        /* Get the first frontend */
-       fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
+       fe0 = vb2_dvb_get_frontend(&port->frontends, 1);
        if (!fe0)
                return -EINVAL;
 
-       /* init struct videobuf_dvb */
+       /* init struct vb2_dvb */
        fe0->dvb.name = dev->name;
 
        /* multi-frontend gate control is undefined or defaults to fe0 */
@@ -1392,7 +1477,7 @@ static int dvb_register(struct cx23885_tsport *port)
                        fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend);
                }
                /* MFE frontend 2 */
-               fe1 = videobuf_dvb_get_frontend(&port->frontends, 2);
+               fe1 = vb2_dvb_get_frontend(&port->frontends, 2);
                if (fe1 == NULL)
                        goto frontend_detach;
                /* DVB-C init */
@@ -1491,7 +1576,7 @@ static int dvb_register(struct cx23885_tsport *port)
                                        &hauppauge_hvr4400_si2165_config,
                                        &i2c_bus->i2c_adap);
                        if (fe0->dvb.frontend != NULL) {
-                               fe0->dvb.frontend->ops.i2c_gate_ctrl = 0;
+                               fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
                                if (!dvb_attach(tda18271_attach,
                                                fe0->dvb.frontend,
                                                0x60, &i2c_bus2->i2c_adap,
@@ -1501,6 +1586,97 @@ static int dvb_register(struct cx23885_tsport *port)
                        break;
                }
                break;
+       case CX23885_BOARD_DVBSKY_T9580:
+               i2c_bus = &dev->i2c_bus[0];
+               i2c_bus2 = &dev->i2c_bus[1];
+               switch (port->nr) {
+               /* port b - satellite */
+               case 1:
+                       /* attach frontend */
+                       fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+                                       &dvbsky_t9580_m88ds3103_config,
+                                       &i2c_bus2->i2c_adap, &adapter);
+                       if (fe0->dvb.frontend == NULL)
+                               break;
+
+                       /* attach tuner */
+                       m88ts2022_config.fe = fe0->dvb.frontend;
+                       m88ts2022_config.clock = 27000000;
+                       memset(&info, 0, sizeof(struct i2c_board_info));
+                       strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+                       info.addr = 0x60;
+                       info.platform_data = &m88ts2022_config;
+                       request_module(info.type);
+                       client_tuner = i2c_new_device(adapter, &info);
+                       if (client_tuner == NULL ||
+                                       client_tuner->dev.driver == NULL)
+                               goto frontend_detach;
+                       if (!try_module_get(client_tuner->dev.driver->owner)) {
+                               i2c_unregister_device(client_tuner);
+                               goto frontend_detach;
+                       }
+
+                       /* delegate signal strength measurement to tuner */
+                       fe0->dvb.frontend->ops.read_signal_strength =
+                               fe0->dvb.frontend->ops.tuner_ops.get_rf_strength;
+
+                       /*
+                        * for setting the voltage we need to set GPIOs on
+                        * the card.
+                        */
+                       port->fe_set_voltage =
+                               fe0->dvb.frontend->ops.set_voltage;
+                       fe0->dvb.frontend->ops.set_voltage =
+                               dvbsky_t9580_set_voltage;
+
+                       port->i2c_client_tuner = client_tuner;
+
+                       break;
+               /* port c - terrestrial/cable */
+               case 2:
+                       /* attach frontend */
+                       si2168_config.i2c_adapter = &adapter;
+                       si2168_config.fe = &fe0->dvb.frontend;
+                       si2168_config.ts_mode = SI2168_TS_SERIAL;
+                       memset(&info, 0, sizeof(struct i2c_board_info));
+                       strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+                       info.addr = 0x64;
+                       info.platform_data = &si2168_config;
+                       request_module(info.type);
+                       client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info);
+                       if (client_demod == NULL ||
+                                       client_demod->dev.driver == NULL)
+                               goto frontend_detach;
+                       if (!try_module_get(client_demod->dev.driver->owner)) {
+                               i2c_unregister_device(client_demod);
+                               goto frontend_detach;
+                       }
+                       port->i2c_client_demod = client_demod;
+
+                       /* attach tuner */
+                       si2157_config.fe = fe0->dvb.frontend;
+                       memset(&info, 0, sizeof(struct i2c_board_info));
+                       strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+                       info.addr = 0x60;
+                       info.platform_data = &si2157_config;
+                       request_module(info.type);
+                       client_tuner = i2c_new_device(adapter, &info);
+                       if (client_tuner == NULL ||
+                                       client_tuner->dev.driver == NULL) {
+                               module_put(client_demod->dev.driver->owner);
+                               i2c_unregister_device(client_demod);
+                               goto frontend_detach;
+                       }
+                       if (!try_module_get(client_tuner->dev.driver->owner)) {
+                               i2c_unregister_device(client_tuner);
+                               module_put(client_demod->dev.driver->owner);
+                               i2c_unregister_device(client_demod);
+                               goto frontend_detach;
+                       }
+                       port->i2c_client_tuner = client_tuner;
+                       break;
+               }
+               break;
        default:
                printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
                        " isn't supported yet\n",
@@ -1532,7 +1708,7 @@ static int dvb_register(struct cx23885_tsport *port)
                fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
 
        /* register everything */
-       ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
+       ret = vb2_dvb_register_bus(&port->frontends, THIS_MODULE, port,
                                        &dev->pci->dev, adapter_nr, mfe_shared);
        if (ret)
                goto frontend_detach;
@@ -1575,20 +1751,36 @@ static int dvb_register(struct cx23885_tsport *port)
                memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
                break;
                }
+       case CX23885_BOARD_DVBSKY_T9580: {
+               u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+               if (port->nr > 2)
+                       break;
+
+               /* Read entire EEPROM */
+               dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+               tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom,
+                               sizeof(eeprom));
+               printk(KERN_INFO "DVBSky T9580 port %d MAC address: %pM\n",
+                       port->nr, eeprom + 0xc0 + (port->nr-1) * 8);
+               memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 +
+                       (port->nr-1) * 8, 6);
+               break;
+               }
        }
 
        return ret;
 
 frontend_detach:
        port->gate_ctrl = NULL;
-       videobuf_dvb_dealloc_frontends(&port->frontends);
+       vb2_dvb_dealloc_frontends(&port->frontends);
        return -EINVAL;
 }
 
 int cx23885_dvb_register(struct cx23885_tsport *port)
 {
 
-       struct videobuf_dvb_frontend *fe0;
+       struct vb2_dvb_frontend *fe0;
        struct cx23885_dev *dev = port->dev;
        int err, i;
 
@@ -1605,13 +1797,15 @@ int cx23885_dvb_register(struct cx23885_tsport *port)
                port->num_frontends);
 
        for (i = 1; i <= port->num_frontends; i++) {
-               if (videobuf_dvb_alloc_frontend(
+               struct vb2_queue *q;
+
+               if (vb2_dvb_alloc_frontend(
                        &port->frontends, i) == NULL) {
                        printk(KERN_ERR "%s() failed to alloc\n", __func__);
                        return -ENOMEM;
                }
 
-               fe0 = videobuf_dvb_get_frontend(&port->frontends, i);
+               fe0 = vb2_dvb_get_frontend(&port->frontends, i);
                if (!fe0)
                        err = -EINVAL;
 
@@ -1627,10 +1821,21 @@ int cx23885_dvb_register(struct cx23885_tsport *port)
                /* dvb stuff */
                /* We have to init the queue for each frontend on a port. */
                printk(KERN_INFO "%s: cx23885 based dvb card\n", dev->name);
-               videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops,
-                           &dev->pci->dev, &port->slock,
-                           V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
-                           sizeof(struct cx23885_buffer), port, NULL);
+               q = &fe0->dvb.dvbq;
+               q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+               q->gfp_flags = GFP_DMA32;
+               q->min_buffers_needed = 2;
+               q->drv_priv = port;
+               q->buf_struct_size = sizeof(struct cx23885_buffer);
+               q->ops = &dvb_qops;
+               q->mem_ops = &vb2_dma_sg_memops;
+               q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+               q->lock = &dev->lock;
+
+               err = vb2_queue_init(q);
+               if (err < 0)
+                       return err;
        }
        err = dvb_register(port);
        if (err != 0)
@@ -1642,18 +1847,27 @@ int cx23885_dvb_register(struct cx23885_tsport *port)
 
 int cx23885_dvb_unregister(struct cx23885_tsport *port)
 {
-       struct videobuf_dvb_frontend *fe0;
-
-       /* FIXME: in an error condition where the we have
-        * an expected number of frontends (attach problem)
-        * then this might not clean up correctly, if 1
-        * is invalid.
-        * This comment only applies to future boards IF they
-        * implement MFE support.
-        */
-       fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
+       struct vb2_dvb_frontend *fe0;
+       struct i2c_client *client;
+
+       /* remove I2C client for tuner */
+       client = port->i2c_client_tuner;
+       if (client) {
+               module_put(client->dev.driver->owner);
+               i2c_unregister_device(client);
+       }
+
+       /* remove I2C client for demodulator */
+       client = port->i2c_client_demod;
+       if (client) {
+               module_put(client->dev.driver->owner);
+               i2c_unregister_device(client);
+       }
+
+       fe0 = vb2_dvb_get_frontend(&port->frontends, 1);
+
        if (fe0 && fe0->dvb.frontend)
-               videobuf_dvb_unregister_bus(&port->frontends);
+               vb2_dvb_unregister_bus(&port->frontends);
 
        switch (port->dev->board) {
        case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
@@ -1668,4 +1882,3 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port)
 
        return 0;
 }
-
index 5444cc526008706d1a6b7a4c87e4aa0da923a22f..6f817d8732da8ca6a19ed7f742b78d85f66c4da9 100644 (file)
  * 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"
index 4887314339cbfa9071db9a898e985fae2495a9c8..fd71306af6e2f8facfc64964a8c05eb88fe3c146 100644 (file)
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
@@ -386,11 +382,3 @@ void cx23885_av_clk(struct cx23885_dev *dev, int enable)
 
        i2c_xfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
 }
-
-/* ----------------------------------------------------------------------- */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index 1940c18e186cfbb6d0fbac1b579bdee75a040231..9d37fe6616913f268cd52499f3c7d089831aeb1c 100644 (file)
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- *  02110-1301, USA.
  */
 
 #include <linux/slab.h>
index 87dc44e69977e2d38fb9c168711a0bf04c54f671..6199c7e86e83133779d1c07f7055599aab4a21de 100644 (file)
  *  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.
  */
 
 #ifndef _CX23885_INPUT_H_
index 271d69d1ca8c7c0a4c0796151353e97c4961ab3e..d2cdd40f79f54611b9b6aa46ad7a0d5a9b00d9f0 100644 (file)
  *  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"
@@ -28,7 +24,7 @@
 int cx23885_g_chip_info(struct file *file, void *fh,
                         struct v4l2_dbg_chip_info *chip)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        if (chip->match.addr > 1)
                return -EINVAL;
@@ -64,7 +60,7 @@ static int cx23417_g_register(struct cx23885_dev *dev,
 int cx23885_g_register(struct file *file, void *fh,
                       struct v4l2_dbg_register *reg)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        if (reg->match.addr > 1)
                return -EINVAL;
@@ -96,7 +92,7 @@ static int cx23417_s_register(struct cx23885_dev *dev,
 int cx23885_s_register(struct file *file, void *fh,
                       const struct v4l2_dbg_register *reg)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        if (reg->match.addr > 1)
                return -EINVAL;
index 92d9f077436679d0402b416a70384e9775923f96..cc5dbb6c1afccc54cc43deaf2db4c1abd4b1d56a 100644 (file)
  *  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 _CX23885_IOCTL_H_
index bfef193592916dd46ed734dac478ac03e1eab653..89dc4cc3e1cea427d3aef91201548e38e91e9cda 100644 (file)
  *  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 <media/v4l2-device.h>
index 0c9d8bda9e2895d1640c96c6b89e16cb95f499ec..8e93d1f10ae0d89398e6c5b9421d332ddec55c83 100644 (file)
  *  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.
  */
 
 #ifndef _CX23885_IR_H_
index a99936e0cbc270c5d9fe5ad29eeafd0101c4304d..2d3cbafe24023420162a9a947234e0924aa7aaf7 100644 (file)
  *  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 _CX23885_REG_H_
index a1154f035bc185daa692f6ac5443a5067ff46169..a7c6ef8f3ea3a7f1e96ce9a7642e3056ea27543c 100644 (file)
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/kernel.h>
@@ -42,33 +38,32 @@ MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
 /* ------------------------------------------------------------------ */
 
 #define VBI_LINE_LENGTH 1440
-#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 VBI_NTSC_LINE_COUNT 12
+#define VBI_PAL_LINE_COUNT 18
 
 
 int cx23885_vbi_fmt(struct file *file, void *priv,
        struct v4l2_format *f)
 {
-       struct cx23885_fh *fh = priv;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
+       f->fmt.vbi.sampling_rate = 27000000;
+       f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+       f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+       f->fmt.vbi.offset = 0;
+       f->fmt.vbi.flags = 0;
        if (dev->tvnorm & V4L2_STD_525_60) {
                /* ntsc */
-               f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
-               f->fmt.vbi.sampling_rate = 27000000;
-               f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
-               f->fmt.vbi.offset = 0;
-               f->fmt.vbi.flags = 0;
-               f->fmt.vbi.start[0] = 10;
-               f->fmt.vbi.count[0] = 17;
-               f->fmt.vbi.start[1] = 263 + 10 + 1;
-               f->fmt.vbi.count[1] = 17;
+               f->fmt.vbi.start[0] = V4L2_VBI_ITU_525_F1_START + 9;
+               f->fmt.vbi.start[1] = V4L2_VBI_ITU_525_F2_START + 9;
+               f->fmt.vbi.count[0] = VBI_NTSC_LINE_COUNT;
+               f->fmt.vbi.count[1] = VBI_NTSC_LINE_COUNT;
        } else if (dev->tvnorm & V4L2_STD_625_50) {
                /* pal */
-               f->fmt.vbi.sampling_rate = 35468950;
-               f->fmt.vbi.start[0] = 7 - 1;
-               f->fmt.vbi.start[1] = 319 - 1;
+               f->fmt.vbi.start[0] = V4L2_VBI_ITU_625_F1_START + 5;
+               f->fmt.vbi.start[1] = V4L2_VBI_ITU_625_F2_START + 5;
+               f->fmt.vbi.count[0] = VBI_PAL_LINE_COUNT;
+               f->fmt.vbi.count[1] = VBI_PAL_LINE_COUNT;
        }
 
        return 0;
@@ -94,15 +89,6 @@ int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status)
                handled++;
        }
 
-       if (status & VID_BC_MSK_VBI_RISCI2) {
-               dprintk(1, "%s() VID_BC_MSK_VBI_RISCI2\n", __func__);
-               dprintk(2, "stopper vbi\n");
-               spin_lock(&dev->slock);
-               cx23885_restart_vbi_queue(dev, &dev->vbiq);
-               spin_unlock(&dev->slock);
-               handled++;
-       }
-
        return handled;
 }
 
@@ -114,13 +100,13 @@ static int cx23885_start_vbi_dma(struct cx23885_dev    *dev,
 
        /* setup fifo + format */
        cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02],
-                               buf->vb.width, buf->risc.dma);
+                               VBI_LINE_LENGTH, buf->risc.dma);
 
        /* reset counter */
        cx_write(VID_A_GPCNT_CTL, 3);
        cx_write(VID_A_VBI_CTRL, 3);
        cx_write(VBI_A_GPCNT_CTL, 3);
-       q->count = 1;
+       q->count = 0;
 
        /* enable irq */
        cx23885_irq_add_enable(dev, 0x01);
@@ -133,163 +119,153 @@ static int cx23885_start_vbi_dma(struct cx23885_dev    *dev,
        return 0;
 }
 
+/* ------------------------------------------------------------------ */
 
-int cx23885_restart_vbi_queue(struct cx23885_dev    *dev,
-                            struct cx23885_dmaqueue *q)
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *num_buffers, unsigned int *num_planes,
+                          unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct cx23885_buffer *buf;
-       struct list_head *item;
-
-       if (list_empty(&q->active))
-               return 0;
-
-       buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue);
-       dprintk(2, "restart_queue [%p/%d]: restart dma\n",
-               buf, buf->vb.i);
-       cx23885_start_vbi_dma(dev, q, buf);
-       list_for_each(item, &q->active) {
-               buf = list_entry(item, struct cx23885_buffer, vb.queue);
-               buf->count = q->count++;
-       }
-       mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30));
+       struct cx23885_dev *dev = q->drv_priv;
+       unsigned lines = VBI_PAL_LINE_COUNT;
+
+       if (dev->tvnorm & V4L2_STD_525_60)
+               lines = VBI_NTSC_LINE_COUNT;
+       *num_planes = 1;
+       sizes[0] = lines * VBI_LINE_LENGTH * 2;
        return 0;
 }
 
-void cx23885_vbi_timeout(unsigned long data)
+static int buffer_prepare(struct vb2_buffer *vb)
 {
-       struct cx23885_dev *dev = (struct cx23885_dev *)data;
-       struct cx23885_dmaqueue *q = &dev->vbiq;
-       struct cx23885_buffer *buf;
-       unsigned long flags;
+       struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
+       struct cx23885_buffer *buf = container_of(vb,
+               struct cx23885_buffer, vb);
+       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
+       unsigned lines = VBI_PAL_LINE_COUNT;
+       int ret;
 
-       /* Stop the VBI engine */
-       cx_clear(VID_A_DMA_CTL, 0x22);
+       if (dev->tvnorm & V4L2_STD_525_60)
+               lines = VBI_NTSC_LINE_COUNT;
 
-       spin_lock_irqsave(&dev->slock, flags);
-       while (!list_empty(&q->active)) {
-               buf = list_entry(q->active.next, struct cx23885_buffer,
-                       vb.queue);
-               list_del(&buf->vb.queue);
-               buf->vb.state = VIDEOBUF_ERROR;
-               wake_up(&buf->vb.done);
-               printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->name,
-                      buf, buf->vb.i, (unsigned long)buf->risc.dma);
-       }
-       cx23885_restart_vbi_queue(dev, q);
-       spin_unlock_irqrestore(&dev->slock, flags);
-}
+       if (vb2_plane_size(vb, 0) < lines * VBI_LINE_LENGTH * 2)
+               return -EINVAL;
+       vb2_set_plane_payload(vb, 0, lines * VBI_LINE_LENGTH * 2);
 
-/* ------------------------------------------------------------------ */
-#define VBI_LINE_LENGTH 1440
-#define VBI_LINE_COUNT 17
+       ret = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
+       if (!ret)
+               return -EIO;
 
-static int
-vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
-{
-       *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
-       if (0 == *count)
-               *count = vbibufs;
-       if (*count < 2)
-               *count = 2;
-       if (*count > 32)
-               *count = 32;
+       cx23885_risc_vbibuffer(dev->pci, &buf->risc,
+                        sgt->sgl,
+                        0, VBI_LINE_LENGTH * lines,
+                        VBI_LINE_LENGTH, 0,
+                        lines);
        return 0;
 }
 
-static int
-vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
-           enum v4l2_field field)
+static void buffer_finish(struct vb2_buffer *vb)
 {
-       struct cx23885_fh *fh  = q->priv_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
        struct cx23885_buffer *buf = container_of(vb,
                struct cx23885_buffer, vb);
-       struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
-       unsigned int size;
-       int rc;
-
-       size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
-       if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
-               return -EINVAL;
+       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
 
-       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-               buf->vb.width  = VBI_LINE_LENGTH;
-               buf->vb.height = VBI_LINE_COUNT;
-               buf->vb.size   = size;
-               buf->vb.field  = V4L2_FIELD_SEQ_TB;
-
-               rc = videobuf_iolock(q, &buf->vb, NULL);
-               if (0 != rc)
-                       goto fail;
-               cx23885_risc_vbibuffer(dev->pci, &buf->risc,
-                                dma->sglist,
-                                0, buf->vb.width * buf->vb.height,
-                                buf->vb.width, 0,
-                                buf->vb.height);
-       }
-       buf->vb.state = VIDEOBUF_PREPARED;
-       return 0;
+       cx23885_free_buffer(vb->vb2_queue->drv_priv, buf);
 
- fail:
-       cx23885_free_buffer(q, buf);
-       return rc;
+       dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
 }
 
-static void
-vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+/*
+ * The risc program for each buffer works as follows: it starts with a simple
+ * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the
+ * buffer follows and at the end we have a JUMP back to the start + 12 (skipping
+ * the initial JUMP).
+ *
+ * This is the risc program of the first buffer to be queued if the active list
+ * is empty and it just keeps DMAing this buffer without generating any
+ * interrupts.
+ *
+ * If a new buffer is added then the initial JUMP in the code for that buffer
+ * will generate an interrupt which signals that the previous buffer has been
+ * DMAed successfully and that it can be returned to userspace.
+ *
+ * It also sets the final jump of the previous buffer to the start of the new
+ * buffer, thus chaining the new buffer into the DMA chain. This is a single
+ * atomic u32 write, so there is no race condition.
+ *
+ * The end-result of all this that you only get an interrupt when a buffer
+ * is ready, so the control flow is very easy.
+ */
+static void buffer_queue(struct vb2_buffer *vb)
 {
-       struct cx23885_buffer   *buf =
-               container_of(vb, struct cx23885_buffer, vb);
-       struct cx23885_buffer   *prev;
-       struct cx23885_fh       *fh   = vq->priv_data;
-       struct cx23885_dev      *dev  = fh->dev;
-       struct cx23885_dmaqueue *q    = &dev->vbiq;
-
-       /* add jump to stopper */
-       buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
-       buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+       struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
+       struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb);
+       struct cx23885_buffer *prev;
+       struct cx23885_dmaqueue *q = &dev->vbiq;
+       unsigned long flags;
+
+       buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12);
+       buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
+       buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12);
        buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
 
        if (list_empty(&q->active)) {
-               list_add_tail(&buf->vb.queue, &q->active);
-               cx23885_start_vbi_dma(dev, q, buf);
-               buf->vb.state = VIDEOBUF_ACTIVE;
-               buf->count    = q->count++;
-               mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30));
+               spin_lock_irqsave(&dev->slock, flags);
+               list_add_tail(&buf->queue, &q->active);
+               spin_unlock_irqrestore(&dev->slock, flags);
                dprintk(2, "[%p/%d] vbi_queue - first active\n",
-                       buf, buf->vb.i);
+                       buf, buf->vb.v4l2_buf.index);
 
        } else {
+               buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
                prev = list_entry(q->active.prev, struct cx23885_buffer,
-                       vb.queue);
-               list_add_tail(&buf->vb.queue, &q->active);
-               buf->vb.state = VIDEOBUF_ACTIVE;
-               buf->count    = q->count++;
+                       queue);
+               spin_lock_irqsave(&dev->slock, flags);
+               list_add_tail(&buf->queue, &q->active);
+               spin_unlock_irqrestore(&dev->slock, flags);
                prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-               prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63-32 */
                dprintk(2, "[%p/%d] buffer_queue - append to active\n",
-                       buf, buf->vb.i);
+                       buf, buf->vb.v4l2_buf.index);
        }
 }
 
-static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count)
 {
-       struct cx23885_buffer *buf =
-               container_of(vb, struct cx23885_buffer, vb);
+       struct cx23885_dev *dev = q->drv_priv;
+       struct cx23885_dmaqueue *dmaq = &dev->vbiq;
+       struct cx23885_buffer *buf = list_entry(dmaq->active.next,
+                       struct cx23885_buffer, queue);
 
-       cx23885_free_buffer(q, buf);
+       cx23885_start_vbi_dma(dev, dmaq, buf);
+       return 0;
 }
 
-struct videobuf_queue_ops cx23885_vbi_qops = {
-       .buf_setup    = vbi_setup,
-       .buf_prepare  = vbi_prepare,
-       .buf_queue    = vbi_queue,
-       .buf_release  = vbi_release,
-};
+static void cx23885_stop_streaming(struct vb2_queue *q)
+{
+       struct cx23885_dev *dev = q->drv_priv;
+       struct cx23885_dmaqueue *dmaq = &dev->vbiq;
+       unsigned long flags;
 
-/* ------------------------------------------------------------------ */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+       cx_clear(VID_A_DMA_CTL, 0x22); /* FIFO and RISC enable */
+       spin_lock_irqsave(&dev->slock, flags);
+       while (!list_empty(&dmaq->active)) {
+               struct cx23885_buffer *buf = list_entry(dmaq->active.next,
+                       struct cx23885_buffer, queue);
+
+               list_del(&buf->queue);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+       }
+       spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+
+struct vb2_ops cx23885_vbi_qops = {
+       .queue_setup    = queue_setup,
+       .buf_prepare  = buffer_prepare,
+       .buf_finish = buffer_finish,
+       .buf_queue    = buffer_queue,
+       .wait_prepare = vb2_ops_wait_prepare,
+       .wait_finish = vb2_ops_wait_finish,
+       .start_streaming = cx23885_start_streaming,
+       .stop_streaming = cx23885_stop_streaming,
+};
index 91e4cb457296ccdef2b1b777e6f8594341a71a1d..682a4f95df6bd09f062d0c58e7ac3185b158db6f 100644 (file)
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/init.h>
@@ -35,6 +31,7 @@
 #include "cx23885-video.h"
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
 #include "cx23885-ioctl.h"
 #include "tuner-xc2028.h"
 
@@ -48,15 +45,12 @@ MODULE_LICENSE("GPL");
 
 static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
 static unsigned int vbi_nr[]   = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
-static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
 
 module_param_array(video_nr, int, NULL, 0444);
 module_param_array(vbi_nr,   int, NULL, 0444);
-module_param_array(radio_nr, int, NULL, 0444);
 
 MODULE_PARM_DESC(video_nr, "video device numbers");
 MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
-MODULE_PARM_DESC(radio_nr, "radio device numbers");
 
 static unsigned int video_debug;
 module_param(video_debug, int, 0644);
@@ -79,77 +73,14 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
 /* static data                                                         */
 
 #define FORMAT_FLAGS_PACKED       0x01
-#if 0
-static struct cx23885_fmt formats[] = {
-       {
-               .name     = "8 bpp, gray",
-               .fourcc   = V4L2_PIX_FMT_GREY,
-               .depth    = 8,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-               .name     = "15 bpp RGB, le",
-               .fourcc   = V4L2_PIX_FMT_RGB555,
-               .depth    = 16,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-               .name     = "15 bpp RGB, be",
-               .fourcc   = V4L2_PIX_FMT_RGB555X,
-               .depth    = 16,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-               .name     = "16 bpp RGB, le",
-               .fourcc   = V4L2_PIX_FMT_RGB565,
-               .depth    = 16,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-               .name     = "16 bpp RGB, be",
-               .fourcc   = V4L2_PIX_FMT_RGB565X,
-               .depth    = 16,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-               .name     = "24 bpp RGB, le",
-               .fourcc   = V4L2_PIX_FMT_BGR24,
-               .depth    = 24,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-               .name     = "32 bpp RGB, le",
-               .fourcc   = V4L2_PIX_FMT_BGR32,
-               .depth    = 32,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-               .name     = "32 bpp RGB, be",
-               .fourcc   = V4L2_PIX_FMT_RGB32,
-               .depth    = 32,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-               .name     = "4:2:2, packed, YUYV",
-               .fourcc   = V4L2_PIX_FMT_YUYV,
-               .depth    = 16,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-               .name     = "4:2:2, packed, UYVY",
-               .fourcc   = V4L2_PIX_FMT_UYVY,
-               .depth    = 16,
-               .flags    = FORMAT_FLAGS_PACKED,
-       },
-};
-#else
 static struct cx23885_fmt formats[] = {
        {
-#if 0
-               .name     = "4:2:2, packed, UYVY",
-               .fourcc   = V4L2_PIX_FMT_UYVY,
-               .depth    = 16,
-               .flags    = FORMAT_FLAGS_PACKED,
-       }, {
-#endif
                .name     = "4:2:2, packed, YUYV",
                .fourcc   = V4L2_PIX_FMT_YUYV,
                .depth    = 16,
                .flags    = FORMAT_FLAGS_PACKED,
        }
 };
-#endif
 
 static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
 {
@@ -158,163 +89,27 @@ static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
        for (i = 0; i < ARRAY_SIZE(formats); i++)
                if (formats[i].fourcc == fourcc)
                        return formats+i;
-
-       printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__,
-               (fourcc & 0xff),
-               ((fourcc >> 8) & 0xff),
-               ((fourcc >> 16) & 0xff),
-               ((fourcc >> 24) & 0xff)
-               );
        return NULL;
 }
 
 /* ------------------------------------------------------------------- */
 
-static const struct v4l2_queryctrl no_ctl = {
-       .name  = "42",
-       .flags = V4L2_CTRL_FLAG_DISABLED,
-};
-
-static struct cx23885_ctrl cx23885_ctls[] = {
-       /* --- video --- */
-       {
-               .v = {
-                       .id            = V4L2_CID_BRIGHTNESS,
-                       .name          = "Brightness",
-                       .minimum       = 0x00,
-                       .maximum       = 0xff,
-                       .step          = 1,
-                       .default_value = 0x7f,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .off                   = 128,
-               .reg                   = LUMA_CTRL,
-               .mask                  = 0x00ff,
-               .shift                 = 0,
-       }, {
-               .v = {
-                       .id            = V4L2_CID_CONTRAST,
-                       .name          = "Contrast",
-                       .minimum       = 0,
-                       .maximum       = 0x7f,
-                       .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       = -127,
-                       .maximum       = 128,
-                       .step          = 1,
-                       .default_value = 0x0,
-                       .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       = 0x7f,
-                       .step          = 1,
-                       .default_value = 0x3f,
-                       .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       = 65535,
-                       .step          = 65535 / 100,
-                       .default_value = 65535,
-                       .type          = V4L2_CTRL_TYPE_INTEGER,
-               },
-               .reg                   = PATH1_VOL_CTL,
-               .mask                  = 0xff,
-               .shift                 = 0,
-       }
-};
-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,
-       V4L2_CID_CONTRAST,
-       V4L2_CID_SATURATION,
-       V4L2_CID_HUE,
-       V4L2_CID_AUDIO_VOLUME,
-       V4L2_CID_AUDIO_MUTE,
-       0
-};
-
-static const u32 *ctrl_classes[] = {
-       cx23885_user_ctrls,
-       NULL
-};
-
 void cx23885_video_wakeup(struct cx23885_dev *dev,
        struct cx23885_dmaqueue *q, u32 count)
 {
        struct cx23885_buffer *buf;
-       int bc;
-
-       for (bc = 0;; bc++) {
-               if (list_empty(&q->active))
-                       break;
-               buf = list_entry(q->active.next,
-                                struct cx23885_buffer, vb.queue);
-
-               /* count comes from the hw and is is 16bit wide --
-                * this trick handles wrap-arounds correctly for
-                * up to 32767 buffers in flight... */
-               if ((s16) (count - buf->count) < 0)
-                       break;
-
-               v4l2_get_timestamp(&buf->vb.ts);
-               dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
-                       count, buf->count);
-               buf->vb.state = VIDEOBUF_DONE;
-               list_del(&buf->vb.queue);
-               wake_up(&buf->vb.done);
-       }
+
        if (list_empty(&q->active))
-               del_timer(&q->timeout);
-       else
-               mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
-       if (bc != 1)
-               printk(KERN_ERR "%s: %d buffers handled (should be 1)\n",
-                       __func__, bc);
+               return;
+       buf = list_entry(q->active.next,
+                       struct cx23885_buffer, queue);
+
+       buf->vb.v4l2_buf.sequence = q->count++;
+       v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+       dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.v4l2_buf.index,
+                       count, q->count);
+       list_del(&buf->queue);
+       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
 }
 
 int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
@@ -324,6 +119,12 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
                (unsigned int)norm,
                v4l2_norm_to_name(norm));
 
+       if (dev->tvnorm != norm) {
+               if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq) ||
+                   vb2_is_busy(&dev->vb2_mpegq))
+                       return -EBUSY;
+       }
+
        dev->tvnorm = norm;
 
        call_all(dev, video, s_std, norm);
@@ -345,79 +146,13 @@ static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
        *vfd = *template;
        vfd->v4l2_dev = &dev->v4l2_dev;
        vfd->release = video_device_release;
+       vfd->lock = &dev->lock;
        snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
                 cx23885_boards[dev->board].name, type);
        video_set_drvdata(vfd, dev);
        return vfd;
 }
 
-static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl)
-{
-       int i;
-
-       if (qctrl->id < V4L2_CID_BASE ||
-           qctrl->id >= V4L2_CID_LASTP1)
-               return -EINVAL;
-       for (i = 0; i < CX23885_CTLS; i++)
-               if (cx23885_ctls[i].v.id == qctrl->id)
-                       break;
-       if (i == CX23885_CTLS) {
-               *qctrl = no_ctl;
-               return 0;
-       }
-       *qctrl = cx23885_ctls[i].v;
-       return 0;
-}
-
-/* ------------------------------------------------------------------- */
-/* resource management                                                 */
-
-static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh,
-       unsigned int bit)
-{
-       dprintk(1, "%s()\n", __func__);
-       if (fh->resources & bit)
-               /* have it already allocated */
-               return 1;
-
-       /* is it free? */
-       mutex_lock(&dev->lock);
-       if (dev->resources & bit) {
-               /* no, someone else uses it */
-               mutex_unlock(&dev->lock);
-               return 0;
-       }
-       /* it's free, grab it */
-       fh->resources  |= bit;
-       dev->resources |= bit;
-       dprintk(1, "res: get %d\n", bit);
-       mutex_unlock(&dev->lock);
-       return 1;
-}
-
-static int res_check(struct cx23885_fh *fh, unsigned int bit)
-{
-       return fh->resources & bit;
-}
-
-static int res_locked(struct cx23885_dev *dev, unsigned int bit)
-{
-       return dev->resources & bit;
-}
-
-static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
-       unsigned int bits)
-{
-       BUG_ON((fh->resources & bits) != bits);
-       dprintk(1, "%s()\n", __func__);
-
-       mutex_lock(&dev->lock);
-       fh->resources  &= ~bits;
-       dev->resources &= ~bits;
-       dprintk(1, "res: put %d\n", bits);
-       mutex_unlock(&dev->lock);
-}
-
 int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
 {
        /* 8 bit registers, 8 bit values */
@@ -567,7 +302,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev,
 
        /* reset counter */
        cx_write(VID_A_GPCNT_CTL, 3);
-       q->count = 1;
+       q->count = 0;
 
        /* enable irq */
        cx23885_irq_add_enable(dev, 0x01);
@@ -580,479 +315,206 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev,
        return 0;
 }
 
-
-static int cx23885_restart_video_queue(struct cx23885_dev *dev,
-                              struct cx23885_dmaqueue *q)
-{
-       struct cx23885_buffer *buf, *prev;
-       struct list_head *item;
-       dprintk(1, "%s()\n", __func__);
-
-       if (!list_empty(&q->active)) {
-               buf = list_entry(q->active.next, struct cx23885_buffer,
-                       vb.queue);
-               dprintk(2, "restart_queue [%p/%d]: restart dma\n",
-                       buf, buf->vb.i);
-               cx23885_start_video_dma(dev, q, buf);
-               list_for_each(item, &q->active) {
-                       buf = list_entry(item, struct cx23885_buffer,
-                               vb.queue);
-                       buf->count    = q->count++;
-               }
-               mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
-               return 0;
-       }
-
-       prev = NULL;
-       for (;;) {
-               if (list_empty(&q->queued))
-                       return 0;
-               buf = list_entry(q->queued.next, struct cx23885_buffer,
-                       vb.queue);
-               if (NULL == prev) {
-                       list_move_tail(&buf->vb.queue, &q->active);
-                       cx23885_start_video_dma(dev, q, buf);
-                       buf->vb.state = VIDEOBUF_ACTIVE;
-                       buf->count    = q->count++;
-                       mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
-                       dprintk(2, "[%p/%d] restart_queue - first active\n",
-                               buf, buf->vb.i);
-
-               } else if (prev->vb.width  == buf->vb.width  &&
-                          prev->vb.height == buf->vb.height &&
-                          prev->fmt       == buf->fmt) {
-                       list_move_tail(&buf->vb.queue, &q->active);
-                       buf->vb.state = VIDEOBUF_ACTIVE;
-                       buf->count    = q->count++;
-                       prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-                       prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
-                       dprintk(2, "[%p/%d] restart_queue - move to active\n",
-                               buf, buf->vb.i);
-               } else {
-                       return 0;
-               }
-               prev = buf;
-       }
-}
-
-static int buffer_setup(struct videobuf_queue *q, unsigned int *count,
-       unsigned int *size)
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *num_buffers, unsigned int *num_planes,
+                          unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct cx23885_fh *fh = q->priv_data;
+       struct cx23885_dev *dev = q->drv_priv;
 
-       *size = fh->fmt->depth*fh->width*fh->height >> 3;
-       if (0 == *count)
-               *count = 32;
-       if (*size * *count > vid_limit * 1024 * 1024)
-               *count = (vid_limit * 1024 * 1024) / *size;
+       *num_planes = 1;
+       sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3;
        return 0;
 }
 
-static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
-              enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb)
 {
-       struct cx23885_fh *fh  = q->priv_data;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
        struct cx23885_buffer *buf =
                container_of(vb, struct cx23885_buffer, vb);
-       int rc, init_buffer = 0;
        u32 line0_offset, line1_offset;
-       struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
        int field_tff;
+       int ret;
 
-       BUG_ON(NULL == fh->fmt);
-       if (fh->width  < 48 || fh->width  > norm_maxw(dev->tvnorm) ||
-           fh->height < 32 || fh->height > norm_maxh(dev->tvnorm))
-               return -EINVAL;
-       buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
-       if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+       buf->bpl = (dev->width * dev->fmt->depth) >> 3;
+
+       if (vb2_plane_size(vb, 0) < dev->height * buf->bpl)
                return -EINVAL;
+       vb2_set_plane_payload(vb, 0, dev->height * buf->bpl);
 
-       if (buf->fmt       != fh->fmt    ||
-           buf->vb.width  != fh->width  ||
-           buf->vb.height != fh->height ||
-           buf->vb.field  != field) {
-               buf->fmt       = fh->fmt;
-               buf->vb.width  = fh->width;
-               buf->vb.height = fh->height;
-               buf->vb.field  = field;
-               init_buffer = 1;
-       }
+       ret = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
+       if (!ret)
+               return -EIO;
 
-       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-               init_buffer = 1;
-               rc = videobuf_iolock(q, &buf->vb, NULL);
-               if (0 != rc)
-                       goto fail;
-       }
+       switch (dev->field) {
+       case V4L2_FIELD_TOP:
+               cx23885_risc_buffer(dev->pci, &buf->risc,
+                               sgt->sgl, 0, UNSET,
+                               buf->bpl, 0, dev->height);
+               break;
+       case V4L2_FIELD_BOTTOM:
+               cx23885_risc_buffer(dev->pci, &buf->risc,
+                               sgt->sgl, UNSET, 0,
+                               buf->bpl, 0, dev->height);
+               break;
+       case V4L2_FIELD_INTERLACED:
+               if (dev->tvnorm & V4L2_STD_525_60)
+                       /* NTSC or  */
+                       field_tff = 1;
+               else
+                       field_tff = 0;
+
+               if (cx23885_boards[dev->board].force_bff)
+                       /* PAL / SECAM OR 888 in NTSC MODE */
+                       field_tff = 0;
 
-       if (init_buffer) {
-               buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
-               switch (buf->vb.field) {
-               case V4L2_FIELD_TOP:
-                       cx23885_risc_buffer(dev->pci, &buf->risc,
-                                        dma->sglist, 0, UNSET,
-                                        buf->bpl, 0, buf->vb.height);
-                       break;
-               case V4L2_FIELD_BOTTOM:
-                       cx23885_risc_buffer(dev->pci, &buf->risc,
-                                        dma->sglist, UNSET, 0,
-                                        buf->bpl, 0, buf->vb.height);
-                       break;
-               case V4L2_FIELD_INTERLACED:
-                       if (dev->tvnorm & V4L2_STD_NTSC)
-                               /* NTSC or  */
-                               field_tff = 1;
-                       else
-                               field_tff = 0;
-
-                       if (cx23885_boards[dev->board].force_bff)
-                               /* PAL / SECAM OR 888 in NTSC MODE */
-                               field_tff = 0;
-
-                       if (field_tff) {
-                               /* cx25840 transmits NTSC bottom field first */
-                               dprintk(1, "%s() Creating TFF/NTSC risc\n",
+               if (field_tff) {
+                       /* cx25840 transmits NTSC bottom field first */
+                       dprintk(1, "%s() Creating TFF/NTSC risc\n",
                                        __func__);
-                               line0_offset = buf->bpl;
-                               line1_offset = 0;
-                       } else {
-                               /* All other formats are top field first */
-                               dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n",
+                       line0_offset = buf->bpl;
+                       line1_offset = 0;
+               } else {
+                       /* All other formats are top field first */
+                       dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n",
                                        __func__);
-                               line0_offset = 0;
-                               line1_offset = buf->bpl;
-                       }
-                       cx23885_risc_buffer(dev->pci, &buf->risc,
-                                       dma->sglist, line0_offset,
-                                       line1_offset,
-                                       buf->bpl, buf->bpl,
-                                       buf->vb.height >> 1);
-                       break;
-               case V4L2_FIELD_SEQ_TB:
-                       cx23885_risc_buffer(dev->pci, &buf->risc,
-                                        dma->sglist,
-                                        0, buf->bpl * (buf->vb.height >> 1),
-                                        buf->bpl, 0,
-                                        buf->vb.height >> 1);
-                       break;
-               case V4L2_FIELD_SEQ_BT:
-                       cx23885_risc_buffer(dev->pci, &buf->risc,
-                                        dma->sglist,
-                                        buf->bpl * (buf->vb.height >> 1), 0,
-                                        buf->bpl, 0,
-                                        buf->vb.height >> 1);
-                       break;
-               default:
-                       BUG();
+                       line0_offset = 0;
+                       line1_offset = buf->bpl;
                }
+               cx23885_risc_buffer(dev->pci, &buf->risc,
+                               sgt->sgl, line0_offset,
+                               line1_offset,
+                               buf->bpl, buf->bpl,
+                               dev->height >> 1);
+               break;
+       case V4L2_FIELD_SEQ_TB:
+               cx23885_risc_buffer(dev->pci, &buf->risc,
+                               sgt->sgl,
+                               0, buf->bpl * (dev->height >> 1),
+                               buf->bpl, 0,
+                               dev->height >> 1);
+               break;
+       case V4L2_FIELD_SEQ_BT:
+               cx23885_risc_buffer(dev->pci, &buf->risc,
+                               sgt->sgl,
+                               buf->bpl * (dev->height >> 1), 0,
+                               buf->bpl, 0,
+                               dev->height >> 1);
+               break;
+       default:
+               BUG();
        }
-       dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
-               buf, buf->vb.i,
-               fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
+       dprintk(2, "[%p/%d] buffer_init - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+               buf, buf->vb.v4l2_buf.index,
+               dev->width, dev->height, dev->fmt->depth, dev->fmt->name,
                (unsigned long)buf->risc.dma);
-
-       buf->vb.state = VIDEOBUF_PREPARED;
        return 0;
+}
+
+static void buffer_finish(struct vb2_buffer *vb)
+{
+       struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
+       struct cx23885_buffer *buf = container_of(vb,
+               struct cx23885_buffer, vb);
+       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
+
+       cx23885_free_buffer(vb->vb2_queue->drv_priv, buf);
 
- fail:
-       cx23885_free_buffer(q, buf);
-       return rc;
+       dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
 }
 
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+/*
+ * The risc program for each buffer works as follows: it starts with a simple
+ * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the
+ * buffer follows and at the end we have a JUMP back to the start + 12 (skipping
+ * the initial JUMP).
+ *
+ * This is the risc program of the first buffer to be queued if the active list
+ * is empty and it just keeps DMAing this buffer without generating any
+ * interrupts.
+ *
+ * If a new buffer is added then the initial JUMP in the code for that buffer
+ * will generate an interrupt which signals that the previous buffer has been
+ * DMAed successfully and that it can be returned to userspace.
+ *
+ * It also sets the final jump of the previous buffer to the start of the new
+ * buffer, thus chaining the new buffer into the DMA chain. This is a single
+ * atomic u32 write, so there is no race condition.
+ *
+ * The end-result of all this that you only get an interrupt when a buffer
+ * is ready, so the control flow is very easy.
+ */
+static void buffer_queue(struct vb2_buffer *vb)
 {
+       struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
        struct cx23885_buffer   *buf = container_of(vb,
                struct cx23885_buffer, vb);
        struct cx23885_buffer   *prev;
-       struct cx23885_fh       *fh   = vq->priv_data;
-       struct cx23885_dev      *dev  = fh->dev;
        struct cx23885_dmaqueue *q    = &dev->vidq;
+       unsigned long flags;
 
-       /* add jump to stopper */
-       buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
-       buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+       /* add jump to start */
+       buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12);
+       buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
+       buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12);
        buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
 
-       if (!list_empty(&q->queued)) {
-               list_add_tail(&buf->vb.queue, &q->queued);
-               buf->vb.state = VIDEOBUF_QUEUED;
-               dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
-                       buf, buf->vb.i);
-
-       } else if (list_empty(&q->active)) {
-               list_add_tail(&buf->vb.queue, &q->active);
-               cx23885_start_video_dma(dev, q, buf);
-               buf->vb.state = VIDEOBUF_ACTIVE;
-               buf->count    = q->count++;
-               mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+       spin_lock_irqsave(&dev->slock, flags);
+       if (list_empty(&q->active)) {
+               list_add_tail(&buf->queue, &q->active);
                dprintk(2, "[%p/%d] buffer_queue - first active\n",
-                       buf, buf->vb.i);
-
+                       buf, buf->vb.v4l2_buf.index);
        } else {
+               buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
                prev = list_entry(q->active.prev, struct cx23885_buffer,
-                       vb.queue);
-               if (prev->vb.width  == buf->vb.width  &&
-                   prev->vb.height == buf->vb.height &&
-                   prev->fmt       == buf->fmt) {
-                       list_add_tail(&buf->vb.queue, &q->active);
-                       buf->vb.state = VIDEOBUF_ACTIVE;
-                       buf->count    = q->count++;
-                       prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-                       /* 64 bit bits 63-32 */
-                       prev->risc.jmp[2] = cpu_to_le32(0);
-                       dprintk(2, "[%p/%d] buffer_queue - append to active\n",
-                               buf, buf->vb.i);
-
-               } else {
-                       list_add_tail(&buf->vb.queue, &q->queued);
-                       buf->vb.state = VIDEOBUF_QUEUED;
-                       dprintk(2, "[%p/%d] buffer_queue - first queued\n",
-                               buf, buf->vb.i);
-               }
-       }
-}
-
-static void buffer_release(struct videobuf_queue *q,
-       struct videobuf_buffer *vb)
-{
-       struct cx23885_buffer *buf = container_of(vb,
-               struct cx23885_buffer, vb);
-
-       cx23885_free_buffer(q, buf);
-}
-
-static struct videobuf_queue_ops cx23885_video_qops = {
-       .buf_setup    = buffer_setup,
-       .buf_prepare  = buffer_prepare,
-       .buf_queue    = buffer_queue,
-       .buf_release  = buffer_release,
-};
-
-static struct videobuf_queue *get_queue(struct cx23885_fh *fh)
-{
-       switch (fh->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               return &fh->vidq;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               return &fh->vbiq;
-       default:
-               BUG();
-               return NULL;
-       }
-}
-
-static int get_resource(struct cx23885_fh *fh)
-{
-       switch (fh->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               return RESOURCE_VIDEO;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               return RESOURCE_VBI;
-       default:
-               BUG();
-               return 0;
+                       queue);
+               list_add_tail(&buf->queue, &q->active);
+               prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+               dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+                               buf, buf->vb.v4l2_buf.index);
        }
+       spin_unlock_irqrestore(&dev->slock, flags);
 }
 
-static int video_open(struct file *file)
+static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count)
 {
-       struct video_device *vdev = video_devdata(file);
-       struct cx23885_dev *dev = video_drvdata(file);
-       struct cx23885_fh *fh;
-       enum v4l2_buf_type type = 0;
-       int radio = 0;
-
-       switch (vdev->vfl_type) {
-       case VFL_TYPE_GRABBER:
-               type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               break;
-       case VFL_TYPE_VBI:
-               type = V4L2_BUF_TYPE_VBI_CAPTURE;
-               break;
-       case VFL_TYPE_RADIO:
-               radio = 1;
-               break;
-       }
-
-       dprintk(1, "open dev=%s radio=%d type=%s\n",
-               video_device_node_name(vdev), radio, v4l2_type_names[type]);
-
-       /* allocate + initialize per filehandle data */
-       fh = kzalloc(sizeof(*fh), GFP_KERNEL);
-       if (NULL == fh)
-               return -ENOMEM;
-
-       file->private_data = fh;
-       fh->dev      = dev;
-       fh->radio    = radio;
-       fh->type     = type;
-       fh->width    = 320;
-       fh->height   = 240;
-       fh->fmt      = format_by_fourcc(V4L2_PIX_FMT_YUYV);
-
-       videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops,
-                           &dev->pci->dev, &dev->slock,
-                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                           V4L2_FIELD_INTERLACED,
-                           sizeof(struct cx23885_buffer),
-                           fh, NULL);
-
-       videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops,
-               &dev->pci->dev, &dev->slock,
-               V4L2_BUF_TYPE_VBI_CAPTURE,
-               V4L2_FIELD_SEQ_TB,
-               sizeof(struct cx23885_buffer),
-               fh, NULL);
-
-
-       dprintk(1, "post videobuf_queue_init()\n");
+       struct cx23885_dev *dev = q->drv_priv;
+       struct cx23885_dmaqueue *dmaq = &dev->vidq;
+       struct cx23885_buffer *buf = list_entry(dmaq->active.next,
+                       struct cx23885_buffer, queue);
 
+       cx23885_start_video_dma(dev, dmaq, buf);
        return 0;
 }
 
-static ssize_t video_read(struct file *file, char __user *data,
-       size_t count, loff_t *ppos)
+static void cx23885_stop_streaming(struct vb2_queue *q)
 {
-       struct cx23885_fh *fh = file->private_data;
-
-       switch (fh->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (res_locked(fh->dev, RESOURCE_VIDEO))
-                       return -EBUSY;
-               return videobuf_read_one(&fh->vidq, data, count, ppos,
-                                        file->f_flags & O_NONBLOCK);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               if (!res_get(fh->dev, fh, RESOURCE_VBI))
-                       return -EBUSY;
-               return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
-                                           file->f_flags & O_NONBLOCK);
-       default:
-               BUG();
-               return 0;
-       }
-}
-
-static unsigned int video_poll(struct file *file,
-       struct poll_table_struct *wait)
-{
-       struct cx23885_fh *fh = file->private_data;
-       struct cx23885_buffer *buf;
-       unsigned int rc = POLLERR;
-
-       if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
-               if (!res_get(fh->dev, fh, RESOURCE_VBI))
-                       return POLLERR;
-               return videobuf_poll_stream(file, &fh->vbiq, wait);
-       }
-
-       mutex_lock(&fh->vidq.vb_lock);
-       if (res_check(fh, RESOURCE_VIDEO)) {
-               /* streaming capture */
-               if (list_empty(&fh->vidq.stream))
-                       goto done;
-               buf = list_entry(fh->vidq.stream.next,
-                       struct cx23885_buffer, vb.stream);
-       } else {
-               /* read() capture */
-               buf = (struct cx23885_buffer *)fh->vidq.read_buf;
-               if (NULL == buf)
-                       goto done;
-       }
-       poll_wait(file, &buf->vb.done, wait);
-       if (buf->vb.state == VIDEOBUF_DONE ||
-           buf->vb.state == VIDEOBUF_ERROR)
-               rc =  POLLIN|POLLRDNORM;
-       else
-               rc = 0;
-done:
-       mutex_unlock(&fh->vidq.vb_lock);
-       return rc;
-}
-
-static int video_release(struct file *file)
-{
-       struct cx23885_fh *fh = file->private_data;
-       struct cx23885_dev *dev = fh->dev;
-
-       /* turn off overlay */
-       if (res_check(fh, RESOURCE_OVERLAY)) {
-               /* FIXME */
-               res_free(dev, fh, RESOURCE_OVERLAY);
-       }
+       struct cx23885_dev *dev = q->drv_priv;
+       struct cx23885_dmaqueue *dmaq = &dev->vidq;
+       unsigned long flags;
 
-       /* stop video capture */
-       if (res_check(fh, RESOURCE_VIDEO)) {
-               videobuf_queue_cancel(&fh->vidq);
-               res_free(dev, fh, RESOURCE_VIDEO);
-       }
-       if (fh->vidq.read_buf) {
-               buffer_release(&fh->vidq, fh->vidq.read_buf);
-               kfree(fh->vidq.read_buf);
-       }
+       cx_clear(VID_A_DMA_CTL, 0x11);
+       spin_lock_irqsave(&dev->slock, flags);
+       while (!list_empty(&dmaq->active)) {
+               struct cx23885_buffer *buf = list_entry(dmaq->active.next,
+                       struct cx23885_buffer, queue);
 
-       /* stop vbi capture */
-       if (res_check(fh, RESOURCE_VBI)) {
-               if (fh->vbiq.streaming)
-                       videobuf_streamoff(&fh->vbiq);
-               if (fh->vbiq.reading)
-                       videobuf_read_stop(&fh->vbiq);
-               res_free(dev, fh, RESOURCE_VBI);
+               list_del(&buf->queue);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
        }
-
-       videobuf_mmap_free(&fh->vidq);
-       videobuf_mmap_free(&fh->vbiq);
-
-       file->private_data = NULL;
-       kfree(fh);
-
-       /* We are not putting the tuner to sleep here on exit, because
-        * we want to use the mpeg encoder in another session to capture
-        * tuner video. Closing this will result in no video to the encoder.
-        */
-
-       return 0;
-}
-
-static int video_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct cx23885_fh *fh = file->private_data;
-
-       return videobuf_mmap_mapper(get_queue(fh), vma);
-}
-
-/* ------------------------------------------------------------------ */
-/* VIDEO CTRL IOCTLS                                                  */
-
-int cx23885_get_control(struct cx23885_dev *dev,
-       struct v4l2_control *ctl)
-{
-       dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__);
-       call_all(dev, core, g_ctrl, ctl);
-       return 0;
-}
-
-int cx23885_set_control(struct cx23885_dev *dev,
-       struct v4l2_control *ctl)
-{
-       dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__);
-       call_all(dev, core, s_ctrl, ctl);
-
-       return 0;
+       spin_unlock_irqrestore(&dev->slock, flags);
 }
 
-static void init_controls(struct cx23885_dev *dev)
-{
-       struct v4l2_control ctrl;
-       int i;
-
-       for (i = 0; i < CX23885_CTLS; i++) {
-               ctrl.id = cx23885_ctls[i].v.id;
-               ctrl.value = cx23885_ctls[i].v.default_value;
-
-               cx23885_set_control(dev, &ctrl);
-       }
-}
+static struct vb2_ops cx23885_video_qops = {
+       .queue_setup    = queue_setup,
+       .buf_prepare  = buffer_prepare,
+       .buf_finish = buffer_finish,
+       .buf_queue    = buffer_queue,
+       .wait_prepare = vb2_ops_wait_prepare,
+       .wait_finish = vb2_ops_wait_finish,
+       .start_streaming = cx23885_start_streaming,
+       .stop_streaming = cx23885_stop_streaming,
+};
 
 /* ------------------------------------------------------------------ */
 /* VIDEO IOCTLS                                                       */
@@ -1060,16 +522,17 @@ static void init_controls(struct cx23885_dev *dev)
 static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
        struct v4l2_format *f)
 {
-       struct cx23885_fh *fh   = priv;
+       struct cx23885_dev *dev = video_drvdata(file);
 
-       f->fmt.pix.width        = fh->width;
-       f->fmt.pix.height       = fh->height;
-       f->fmt.pix.field        = fh->vidq.field;
-       f->fmt.pix.pixelformat  = fh->fmt->fourcc;
+       f->fmt.pix.width        = dev->width;
+       f->fmt.pix.height       = dev->height;
+       f->fmt.pix.field        = dev->field;
+       f->fmt.pix.pixelformat  = dev->fmt->fourcc;
        f->fmt.pix.bytesperline =
-               (f->fmt.pix.width * fh->fmt->depth) >> 3;
+               (f->fmt.pix.width * dev->fmt->depth) >> 3;
        f->fmt.pix.sizeimage =
                f->fmt.pix.height * f->fmt.pix.bytesperline;
+       f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
 
        return 0;
 }
@@ -1077,7 +540,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
        struct v4l2_format *f)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        struct cx23885_fmt *fmt;
        enum v4l2_field   field;
        unsigned int      maxw, maxh;
@@ -1102,9 +565,12 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                maxh = maxh / 2;
                break;
        case V4L2_FIELD_INTERLACED:
+       case V4L2_FIELD_SEQ_TB:
+       case V4L2_FIELD_SEQ_BT:
                break;
        default:
-               return -EINVAL;
+               field = V4L2_FIELD_INTERLACED;
+               break;
        }
 
        f->fmt.pix.field = field;
@@ -1114,6 +580,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                (f->fmt.pix.width * fmt->depth) >> 3;
        f->fmt.pix.sizeimage =
                f->fmt.pix.height * f->fmt.pix.bytesperline;
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
 
        return 0;
 }
@@ -1121,8 +588,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
        struct v4l2_format *f)
 {
-       struct cx23885_fh *fh = priv;
-       struct cx23885_dev *dev  = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        struct v4l2_mbus_framefmt mbus_fmt;
        int err;
 
@@ -1131,34 +597,44 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 
        if (0 != err)
                return err;
-       fh->fmt        = format_by_fourcc(f->fmt.pix.pixelformat);
-       fh->width      = f->fmt.pix.width;
-       fh->height     = f->fmt.pix.height;
-       fh->vidq.field = f->fmt.pix.field;
+
+       if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq) ||
+           vb2_is_busy(&dev->vb2_mpegq))
+               return -EBUSY;
+
+       dev->fmt        = format_by_fourcc(f->fmt.pix.pixelformat);
+       dev->width      = f->fmt.pix.width;
+       dev->height     = f->fmt.pix.height;
+       dev->field      = f->fmt.pix.field;
        dprintk(2, "%s() width=%d height=%d field=%d\n", __func__,
-               fh->width, fh->height, fh->vidq.field);
+               dev->width, dev->height, dev->field);
        v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
        call_all(dev, video, s_mbus_fmt, &mbus_fmt);
        v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+       /* s_mbus_fmt overwrites f->fmt.pix.field, restore it */
+       f->fmt.pix.field = dev->field;
        return 0;
 }
 
 static int vidioc_querycap(struct file *file, void  *priv,
        struct v4l2_capability *cap)
 {
-       struct cx23885_dev *dev  = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
 
        strcpy(cap->driver, "cx23885");
        strlcpy(cap->card, cx23885_boards[dev->board].name,
                sizeof(cap->card));
        sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
-       cap->capabilities =
-               V4L2_CAP_VIDEO_CAPTURE |
-               V4L2_CAP_READWRITE     |
-               V4L2_CAP_STREAMING     |
-               V4L2_CAP_VBI_CAPTURE;
+       cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO;
        if (dev->tuner_type != TUNER_ABSENT)
-               cap->capabilities |= V4L2_CAP_TUNER;
+               cap->device_caps |= V4L2_CAP_TUNER;
+       if (vdev->vfl_type == VFL_TYPE_VBI)
+               cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
+       else
+               cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
+       cap->capabilities = cap->device_caps | V4L2_CAP_VBI_CAPTURE |
+               V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_DEVICE_CAPS;
        return 0;
 }
 
@@ -1175,85 +651,9 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
        return 0;
 }
 
-static int vidioc_reqbufs(struct file *file, void *priv,
-       struct v4l2_requestbuffers *p)
-{
-       struct cx23885_fh *fh = priv;
-       return videobuf_reqbufs(get_queue(fh), p);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
-       struct v4l2_buffer *p)
-{
-       struct cx23885_fh *fh = priv;
-       return videobuf_querybuf(get_queue(fh), p);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv,
-       struct v4l2_buffer *p)
-{
-       struct cx23885_fh *fh = priv;
-       return videobuf_qbuf(get_queue(fh), p);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv,
-       struct v4l2_buffer *p)
-{
-       struct cx23885_fh *fh = priv;
-       return videobuf_dqbuf(get_queue(fh), p,
-                               file->f_flags & O_NONBLOCK);
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
-       enum v4l2_buf_type i)
-{
-       struct cx23885_fh *fh = priv;
-       struct cx23885_dev *dev = fh->dev;
-       dprintk(1, "%s()\n", __func__);
-
-       if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
-               (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
-               return -EINVAL;
-       if (unlikely(i != fh->type))
-               return -EINVAL;
-
-       if (unlikely(!res_get(dev, fh, get_resource(fh))))
-               return -EBUSY;
-
-       /* Don't start VBI streaming unless vida streaming
-        * has already started.
-        */
-       if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) &&
-               ((cx_read(VID_A_DMA_CTL) & 0x11) == 0))
-               return -EINVAL;
-
-       return videobuf_streamon(get_queue(fh));
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
-       struct cx23885_fh *fh = priv;
-       struct cx23885_dev *dev = fh->dev;
-       int err, res;
-       dprintk(1, "%s()\n", __func__);
-
-       if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
-               (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
-               return -EINVAL;
-       if (i != fh->type)
-               return -EINVAL;
-
-       res = get_resource(fh);
-       err = videobuf_streamoff(get_queue(fh));
-       if (err < 0)
-               return err;
-       res_free(dev, fh, res);
-       return 0;
-}
-
 static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        dprintk(1, "%s()\n", __func__);
 
        *id = dev->tvnorm;
@@ -1262,14 +662,10 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
 
 static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        dprintk(1, "%s()\n", __func__);
 
-       mutex_lock(&dev->lock);
-       cx23885_set_tvnorm(dev, tvnorms);
-       mutex_unlock(&dev->lock);
-
-       return 0;
+       return cx23885_set_tvnorm(dev, tvnorms);
 }
 
 int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
@@ -1299,16 +695,16 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
        i->index = n;
        i->type  = V4L2_INPUT_TYPE_CAMERA;
        strcpy(i->name, iname[INPUT(n)->type]);
+       i->std = CX23885_NORMS;
        if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
                (CX23885_VMUX_CABLE == INPUT(n)->type)) {
                i->type = V4L2_INPUT_TYPE_TUNER;
-               i->std = CX23885_NORMS;
+               i->audioset = 4;
+       } else {
+               /* Two selectable audio inputs for non-tv inputs */
+               i->audioset = 3;
        }
 
-       /* Two selectable audio inputs for non-tv inputs */
-       if (INPUT(n)->type != CX23885_VMUX_TELEVISION)
-               i->audioset = 0x3;
-
        if (dev->input == n) {
                /* enum'd input matches our configured input.
                 * Ask the video decoder to process the call
@@ -1324,14 +720,14 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
 static int vidioc_enum_input(struct file *file, void *priv,
                                struct v4l2_input *i)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        dprintk(1, "%s()\n", __func__);
        return cx23885_enum_input(dev, i);
 }
 
 int cx23885_get_input(struct file *file, void *priv, unsigned int *i)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        *i = dev->input;
        dprintk(1, "%s() returns %d\n", __func__, *i);
@@ -1345,7 +741,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
 
 int cx23885_set_input(struct file *file, void *priv, unsigned int i)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        dprintk(1, "%s(%d)\n", __func__, i);
 
@@ -1357,13 +753,11 @@ int cx23885_set_input(struct file *file, void *priv, unsigned int i)
        if (INPUT(i)->type == 0)
                return -EINVAL;
 
-       mutex_lock(&dev->lock);
        cx23885_video_mux(dev, i);
 
        /* By default establish the default audio input for the card also */
        /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */
        cx23885_audio_mux(dev, i);
-       mutex_unlock(&dev->lock);
        return 0;
 }
 
@@ -1374,39 +768,32 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
 
 static int vidioc_log_status(struct file *file, void *priv)
 {
-       struct cx23885_fh  *fh  = priv;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
-       printk(KERN_INFO
-               "%s/0: ============  START LOG STATUS  ============\n",
-               dev->name);
        call_all(dev, core, log_status);
-       printk(KERN_INFO
-               "%s/0: =============  END LOG STATUS  =============\n",
-               dev->name);
        return 0;
 }
 
 static int cx23885_query_audinput(struct file *file, void *priv,
        struct v4l2_audio *i)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        static const char *iname[] = {
                [0] = "Baseband L/R 1",
                [1] = "Baseband L/R 2",
+               [2] = "TV",
        };
        unsigned int n;
        dprintk(1, "%s()\n", __func__);
 
        n = i->index;
-       if (n >= 2)
+       if (n >= 3)
                return -EINVAL;
 
        memset(i, 0, sizeof(*i));
        i->index = n;
        strcpy(i->name, iname[n]);
-       i->capability  = V4L2_AUDCAP_STEREO;
-       i->mode  = V4L2_AUDMODE_AVL;
+       i->capability = V4L2_AUDCAP_STEREO;
        return 0;
 
 }
@@ -1420,9 +807,13 @@ static int vidioc_enum_audinput(struct file *file, void *priv,
 static int vidioc_g_audinput(struct file *file, void *priv,
        struct v4l2_audio *i)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
-       i->index = dev->audinput;
+       if ((CX23885_VMUX_TELEVISION == INPUT(dev->input)->type) ||
+               (CX23885_VMUX_CABLE == INPUT(dev->input)->type))
+               i->index = 2;
+       else
+               i->index = dev->audinput;
        dprintk(1, "%s(input=%d)\n", __func__, i->index);
 
        return cx23885_query_audinput(file, priv, i);
@@ -1431,8 +822,13 @@ static int vidioc_g_audinput(struct file *file, void *priv,
 static int vidioc_s_audinput(struct file *file, void *priv,
        const struct v4l2_audio *i)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-       if (i->index >= 2)
+       struct cx23885_dev *dev = video_drvdata(file);
+
+       if ((CX23885_VMUX_TELEVISION == INPUT(dev->input)->type) ||
+               (CX23885_VMUX_CABLE == INPUT(dev->input)->type)) {
+               return i->index != 2 ? -EINVAL : 0;
+       }
+       if (i->index > 1)
                return -EINVAL;
 
        dprintk(1, "%s(%d)\n", __func__, i->index);
@@ -1445,35 +841,10 @@ static int vidioc_s_audinput(struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-                               struct v4l2_queryctrl *qctrl)
-{
-       qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
-       if (unlikely(qctrl->id == 0))
-               return -EINVAL;
-       return cx23885_ctrl_query(qctrl);
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-                               struct v4l2_control *ctl)
-{
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
-       return cx23885_get_control(dev, ctl);
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-                               struct v4l2_control *ctl)
-{
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
-       return cx23885_set_control(dev, ctl);
-}
-
 static int vidioc_g_tuner(struct file *file, void *priv,
                                struct v4l2_tuner *t)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        if (dev->tuner_type == TUNER_ABSENT)
                return -EINVAL;
@@ -1489,7 +860,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
 static int vidioc_s_tuner(struct file *file, void *priv,
                                const struct v4l2_tuner *t)
 {
-       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        if (dev->tuner_type == TUNER_ABSENT)
                return -EINVAL;
@@ -1504,14 +875,12 @@ static int vidioc_s_tuner(struct file *file, void *priv,
 static int vidioc_g_frequency(struct file *file, void *priv,
                                struct v4l2_frequency *f)
 {
-       struct cx23885_fh *fh = priv;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
 
        if (dev->tuner_type == TUNER_ABSENT)
                return -EINVAL;
 
-       /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
-       f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       f->type = V4L2_TUNER_ANALOG_TV;
        f->frequency = dev->freq;
 
        call_all(dev, tuner, g_frequency, f);
@@ -1521,20 +890,23 @@ static int vidioc_g_frequency(struct file *file, void *priv,
 
 static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency *f)
 {
-       struct v4l2_control ctrl;
+       struct v4l2_ctrl *mute;
+       int old_mute_val = 1;
 
        if (dev->tuner_type == TUNER_ABSENT)
                return -EINVAL;
        if (unlikely(f->tuner != 0))
                return -EINVAL;
 
-       mutex_lock(&dev->lock);
        dev->freq = f->frequency;
 
        /* I need to mute audio here */
-       ctrl.id = V4L2_CID_AUDIO_MUTE;
-       ctrl.value = 1;
-       cx23885_set_control(dev, &ctrl);
+       mute = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_AUDIO_MUTE);
+       if (mute) {
+               old_mute_val = v4l2_ctrl_g_ctrl(mute);
+               if (!old_mute_val)
+                       v4l2_ctrl_s_ctrl(mute, 1);
+       }
 
        call_all(dev, tuner, s_frequency, f);
 
@@ -1542,10 +914,8 @@ static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency
        msleep(100);
 
        /* I need to unmute audio here */
-       ctrl.value = 0;
-       cx23885_set_control(dev, &ctrl);
-
-       mutex_unlock(&dev->lock);
+       if (old_mute_val == 0)
+               v4l2_ctrl_s_ctrl(mute, old_mute_val);
 
        return 0;
 }
@@ -1553,8 +923,9 @@ static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency
 static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
        const struct v4l2_frequency *f)
 {
-       struct v4l2_control ctrl;
-       struct videobuf_dvb_frontend *vfe;
+       struct v4l2_ctrl *mute;
+       int old_mute_val = 1;
+       struct vb2_dvb_frontend *vfe;
        struct dvb_frontend *fe;
 
        struct analog_parameters params = {
@@ -1564,21 +935,22 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
                .frequency = f->frequency
        };
 
-       mutex_lock(&dev->lock);
        dev->freq = f->frequency;
 
        /* I need to mute audio here */
-       ctrl.id = V4L2_CID_AUDIO_MUTE;
-       ctrl.value = 1;
-       cx23885_set_control(dev, &ctrl);
+       mute = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_AUDIO_MUTE);
+       if (mute) {
+               old_mute_val = v4l2_ctrl_g_ctrl(mute);
+               if (!old_mute_val)
+                       v4l2_ctrl_s_ctrl(mute, 1);
+       }
 
        /* If HVR1850 */
        dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__,
                params.frequency, f->tuner, params.std);
 
-       vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1);
+       vfe = vb2_dvb_get_frontend(&dev->ts2.frontends, 1);
        if (!vfe) {
-               mutex_unlock(&dev->lock);
                return -EINVAL;
        }
 
@@ -1600,10 +972,8 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
        msleep(100);
 
        /* I need to unmute audio here */
-       ctrl.value = 0;
-       cx23885_set_control(dev, &ctrl);
-
-       mutex_unlock(&dev->lock);
+       if (old_mute_val == 0)
+               v4l2_ctrl_s_ctrl(mute, old_mute_val);
 
        return 0;
 }
@@ -1611,8 +981,7 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
 int cx23885_set_frequency(struct file *file, void *priv,
        const struct v4l2_frequency *f)
 {
-       struct cx23885_fh *fh = priv;
-       struct cx23885_dev *dev = fh->dev;
+       struct cx23885_dev *dev = video_drvdata(file);
        int ret;
 
        switch (dev->board) {
@@ -1636,28 +1005,6 @@ static int vidioc_s_frequency(struct file *file, void *priv,
 
 /* ----------------------------------------------------------- */
 
-static void cx23885_vid_timeout(unsigned long data)
-{
-       struct cx23885_dev *dev = (struct cx23885_dev *)data;
-       struct cx23885_dmaqueue *q = &dev->vidq;
-       struct cx23885_buffer *buf;
-       unsigned long flags;
-
-       spin_lock_irqsave(&dev->slock, flags);
-       while (!list_empty(&q->active)) {
-               buf = list_entry(q->active.next,
-                       struct cx23885_buffer, vb.queue);
-               list_del(&buf->vb.queue);
-               buf->vb.state = VIDEOBUF_ERROR;
-               wake_up(&buf->vb.done);
-               printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n",
-                       dev->name, buf, buf->vb.i,
-                       (unsigned long)buf->risc.dma);
-       }
-       cx23885_restart_video_queue(dev, q);
-       spin_unlock_irqrestore(&dev->slock, flags);
-}
-
 int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
 {
        u32 mask, count;
@@ -1702,13 +1049,6 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
                spin_unlock(&dev->slock);
                handled++;
        }
-       if (status & VID_BC_MSK_RISCI2) {
-               dprintk(2, "stopper video\n");
-               spin_lock(&dev->slock);
-               cx23885_restart_video_queue(dev, &dev->vidq);
-               spin_unlock(&dev->slock);
-               handled++;
-       }
 
        /* Allow the VBI framework to process it's payload */
        handled += cx23885_vbi_irq(dev, status);
@@ -1721,12 +1061,12 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
 
 static const struct v4l2_file_operations video_fops = {
        .owner         = THIS_MODULE,
-       .open          = video_open,
-       .release       = video_release,
-       .read          = video_read,
-       .poll          = video_poll,
-       .mmap          = video_mmap,
-       .ioctl         = video_ioctl2,
+       .open           = v4l2_fh_open,
+       .release        = vb2_fop_release,
+       .read           = vb2_fop_read,
+       .poll           = vb2_fop_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = vb2_fop_mmap,
 };
 
 static const struct v4l2_ioctl_ops video_ioctl_ops = {
@@ -1738,21 +1078,19 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_g_fmt_vbi_cap     = cx23885_vbi_fmt,
        .vidioc_try_fmt_vbi_cap   = cx23885_vbi_fmt,
        .vidioc_s_fmt_vbi_cap     = cx23885_vbi_fmt,
-       .vidioc_reqbufs       = vidioc_reqbufs,
-       .vidioc_querybuf      = vidioc_querybuf,
-       .vidioc_qbuf          = vidioc_qbuf,
-       .vidioc_dqbuf         = vidioc_dqbuf,
+       .vidioc_reqbufs       = vb2_ioctl_reqbufs,
+       .vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf      = vb2_ioctl_querybuf,
+       .vidioc_qbuf          = vb2_ioctl_qbuf,
+       .vidioc_dqbuf         = vb2_ioctl_dqbuf,
+       .vidioc_streamon      = vb2_ioctl_streamon,
+       .vidioc_streamoff     = vb2_ioctl_streamoff,
        .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_log_status    = vidioc_log_status,
-       .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,
@@ -1765,6 +1103,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_enumaudio     = vidioc_enum_audinput,
        .vidioc_g_audio       = vidioc_g_audinput,
        .vidioc_s_audio       = vidioc_s_audinput,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 };
 
 static struct video_device cx23885_vbi_template;
@@ -1775,14 +1115,6 @@ static struct video_device cx23885_video_template = {
        .tvnorms              = CX23885_NORMS,
 };
 
-static const struct v4l2_file_operations radio_fops = {
-       .owner         = THIS_MODULE,
-       .open          = video_open,
-       .release       = video_release,
-       .ioctl         = video_ioctl2,
-};
-
-
 void cx23885_video_unregister(struct cx23885_dev *dev)
 {
        dprintk(1, "%s()\n", __func__);
@@ -1794,7 +1126,6 @@ void cx23885_video_unregister(struct cx23885_dev *dev)
                else
                        video_device_release(dev->vbi_dev);
                dev->vbi_dev = NULL;
-               btcx_riscmem_free(dev->pci, &dev->vbiq.stopper);
        }
        if (dev->video_dev) {
                if (video_is_registered(dev->video_dev))
@@ -1802,8 +1133,6 @@ void cx23885_video_unregister(struct cx23885_dev *dev)
                else
                        video_device_release(dev->video_dev);
                dev->video_dev = NULL;
-
-               btcx_riscmem_free(dev->pci, &dev->vidq.stopper);
        }
 
        if (dev->audio_dev)
@@ -1812,6 +1141,7 @@ void cx23885_video_unregister(struct cx23885_dev *dev)
 
 int cx23885_video_register(struct cx23885_dev *dev)
 {
+       struct vb2_queue *q;
        int err;
 
        dprintk(1, "%s()\n", __func__);
@@ -1822,24 +1152,16 @@ int cx23885_video_register(struct cx23885_dev *dev)
        strcpy(cx23885_vbi_template.name, "cx23885-vbi");
 
        dev->tvnorm = V4L2_STD_NTSC_M;
+       dev->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
+       dev->field = V4L2_FIELD_INTERLACED;
+       dev->width = 720;
+       dev->height = norm_maxh(dev->tvnorm);
 
        /* init video dma queues */
        INIT_LIST_HEAD(&dev->vidq.active);
-       INIT_LIST_HEAD(&dev->vidq.queued);
-       dev->vidq.timeout.function = cx23885_vid_timeout;
-       dev->vidq.timeout.data = (unsigned long)dev;
-       init_timer(&dev->vidq.timeout);
-       cx23885_risc_stopper(dev->pci, &dev->vidq.stopper,
-               VID_A_DMA_CTL, 0x11, 0x00);
 
        /* init vbi dma queues */
        INIT_LIST_HEAD(&dev->vbiq.active);
-       INIT_LIST_HEAD(&dev->vbiq.queued);
-       dev->vbiq.timeout.function = cx23885_vbi_timeout;
-       dev->vbiq.timeout.data = (unsigned long)dev;
-       init_timer(&dev->vbiq.timeout);
-       cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper,
-               VID_A_DMA_CTL, 0x22, 0x00);
 
        cx23885_irq_add_enable(dev, 0x01);
 
@@ -1893,9 +1215,49 @@ int cx23885_video_register(struct cx23885_dev *dev)
                }
        }
 
+       /* initial device configuration */
+       mutex_lock(&dev->lock);
+       cx23885_set_tvnorm(dev, dev->tvnorm);
+       cx23885_video_mux(dev, 0);
+       cx23885_audio_mux(dev, 0);
+       mutex_unlock(&dev->lock);
+
+       q = &dev->vb2_vidq;
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+       q->gfp_flags = GFP_DMA32;
+       q->min_buffers_needed = 2;
+       q->drv_priv = dev;
+       q->buf_struct_size = sizeof(struct cx23885_buffer);
+       q->ops = &cx23885_video_qops;
+       q->mem_ops = &vb2_dma_sg_memops;
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->lock = &dev->lock;
+
+       err = vb2_queue_init(q);
+       if (err < 0)
+               goto fail_unreg;
+
+       q = &dev->vb2_vbiq;
+       q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+       q->gfp_flags = GFP_DMA32;
+       q->min_buffers_needed = 2;
+       q->drv_priv = dev;
+       q->buf_struct_size = sizeof(struct cx23885_buffer);
+       q->ops = &cx23885_vbi_qops;
+       q->mem_ops = &vb2_dma_sg_memops;
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->lock = &dev->lock;
+
+       err = vb2_queue_init(q);
+       if (err < 0)
+               goto fail_unreg;
+
        /* register Video device */
        dev->video_dev = cx23885_vdev_init(dev, dev->pci,
                &cx23885_video_template, "video");
+       dev->video_dev->queue = &dev->vb2_vidq;
        err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
                                    video_nr[dev->nr]);
        if (err < 0) {
@@ -1909,6 +1271,7 @@ int cx23885_video_register(struct cx23885_dev *dev)
        /* register VBI device */
        dev->vbi_dev = cx23885_vdev_init(dev, dev->pci,
                &cx23885_vbi_template, "vbi");
+       dev->vbi_dev->queue = &dev->vb2_vbiq;
        err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
                                    vbi_nr[dev->nr]);
        if (err < 0) {
@@ -1922,18 +1285,9 @@ int cx23885_video_register(struct cx23885_dev *dev)
        /* Register ALSA audio device */
        dev->audio_dev = cx23885_audio_register(dev);
 
-       /* initial device configuration */
-       mutex_lock(&dev->lock);
-       cx23885_set_tvnorm(dev, dev->tvnorm);
-       init_controls(dev);
-       cx23885_video_mux(dev, 0);
-       cx23885_audio_mux(dev, 0);
-       mutex_unlock(&dev->lock);
-
        return 0;
 
 fail_unreg:
        cx23885_video_unregister(dev);
        return err;
 }
-
index c961a2b0de0ffa8c51ecffe0cbebb89085f3d330..291e8f3189f0ce1def25c2896dd5eaf1f8bbd696 100644 (file)
  *  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.
  */
 
 #ifndef _CX23885_VIDEO_H_
index 0e086c03da672ae241f0501aa72b85cd9f3b79f1..6c35e61159696ad4be7ee930d42a74e4636b98a1 100644 (file)
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/pci.h>
 #include <linux/slab.h>
 
 #include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
 #include <media/tuner.h>
 #include <media/tveeprom.h>
-#include <media/videobuf-dma-sg.h>
-#include <media/videobuf-dvb.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-dvb.h>
 #include <media/rc-core.h>
 
-#include "btcx-risc.h"
 #include "cx23885-reg.h"
 #include "media/cx2341x.h"
 
 #include <linux/mutex.h>
 
-#define CX23885_VERSION "0.0.3"
+#define CX23885_VERSION "0.0.4"
 
 #define UNSET (-1U)
 
@@ -46,9 +43,6 @@
 /* Max number of inputs by card */
 #define MAX_CX23885_INPUT 8
 #define INPUT(nr) (&cx23885_boards[dev->board].input[nr])
-#define RESOURCE_OVERLAY       1
-#define RESOURCE_VIDEO         2
-#define RESOURCE_VBI           4
 
 #define BUFFER_TIMEOUT     (HZ)  /* 0.5 seconds */
 
@@ -98,6 +92,7 @@
 #define CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200 42
 #define CX23885_BOARD_HAUPPAUGE_IMPACTVCBE     43
 #define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2 44
+#define CX23885_BOARD_DVBSKY_T9580             45
 
 #define GPIO_0 0x00000001
 #define GPIO_1 0x00000002
@@ -131,14 +126,6 @@ struct cx23885_fmt {
        u32   cxformat;
 };
 
-struct cx23885_ctrl {
-       struct v4l2_queryctrl v;
-       u32                   off;
-       u32                   reg;
-       u32                   mask;
-       u32                   shift;
-};
-
 struct cx23885_tvnorm {
        char            *name;
        v4l2_std_id     id;
@@ -146,30 +133,6 @@ struct cx23885_tvnorm {
        u32             cxoformat;
 };
 
-struct cx23885_fh {
-       struct cx23885_dev         *dev;
-       enum v4l2_buf_type         type;
-       int                        radio;
-       u32                        resources;
-
-       /* video overlay */
-       struct v4l2_window         win;
-       struct v4l2_clip           *clips;
-       unsigned int               nclips;
-
-       /* video capture */
-       struct cx23885_fmt         *fmt;
-       unsigned int               width, height;
-
-       /* vbi capture */
-       struct videobuf_queue      vidq;
-       struct videobuf_queue      vbiq;
-
-       /* MPEG Encoder specifics ONLY */
-       struct videobuf_queue      mpegq;
-       atomic_t                   v4l_reading;
-};
-
 enum cx23885_itype {
        CX23885_VMUX_COMPOSITE1 = 1,
        CX23885_VMUX_COMPOSITE2,
@@ -189,14 +152,22 @@ enum cx23885_src_sel_type {
        CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO
 };
 
+struct cx23885_riscmem {
+       unsigned int   size;
+       __le32         *cpu;
+       __le32         *jmp;
+       dma_addr_t     dma;
+};
+
 /* buffer for one video frame */
 struct cx23885_buffer {
        /* common v4l buffer stuff -- must be first */
-       struct videobuf_buffer vb;
+       struct vb2_buffer vb;
+       struct list_head queue;
 
        /* cx23885 specific */
        unsigned int           bpl;
-       struct btcx_riscmem    risc;
+       struct cx23885_riscmem risc;
        struct cx23885_fmt     *fmt;
        u32                    count;
 };
@@ -268,9 +239,6 @@ struct cx23885_i2c {
 
 struct cx23885_dmaqueue {
        struct list_head       active;
-       struct list_head       queued;
-       struct timer_list      timeout;
-       struct btcx_riscmem    stopper;
        u32                    count;
 };
 
@@ -280,7 +248,7 @@ struct cx23885_tsport {
        int                        nr;
        int                        sram_chno;
 
-       struct videobuf_dvb_frontends frontends;
+       struct vb2_dvb_frontends   frontends;
 
        /* dma queues */
        struct cx23885_dmaqueue    mpegq;
@@ -326,7 +294,12 @@ struct cx23885_tsport {
        /* Workaround for a temp dvb_frontend that the tuner can attached to */
        struct dvb_frontend analog_fe;
 
+       struct i2c_client *i2c_client_demod;
+       struct i2c_client *i2c_client_tuner;
+
        int (*set_frontend)(struct dvb_frontend *fe);
+       int (*fe_set_voltage)(struct dvb_frontend *fe,
+                               fe_sec_voltage_t voltage);
 };
 
 struct cx23885_kernel_ir {
@@ -339,8 +312,11 @@ struct cx23885_kernel_ir {
 
 struct cx23885_audio_buffer {
        unsigned int            bpl;
-       struct btcx_riscmem     risc;
-       struct videobuf_dmabuf  dma;
+       struct cx23885_riscmem  risc;
+       void                    *vaddr;
+       struct scatterlist      *sglist;
+       int                     sglen;
+       int                     nr_pages;
 };
 
 struct cx23885_audio_dev {
@@ -358,8 +334,6 @@ struct cx23885_audio_dev {
        unsigned int            period_size;
        unsigned int            num_periods;
 
-       struct videobuf_dmabuf  *dma_risc;
-
        struct cx23885_audio_buffer   *buf;
 
        struct snd_pcm_substream *substream;
@@ -368,6 +342,7 @@ struct cx23885_audio_dev {
 struct cx23885_dev {
        atomic_t                   refcount;
        struct v4l2_device         v4l2_dev;
+       struct v4l2_ctrl_handler   ctrl_handler;
 
        /* pci stuff */
        struct pci_dev             *pci;
@@ -407,7 +382,6 @@ struct cx23885_dev {
        } bridge;
 
        /* Analog video */
-       u32                        resources;
        unsigned int               input;
        unsigned int               audinput; /* Selectable audio input */
        u32                        tvaudio;
@@ -417,7 +391,6 @@ struct cx23885_dev {
        unsigned int               tuner_bus;
        unsigned int               radio_type;
        unsigned char              radio_addr;
-       unsigned int               has_radio;
        struct v4l2_subdev         *sd_cx25840;
        struct work_struct         cx25840_work;
 
@@ -435,17 +408,24 @@ struct cx23885_dev {
        u32                        freq;
        struct video_device        *video_dev;
        struct video_device        *vbi_dev;
-       struct video_device        *radio_dev;
+
+       /* video capture */
+       struct cx23885_fmt         *fmt;
+       unsigned int               width, height;
+       unsigned                   field;
 
        struct cx23885_dmaqueue    vidq;
+       struct vb2_queue           vb2_vidq;
        struct cx23885_dmaqueue    vbiq;
+       struct vb2_queue           vb2_vbiq;
+
        spinlock_t                 slock;
 
        /* MPEG Encoder ONLY settings */
        u32                        cx23417_mailbox;
-       struct cx2341x_mpeg_params mpeg_params;
+       struct cx2341x_handler     cxhdl;
        struct video_device        *v4l_device;
-       atomic_t                   v4l_reader_count;
+       struct vb2_queue           vb2_mpegq;
        struct cx23885_tvnorm      encodernorm;
 
        /* Analog raw audio */
@@ -521,26 +501,21 @@ extern int cx23885_sram_channel_setup(struct cx23885_dev *dev,
 extern void cx23885_sram_channel_dump(struct cx23885_dev *dev,
        struct sram_channel *ch);
 
-extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
-       u32 reg, u32 mask, u32 value);
-
-extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+extern int cx23885_risc_buffer(struct pci_dev *pci, struct cx23885_riscmem *risc,
        struct scatterlist *sglist,
        unsigned int top_offset, unsigned int bottom_offset,
        unsigned int bpl, unsigned int padding, unsigned int lines);
 
 extern int cx23885_risc_vbibuffer(struct pci_dev *pci,
-       struct btcx_riscmem *risc, struct scatterlist *sglist,
+       struct cx23885_riscmem *risc, struct scatterlist *sglist,
        unsigned int top_offset, unsigned int bottom_offset,
        unsigned int bpl, unsigned int padding, unsigned int lines);
 
+int cx23885_start_dma(struct cx23885_tsport *port,
+                            struct cx23885_dmaqueue *q,
+                            struct cx23885_buffer   *buf);
 void cx23885_cancel_buffers(struct cx23885_tsport *port);
 
-extern int cx23885_restart_queue(struct cx23885_tsport *port,
-                               struct cx23885_dmaqueue *q);
-
-extern void cx23885_wakeup(struct cx23885_tsport *port,
-                          struct cx23885_dmaqueue *q, u32 count);
 
 extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask);
 extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask);
@@ -574,13 +549,11 @@ extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev);
 extern int cx23885_dvb_register(struct cx23885_tsport *port);
 extern int cx23885_dvb_unregister(struct cx23885_tsport *port);
 
-extern int cx23885_buf_prepare(struct videobuf_queue *q,
-                              struct cx23885_tsport *port,
-                              struct cx23885_buffer *buf,
-                              enum v4l2_field field);
+extern int cx23885_buf_prepare(struct cx23885_buffer *buf,
+                              struct cx23885_tsport *port);
 extern void cx23885_buf_queue(struct cx23885_tsport *port,
                              struct cx23885_buffer *buf);
-extern void cx23885_free_buffer(struct videobuf_queue *q,
+extern void cx23885_free_buffer(struct cx23885_dev *dev,
                                struct cx23885_buffer *buf);
 
 /* ----------------------------------------------------------- */
@@ -595,8 +568,6 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i);
 int cx23885_set_input(struct file *file, void *priv, unsigned int i);
 int cx23885_get_input(struct file *file, void *priv, unsigned int *i);
 int cx23885_set_frequency(struct file *file, void *priv, const struct v4l2_frequency *f);
-int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
-int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
 int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm);
 
 /* ----------------------------------------------------------- */
@@ -604,9 +575,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm);
 extern int cx23885_vbi_fmt(struct file *file, void *priv,
        struct v4l2_format *f);
 extern void cx23885_vbi_timeout(unsigned long data);
-extern struct videobuf_queue_ops cx23885_vbi_qops;
-extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
-       struct cx23885_dmaqueue *q);
+extern struct vb2_ops cx23885_vbi_qops;
 extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status);
 
 /* cx23885-i2c.c                                                */
@@ -638,7 +607,7 @@ extern struct cx23885_audio_dev *cx23885_audio_register(
 extern void cx23885_audio_unregister(struct cx23885_dev *dev);
 extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask);
 extern int cx23885_risc_databuffer(struct pci_dev *pci,
-                                  struct btcx_riscmem *risc,
+                                  struct cx23885_riscmem *risc,
                                   struct scatterlist *sglist,
                                   unsigned int bpl,
                                   unsigned int lines,
@@ -649,15 +618,10 @@ extern int cx23885_risc_databuffer(struct pci_dev *pci,
 
 static inline unsigned int norm_maxw(v4l2_std_id norm)
 {
-       return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+       return (norm & V4L2_STD_525_60) ? 720 : 768;
 }
 
 static inline unsigned int norm_maxh(v4l2_std_id norm)
 {
-       return (norm & V4L2_STD_625_50) ? 576 : 480;
-}
-
-static inline unsigned int norm_swidth(v4l2_std_id norm)
-{
-       return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
+       return (norm & V4L2_STD_525_60) ? 480 : 576;
 }
index c2ff5fc01157c51e72ca2147a73f447f20be2e86..c1aa888af705434148f3915c42b70a07a01d361b 100644 (file)
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- *  02110-1301, USA.
  */
 
 #include <linux/kfifo.h>
index d2de41caaf1d6e902354adfacb6e8611cc7948d3..ff74a93575d681fee7a83856ab53b775cb7db04e 100644 (file)
  *  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.
  */
 
 #ifndef _CX23888_IR_H_
index 98a48f500684ce1f6256763a0d9c6cceef11c868..b6542ee4385b1dab71a644c965d4d090ded69f97 100644 (file)
  * 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.
  */
 
 #
index 13926e18febae16fdad84acd7bbdefd61f266f4b..90cac5b655d5dbbe6d05ff3fee79a3b8d5377373 100644 (file)
  * 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
index 0044fef7ca24e2adac4d08f55f6d32bc9b08ed5d..76d9487aafc85c8c07bc8cb1dae59efd10fb2d10 100644 (file)
  * 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"
index d26ae4b1590ece5c745576466ca32a49ed96bd76..daaa212adfbab900c8a8801b99d40fa64a1b343e 100644 (file)
@@ -17,9 +17,5 @@
  * 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);
index 1f43be0b04c8e91dc5012275d3d040eb0bd92a2d..a664997e1958dccdba7fbf6315d1d5b4606ca084 100644 (file)
@@ -330,8 +330,9 @@ int cx25821_write_frame(struct cx25821_channel *chan,
 
        if (frame_size - curpos < count)
                count = frame_size - curpos;
-       memcpy((char *)out->_data_buf_virt_addr + frame_offset + curpos,
-                       data, count);
+       if (copy_from_user((__force char *)out->_data_buf_virt_addr + frame_offset + curpos,
+                               data, count))
+               return -EFAULT;
        curpos += count;
        if (curpos == frame_size) {
                out->_frame_count++;
index e18a7ace08b1b1a41667e10e5eb47d31f07deb91..851754bf1291475f6dc1c383401d753edb864edf 100644 (file)
@@ -78,19 +78,19 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 0,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE2,
                        .vmux   = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE3,
                        .vmux   = 2,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE4,
                        .vmux   = 3,
-               }},
+               } },
        },
        [CX88_BOARD_HAUPPAUGE] = {
                .name           = "Hauppauge WinTV 34xxx models",
@@ -99,23 +99,23 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0xff00,  // internal decoder
-               },{
+               }, {
                        .type   = CX88_VMUX_DEBUG,
                        .vmux   = 0,
                        .gpio0  = 0xff01,  // mono from tuner chip
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0xff02,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0xff02,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0xff01,
@@ -127,13 +127,13 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
-               }},
+               } },
        },
        [CX88_BOARD_PIXELVIEW] = {
                .name           = "PixelView",
@@ -141,17 +141,17 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0xff00,  // internal decoder
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
-               }},
+               } },
                .radio = {
                         .type  = CX88_RADIO,
                         .gpio0 = 0xff10,
@@ -164,19 +164,19 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT | TDA9887_INTERCARRIER,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x03ff,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x03fe,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x03fe,
-               }},
+               } },
        },
        [CX88_BOARD_WINFAST2000XP_EXPERT] = {
                .name           = "Leadtek Winfast 2000XP Expert",
@@ -185,28 +185,28 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x00F5e700,
                        .gpio1  = 0x00003004,
                        .gpio2  = 0x00F5e700,
                        .gpio3  = 0x02000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x00F5c700,
                        .gpio1  = 0x00003004,
                        .gpio2  = 0x00F5c700,
                        .gpio3  = 0x02000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x00F5c700,
                        .gpio1  = 0x00003004,
                        .gpio2  = 0x00F5c700,
                        .gpio3  = 0x02000000,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x00F5d700,
@@ -222,19 +222,19 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio1  = 0xe09f,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio1  = 0xe05f,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio1  = 0xe05f,
-               }},
+               } },
                .radio = {
                        .gpio1  = 0xe0df,
                        .type   = CX88_RADIO,
@@ -249,25 +249,25 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT | TDA9887_INTERCARRIER_NTSC,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x000040bf,
                        .gpio1  = 0x000080c0,
                        .gpio2  = 0x0000ff40,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x000040bf,
                        .gpio1  = 0x000080c0,
                        .gpio2  = 0x0000ff40,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x000040bf,
                        .gpio1  = 0x000080c0,
                        .gpio2  = 0x0000ff40,
-               }},
+               } },
                .radio = {
                         .type   = CX88_RADIO,
                         .vmux   = 3,
@@ -283,14 +283,14 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0035e700,
                        .gpio1  = 0x00003004,
                        .gpio2  = 0x0035e700,
                        .gpio3  = 0x02000000,
-               },{
+               }, {
 
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
@@ -298,14 +298,14 @@ static const struct cx88_board cx88_boards[] = {
                        .gpio1  = 0x00003004,
                        .gpio2  = 0x0035c700,
                        .gpio3  = 0x02000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x0035c700,
                        .gpio1  = 0x0035c700,
                        .gpio2  = 0x02000000,
                        .gpio3  = 0x02000000,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x0035d700,
@@ -322,22 +322,22 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0000bde2,
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x0000bde6,
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x0000bde6,
                        .audioroute = 1,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x0000bd62,
@@ -351,16 +351,16 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 0,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE2,
                        .vmux   = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
-               }},
+               } },
        },
        [CX88_BOARD_PROLINK_PLAYTVPVR] = {
                .name           = "Prolink PlayTV PVR",
@@ -369,19 +369,19 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0xbff0,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0xbff3,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0xbff3,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0xbff0,
@@ -394,16 +394,16 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0000fde6,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
                        .audioroute = 1,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x0000fde2,
@@ -417,22 +417,22 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x00000fbf,
                        .gpio2  = 0x0000fc08,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x00000fbf,
                        .gpio2  = 0x0000fc68,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x00000fbf,
                        .gpio2  = 0x0000fc68,
-               }},
+               } },
        },
        [CX88_BOARD_KWORLD_DVB_T] = {
                .name           = "KWorld/VStream XPert DVB-T",
@@ -440,17 +440,17 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x0700,
                        .gpio2  = 0x0101,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x0700,
                        .gpio2  = 0x0101,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = {
@@ -459,15 +459,15 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x000027df,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x000027df,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_KWORLD_LTV883] = {
@@ -476,23 +476,23 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x07f8,
-               },{
+               }, {
                        .type   = CX88_VMUX_DEBUG,
                        .vmux   = 0,
                        .gpio0  = 0x07f9,  // mono from tuner chip
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x000007fa,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x000007fa,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x000007f8,
@@ -521,23 +521,23 @@ static const struct cx88_board cx88_boards[] = {
                    0 - normal RF
                    1 - high RF
                */
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0f0d,
-               },{
+               }, {
                        .type   = CX88_VMUX_CABLE,
                        .vmux   = 0,
                        .gpio0  = 0x0f05,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x0f00,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x0f00,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_HAUPPAUGE_DVB_T1] = {
@@ -546,10 +546,10 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_CONEXANT_DVB_T1] = {
@@ -558,10 +558,10 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_PROVIDEO_PV259] = {
@@ -570,11 +570,11 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .audioroute = 1,
-               }},
+               } },
                .mpeg           = CX88_MPEG_BLACKBIRD,
        },
        [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = {
@@ -583,15 +583,15 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x000027df,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x000027df,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_DNTV_LIVE_DVB_T] = {
@@ -600,17 +600,17 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x00000700,
                        .gpio2  = 0x00000101,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x00000700,
                        .gpio2  = 0x00000101,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_PCHDTV_HD3000] = {
@@ -632,19 +632,19 @@ static const struct cx88_board cx88_boards[] = {
                 *
                 * GPIO[16] = Remote control input
                 */
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x00008484,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x00008400,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x00008400,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x00008404,
@@ -659,25 +659,25 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0xed1a,
                        .gpio2  = 0x00ff,
-               },{
+               }, {
                        .type   = CX88_VMUX_DEBUG,
                        .vmux   = 0,
                        .gpio0  = 0xff01,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0xff02,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0xed92,
                        .gpio2  = 0x00ff,
-               }},
+               } },
                .radio = {
                         .type   = CX88_RADIO,
                         .gpio0  = 0xed96,
@@ -692,22 +692,22 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x00009d80,
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x00009d76,
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x00009d76,
                        .audioroute = 1,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x00009d00,
@@ -722,19 +722,19 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 1,
                        .gpio1  = 0x0000e03f,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 2,
                        .gpio1  = 0x0000e07f,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 3,
                        .gpio1  = 0x0000e07f,
-               }}
+               } }
        },
        [CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = {
                .name           = "PixelView PlayTV Ultra Pro (Stereo)",
@@ -745,19 +745,19 @@ static const struct cx88_board cx88_boards[] = {
                .radio_addr     = ADDR_UNSET,
                /* Some variants use a tda9874 and so need the tvaudio module. */
                .audio_chip     = CX88_AUDIO_TVAUDIO,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0xbf61,  /* internal decoder */
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0xbf63,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0xbf63,
-               }},
+               } },
                .radio = {
                         .type  = CX88_RADIO,
                         .gpio0 = 0xbf60,
@@ -770,19 +770,19 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x97ed,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x97e9,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x97e9,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_ADSTECH_DVB_T_PCI] = {
@@ -791,32 +791,32 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x0700,
                        .gpio2  = 0x0101,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x0700,
                        .gpio2  = 0x0101,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = {
                .name           = "TerraTec Cinergy 1400 DVB-T",
                .tuner_type     = TUNER_ABSENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 2,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = {
@@ -826,19 +826,19 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x87fd,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x87f9,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x87f9,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_AVERMEDIA_ULTRATV_MC_550] = {
@@ -848,22 +848,22 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 0,
                        .gpio0  = 0x0000cd73,
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 1,
                        .gpio0  = 0x0000cd73,
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 3,
                        .gpio0  = 0x0000cdb3,
                        .audioroute = 1,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .vmux   = 2,
@@ -876,21 +876,21 @@ static const struct cx88_board cx88_boards[] = {
                 /* Alexander Wold <awold@bigfoot.com> */
                 .name           = "Kworld V-Stream Xpert DVD",
                 .tuner_type     = UNSET,
-                .input          = {{
+                .input          = { {
                         .type   = CX88_VMUX_COMPOSITE1,
                         .vmux   = 1,
                         .gpio0  = 0x03000000,
                         .gpio1  = 0x01000000,
                         .gpio2  = 0x02000000,
                         .gpio3  = 0x00100000,
-                },{
+                }, {
                         .type   = CX88_VMUX_SVIDEO,
                         .vmux   = 2,
                         .gpio0  = 0x03000000,
                         .gpio1  = 0x01000000,
                         .gpio2  = 0x02000000,
                         .gpio3  = 0x00100000,
-                }},
+                } },
        },
        [CX88_BOARD_ATI_HDTVWONDER] = {
                .name           = "ATI HDTV Wonder",
@@ -898,28 +898,28 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x00000ff7,
                        .gpio1  = 0x000000ff,
                        .gpio2  = 0x00000001,
                        .gpio3  = 0x00000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x00000ffe,
                        .gpio1  = 0x000000ff,
                        .gpio2  = 0x00000001,
                        .gpio3  = 0x00000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x00000ffe,
                        .gpio1  = 0x000000ff,
                        .gpio2  = 0x00000001,
                        .gpio3  = 0x00000000,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_WINFAST_DTV1000] = {
@@ -928,16 +928,16 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_AVERTV_303] = {
@@ -947,28 +947,28 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x00ff,
                        .gpio1  = 0xe09f,
                        .gpio2  = 0x0010,
                        .gpio3  = 0x0000,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x00ff,
                        .gpio1  = 0xe05f,
                        .gpio2  = 0x0010,
                        .gpio3  = 0x0000,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x00ff,
                        .gpio1  = 0xe05f,
                        .gpio2  = 0x0010,
                        .gpio3  = 0x0000,
-               }},
+               } },
        },
        [CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = {
                .name           = "Hauppauge Nova-S-Plus DVB-S",
@@ -978,22 +978,22 @@ static const struct cx88_board cx88_boards[] = {
                .radio_addr     = ADDR_UNSET,
                .audio_chip     = CX88_AUDIO_WM8775,
                .i2sinputcntl   = 2,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                        /* 2: Line-In */
                        .audioroute = 2,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        /* 2: Line-In */
                        .audioroute = 2,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        /* 2: Line-In */
                        .audioroute = 2,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = {
@@ -1002,10 +1002,10 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_KWORLD_DVBS_100] = {
@@ -1015,22 +1015,22 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .audio_chip = CX88_AUDIO_WM8775,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                        /* 2: Line-In */
                        .audioroute = 2,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        /* 2: Line-In */
                        .audioroute = 2,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        /* 2: Line-In */
                        .audioroute = 2,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_HAUPPAUGE_HVR1100] = {
@@ -1040,16 +1040,16 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
-               }},
+               } },
                /* fixme: Add radio support */
                .mpeg           = CX88_MPEG_DVB,
        },
@@ -1060,13 +1060,13 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
-               }},
+               } },
                /* fixme: Add radio support */
                .mpeg           = CX88_MPEG_DVB,
        },
@@ -1078,19 +1078,19 @@ static const struct cx88_board cx88_boards[] = {
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
                                  TDA9887_PORT2_ACTIVE,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0xf80808,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0xf80808,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0xf80808,
-               }},
+               } },
                .radio = {
                         .type  = CX88_RADIO,
                         .gpio0 = 0xf80808,
@@ -1106,17 +1106,17 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x0700,
                        .gpio2  = 0x0101,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x0700,
                        .gpio2  = 0x0101,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = {
@@ -1125,15 +1125,15 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x000067df,
-                },{
+                }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x000067df,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = {
@@ -1142,22 +1142,22 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x3de2,
                        .gpio2  = 0x00ff,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x3de6,
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x3de6,
                        .audioroute = 1,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x3de6,
@@ -1171,19 +1171,19 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0000a75f,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x0000a75b,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x0000a75b,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_PCHDTV_HD5500] = {
@@ -1193,19 +1193,19 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x87fd,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x87f9,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x87f9,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_KWORLD_MCE200_DELUXE] = {
@@ -1217,11 +1217,11 @@ static const struct cx88_board cx88_boards[] = {
                .tda9887_conf   = TDA9887_PRESENT,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0000BDE6
-               }},
+               } },
                .mpeg           = CX88_MPEG_BLACKBIRD,
        },
        [CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = {
@@ -1233,11 +1233,11 @@ static const struct cx88_board cx88_boards[] = {
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
                                  TDA9887_PORT2_ACTIVE,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x5da6,
-               }},
+               } },
                .mpeg           = CX88_MPEG_BLACKBIRD,
        },
        [CX88_BOARD_NPGTECH_REALTV_TOP10FM] = {
@@ -1246,19 +1246,19 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0788,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x078b,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x078b,
-               }},
+               } },
                .radio = {
                         .type  = CX88_RADIO,
                         .gpio0 = 0x074a,
@@ -1271,7 +1271,7 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x00017304,
@@ -1299,7 +1299,7 @@ static const struct cx88_board cx88_boards[] = {
                        .gpio1  = 0x0000b207,
                        .gpio2  = 0x0001d701,
                        .gpio3  = 0x02000000,
-               }},
+               } },
                .radio = {
                         .type  = CX88_RADIO,
                         .gpio0 = 0x00015702,
@@ -1316,35 +1316,35 @@ static const struct cx88_board cx88_boards[] = {
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x00017300,
                        .gpio1  = 0x00008207,
                        .gpio2  = 0x00000000,
                        .gpio3  = 0x02000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x00018300,
                        .gpio1  = 0x0000f207,
                        .gpio2  = 0x00017304,
                        .gpio3  = 0x02000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x00018301,
                        .gpio1  = 0x0000f207,
                        .gpio2  = 0x00017304,
                        .gpio3  = 0x02000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x00018301,
                        .gpio1  = 0x0000f207,
                        .gpio2  = 0x00017304,
                        .gpio3  = 0x02000000,
-               }},
+               } },
                .radio = {
                         .type  = CX88_RADIO,
                         .gpio0 = 0x00015702,
@@ -1360,13 +1360,13 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type    = UNSET,
                .tuner_addr    = ADDR_UNSET,
                .radio_addr    = ADDR_UNSET,
-               .input  = {{
+               .input  = { {
                        .type  = CX88_VMUX_DVB,
                        .vmux  = 0,
-               },{
+               }, {
                        .type  = CX88_VMUX_COMPOSITE1,
                        .vmux  = 1,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_HAUPPAUGE_HVR3000] = {
@@ -1377,25 +1377,25 @@ static const struct cx88_board cx88_boards[] = {
                .radio_addr     = ADDR_UNSET,
                .tda9887_conf   = TDA9887_PRESENT,
                .audio_chip     = CX88_AUDIO_WM8775,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x84bf,
                        /* 1: TV Audio / FM Mono */
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x84bf,
                        /* 2: Line-In */
                        .audioroute = 2,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x84bf,
                        /* 2: Line-In */
                        .audioroute = 2,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x84bf,
@@ -1411,19 +1411,19 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0709,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x070b,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x070b,
-               }},
+               } },
        },
        [CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
                .name           = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
@@ -1431,28 +1431,28 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x003fffff,
                        .gpio1  = 0x00e00000,
                        .gpio2  = 0x003fffff,
                        .gpio3  = 0x02000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x003fffff,
                        .gpio1  = 0x00e00000,
                        .gpio2  = 0x003fffff,
                        .gpio3  = 0x02000000,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x003fffff,
                        .gpio1  = 0x00e00000,
                        .gpio2  = 0x003fffff,
                        .gpio3  = 0x02000000,
-               }},
+               } },
        },
        [CX88_BOARD_HAUPPAUGE_HVR1300] = {
                .name           = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
@@ -1465,25 +1465,25 @@ static const struct cx88_board cx88_boards[] = {
                /*
                 * gpio0 as reported by Mike Crash <mike AT mikecrash.com>
                 */
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0xef88,
                        /* 1: TV Audio / FM Mono */
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0xef88,
                        /* 2: Line-In */
                        .audioroute = 2,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0xef88,
                        /* 2: Line-In */
                        .audioroute = 2,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
                .radio = {
                        .type   = CX88_RADIO,
@@ -1510,19 +1510,19 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DEBUG,
                        .vmux   = 3,
                        .gpio0  = 0x04ff,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x07fa,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x07fa,
-               }},
+               } },
        },
        [CX88_BOARD_PINNACLE_PCTV_HD_800i] = {
                .name           = "Pinnacle PCTV HD 800i",
@@ -1530,24 +1530,24 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x04fb,
                        .gpio1  = 0x10ff,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x04fb,
                        .gpio1  = 0x10ef,
                        .audioroute = 1,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x04fb,
                        .gpio1  = 0x10ef,
                        .audioroute = 1,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = {
@@ -1557,7 +1557,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x000027df, /* Unconfirmed */
@@ -1815,19 +1815,19 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x10df,
-               },{
+               }, {
                        .type   = CX88_VMUX_COMPOSITE1,
                        .vmux   = 1,
                        .gpio0  = 0x16d9,
-               },{
+               }, {
                        .type   = CX88_VMUX_SVIDEO,
                        .vmux   = 2,
                        .gpio0  = 0x16d9,
-               }},
+               } },
                .mpeg           = CX88_MPEG_DVB,
        },
        [CX88_BOARD_PROLINK_PV_8000GT] = {
@@ -1967,7 +1967,7 @@ static const struct cx88_board cx88_boards[] = {
                 * 3: Line-In Expansion
                 * 4: FM Stereo
                 */
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0xc4bf,
@@ -2001,7 +2001,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                } },
@@ -2013,7 +2013,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                } },
@@ -2025,7 +2025,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                } },
@@ -2037,7 +2037,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                } },
@@ -2049,7 +2049,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                } },
@@ -2061,7 +2061,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                } },
@@ -2073,7 +2073,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                        .gpio0  = 0x8080,
@@ -2086,7 +2086,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                } },
@@ -2098,7 +2098,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                } },
@@ -2110,7 +2110,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_DVB,
                        .vmux   = 0,
                } },
@@ -2170,7 +2170,7 @@ static const struct cx88_board cx88_boards[] = {
                 * 13: audio source (0=tuner audio,1=line in)
                 * 14: FM (0=on,1=off ???)
                 */
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0400,       /* pin 2 = 0 */
@@ -2211,7 +2211,7 @@ static const struct cx88_board cx88_boards[] = {
                 * 13: audio source (0=tuner audio,1=line in)
                 * 14: FM (0=on,1=off ???)
                 */
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0400,       /* pin 2 = 0 */
@@ -2229,7 +2229,7 @@ static const struct cx88_board cx88_boards[] = {
                        .gpio0  = 0x0400,       /* pin 2 = 0 */
                        .gpio1  = 0x6060,       /* pin 13 = 1, pin 14 = 1 */
                        .gpio2  = 0x0000,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x0400,       /* pin 2 = 0 */
@@ -2252,7 +2252,7 @@ static const struct cx88_board cx88_boards[] = {
                 *  14: 0: FM radio
                 *  16: 0: RF input is cable
                 */
-               .input          = {{
+               .input          = { {
                        .type   = CX88_VMUX_TELEVISION,
                        .vmux   = 0,
                        .gpio0  = 0x0403,
@@ -2280,7 +2280,7 @@ static const struct cx88_board cx88_boards[] = {
                        .gpio1  = 0xF0F7,
                        .gpio2  = 0x0101,
                        .gpio3  = 0x0000,
-               }},
+               } },
                .radio = {
                        .type   = CX88_RADIO,
                        .gpio0  = 0x0403,
@@ -2308,7 +2308,7 @@ static const struct cx88_board cx88_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
-               .input          = {{
+               .input          = { {
                       .type   = CX88_VMUX_DVB,
                       .vmux   = 0,
                } },
@@ -2324,19 +2324,19 @@ static const struct cx88_subid cx88_subids[] = {
                .subvendor = 0x0070,
                .subdevice = 0x3400,
                .card      = CX88_BOARD_HAUPPAUGE,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x3401,
                .card      = CX88_BOARD_HAUPPAUGE,
-       },{
+       }, {
                .subvendor = 0x14c7,
                .subdevice = 0x0106,
                .card      = CX88_BOARD_GDI,
-       },{
+       }, {
                .subvendor = 0x14c7,
                .subdevice = 0x0107, /* with mpeg encoder */
                .card      = CX88_BOARD_GDI,
-       },{
+       }, {
                .subvendor = PCI_VENDOR_ID_ATI,
                .subdevice = 0x00f8,
                .card      = CX88_BOARD_ATI_WONDER_PRO,
@@ -2348,176 +2348,176 @@ static const struct cx88_subid cx88_subids[] = {
                .subvendor = 0x107d,
                .subdevice = 0x6611,
                .card      = CX88_BOARD_WINFAST2000XP_EXPERT,
-       },{
+       }, {
                .subvendor = 0x107d,
                .subdevice = 0x6613,    /* NTSC */
                .card      = CX88_BOARD_WINFAST2000XP_EXPERT,
-       },{
+       }, {
                .subvendor = 0x107d,
                .subdevice = 0x6620,
                .card      = CX88_BOARD_WINFAST_DV2000,
-       },{
+       }, {
                .subvendor = 0x107d,
                .subdevice = 0x663b,
                .card      = CX88_BOARD_LEADTEK_PVR2000,
-       },{
+       }, {
                .subvendor = 0x107d,
                .subdevice = 0x663c,
                .card      = CX88_BOARD_LEADTEK_PVR2000,
-       },{
+       }, {
                .subvendor = 0x1461,
                .subdevice = 0x000b,
                .card      = CX88_BOARD_AVERTV_STUDIO_303,
-       },{
+       }, {
                .subvendor = 0x1462,
                .subdevice = 0x8606,
                .card      = CX88_BOARD_MSI_TVANYWHERE_MASTER,
-       },{
+       }, {
                .subvendor = 0x10fc,
                .subdevice = 0xd003,
                .card      = CX88_BOARD_IODATA_GVVCP3PCI,
-       },{
+       }, {
                .subvendor = 0x1043,
                .subdevice = 0x4823,  /* with mpeg encoder */
                .card      = CX88_BOARD_ASUS_PVR_416,
-       },{
+       }, {
                .subvendor = 0x17de,
                .subdevice = 0x08a6,
                .card      = CX88_BOARD_KWORLD_DVB_T,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xd810,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xd820,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xdb00,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9002,
                .card      = CX88_BOARD_HAUPPAUGE_DVB_T1,
-       },{
+       }, {
                .subvendor = 0x14f1,
                .subdevice = 0x0187,
                .card      = CX88_BOARD_CONEXANT_DVB_T1,
-       },{
+       }, {
                .subvendor = 0x1540,
                .subdevice = 0x2580,
                .card      = CX88_BOARD_PROVIDEO_PV259,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xdb10,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
-       },{
+       }, {
                .subvendor = 0x1554,
                .subdevice = 0x4811,
                .card      = CX88_BOARD_PIXELVIEW,
-       },{
+       }, {
                .subvendor = 0x7063,
                .subdevice = 0x3000, /* HD-3000 card */
                .card      = CX88_BOARD_PCHDTV_HD3000,
-       },{
+       }, {
                .subvendor = 0x17de,
                .subdevice = 0xa8a6,
                .card      = CX88_BOARD_DNTV_LIVE_DVB_T,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x2801,
                .card      = CX88_BOARD_HAUPPAUGE_ROSLYN,
-       },{
+       }, {
                .subvendor = 0x14f1,
                .subdevice = 0x0342,
                .card      = CX88_BOARD_DIGITALLOGIC_MEC,
-       },{
+       }, {
                .subvendor = 0x10fc,
                .subdevice = 0xd035,
                .card      = CX88_BOARD_IODATA_GVBCTV7E,
-       },{
+       }, {
                .subvendor = 0x1421,
                .subdevice = 0x0334,
                .card      = CX88_BOARD_ADSTECH_DVB_T_PCI,
-       },{
+       }, {
                .subvendor = 0x153b,
                .subdevice = 0x1166,
                .card      = CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xd500,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD,
-       },{
+       }, {
                .subvendor = 0x1461,
                .subdevice = 0x8011,
                .card      = CX88_BOARD_AVERMEDIA_ULTRATV_MC_550,
-       },{
+       }, {
                .subvendor = PCI_VENDOR_ID_ATI,
                .subdevice = 0xa101,
                .card      = CX88_BOARD_ATI_HDTVWONDER,
-       },{
+       }, {
                .subvendor = 0x107d,
                .subdevice = 0x665f,
                .card      = CX88_BOARD_WINFAST_DTV1000,
-       },{
+       }, {
                .subvendor = 0x1461,
                .subdevice = 0x000a,
                .card      = CX88_BOARD_AVERTV_303,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9200,
                .card      = CX88_BOARD_HAUPPAUGE_NOVASE2_S1,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9201,
                .card      = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9202,
                .card      = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
-       },{
+       }, {
                .subvendor = 0x17de,
                .subdevice = 0x08b2,
                .card      = CX88_BOARD_KWORLD_DVBS_100,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9400,
                .card      = CX88_BOARD_HAUPPAUGE_HVR1100,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9402,
                .card      = CX88_BOARD_HAUPPAUGE_HVR1100,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9800,
                .card      = CX88_BOARD_HAUPPAUGE_HVR1100LP,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9802,
                .card      = CX88_BOARD_HAUPPAUGE_HVR1100LP,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9001,
                .card      = CX88_BOARD_HAUPPAUGE_DVB_T1,
-       },{
+       }, {
                .subvendor = 0x1822,
                .subdevice = 0x0025,
                .card      = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
-       },{
+       }, {
                .subvendor = 0x17de,
                .subdevice = 0x08a1,
                .card      = CX88_BOARD_KWORLD_DVB_T_CX22702,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xdb50,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xdb54,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
                /* Re-branded DViCO: DigitalNow DVB-T Dual */
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xdb11,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
@@ -2530,55 +2530,55 @@ static const struct cx88_subid cx88_subids[] = {
                .subvendor = 0x17de,
                .subdevice = 0x0840,
                .card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
-       },{
+       }, {
                .subvendor = 0x1421,
                .subdevice = 0x0305,
                .card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xdb40,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xdb44,
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
-       },{
+       }, {
                .subvendor = 0x7063,
                .subdevice = 0x5500,
                .card      = CX88_BOARD_PCHDTV_HD5500,
-       },{
+       }, {
                .subvendor = 0x17de,
                .subdevice = 0x0841,
                .card      = CX88_BOARD_KWORLD_MCE200_DELUXE,
-       },{
+       }, {
                .subvendor = 0x1822,
                .subdevice = 0x0019,
                .card      = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
-       },{
+       }, {
                .subvendor = 0x1554,
                .subdevice = 0x4813,
                .card      = CX88_BOARD_PIXELVIEW_PLAYTV_P7000,
-       },{
+       }, {
                .subvendor = 0x14f1,
                .subdevice = 0x0842,
                .card      = CX88_BOARD_NPGTECH_REALTV_TOP10FM,
-       },{
+       }, {
                .subvendor = 0x107d,
                .subdevice = 0x665e,
                .card      = CX88_BOARD_WINFAST_DTV2000H,
-       },{
+       }, {
                .subvendor = 0x107d,
                .subdevice = 0x6f2b,
                .card      = CX88_BOARD_WINFAST_DTV2000H_J,
-       },{
+       }, {
                .subvendor = 0x18ac,
                .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */
                .card      = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
-       },{
+       }, {
                .subvendor = 0x14f1,
                .subdevice = 0x0084,
                .card      = CX88_BOARD_GENIATECH_DVBS,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x1404,
                .card      = CX88_BOARD_HAUPPAUGE_HVR3000,
@@ -2590,60 +2590,60 @@ static const struct cx88_subid cx88_subids[] = {
                .subvendor = 0x18ac,
                .subdevice = 0xdccd,
                .card      = CX88_BOARD_SAMSUNG_SMT_7020,
-       },{
+       }, {
                .subvendor = 0x1461,
                .subdevice = 0xc111, /* AverMedia M150-D */
                /* This board is known to work with the ASUS PVR416 config */
                .card      = CX88_BOARD_ASUS_PVR_416,
-       },{
+       }, {
                .subvendor = 0xc180,
                .subdevice = 0xc980,
                .card      = CX88_BOARD_TE_DTV_250_OEM_SWANN,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9600,
                .card      = CX88_BOARD_HAUPPAUGE_HVR1300,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9601,
                .card      = CX88_BOARD_HAUPPAUGE_HVR1300,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9602,
                .card      = CX88_BOARD_HAUPPAUGE_HVR1300,
-       },{
+       }, {
                .subvendor = 0x107d,
                .subdevice = 0x6632,
                .card      = CX88_BOARD_LEADTEK_PVR2000,
-       },{
+       }, {
                .subvendor = 0x12ab,
                .subdevice = 0x2300, /* Club3D Zap TV2100 */
                .card      = CX88_BOARD_KWORLD_DVB_T_CX22702,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x9000,
                .card      = CX88_BOARD_HAUPPAUGE_DVB_T1,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x1400,
                .card      = CX88_BOARD_HAUPPAUGE_HVR3000,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x1401,
                .card      = CX88_BOARD_HAUPPAUGE_HVR3000,
-       },{
+       }, {
                .subvendor = 0x0070,
                .subdevice = 0x1402,
                .card      = CX88_BOARD_HAUPPAUGE_HVR3000,
-       },{
+       }, {
                .subvendor = 0x1421,
                .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */
                .card      = CX88_BOARD_KWORLD_DVBS_100,
-       },{
+       }, {
                .subvendor = 0x1421,
                .subdevice = 0x0390,
                .card      = CX88_BOARD_ADSTECH_PTV_390,
-       },{
+       }, {
                .subvendor = 0x11bd,
                .subdevice = 0x0051,
                .card      = CX88_BOARD_PINNACLE_PCTV_HD_800i,
index ed8cb9037b6f30f0876ea377c42db799a8dd0e51..ce27e6d4f16ea21df367c0e59930d2446ea82798 100644 (file)
@@ -696,7 +696,6 @@ static struct videobuf_queue *get_queue(struct file *file)
                return &fh->vbiq;
        default:
                BUG();
-               return NULL;
        }
 }
 
@@ -711,7 +710,6 @@ static int get_resource(struct file *file)
                return RESOURCE_VBI;
        default:
                BUG();
-               return 0;
        }
 }
 
@@ -812,7 +810,6 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
                                            file->f_flags & O_NONBLOCK);
        default:
                BUG();
-               return 0;
        }
 }
 
index da8f848be3b801cfee8ac6a1bba76b8de3da584f..c82e855a0814c87cf03782f1521915276686569a 100644 (file)
@@ -149,7 +149,7 @@ static u32 ddb_i2c_functionality(struct i2c_adapter *adap)
        return I2C_FUNC_SMBUS_EMUL;
 }
 
-struct i2c_algorithm ddb_i2c_algo = {
+static struct i2c_algorithm ddb_i2c_algo = {
        .master_xfer   = ddb_i2c_master_xfer,
        .functionality = ddb_i2c_functionality,
 };
@@ -266,7 +266,7 @@ static void io_free(struct pci_dev *pdev, u8 **vbuf,
        for (i = 0; i < num; i++) {
                if (vbuf[i]) {
                        pci_free_consistent(pdev, size, vbuf[i], pbuf[i]);
-                       vbuf[i] = 0;
+                       vbuf[i] = NULL;
                }
        }
 }
@@ -440,7 +440,7 @@ static u32 ddb_output_free(struct ddb_output *output)
 }
 
 static ssize_t ddb_output_write(struct ddb_output *output,
-                               const u8 *buf, size_t count)
+                               const __user u8 *buf, size_t count)
 {
        struct ddb *dev = output->port->dev;
        u32 idx, off, stat = output->stat;
@@ -506,7 +506,7 @@ static u32 ddb_input_avail(struct ddb_input *input)
        return 0;
 }
 
-static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count)
+static ssize_t ddb_input_read(struct ddb_input *input, __user u8 *buf, size_t count)
 {
        struct ddb *dev = input->port->dev;
        u32 left = count;
@@ -849,7 +849,7 @@ static int dvb_input_attach(struct ddb_input *input)
                return ret;
        input->attached = 4;
 
-       input->fe = 0;
+       input->fe = NULL;
        switch (port->type) {
        case DDB_TUNER_DVBS_ST:
                if (demod_attach_stv0900(input, 0) < 0)
@@ -895,7 +895,7 @@ static int dvb_input_attach(struct ddb_input *input)
 /****************************************************************************/
 /****************************************************************************/
 
-static ssize_t ts_write(struct file *file, const char *buf,
+static ssize_t ts_write(struct file *file, const __user char *buf,
                        size_t count, loff_t *ppos)
 {
        struct dvb_device *dvbdev = file->private_data;
@@ -920,7 +920,7 @@ static ssize_t ts_write(struct file *file, const char *buf,
        return (left == count) ? -EAGAIN : (count - left);
 }
 
-static ssize_t ts_read(struct file *file, char *buf,
+static ssize_t ts_read(struct file *file, __user char *buf,
                       size_t count, loff_t *ppos)
 {
        struct dvb_device *dvbdev = file->private_data;
@@ -975,11 +975,9 @@ static const struct file_operations ci_fops = {
        .open    = dvb_generic_open,
        .release = dvb_generic_release,
        .poll    = ts_poll,
-       .mmap    = 0,
 };
 
 static struct dvb_device dvbdev_ci = {
-       .priv    = 0,
        .readers = -1,
        .writers = -1,
        .users   = -1,
@@ -1038,7 +1036,7 @@ static void output_tasklet(unsigned long data)
 }
 
 
-struct cxd2099_cfg cxd_cfg = {
+static struct cxd2099_cfg cxd_cfg = {
        .bitrate =  62000,
        .adr     =  0x40,
        .polarity = 1,
@@ -1127,7 +1125,7 @@ static void ddb_ports_detach(struct ddb *dev)
                                ddb_output_stop(port->output);
                                dvb_ca_en50221_release(port->en);
                                kfree(port->en);
-                               port->en = 0;
+                               port->en = NULL;
                                dvb_unregister_adapter(&port->output->adap);
                        }
                        break;
@@ -1413,9 +1411,9 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
 #define DDB_MAGIC 'd'
 
 struct ddb_flashio {
-       __u8 *write_buf;
+       __user __u8 *write_buf;
        __u32 write_len;
-       __u8 *read_buf;
+       __user __u8 *read_buf;
        __u32 read_len;
 };
 
@@ -1439,7 +1437,7 @@ static int ddb_open(struct inode *inode, struct file *file)
 static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct ddb *dev = file->private_data;
-       void *parg = (void *)arg;
+       __user void *parg = (__user void *)arg;
        int res;
 
        switch (cmd) {
@@ -1558,7 +1556,7 @@ static void ddb_remove(struct pci_dev *pdev)
        ddb_device_destroy(dev);
 
        ddb_unmap(dev);
-       pci_set_drvdata(pdev, 0);
+       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
 }
 
@@ -1637,7 +1635,7 @@ fail1:
 fail:
        printk(KERN_ERR "fail\n");
        ddb_unmap(dev);
-       pci_set_drvdata(pdev, 0);
+       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
        return -1;
 }
index 8b1b41d2a52d4d465b233d57755fc724d5f9ab56..be87fbd9045646214f61aeda2f573ba1e62b1462 100644 (file)
@@ -156,7 +156,7 @@ struct ddb_port {
 
 struct ddb {
        struct pci_dev        *pdev;
-       unsigned char         *regs;
+       unsigned char __iomem *regs;
        struct ddb_port        port[DDB_MAX_PORT];
        struct ddb_i2c         i2c[DDB_MAX_I2C];
        struct ddb_input       input[DDB_MAX_INPUT];
@@ -173,12 +173,10 @@ struct ddb {
 /****************************************************************************/
 
 #define ddbwritel(_val, _adr)        writel((_val), \
-                                    (char *) (dev->regs+(_adr)))
-#define ddbreadl(_adr)               readl((char *) (dev->regs+(_adr)))
-#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *)      \
-                                    (dev->regs+(_adr)), (_src), (_count))
-#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \
-                                      (dev->regs+(_adr)), (_count))
+                                    dev->regs+(_adr))
+#define ddbreadl(_adr)               readl(dev->regs+(_adr))
+#define ddbcpyto(_adr, _src, _count) memcpy_toio(dev->regs+(_adr), (_src), (_count))
+#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), dev->regs+(_adr), (_count))
 
 /****************************************************************************/
 
index e8826c535ccdb2980fc6e931831f2826bcb51161..ed11716731e95cdb6a51ce90b7df6c4de267e44d 100644 (file)
@@ -614,7 +614,7 @@ static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 
 static void dm1105_set_dma_addr(struct dm1105_dev *dev)
 {
-       dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr));
+       dm_writel(DM1105_STADR, (__force u32)cpu_to_le32(dev->dma_addr));
 }
 
 static int dm1105_dma_map(struct dm1105_dev *dev)
index 7a9b98bc208bc252ae9d1dc334fabb48bf39c5f5..7bf9cbca4fa64fe3e63c3d17ea7432e661069e85 100644 (file)
@@ -81,7 +81,7 @@ static void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc,
        int period_elapsed = 0;
        int length;
 
-       dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zd\n", itvsc,
+       dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zu\n", itvsc,
                pcm_data, num_bytes);
 
        substream = itvsc->capture_pcm_substream;
index ed73edd2bcd308e310197ac3ff9bff2559905bd5..4b0e758a7bce38d68deab9d55a1445e21fee201e 100644 (file)
@@ -65,7 +65,7 @@ retry:
                           the wrong file was sometimes loaded. So we check filesizes to
                           see if at least the right-sized file was loaded. If not, then we
                           retry. */
-                       IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size);
+                       IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zu)\n", fn, size, fw->size);
                        release_firmware(fw);
                        retries--;
                        goto retry;
@@ -76,7 +76,7 @@ retry:
                        dst++;
                        src++;
                }
-               IVTV_INFO("Loaded %s firmware (%zd bytes)\n", fn, fw->size);
+               IVTV_INFO("Loaded %s firmware (%zu bytes)\n", fn, fw->size);
                release_firmware(fw);
                return size;
        }
index 19a7c9b990a393b93adb27818d069560f1027bda..ab6d5d25aa6fdd1a757814c18318dc5881e47a41 100644 (file)
@@ -192,11 +192,11 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
                if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
                    s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
                        s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET);
-                       write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
+                       write_dec_sync(DMA_MAGIC_COOKIE, offset - IVTV_DECODER_OFFSET);
                }
                else {
                        s->pending_backup = read_enc(offset);
-                       write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
+                       write_enc_sync(DMA_MAGIC_COOKIE, offset);
                }
                s->pending_offset = offset;
        }
@@ -275,13 +275,11 @@ static void dma_post(struct ivtv_stream *s)
 
                if (x == 0 && ivtv_use_dma(s)) {
                        offset = s->dma_last_offset;
-                       if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
+                       if (le32_to_cpu(u32buf[offset / 4]) != DMA_MAGIC_COOKIE)
                        {
-                               for (offset = 0; offset < 64; offset++) {
-                                       if (u32buf[offset] == DMA_MAGIC_COOKIE) {
+                               for (offset = 0; offset < 64; offset++)
+                                       if (le32_to_cpu(u32buf[offset]) == DMA_MAGIC_COOKIE)
                                                break;
-                                       }
-                               }
                                offset *= 4;
                                if (offset == 256) {
                                        IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name);
index 68a29f8bdf732b1e7bad70cba5afb5f365c86e6e..1032db6bb789548633cf9c62cac0d8b7307fe1a7 100644 (file)
@@ -34,7 +34,7 @@
 #include "mantis_dvb.h"
 #include "hopper_vp3028.h"
 
-struct zl10353_config hopper_vp3028_config = {
+static struct zl10353_config hopper_vp3028_config = {
        .demod_address  = 0x0f,
 };
 
index f2410cf0a6bf0ec980b475f8785d5afb7a58ec52..8ff448bb792d796f6a58273ca247a34487f7e459 100644 (file)
@@ -127,7 +127,7 @@ struct mantis_pci {
        u32                     last_block;
        u8                      *buf_cpu;
        dma_addr_t              buf_dma;
-       u32                     *risc_cpu;
+       __le32                  *risc_cpu;
        dma_addr_t              risc_dma;
 
        struct tasklet_struct   tasklet;
index 115003e8d19d453f30a2db59664d3a561c614116..12a6adb2bd7e167cc575abc04588ca018a4b39b7 100644 (file)
@@ -35,7 +35,7 @@
 #include "mantis_vp1033.h"
 #include "mantis_reg.h"
 
-u8 lgtdqcs001f_inittab[] = {
+static u8 lgtdqcs001f_inittab[] = {
        0x01, 0x15,
        0x02, 0x30,
        0x03, 0x00,
@@ -150,7 +150,7 @@ static int lgtdqcs001f_set_symbol_rate(struct dvb_frontend *fe,
        return 0;
 }
 
-struct stv0299_config lgtdqcs001f_config = {
+static struct stv0299_config lgtdqcs001f_config = {
        .demod_address          = 0x68,
        .inittab                = lgtdqcs001f_inittab,
        .mclk                   = 88000000UL,
index 430ae84ce528dd99545b061929928233950dc845..7c1bd167225c28b3bf6c9819e7294e7e197a833d 100644 (file)
@@ -36,7 +36,7 @@
 #include "mantis_vp1034.h"
 #include "mantis_reg.h"
 
-struct mb86a16_config vp1034_mb86a16_config = {
+static struct mb86a16_config vp1034_mb86a16_config = {
        .demod_address  = 0x08,
        .set_voltage    = vp1034_set_voltage,
 };
index 07a20748b707ef5356bd40fac96dc891d974716e..7082fcbc94a1001d6412b3b704673c807144f8c1 100644 (file)
@@ -263,7 +263,7 @@ static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = {
        { 0xffff                        , 0xff },
 };
 
-struct stb0899_config vp1041_stb0899_config = {
+static struct stb0899_config vp1041_stb0899_config = {
        .init_dev               = vp1041_stb0899_s1_init_1,
        .init_s2_demod          = stb0899_s2_init_2,
        .init_s1_demod          = vp1041_stb0899_s1_init_3,
@@ -300,7 +300,7 @@ struct stb0899_config vp1041_stb0899_config = {
        .tuner_set_rfsiggain    = NULL,
 };
 
-struct stb6100_config vp1041_stb6100_config = {
+static struct stb6100_config vp1041_stb6100_config = {
        .tuner_address  = 0x60,
        .refclock       = 27000000,
 };
index 1ca6837fbe468a8da8f82fe7818a4240880e5004..8d48b5abe04a5eb42d3eb0586b85e6884751ca9f 100644 (file)
 #define MANTIS_MODEL_NAME      "VP-2033"
 #define MANTIS_DEV_TYPE                "DVB-C"
 
-struct tda1002x_config vp2033_tda1002x_cu1216_config = {
+static struct tda1002x_config vp2033_tda1002x_cu1216_config = {
        .demod_address = 0x18 >> 1,
        .invert = 1,
 };
 
-struct tda10023_config vp2033_tda10023_cu1216_config = {
+static struct tda10023_config vp2033_tda10023_cu1216_config = {
        .demod_address = 0x18 >> 1,
        .invert = 1,
 };
index d480741afd786dc3e13420077d43f4567f38e666..8dd17d7c08819b83dedf4110571223daa12f1025 100644 (file)
 #define MANTIS_MODEL_NAME      "VP-2040"
 #define MANTIS_DEV_TYPE                "DVB-C"
 
-struct tda1002x_config vp2040_tda1002x_cu1216_config = {
+static struct tda1002x_config vp2040_tda1002x_cu1216_config = {
        .demod_address  = 0x18 >> 1,
        .invert         = 1,
 };
 
-struct tda10023_config vp2040_tda10023_cu1216_config = {
+static struct tda10023_config vp2040_tda10023_cu1216_config = {
        .demod_address  = 0x18 >> 1,
        .invert         = 1,
 };
index c09308cd3ac677e0d53ace29e525039e3482dc04..5c1dd925bdd5000ed61dd59c6bcd24c76d5717c0 100644 (file)
 #include "mantis_dvb.h"
 #include "mantis_vp3030.h"
 
-struct zl10353_config mantis_vp3030_config = {
+static struct zl10353_config mantis_vp3030_config = {
        .demod_address          = 0x0f,
 };
 
-struct tda665x_config env57h12d5_config = {
+static struct tda665x_config env57h12d5_config = {
        .name                   = "ENV57H12D5 (ET-50DT)",
        .addr                   = 0x60,
        .frequency_min          =  47000000,
index 9e82d2105d5365659be6dd7560927066f6427235..039bed3cc919d2f5624e1635327e126abdfca064 100644 (file)
@@ -696,7 +696,7 @@ static struct ngene_info ngene_info_m780 = {
        .demod_attach   = { NULL, demod_attach_lg330x },
 
        /* Ensure these are NULL else the frame will call them (as funcs) */
-       .tuner_attach   = { 0, 0, 0, 0 },
+       .tuner_attach   = { NULL, NULL, NULL, NULL },
        .fe_config      = { NULL, &aver_m780 },
        .avf            = { 0 },
 
index 4930b55fd5f4f855399ebb6c828daece0c579ce8..e29bc3af4bafe3ad25a8e059c9d0697b12940874 100644 (file)
@@ -57,15 +57,13 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 #define dprintk        if (debug) printk
 
-#define ngwriteb(dat, adr)         writeb((dat), (char *)(dev->iomem + (adr)))
-#define ngwritel(dat, adr)         writel((dat), (char *)(dev->iomem + (adr)))
-#define ngwriteb(dat, adr)         writeb((dat), (char *)(dev->iomem + (adr)))
+#define ngwriteb(dat, adr)         writeb((dat), dev->iomem + (adr))
+#define ngwritel(dat, adr)         writel((dat), dev->iomem + (adr))
+#define ngwriteb(dat, adr)         writeb((dat), dev->iomem + (adr))
 #define ngreadl(adr)               readl(dev->iomem + (adr))
 #define ngreadb(adr)               readb(dev->iomem + (adr))
-#define ngcpyto(adr, src, count)   memcpy_toio((char *) \
-                                  (dev->iomem + (adr)), (src), (count))
-#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \
-                                  (dev->iomem + (adr)), (count))
+#define ngcpyto(adr, src, count)   memcpy_toio(dev->iomem + (adr), (src), (count))
+#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), dev->iomem + (adr), (count))
 
 /****************************************************************************/
 /* nGene interrupt handler **************************************************/
@@ -1592,7 +1590,7 @@ static void cxd_detach(struct ngene *dev)
 
        dvb_ca_en50221_release(ci->en);
        kfree(ci->en);
-       ci->en = 0;
+       ci->en = NULL;
 }
 
 /***********************************/
index fcb16a615aab002768f80b3e7d6c5cf66ac5f123..59bb2858c8d0127c8779f3323b8f1d1a25b832e2 100644 (file)
@@ -47,7 +47,7 @@
 /* COMMAND API interface ****************************************************/
 /****************************************************************************/
 
-static ssize_t ts_write(struct file *file, const char *buf,
+static ssize_t ts_write(struct file *file, const char __user *buf,
                        size_t count, loff_t *ppos)
 {
        struct dvb_device *dvbdev = file->private_data;
@@ -59,12 +59,12 @@ static ssize_t ts_write(struct file *file, const char *buf,
                                     (&dev->tsout_rbuf) >= count) < 0)
                return 0;
 
-       dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count);
+       dvb_ringbuffer_write_user(&dev->tsout_rbuf, buf, count);
 
        return count;
 }
 
-static ssize_t ts_read(struct file *file, char *buf,
+static ssize_t ts_read(struct file *file, char __user *buf,
                       size_t count, loff_t *ppos)
 {
        struct dvb_device *dvbdev = file->private_data;
@@ -97,7 +97,6 @@ static const struct file_operations ci_fops = {
 };
 
 struct dvb_device ngene_dvbdev_ci = {
-       .priv    = 0,
        .readers = -1,
        .writers = -1,
        .users   = -1,
index 22c39ff6bfa0e90c278d08c0cbc84d156c8e5732..51e2fbd18b1b1532cd3d7c1ef83c3b258c75219d 100644 (file)
@@ -737,7 +737,7 @@ typedef void (tx_cb_t)(struct ngene *, u32);
 struct ngene {
        int                   nr;
        struct pci_dev       *pci_dev;
-       unsigned char        *iomem;
+       unsigned char __iomem *iomem;
 
        /*struct i2c_adapter  i2c_adapter;*/
 
diff --git a/drivers/media/pci/pt3/Kconfig b/drivers/media/pci/pt3/Kconfig
new file mode 100644 (file)
index 0000000..16c208a
--- /dev/null
@@ -0,0 +1,10 @@
+config DVB_PT3
+       tristate "Earthsoft PT3 cards"
+       depends on DVB_CORE && PCI && I2C
+       select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_QM1D1C0042 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_MXL301RF if MEDIA_SUBDRV_AUTOSELECT
+       help
+         Support for Earthsoft PT3 PCIe cards.
+
+         Say Y or M if you own such a device and want to use it.
diff --git a/drivers/media/pci/pt3/Makefile b/drivers/media/pci/pt3/Makefile
new file mode 100644 (file)
index 0000000..396f146
--- /dev/null
@@ -0,0 +1,8 @@
+
+earth-pt3-objs += pt3.o pt3_i2c.o pt3_dma.o
+
+obj-$(CONFIG_DVB_PT3) += earth-pt3.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c
new file mode 100644 (file)
index 0000000..1fdeac1
--- /dev/null
@@ -0,0 +1,876 @@
+/*
+ * Earthsoft PT3 driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+
+#include <linux/freezer.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+
+#include "pt3.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static bool one_adapter;
+module_param(one_adapter, bool, 0444);
+MODULE_PARM_DESC(one_adapter, "Place FE's together under one adapter.");
+
+static int num_bufs = 4;
+module_param(num_bufs, int, 0444);
+MODULE_PARM_DESC(num_bufs, "Number of DMA buffer (188KiB) per FE.");
+
+
+static const struct i2c_algorithm pt3_i2c_algo = {
+       .master_xfer   = &pt3_i2c_master_xfer,
+       .functionality = &pt3_i2c_functionality,
+};
+
+static const struct pt3_adap_config adap_conf[PT3_NUM_FE] = {
+       {
+               .demod_info = {
+                       I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x11),
+               },
+               .tuner_info = {
+                       I2C_BOARD_INFO("qm1d1c0042", 0x63),
+               },
+               .tuner_cfg.qm1d1c0042 = {
+                       .lpf = 1,
+               },
+               .init_freq = 1049480 - 300,
+       },
+       {
+               .demod_info = {
+                       I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x10),
+               },
+               .tuner_info = {
+                       I2C_BOARD_INFO("mxl301rf", 0x62),
+               },
+               .init_freq = 515142857,
+       },
+       {
+               .demod_info = {
+                       I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x13),
+               },
+               .tuner_info = {
+                       I2C_BOARD_INFO("qm1d1c0042", 0x60),
+               },
+               .tuner_cfg.qm1d1c0042 = {
+                       .lpf = 1,
+               },
+               .init_freq = 1049480 + 300,
+       },
+       {
+               .demod_info = {
+                       I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x12),
+               },
+               .tuner_info = {
+                       I2C_BOARD_INFO("mxl301rf", 0x61),
+               },
+               .init_freq = 521142857,
+       },
+};
+
+
+struct reg_val {
+       u8 reg;
+       u8 val;
+};
+
+static int
+pt3_demod_write(struct pt3_adapter *adap, const struct reg_val *data, int num)
+{
+       struct i2c_msg msg;
+       int i, ret;
+
+       ret = 0;
+       msg.addr = adap->i2c_demod->addr;
+       msg.flags = 0;
+       msg.len = 2;
+       for (i = 0; i < num; i++) {
+               msg.buf = (u8 *)&data[i];
+               ret = i2c_transfer(adap->i2c_demod->adapter, &msg, 1);
+               if (ret == 0)
+                       ret = -EREMOTE;
+               if (ret < 0)
+                       return ret;
+       }
+       return 0;
+}
+
+static inline void pt3_lnb_ctrl(struct pt3_board *pt3, bool on)
+{
+       iowrite32((on ? 0x0f : 0x0c), pt3->regs[0] + REG_SYSTEM_W);
+}
+
+static inline struct pt3_adapter *pt3_find_adapter(struct dvb_frontend *fe)
+{
+       struct pt3_board *pt3;
+       int i;
+
+       if (one_adapter) {
+               pt3 = fe->dvb->priv;
+               for (i = 0; i < PT3_NUM_FE; i++)
+                       if (pt3->adaps[i]->fe == fe)
+                               return pt3->adaps[i];
+       }
+       return container_of(fe->dvb, struct pt3_adapter, dvb_adap);
+}
+
+/*
+ * all 4 tuners in PT3 are packaged in a can module (Sharp VA4M6JC2103).
+ * it seems that they share the power lines and Amp power line and
+ * adaps[3] controls those powers.
+ */
+static int
+pt3_set_tuner_power(struct pt3_board *pt3, bool tuner_on, bool amp_on)
+{
+       struct reg_val rv = { 0x1e, 0x99 };
+
+       if (tuner_on)
+               rv.val |= 0x40;
+       if (amp_on)
+               rv.val |= 0x04;
+       return pt3_demod_write(pt3->adaps[PT3_NUM_FE - 1], &rv, 1);
+}
+
+static int pt3_set_lna(struct dvb_frontend *fe)
+{
+       struct pt3_adapter *adap;
+       struct pt3_board *pt3;
+       u32 val;
+       int ret;
+
+       /* LNA is shared btw. 2 TERR-tuners */
+
+       adap = pt3_find_adapter(fe);
+       val = fe->dtv_property_cache.lna;
+       if (val == LNA_AUTO || val == adap->cur_lna)
+               return 0;
+
+       pt3 = adap->dvb_adap.priv;
+       if (mutex_lock_interruptible(&pt3->lock))
+               return -ERESTARTSYS;
+       if (val)
+               pt3->lna_on_cnt++;
+       else
+               pt3->lna_on_cnt--;
+
+       if (val && pt3->lna_on_cnt <= 1) {
+               pt3->lna_on_cnt = 1;
+               ret = pt3_set_tuner_power(pt3, true, true);
+       } else if (!val && pt3->lna_on_cnt <= 0) {
+               pt3->lna_on_cnt = 0;
+               ret = pt3_set_tuner_power(pt3, true, false);
+       } else
+               ret = 0;
+       mutex_unlock(&pt3->lock);
+       adap->cur_lna = (val != 0);
+       return ret;
+}
+
+static int pt3_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+       struct pt3_adapter *adap;
+       struct pt3_board *pt3;
+       bool on;
+
+       /* LNB power is shared btw. 2 SAT-tuners */
+
+       adap = pt3_find_adapter(fe);
+       on = (volt != SEC_VOLTAGE_OFF);
+       if (on == adap->cur_lnb)
+               return 0;
+       adap->cur_lnb = on;
+       pt3 = adap->dvb_adap.priv;
+       if (mutex_lock_interruptible(&pt3->lock))
+               return -ERESTARTSYS;
+       if (on)
+               pt3->lnb_on_cnt++;
+       else
+               pt3->lnb_on_cnt--;
+
+       if (on && pt3->lnb_on_cnt <= 1) {
+               pt3->lnb_on_cnt = 1;
+               pt3_lnb_ctrl(pt3, true);
+       } else if (!on && pt3->lnb_on_cnt <= 0) {
+               pt3->lnb_on_cnt = 0;
+               pt3_lnb_ctrl(pt3, false);
+       }
+       mutex_unlock(&pt3->lock);
+       return 0;
+}
+
+/* register values used in pt3_fe_init() */
+
+static const struct reg_val init0_sat[] = {
+       { 0x03, 0x01 },
+       { 0x1e, 0x10 },
+};
+static const struct reg_val init0_ter[] = {
+       { 0x01, 0x40 },
+       { 0x1c, 0x10 },
+};
+static const struct reg_val cfg_sat[] = {
+       { 0x1c, 0x15 },
+       { 0x1f, 0x04 },
+};
+static const struct reg_val cfg_ter[] = {
+       { 0x1d, 0x01 },
+};
+
+/*
+ * pt3_fe_init: initialize demod sub modules and ISDB-T tuners all at once.
+ *
+ * As for demod IC (TC90522) and ISDB-T tuners (MxL301RF),
+ * the i2c sequences for init'ing them are not public and hidden in a ROM,
+ * and include the board specific configurations as well.
+ * They are stored in a lump and cannot be taken out / accessed separately,
+ * thus cannot be moved to the FE/tuner driver.
+ */
+static int pt3_fe_init(struct pt3_board *pt3)
+{
+       int i, ret;
+       struct dvb_frontend *fe;
+
+       pt3_i2c_reset(pt3);
+       ret = pt3_init_all_demods(pt3);
+       if (ret < 0) {
+               dev_warn(&pt3->pdev->dev, "Failed to init demod chips.");
+               return ret;
+       }
+
+       /* additional config? */
+       for (i = 0; i < PT3_NUM_FE; i++) {
+               fe = pt3->adaps[i]->fe;
+
+               if (fe->ops.delsys[0] == SYS_ISDBS)
+                       ret = pt3_demod_write(pt3->adaps[i],
+                                             init0_sat, ARRAY_SIZE(init0_sat));
+               else
+                       ret = pt3_demod_write(pt3->adaps[i],
+                                             init0_ter, ARRAY_SIZE(init0_ter));
+               if (ret < 0) {
+                       dev_warn(&pt3->pdev->dev,
+                                "demod[%d] faild in init sequence0.", i);
+                       return ret;
+               }
+               ret = fe->ops.init(fe);
+               if (ret < 0)
+                       return ret;
+       }
+
+       usleep_range(2000, 4000);
+       ret = pt3_set_tuner_power(pt3, true, false);
+       if (ret < 0) {
+               dev_warn(&pt3->pdev->dev, "Failed to control tuner module.");
+               return ret;
+       }
+
+       /* output pin configuration */
+       for (i = 0; i < PT3_NUM_FE; i++) {
+               fe = pt3->adaps[i]->fe;
+               if (fe->ops.delsys[0] == SYS_ISDBS)
+                       ret = pt3_demod_write(pt3->adaps[i],
+                                               cfg_sat, ARRAY_SIZE(cfg_sat));
+               else
+                       ret = pt3_demod_write(pt3->adaps[i],
+                                               cfg_ter, ARRAY_SIZE(cfg_ter));
+               if (ret < 0) {
+                       dev_warn(&pt3->pdev->dev,
+                                "demod[%d] faild in init sequence1.", i);
+                       return ret;
+               }
+       }
+       usleep_range(4000, 6000);
+
+       for (i = 0; i < PT3_NUM_FE; i++) {
+               fe = pt3->adaps[i]->fe;
+               if (fe->ops.delsys[0] != SYS_ISDBS)
+                       continue;
+               /* init and wake-up ISDB-S tuners */
+               ret = fe->ops.tuner_ops.init(fe);
+               if (ret < 0) {
+                       dev_warn(&pt3->pdev->dev,
+                                "Failed to init SAT-tuner[%d].", i);
+                       return ret;
+               }
+       }
+       ret = pt3_init_all_mxl301rf(pt3);
+       if (ret < 0) {
+               dev_warn(&pt3->pdev->dev, "Failed to init TERR-tuners.");
+               return ret;
+       }
+
+       ret = pt3_set_tuner_power(pt3, true, true);
+       if (ret < 0) {
+               dev_warn(&pt3->pdev->dev, "Failed to control tuner module.");
+               return ret;
+       }
+
+       /* Wake up all tuners and make an initial tuning,
+        * in order to avoid interference among the tuners in the module,
+        * according to the doc from the manufacturer.
+        */
+       for (i = 0; i < PT3_NUM_FE; i++) {
+               fe = pt3->adaps[i]->fe;
+               ret = 0;
+               if (fe->ops.delsys[0] == SYS_ISDBT)
+                       ret = fe->ops.tuner_ops.init(fe);
+               /* set only when called from pt3_probe(), not resume() */
+               if (ret == 0 && fe->dtv_property_cache.frequency == 0) {
+                       fe->dtv_property_cache.frequency =
+                                               adap_conf[i].init_freq;
+                       ret = fe->ops.tuner_ops.set_params(fe);
+               }
+               if (ret < 0) {
+                       dev_warn(&pt3->pdev->dev,
+                                "Failed in initial tuning of tuner[%d].", i);
+                       return ret;
+               }
+       }
+
+       /* and sleep again, waiting to be opened by users. */
+       for (i = 0; i < PT3_NUM_FE; i++) {
+               fe = pt3->adaps[i]->fe;
+               if (fe->ops.tuner_ops.sleep)
+                       ret = fe->ops.tuner_ops.sleep(fe);
+               if (ret < 0)
+                       break;
+               if (fe->ops.sleep)
+                       ret = fe->ops.sleep(fe);
+               if (ret < 0)
+                       break;
+               if (fe->ops.delsys[0] == SYS_ISDBS)
+                       fe->ops.set_voltage = &pt3_set_voltage;
+               else
+                       fe->ops.set_lna = &pt3_set_lna;
+       }
+       if (i < PT3_NUM_FE) {
+               dev_warn(&pt3->pdev->dev, "FE[%d] failed to standby.", i);
+               return ret;
+       }
+       return 0;
+}
+
+
+static int pt3_attach_fe(struct pt3_board *pt3, int i)
+{
+       struct i2c_board_info info;
+       struct tc90522_config cfg;
+       struct i2c_client *cl;
+       struct dvb_adapter *dvb_adap;
+       int ret;
+
+       info = adap_conf[i].demod_info;
+       cfg = adap_conf[i].demod_cfg;
+       cfg.tuner_i2c = NULL;
+       info.platform_data = &cfg;
+
+       ret = -ENODEV;
+       request_module("tc90522");
+       cl = i2c_new_device(&pt3->i2c_adap, &info);
+       if (!cl || !cl->dev.driver)
+               return -ENODEV;
+       pt3->adaps[i]->i2c_demod = cl;
+       if (!try_module_get(cl->dev.driver->owner))
+               goto err_demod_i2c_unregister_device;
+
+       if (!strncmp(cl->name, TC90522_I2C_DEV_SAT, sizeof(cl->name))) {
+               struct qm1d1c0042_config tcfg;
+
+               tcfg = adap_conf[i].tuner_cfg.qm1d1c0042;
+               tcfg.fe = cfg.fe;
+               info = adap_conf[i].tuner_info;
+               info.platform_data = &tcfg;
+               request_module("qm1d1c0042");
+               cl = i2c_new_device(cfg.tuner_i2c, &info);
+       } else {
+               struct mxl301rf_config tcfg;
+
+               tcfg = adap_conf[i].tuner_cfg.mxl301rf;
+               tcfg.fe = cfg.fe;
+               info = adap_conf[i].tuner_info;
+               info.platform_data = &tcfg;
+               request_module("mxl301rf");
+               cl = i2c_new_device(cfg.tuner_i2c, &info);
+       }
+       if (!cl || !cl->dev.driver)
+               goto err_demod_module_put;
+       pt3->adaps[i]->i2c_tuner = cl;
+       if (!try_module_get(cl->dev.driver->owner))
+               goto err_tuner_i2c_unregister_device;
+
+       dvb_adap = &pt3->adaps[one_adapter ? 0 : i]->dvb_adap;
+       ret = dvb_register_frontend(dvb_adap, cfg.fe);
+       if (ret < 0)
+               goto err_tuner_module_put;
+       pt3->adaps[i]->fe = cfg.fe;
+       return 0;
+
+err_tuner_module_put:
+       module_put(pt3->adaps[i]->i2c_tuner->dev.driver->owner);
+err_tuner_i2c_unregister_device:
+       i2c_unregister_device(pt3->adaps[i]->i2c_tuner);
+err_demod_module_put:
+       module_put(pt3->adaps[i]->i2c_demod->dev.driver->owner);
+err_demod_i2c_unregister_device:
+       i2c_unregister_device(pt3->adaps[i]->i2c_demod);
+
+       return ret;
+}
+
+
+static int pt3_fetch_thread(void *data)
+{
+       struct pt3_adapter *adap = data;
+       ktime_t delay;
+       bool was_frozen;
+
+#define PT3_INITIAL_BUF_DROPS 4
+#define PT3_FETCH_DELAY 10
+#define PT3_FETCH_DELAY_DELTA 2
+
+       pt3_init_dmabuf(adap);
+       adap->num_discard = PT3_INITIAL_BUF_DROPS;
+
+       dev_dbg(adap->dvb_adap.device,
+               "PT3: [%s] started.\n", adap->thread->comm);
+       set_freezable();
+       while (!kthread_freezable_should_stop(&was_frozen)) {
+               if (was_frozen)
+                       adap->num_discard = PT3_INITIAL_BUF_DROPS;
+
+               pt3_proc_dma(adap);
+
+               delay = ktime_set(0, PT3_FETCH_DELAY * NSEC_PER_MSEC);
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               freezable_schedule_hrtimeout_range(&delay,
+                                       PT3_FETCH_DELAY_DELTA * NSEC_PER_MSEC,
+                                       HRTIMER_MODE_REL);
+       }
+       dev_dbg(adap->dvb_adap.device,
+               "PT3: [%s] exited.\n", adap->thread->comm);
+       adap->thread = NULL;
+       return 0;
+}
+
+static int pt3_start_streaming(struct pt3_adapter *adap)
+{
+       struct task_struct *thread;
+
+       /* start fetching thread */
+       thread = kthread_run(pt3_fetch_thread, adap, "pt3-ad%i-dmx%i",
+                               adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
+       if (IS_ERR(thread)) {
+               int ret = PTR_ERR(thread);
+
+               dev_warn(adap->dvb_adap.device,
+                       "PT3 (adap:%d, dmx:%d): failed to start kthread.\n",
+                       adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
+               return ret;
+       }
+       adap->thread = thread;
+
+       return pt3_start_dma(adap);
+}
+
+static int pt3_stop_streaming(struct pt3_adapter *adap)
+{
+       int ret;
+
+       ret = pt3_stop_dma(adap);
+       if (ret)
+               dev_warn(adap->dvb_adap.device,
+                       "PT3: failed to stop streaming of adap:%d/FE:%d\n",
+                       adap->dvb_adap.num, adap->fe->id);
+
+       /* kill the fetching thread */
+       ret = kthread_stop(adap->thread);
+       return ret;
+}
+
+static int pt3_start_feed(struct dvb_demux_feed *feed)
+{
+       struct pt3_adapter *adap;
+
+       if (signal_pending(current))
+               return -EINTR;
+
+       adap = container_of(feed->demux, struct pt3_adapter, demux);
+       adap->num_feeds++;
+       if (adap->thread)
+               return 0;
+       if (adap->num_feeds != 1) {
+               dev_warn(adap->dvb_adap.device,
+                       "%s: unmatched start/stop_feed in adap:%i/dmx:%i.\n",
+                       __func__, adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
+               adap->num_feeds = 1;
+       }
+
+       return pt3_start_streaming(adap);
+
+}
+
+static int pt3_stop_feed(struct dvb_demux_feed *feed)
+{
+       struct pt3_adapter *adap;
+
+       adap = container_of(feed->demux, struct pt3_adapter, demux);
+
+       adap->num_feeds--;
+       if (adap->num_feeds > 0 || !adap->thread)
+               return 0;
+       adap->num_feeds = 0;
+
+       return pt3_stop_streaming(adap);
+}
+
+
+static int pt3_alloc_adapter(struct pt3_board *pt3, int index)
+{
+       int ret;
+       struct pt3_adapter *adap;
+       struct dvb_adapter *da;
+
+       adap = kzalloc(sizeof(*adap), GFP_KERNEL);
+       if (!adap) {
+               dev_err(&pt3->pdev->dev, "failed to alloc mem for adapter.\n");
+               return -ENOMEM;
+       }
+       pt3->adaps[index] = adap;
+       adap->adap_idx = index;
+
+       if (index == 0 || !one_adapter) {
+               ret = dvb_register_adapter(&adap->dvb_adap, "PT3 DVB",
+                               THIS_MODULE, &pt3->pdev->dev, adapter_nr);
+               if (ret < 0) {
+                       dev_err(&pt3->pdev->dev,
+                               "failed to register adapter dev.\n");
+                       goto err_mem;
+               }
+               da = &adap->dvb_adap;
+       } else
+               da = &pt3->adaps[0]->dvb_adap;
+
+       adap->dvb_adap.priv = pt3;
+       adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+       adap->demux.priv = adap;
+       adap->demux.feednum = 256;
+       adap->demux.filternum = 256;
+       adap->demux.start_feed = pt3_start_feed;
+       adap->demux.stop_feed = pt3_stop_feed;
+       ret = dvb_dmx_init(&adap->demux);
+       if (ret < 0) {
+               dev_err(&pt3->pdev->dev, "failed to init dmx dev.\n");
+               goto err_adap;
+       }
+
+       adap->dmxdev.filternum = 256;
+       adap->dmxdev.demux = &adap->demux.dmx;
+       ret = dvb_dmxdev_init(&adap->dmxdev, da);
+       if (ret < 0) {
+               dev_err(&pt3->pdev->dev, "failed to init dmxdev.\n");
+               goto err_demux;
+       }
+
+       ret = pt3_alloc_dmabuf(adap);
+       if (ret) {
+               dev_err(&pt3->pdev->dev, "failed to alloc DMA buffers.\n");
+               goto err_dmabuf;
+       }
+
+       return 0;
+
+err_dmabuf:
+       pt3_free_dmabuf(adap);
+       dvb_dmxdev_release(&adap->dmxdev);
+err_demux:
+       dvb_dmx_release(&adap->demux);
+err_adap:
+       if (index == 0 || !one_adapter)
+               dvb_unregister_adapter(da);
+err_mem:
+       kfree(adap);
+       pt3->adaps[index] = NULL;
+       return ret;
+}
+
+static void pt3_cleanup_adapter(struct pt3_board *pt3, int index)
+{
+       struct pt3_adapter *adap;
+       struct dmx_demux *dmx;
+
+       adap = pt3->adaps[index];
+       if (adap == NULL)
+               return;
+
+       /* stop demux kthread */
+       if (adap->thread)
+               pt3_stop_streaming(adap);
+
+       dmx = &adap->demux.dmx;
+       dmx->close(dmx);
+       if (adap->fe) {
+               adap->fe->callback = NULL;
+               if (adap->fe->frontend_priv)
+                       dvb_unregister_frontend(adap->fe);
+               if (adap->i2c_tuner) {
+                       module_put(adap->i2c_tuner->dev.driver->owner);
+                       i2c_unregister_device(adap->i2c_tuner);
+               }
+               if (adap->i2c_demod) {
+                       module_put(adap->i2c_demod->dev.driver->owner);
+                       i2c_unregister_device(adap->i2c_demod);
+               }
+       }
+       pt3_free_dmabuf(adap);
+       dvb_dmxdev_release(&adap->dmxdev);
+       dvb_dmx_release(&adap->demux);
+       if (index == 0 || !one_adapter)
+               dvb_unregister_adapter(&adap->dvb_adap);
+       kfree(adap);
+       pt3->adaps[index] = NULL;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int pt3_suspend(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pt3_board *pt3 = pci_get_drvdata(pdev);
+       int i;
+       struct pt3_adapter *adap;
+
+       for (i = 0; i < PT3_NUM_FE; i++) {
+               adap = pt3->adaps[i];
+               if (adap->num_feeds > 0)
+                       pt3_stop_dma(adap);
+               dvb_frontend_suspend(adap->fe);
+               pt3_free_dmabuf(adap);
+       }
+
+       pt3_lnb_ctrl(pt3, false);
+       pt3_set_tuner_power(pt3, false, false);
+       return 0;
+}
+
+static int pt3_resume(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pt3_board *pt3 = pci_get_drvdata(pdev);
+       int i, ret;
+       struct pt3_adapter *adap;
+
+       ret = pt3_fe_init(pt3);
+       if (ret)
+               return ret;
+
+       if (pt3->lna_on_cnt > 0)
+               pt3_set_tuner_power(pt3, true, true);
+       if (pt3->lnb_on_cnt > 0)
+               pt3_lnb_ctrl(pt3, true);
+
+       for (i = 0; i < PT3_NUM_FE; i++) {
+               adap = pt3->adaps[i];
+               dvb_frontend_resume(adap->fe);
+               ret = pt3_alloc_dmabuf(adap);
+               if (ret) {
+                       dev_err(&pt3->pdev->dev, "failed to alloc DMA bufs.\n");
+                       continue;
+               }
+               if (adap->num_feeds > 0)
+                       pt3_start_dma(adap);
+       }
+
+       return 0;
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+
+static void pt3_remove(struct pci_dev *pdev)
+{
+       struct pt3_board *pt3;
+       int i;
+
+       pt3 = pci_get_drvdata(pdev);
+       for (i = PT3_NUM_FE - 1; i >= 0; i--)
+               pt3_cleanup_adapter(pt3, i);
+       i2c_del_adapter(&pt3->i2c_adap);
+       kfree(pt3->i2c_buf);
+       pci_iounmap(pt3->pdev, pt3->regs[0]);
+       pci_iounmap(pt3->pdev, pt3->regs[1]);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       kfree(pt3);
+}
+
+static int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       u8 rev;
+       u32 ver;
+       int i, ret;
+       struct pt3_board *pt3;
+       struct i2c_adapter *i2c;
+
+       if (pci_read_config_byte(pdev, PCI_REVISION_ID, &rev) || rev != 1)
+               return -ENODEV;
+
+       ret = pci_enable_device(pdev);
+       if (ret < 0)
+               return -ENODEV;
+       pci_set_master(pdev);
+
+       ret = pci_request_regions(pdev, DRV_NAME);
+       if (ret < 0)
+               goto err_disable_device;
+
+       ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+       if (ret == 0)
+               dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
+       else {
+               ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+               if (ret == 0)
+                       dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+               else {
+                       dev_err(&pdev->dev, "Failed to set DMA mask.\n");
+                       goto err_release_regions;
+               }
+               dev_info(&pdev->dev, "Use 32bit DMA.\n");
+       }
+
+       pt3 = kzalloc(sizeof(*pt3), GFP_KERNEL);
+       if (!pt3) {
+               dev_err(&pdev->dev, "Failed to alloc mem for this dev.\n");
+               ret = -ENOMEM;
+               goto err_release_regions;
+       }
+       pci_set_drvdata(pdev, pt3);
+       pt3->pdev = pdev;
+       mutex_init(&pt3->lock);
+       pt3->regs[0] = pci_ioremap_bar(pdev, 0);
+       pt3->regs[1] = pci_ioremap_bar(pdev, 2);
+       if (pt3->regs[0] == NULL || pt3->regs[1] == NULL) {
+               dev_err(&pdev->dev, "Failed to ioremap.\n");
+               ret = -ENOMEM;
+               goto err_kfree;
+       }
+
+       ver = ioread32(pt3->regs[0] + REG_VERSION);
+       if ((ver >> 16) != 0x0301) {
+               dev_warn(&pdev->dev, "PT%d, I/F-ver.:%d not supported",
+                       ver >> 24, (ver & 0x00ff0000) >> 16);
+               ret = -ENODEV;
+               goto err_iounmap;
+       }
+
+       pt3->num_bufs = clamp_val(num_bufs, MIN_DATA_BUFS, MAX_DATA_BUFS);
+
+       pt3->i2c_buf = kmalloc(sizeof(*pt3->i2c_buf), GFP_KERNEL);
+       if (pt3->i2c_buf == NULL) {
+               dev_err(&pdev->dev, "Failed to alloc mem for i2c.\n");
+               ret = -ENOMEM;
+               goto err_iounmap;
+       }
+       i2c = &pt3->i2c_adap;
+       i2c->owner = THIS_MODULE;
+       i2c->algo = &pt3_i2c_algo;
+       i2c->algo_data = NULL;
+       i2c->dev.parent = &pdev->dev;
+       strlcpy(i2c->name, DRV_NAME, sizeof(i2c->name));
+       i2c_set_adapdata(i2c, pt3);
+       ret = i2c_add_adapter(i2c);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to add i2c adapter.\n");
+               goto err_i2cbuf;
+       }
+
+       for (i = 0; i < PT3_NUM_FE; i++) {
+               ret = pt3_alloc_adapter(pt3, i);
+               if (ret < 0)
+                       break;
+
+               ret = pt3_attach_fe(pt3, i);
+               if (ret < 0)
+                       break;
+       }
+       if (i < PT3_NUM_FE) {
+               dev_err(&pdev->dev, "Failed to create FE%d.\n", i);
+               goto err_cleanup_adapters;
+       }
+
+       ret = pt3_fe_init(pt3);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to init frontends.\n");
+               i = PT3_NUM_FE - 1;
+               goto err_cleanup_adapters;
+       }
+
+       dev_info(&pdev->dev,
+               "successfully init'ed PT%d (fw:0x%02x, I/F:0x%02x).\n",
+               ver >> 24, (ver >> 8) & 0xff, (ver >> 16) & 0xff);
+       return 0;
+
+err_cleanup_adapters:
+       while (i >= 0)
+               pt3_cleanup_adapter(pt3, i--);
+       i2c_del_adapter(i2c);
+err_i2cbuf:
+       kfree(pt3->i2c_buf);
+err_iounmap:
+       if (pt3->regs[0])
+               pci_iounmap(pdev, pt3->regs[0]);
+       if (pt3->regs[1])
+               pci_iounmap(pdev, pt3->regs[1]);
+err_kfree:
+       kfree(pt3);
+err_release_regions:
+       pci_release_regions(pdev);
+err_disable_device:
+       pci_disable_device(pdev);
+       return ret;
+
+}
+
+static const struct pci_device_id pt3_id_table[] = {
+       { PCI_DEVICE_SUB(0x1172, 0x4c15, 0xee8d, 0x0368) },
+       { },
+};
+MODULE_DEVICE_TABLE(pci, pt3_id_table);
+
+static SIMPLE_DEV_PM_OPS(pt3_pm_ops, pt3_suspend, pt3_resume);
+
+static struct pci_driver pt3_driver = {
+       .name           = DRV_NAME,
+       .probe          = pt3_probe,
+       .remove         = pt3_remove,
+       .id_table       = pt3_id_table,
+
+       .driver.pm      = &pt3_pm_ops,
+};
+
+module_pci_driver(pt3_driver);
+
+MODULE_DESCRIPTION("Earthsoft PT3 Driver");
+MODULE_AUTHOR("Akihiro TSUKADA");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/pt3/pt3.h b/drivers/media/pci/pt3/pt3.h
new file mode 100644 (file)
index 0000000..1b3f2ad
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Earthsoft PT3 driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+
+#ifndef PT3_H
+#define PT3_H
+
+#include <linux/atomic.h>
+#include <linux/types.h>
+
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dmxdev.h"
+
+#include "tc90522.h"
+#include "mxl301rf.h"
+#include "qm1d1c0042.h"
+
+#define DRV_NAME KBUILD_MODNAME
+
+#define PT3_NUM_FE 4
+
+/*
+ * register index of the FPGA chip
+ */
+#define REG_VERSION    0x00
+#define REG_BUS                0x04
+#define REG_SYSTEM_W   0x08
+#define REG_SYSTEM_R   0x0c
+#define REG_I2C_W      0x10
+#define REG_I2C_R      0x14
+#define REG_RAM_W      0x18
+#define REG_RAM_R      0x1c
+#define REG_DMA_BASE   0x40    /* regs for FE[i] = REG_DMA_BASE + 0x18 * i */
+#define OFST_DMA_DESC_L        0x00
+#define OFST_DMA_DESC_H        0x04
+#define OFST_DMA_CTL   0x08
+#define OFST_TS_CTL    0x0c
+#define OFST_STATUS    0x10
+#define OFST_TS_ERR    0x14
+
+/*
+ * internal buffer for I2C
+ */
+#define PT3_I2C_MAX 4091
+struct pt3_i2cbuf {
+       u8  data[PT3_I2C_MAX];
+       u8  tmp;
+       u32 num_cmds;
+};
+
+/*
+ * DMA things
+ */
+#define TS_PACKET_SZ  188
+/* DMA transfers must not cross 4GiB, so use one page / transfer */
+#define DATA_XFER_SZ   4096
+#define DATA_BUF_XFERS 47
+/* (num_bufs * DATA_BUF_SZ) % TS_PACKET_SZ must be 0 */
+#define DATA_BUF_SZ    (DATA_BUF_XFERS * DATA_XFER_SZ)
+#define MAX_DATA_BUFS  16
+#define MIN_DATA_BUFS   2
+
+#define DESCS_IN_PAGE (PAGE_SIZE / sizeof(struct xfer_desc))
+#define MAX_NUM_XFERS (MAX_DATA_BUFS * DATA_BUF_XFERS)
+#define MAX_DESC_BUFS DIV_ROUND_UP(MAX_NUM_XFERS, DESCS_IN_PAGE)
+
+/* DMA transfer description.
+ * device is passed a pointer to this struct, dma-reads it,
+ * and gets the DMA buffer ring for storing TS data.
+ */
+struct xfer_desc {
+       u32 addr_l; /* bus address of target data buffer */
+       u32 addr_h;
+       u32 size;
+       u32 next_l; /* bus adddress of the next xfer_desc */
+       u32 next_h;
+};
+
+/* A DMA mapping of a page containing xfer_desc's */
+struct xfer_desc_buffer {
+       dma_addr_t b_addr;
+       struct xfer_desc *descs; /* PAGE_SIZE (xfer_desc[DESCS_IN_PAGE]) */
+};
+
+/* A DMA mapping of a data buffer */
+struct dma_data_buffer {
+       dma_addr_t b_addr;
+       u8 *data; /* size: u8[PAGE_SIZE] */
+};
+
+/*
+ * device things
+ */
+struct pt3_adap_config {
+       struct i2c_board_info demod_info;
+       struct tc90522_config demod_cfg;
+
+       struct i2c_board_info tuner_info;
+       union tuner_config {
+               struct qm1d1c0042_config qm1d1c0042;
+               struct mxl301rf_config   mxl301rf;
+       } tuner_cfg;
+       u32 init_freq;
+};
+
+struct pt3_adapter {
+       struct dvb_adapter  dvb_adap;  /* dvb_adap.priv => struct pt3_board */
+       int adap_idx;
+
+       struct dvb_demux    demux;
+       struct dmxdev       dmxdev;
+       struct dvb_frontend *fe;
+       struct i2c_client   *i2c_demod;
+       struct i2c_client   *i2c_tuner;
+
+       /* data fetch thread */
+       struct task_struct *thread;
+       int num_feeds;
+
+       bool cur_lna;
+       bool cur_lnb; /* current LNB power status (on/off) */
+
+       /* items below are for DMA */
+       struct dma_data_buffer buffer[MAX_DATA_BUFS];
+       int buf_idx;
+       int buf_ofs;
+       int num_bufs;  /* == pt3_board->num_bufs */
+       int num_discard; /* how many access units to discard initially */
+
+       struct xfer_desc_buffer desc_buf[MAX_DESC_BUFS];
+       int num_desc_bufs;  /* == num_bufs * DATA_BUF_XFERS / DESCS_IN_PAGE */
+};
+
+
+struct pt3_board {
+       struct pci_dev *pdev;
+       void __iomem *regs[2];
+       /* regs[0]: registers, regs[1]: internal memory, used for I2C */
+
+       struct mutex lock;
+
+       /* LNB power shared among sat-FEs */
+       int lnb_on_cnt; /* LNB power on count */
+
+       /* LNA shared among terr-FEs */
+       int lna_on_cnt; /* booster enabled count */
+
+       int num_bufs;  /* number of DMA buffers allocated/mapped per FE */
+
+       struct i2c_adapter i2c_adap;
+       struct pt3_i2cbuf *i2c_buf;
+
+       struct pt3_adapter *adaps[PT3_NUM_FE];
+};
+
+
+/*
+ * prototypes
+ */
+extern int  pt3_alloc_dmabuf(struct pt3_adapter *adap);
+extern void pt3_init_dmabuf(struct pt3_adapter *adap);
+extern void pt3_free_dmabuf(struct pt3_adapter *adap);
+extern int  pt3_start_dma(struct pt3_adapter *adap);
+extern int  pt3_stop_dma(struct pt3_adapter *adap);
+extern int  pt3_proc_dma(struct pt3_adapter *adap);
+
+extern int  pt3_i2c_master_xfer(struct i2c_adapter *adap,
+                               struct i2c_msg *msgs, int num);
+extern u32  pt3_i2c_functionality(struct i2c_adapter *adap);
+extern void pt3_i2c_reset(struct pt3_board *pt3);
+extern int  pt3_init_all_demods(struct pt3_board *pt3);
+extern int  pt3_init_all_mxl301rf(struct pt3_board *pt3);
+#endif /* PT3_H */
diff --git a/drivers/media/pci/pt3/pt3_dma.c b/drivers/media/pci/pt3/pt3_dma.c
new file mode 100644 (file)
index 0000000..f0ce904
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Earthsoft PT3 driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include "pt3.h"
+
+#define PT3_ACCESS_UNIT (TS_PACKET_SZ * 128)
+#define PT3_BUF_CANARY  (0x74)
+
+static u32 get_dma_base(int idx)
+{
+       int i;
+
+       i = (idx == 1 || idx == 2) ? 3 - idx : idx;
+       return REG_DMA_BASE + 0x18 * i;
+}
+
+int pt3_stop_dma(struct pt3_adapter *adap)
+{
+       struct pt3_board *pt3 = adap->dvb_adap.priv;
+       u32 base;
+       u32 stat;
+       int retry;
+
+       base = get_dma_base(adap->adap_idx);
+       stat = ioread32(pt3->regs[0] + base + OFST_STATUS);
+       if (!(stat & 0x01))
+               return 0;
+
+       iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL);
+       for (retry = 0; retry < 5; retry++) {
+               stat = ioread32(pt3->regs[0] + base + OFST_STATUS);
+               if (!(stat & 0x01))
+                       return 0;
+               msleep(50);
+       }
+       return -EIO;
+}
+
+int pt3_start_dma(struct pt3_adapter *adap)
+{
+       struct pt3_board *pt3 = adap->dvb_adap.priv;
+       u32 base = get_dma_base(adap->adap_idx);
+
+       iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL);
+       iowrite32(lower_32_bits(adap->desc_buf[0].b_addr),
+                       pt3->regs[0] + base + OFST_DMA_DESC_L);
+       iowrite32(upper_32_bits(adap->desc_buf[0].b_addr),
+                       pt3->regs[0] + base + OFST_DMA_DESC_H);
+       iowrite32(0x01, pt3->regs[0] + base + OFST_DMA_CTL);
+       return 0;
+}
+
+
+static u8 *next_unit(struct pt3_adapter *adap, int *idx, int *ofs)
+{
+       *ofs += PT3_ACCESS_UNIT;
+       if (*ofs >= DATA_BUF_SZ) {
+               *ofs -= DATA_BUF_SZ;
+               (*idx)++;
+               if (*idx == adap->num_bufs)
+                       *idx = 0;
+       }
+       return &adap->buffer[*idx].data[*ofs];
+}
+
+int pt3_proc_dma(struct pt3_adapter *adap)
+{
+       int idx, ofs;
+
+       idx = adap->buf_idx;
+       ofs = adap->buf_ofs;
+
+       if (adap->buffer[idx].data[ofs] == PT3_BUF_CANARY)
+               return 0;
+
+       while (*next_unit(adap, &idx, &ofs) != PT3_BUF_CANARY) {
+               u8 *p;
+
+               p = &adap->buffer[adap->buf_idx].data[adap->buf_ofs];
+               if (adap->num_discard > 0)
+                       adap->num_discard--;
+               else if (adap->buf_ofs + PT3_ACCESS_UNIT > DATA_BUF_SZ) {
+                       dvb_dmx_swfilter_packets(&adap->demux, p,
+                               (DATA_BUF_SZ - adap->buf_ofs) / TS_PACKET_SZ);
+                       dvb_dmx_swfilter_packets(&adap->demux,
+                               adap->buffer[idx].data, ofs / TS_PACKET_SZ);
+               } else
+                       dvb_dmx_swfilter_packets(&adap->demux, p,
+                               PT3_ACCESS_UNIT / TS_PACKET_SZ);
+
+               *p = PT3_BUF_CANARY;
+               adap->buf_idx = idx;
+               adap->buf_ofs = ofs;
+       }
+       return 0;
+}
+
+void pt3_init_dmabuf(struct pt3_adapter *adap)
+{
+       int idx, ofs;
+       u8 *p;
+
+       idx = 0;
+       ofs = 0;
+       p = adap->buffer[0].data;
+       /* mark the whole buffers as "not written yet" */
+       while (idx < adap->num_bufs) {
+               p[ofs] = PT3_BUF_CANARY;
+               ofs += PT3_ACCESS_UNIT;
+               if (ofs >= DATA_BUF_SZ) {
+                       ofs -= DATA_BUF_SZ;
+                       idx++;
+                       p = adap->buffer[idx].data;
+               }
+       }
+       adap->buf_idx = 0;
+       adap->buf_ofs = 0;
+}
+
+void pt3_free_dmabuf(struct pt3_adapter *adap)
+{
+       struct pt3_board *pt3;
+       int i;
+
+       pt3 = adap->dvb_adap.priv;
+       for (i = 0; i < adap->num_bufs; i++)
+               dma_free_coherent(&pt3->pdev->dev, DATA_BUF_SZ,
+                       adap->buffer[i].data, adap->buffer[i].b_addr);
+       adap->num_bufs = 0;
+
+       for (i = 0; i < adap->num_desc_bufs; i++)
+               dma_free_coherent(&pt3->pdev->dev, PAGE_SIZE,
+                       adap->desc_buf[i].descs, adap->desc_buf[i].b_addr);
+       adap->num_desc_bufs = 0;
+}
+
+
+int pt3_alloc_dmabuf(struct pt3_adapter *adap)
+{
+       struct pt3_board *pt3;
+       void *p;
+       int i, j;
+       int idx, ofs;
+       int num_desc_bufs;
+       dma_addr_t data_addr, desc_addr;
+       struct xfer_desc *d;
+
+       pt3 = adap->dvb_adap.priv;
+       adap->num_bufs = 0;
+       adap->num_desc_bufs = 0;
+       for (i = 0; i < pt3->num_bufs; i++) {
+               p = dma_alloc_coherent(&pt3->pdev->dev, DATA_BUF_SZ,
+                                       &adap->buffer[i].b_addr, GFP_KERNEL);
+               if (p == NULL)
+                       goto failed;
+               adap->buffer[i].data = p;
+               adap->num_bufs++;
+       }
+       pt3_init_dmabuf(adap);
+
+       /* build circular-linked pointers (xfer_desc) to the data buffers*/
+       idx = 0;
+       ofs = 0;
+       num_desc_bufs =
+               DIV_ROUND_UP(adap->num_bufs * DATA_BUF_XFERS, DESCS_IN_PAGE);
+       for (i = 0; i < num_desc_bufs; i++) {
+               p = dma_alloc_coherent(&pt3->pdev->dev, PAGE_SIZE,
+                                       &desc_addr, GFP_KERNEL);
+               if (p == NULL)
+                       goto failed;
+               adap->num_desc_bufs++;
+               adap->desc_buf[i].descs = p;
+               adap->desc_buf[i].b_addr = desc_addr;
+
+               if (i > 0) {
+                       d = &adap->desc_buf[i - 1].descs[DESCS_IN_PAGE - 1];
+                       d->next_l = lower_32_bits(desc_addr);
+                       d->next_h = upper_32_bits(desc_addr);
+               }
+               for (j = 0; j < DESCS_IN_PAGE; j++) {
+                       data_addr = adap->buffer[idx].b_addr + ofs;
+                       d = &adap->desc_buf[i].descs[j];
+                       d->addr_l = lower_32_bits(data_addr);
+                       d->addr_h = upper_32_bits(data_addr);
+                       d->size = DATA_XFER_SZ;
+
+                       desc_addr += sizeof(struct xfer_desc);
+                       d->next_l = lower_32_bits(desc_addr);
+                       d->next_h = upper_32_bits(desc_addr);
+
+                       ofs += DATA_XFER_SZ;
+                       if (ofs >= DATA_BUF_SZ) {
+                               ofs -= DATA_BUF_SZ;
+                               idx++;
+                               if (idx >= adap->num_bufs) {
+                                       desc_addr = adap->desc_buf[0].b_addr;
+                                       d->next_l = lower_32_bits(desc_addr);
+                                       d->next_h = upper_32_bits(desc_addr);
+                                       return 0;
+                               }
+                       }
+               }
+       }
+       return 0;
+
+failed:
+       pt3_free_dmabuf(adap);
+       return -ENOMEM;
+}
diff --git a/drivers/media/pci/pt3/pt3_i2c.c b/drivers/media/pci/pt3/pt3_i2c.c
new file mode 100644 (file)
index 0000000..ec6a8a2
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Earthsoft PT3 driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+
+#include "pt3.h"
+
+#define PT3_I2C_BASE  2048
+#define PT3_CMD_ADDR_NORMAL 0
+#define PT3_CMD_ADDR_INIT_DEMOD  4096
+#define PT3_CMD_ADDR_INIT_TUNER  (4096 + 2042)
+
+/* masks for I2C status register */
+#define STAT_SEQ_RUNNING 0x1
+#define STAT_SEQ_ERROR   0x6
+#define STAT_NO_SEQ      0x8
+
+#define PT3_I2C_RUN   (1 << 16)
+#define PT3_I2C_RESET (1 << 17)
+
+enum ctl_cmd {
+       I_END,
+       I_ADDRESS,
+       I_CLOCK_L,
+       I_CLOCK_H,
+       I_DATA_L,
+       I_DATA_H,
+       I_RESET,
+       I_SLEEP,
+       I_DATA_L_NOP  = 0x08,
+       I_DATA_H_NOP  = 0x0c,
+       I_DATA_H_READ = 0x0d,
+       I_DATA_H_ACK0 = 0x0e,
+       I_DATA_H_ACK1 = 0x0f,
+};
+
+
+static void cmdbuf_add(struct pt3_i2cbuf *cbuf, enum ctl_cmd cmd)
+{
+       int buf_idx;
+
+       if ((cbuf->num_cmds % 2) == 0)
+               cbuf->tmp = cmd;
+       else {
+               cbuf->tmp |= cmd << 4;
+               buf_idx = cbuf->num_cmds / 2;
+               if (buf_idx < ARRAY_SIZE(cbuf->data))
+                       cbuf->data[buf_idx] = cbuf->tmp;
+       }
+       cbuf->num_cmds++;
+}
+
+static void put_end(struct pt3_i2cbuf *cbuf)
+{
+       cmdbuf_add(cbuf, I_END);
+       if (cbuf->num_cmds % 2)
+               cmdbuf_add(cbuf, I_END);
+}
+
+static void put_start(struct pt3_i2cbuf *cbuf)
+{
+       cmdbuf_add(cbuf, I_DATA_H);
+       cmdbuf_add(cbuf, I_CLOCK_H);
+       cmdbuf_add(cbuf, I_DATA_L);
+       cmdbuf_add(cbuf, I_CLOCK_L);
+}
+
+static void put_byte_write(struct pt3_i2cbuf *cbuf, u8 val)
+{
+       u8 mask;
+
+       mask = 0x80;
+       for (mask = 0x80; mask > 0; mask >>= 1)
+               cmdbuf_add(cbuf, (val & mask) ? I_DATA_H_NOP : I_DATA_L_NOP);
+       cmdbuf_add(cbuf, I_DATA_H_ACK0);
+}
+
+static void put_byte_read(struct pt3_i2cbuf *cbuf, u32 size)
+{
+       int i, j;
+
+       for (i = 0; i < size; i++) {
+               for (j = 0; j < 8; j++)
+                       cmdbuf_add(cbuf, I_DATA_H_READ);
+               cmdbuf_add(cbuf, (i == size - 1) ? I_DATA_H_NOP : I_DATA_L_NOP);
+       }
+}
+
+static void put_stop(struct pt3_i2cbuf *cbuf)
+{
+       cmdbuf_add(cbuf, I_DATA_L);
+       cmdbuf_add(cbuf, I_CLOCK_H);
+       cmdbuf_add(cbuf, I_DATA_H);
+}
+
+
+/* translates msgs to internal commands for bit-banging */
+static void translate(struct pt3_i2cbuf *cbuf, struct i2c_msg *msgs, int num)
+{
+       int i, j;
+       bool rd;
+
+       cbuf->num_cmds = 0;
+       for (i = 0; i < num; i++) {
+               rd = !!(msgs[i].flags & I2C_M_RD);
+               put_start(cbuf);
+               put_byte_write(cbuf, msgs[i].addr << 1 | rd);
+               if (rd)
+                       put_byte_read(cbuf, msgs[i].len);
+               else
+                       for (j = 0; j < msgs[i].len; j++)
+                               put_byte_write(cbuf, msgs[i].buf[j]);
+       }
+       if (num > 0) {
+               put_stop(cbuf);
+               put_end(cbuf);
+       }
+}
+
+static int wait_i2c_result(struct pt3_board *pt3, u32 *result, int max_wait)
+{
+       int i;
+       u32 v;
+
+       for (i = 0; i < max_wait; i++) {
+               v = ioread32(pt3->regs[0] + REG_I2C_R);
+               if (!(v & STAT_SEQ_RUNNING))
+                       break;
+               usleep_range(500, 750);
+       }
+       if (i >= max_wait)
+               return -EIO;
+       if (result)
+               *result = v;
+       return 0;
+}
+
+/* send [pre-]translated i2c msgs stored at addr */
+static int send_i2c_cmd(struct pt3_board *pt3, u32 addr)
+{
+       u32 ret;
+
+       /* make sure that previous transactions had finished */
+       if (wait_i2c_result(pt3, NULL, 50)) {
+               dev_warn(&pt3->pdev->dev, "(%s) prev. transaction stalled\n",
+                               __func__);
+               return -EIO;
+       }
+
+       iowrite32(PT3_I2C_RUN | addr, pt3->regs[0] + REG_I2C_W);
+       usleep_range(200, 300);
+       /* wait for the current transaction to finish */
+       if (wait_i2c_result(pt3, &ret, 500) || (ret & STAT_SEQ_ERROR)) {
+               dev_warn(&pt3->pdev->dev, "(%s) failed.\n", __func__);
+               return -EIO;
+       }
+       return 0;
+}
+
+
+/* init commands for each demod are combined into one transaction
+ *  and hidden in ROM with the address PT3_CMD_ADDR_INIT_DEMOD.
+ */
+int  pt3_init_all_demods(struct pt3_board *pt3)
+{
+       ioread32(pt3->regs[0] + REG_I2C_R);
+       return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_DEMOD);
+}
+
+/* init commands for two ISDB-T tuners are hidden in ROM. */
+int  pt3_init_all_mxl301rf(struct pt3_board *pt3)
+{
+       usleep_range(1000, 2000);
+       return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_TUNER);
+}
+
+void pt3_i2c_reset(struct pt3_board *pt3)
+{
+       iowrite32(PT3_I2C_RESET, pt3->regs[0] + REG_I2C_W);
+}
+
+/*
+ * I2C algorithm
+ */
+int
+pt3_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       struct pt3_board *pt3;
+       struct pt3_i2cbuf *cbuf;
+       int i;
+       void __iomem *p;
+
+       pt3 = i2c_get_adapdata(adap);
+       cbuf = pt3->i2c_buf;
+
+       for (i = 0; i < num; i++)
+               if (msgs[i].flags & I2C_M_RECV_LEN) {
+                       dev_warn(&pt3->pdev->dev,
+                               "(%s) I2C_M_RECV_LEN not supported.\n",
+                               __func__);
+                       return -EINVAL;
+               }
+
+       translate(cbuf, msgs, num);
+       memcpy_toio(pt3->regs[1] + PT3_I2C_BASE + PT3_CMD_ADDR_NORMAL / 2,
+                       cbuf->data, cbuf->num_cmds);
+
+       if (send_i2c_cmd(pt3, PT3_CMD_ADDR_NORMAL) < 0)
+               return -EIO;
+
+       p = pt3->regs[1] + PT3_I2C_BASE;
+       for (i = 0; i < num; i++)
+               if ((msgs[i].flags & I2C_M_RD) && msgs[i].len > 0) {
+                       memcpy_fromio(msgs[i].buf, p, msgs[i].len);
+                       p += msgs[i].len;
+               }
+
+       return num;
+}
+
+u32 pt3_i2c_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C;
+}
index 18ae75546302daf04f66c0d3d213046bb092b1d0..b44e0d70907e2d876facc467e42608fc7fcb5760 100644 (file)
@@ -63,3 +63,11 @@ config VIDEO_SAA7134_DVB
 
          To compile this driver as a module, choose M here: the
          module will be called saa7134-dvb.
+
+config VIDEO_SAA7134_GO7007
+       tristate "go7007 support for saa7134 based TV cards"
+       depends on VIDEO_SAA7134
+       depends on VIDEO_GO7007
+       ---help---
+         Enables saa7134 driver support for boards with go7007
+         MPEG encoder (WIS Voyager or compatible).
index 58de9b085689bc101b01b2641db9e8f91f5a1b1c..09c43da675883058a581c96da0fe031feb73aa1f 100644 (file)
@@ -5,6 +5,7 @@ saa7134-y +=    saa7134-video.o
 saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o
 
 obj-$(CONFIG_VIDEO_SAA7134) +=  saa7134.o saa7134-empress.o
+obj-$(CONFIG_VIDEO_SAA7134_GO7007) += saa7134-go7007.o
 
 obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
 
@@ -14,3 +15,4 @@ ccflags-y += -I$(srctree)/drivers/media/i2c
 ccflags-y += -I$(srctree)/drivers/media/tuners
 ccflags-y += -I$(srctree)/drivers/media/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
+ccflags-y += -I$(srctree)/drivers/media/usb/go7007
index 6e4bdb90aa92e63c13d0d00c5bba90b4b4f09325..3ca078057755bd9a9384ca5f3c4886e7e35345d6 100644 (file)
@@ -5827,6 +5827,29 @@ struct saa7134_board saa7134_boards[] = {
                        .gpio = 0x0000800,
                },
        },
+       [SAA7134_BOARD_WIS_VOYAGER] = {
+               .name           = "WIS Voyager or compatible",
+               .audio_clock    = 0x00200000,
+               .tuner_type     = TUNER_PHILIPS_TDA8290,
+               .radio_type     = UNSET,
+               .tuner_addr     = ADDR_UNSET,
+               .radio_addr     = ADDR_UNSET,
+               .mpeg           = SAA7134_MPEG_GO7007,
+               .inputs         = { {
+                       .name = name_comp1,
+                       .vmux = 0,
+                       .amux = LINE2,
+               }, {
+                       .name = name_tv,
+                       .vmux = 3,
+                       .amux = TV,
+                       .tv   = 1,
+               }, {
+                       .name = name_svideo,
+                       .vmux = 6,
+               .amux = LINE1,
+               } },
+       },
 
 };
 
@@ -7079,6 +7102,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
                .subvendor    = 0x1461, /* Avermedia Technologies Inc */
                .subdevice    = 0x2055, /* AverTV Satellite Hybrid+FM A706 */
                .driver_data  = SAA7134_BOARD_AVERMEDIA_A706,
+       }, {
+               .vendor       = PCI_VENDOR_ID_PHILIPS,
+               .device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+               .subvendor    = 0x1905, /* WIS */
+               .subdevice    = 0x7007,
+               .driver_data  = SAA7134_BOARD_WIS_VOYAGER,
        }, {
                /* --- boards without eeprom + subsystem ID --- */
                .vendor       = PCI_VENDOR_ID_PHILIPS,
index 9ff03a69ced46f3bda31d10c7ac527c04064b68d..236ed725f933c141386d7f430747062a9ef7f473 100644 (file)
@@ -160,6 +160,8 @@ static void request_module_async(struct work_struct *work){
                request_module("saa7134-empress");
        if (card_is_dvb(dev))
                request_module("saa7134-dvb");
+       if (card_is_go7007(dev))
+               request_module("saa7134-go7007");
        if (alsa) {
                if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130)
                        request_module("saa7134-alsa");
@@ -563,8 +565,12 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id)
                        saa7134_irq_vbi_done(dev,status);
 
                if ((report & SAA7134_IRQ_REPORT_DONE_RA2) &&
-                   card_has_mpeg(dev))
-                       saa7134_irq_ts_done(dev,status);
+                   card_has_mpeg(dev)) {
+                       if (dev->mops->irq_ts_done != NULL)
+                               dev->mops->irq_ts_done(dev, status);
+                       else
+                               saa7134_irq_ts_done(dev, status);
+               }
 
                if (report & SAA7134_IRQ_REPORT_GPIO16) {
                        switch (dev->has_remote) {
diff --git a/drivers/media/pci/saa7134/saa7134-go7007.c b/drivers/media/pci/saa7134/saa7134-go7007.c
new file mode 100644 (file)
index 0000000..54e650b
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <asm/byteorder.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "saa7134.h"
+#include "saa7134-reg.h"
+#include "go7007-priv.h"
+
+/*#define GO7007_HPI_DEBUG*/
+
+enum hpi_address {
+       HPI_ADDR_VIDEO_BUFFER = 0xe4,
+       HPI_ADDR_INIT_BUFFER = 0xea,
+       HPI_ADDR_INTR_RET_VALUE = 0xee,
+       HPI_ADDR_INTR_RET_DATA = 0xec,
+       HPI_ADDR_INTR_STATUS = 0xf4,
+       HPI_ADDR_INTR_WR_PARAM = 0xf6,
+       HPI_ADDR_INTR_WR_INDEX = 0xf8,
+};
+
+enum gpio_command {
+       GPIO_COMMAND_RESET = 0x00, /* 000b */
+       GPIO_COMMAND_REQ1  = 0x04, /* 001b */
+       GPIO_COMMAND_WRITE = 0x20, /* 010b */
+       GPIO_COMMAND_REQ2  = 0x24, /* 011b */
+       GPIO_COMMAND_READ  = 0x80, /* 100b */
+       GPIO_COMMAND_VIDEO = 0x84, /* 101b */
+       GPIO_COMMAND_IDLE  = 0xA0, /* 110b */
+       GPIO_COMMAND_ADDR  = 0xA4, /* 111b */
+};
+
+struct saa7134_go7007 {
+       struct v4l2_subdev sd;
+       struct saa7134_dev *dev;
+       u8 *top;
+       u8 *bottom;
+       dma_addr_t top_dma;
+       dma_addr_t bottom_dma;
+};
+
+static inline struct saa7134_go7007 *to_state(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct saa7134_go7007, sd);
+}
+
+static const struct go7007_board_info board_voyager = {
+       .flags           = 0,
+       .sensor_flags    = GO7007_SENSOR_656 |
+                               GO7007_SENSOR_VALID_ENABLE |
+                               GO7007_SENSOR_TV |
+                               GO7007_SENSOR_VBI,
+       .audio_flags    = GO7007_AUDIO_I2S_MODE_1 |
+                               GO7007_AUDIO_WORD_16,
+       .audio_rate      = 48000,
+       .audio_bclk_div  = 8,
+       .audio_main_div  = 2,
+       .hpi_buffer_cap  = 7,
+       .num_inputs      = 1,
+       .inputs          = {
+               {
+                       .name           = "SAA7134",
+               },
+       },
+};
+
+/********************* Driver for GPIO HPI interface *********************/
+
+static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data)
+{
+       saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+
+       /* Write HPI address */
+       saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+       /* Write low byte */
+       saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+       /* Write high byte */
+       saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+       return 0;
+}
+
+static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data)
+{
+       saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+
+       /* Write HPI address */
+       saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+       saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
+
+       /* Read low byte */
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
+       saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+       saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+       *data = saa_readb(SAA7134_GPIO_GPSTATUS0);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+       /* Read high byte */
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
+       saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+       saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+       *data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8;
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+       return 0;
+}
+
+static int saa7134_go7007_interface_reset(struct go7007 *go)
+{
+       struct saa7134_go7007 *saa = go->hpi_context;
+       struct saa7134_dev *dev = saa->dev;
+       u16 intr_val, intr_data;
+       int count = 20;
+
+       saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */
+       saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4);
+       saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET);
+       msleep(1);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
+       msleep(10);
+
+       saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+       saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+       saa_readb(SAA7134_GPIO_GPSTATUS2);
+       /*pr_debug("status is %s\n", saa_readb(SAA7134_GPIO_GPSTATUS2) & 0x40 ? "OK" : "not OK"); */
+
+       /* enter command mode...(?) */
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
+
+       do {
+               saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+               saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+               saa_readb(SAA7134_GPIO_GPSTATUS2);
+               /*pr_info("gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */
+       } while (--count > 0);
+
+       /* Wait for an interrupt to indicate successful hardware reset */
+       if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
+                       (intr_val & ~0x1) != 0x55aa) {
+               pr_err("saa7134-go7007: unable to reset the GO7007\n");
+               return -1;
+       }
+       return 0;
+}
+
+static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data)
+{
+       struct saa7134_go7007 *saa = go->hpi_context;
+       struct saa7134_dev *dev = saa->dev;
+       int i;
+       u16 status_reg;
+
+#ifdef GO7007_HPI_DEBUG
+       pr_debug("saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data);
+#endif
+
+       for (i = 0; i < 100; ++i) {
+               gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
+               if (!(status_reg & 0x0010))
+                       break;
+               msleep(10);
+       }
+       if (i == 100) {
+               pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n",
+                       status_reg);
+               return -1;
+       }
+       gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data);
+       gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr);
+
+       return 0;
+}
+
+static int saa7134_go7007_read_interrupt(struct go7007 *go)
+{
+       struct saa7134_go7007 *saa = go->hpi_context;
+       struct saa7134_dev *dev = saa->dev;
+
+       /* XXX we need to wait if there is no interrupt available */
+       go->interrupt_available = 1;
+       gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value);
+       gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data);
+#ifdef GO7007_HPI_DEBUG
+       pr_debug("saa7134-go7007: ReadInterrupt: %04x %04x\n",
+                       go->interrupt_value, go->interrupt_data);
+#endif
+       return 0;
+}
+
+static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev,
+                                               unsigned long status)
+{
+       struct go7007 *go = video_get_drvdata(dev->empress_dev);
+       struct saa7134_go7007 *saa = go->hpi_context;
+
+       if (!vb2_is_streaming(&go->vidq))
+               return;
+       if (0 != (status & 0x000f0000))
+               pr_debug("saa7134-go7007: irq: lost %ld\n",
+                               (status >> 16) & 0x0f);
+       if (status & 0x100000) {
+               dma_sync_single_for_cpu(&dev->pci->dev,
+                                       saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE);
+               go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE);
+               saa_writel(SAA7134_RS_BA2(5), saa->bottom_dma);
+       } else {
+               dma_sync_single_for_cpu(&dev->pci->dev,
+                                       saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE);
+               go7007_parse_video_stream(go, saa->top, PAGE_SIZE);
+               saa_writel(SAA7134_RS_BA1(5), saa->top_dma);
+       }
+}
+
+static int saa7134_go7007_stream_start(struct go7007 *go)
+{
+       struct saa7134_go7007 *saa = go->hpi_context;
+       struct saa7134_dev *dev = saa->dev;
+
+       saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top),
+                       0, PAGE_SIZE, DMA_FROM_DEVICE);
+       if (dma_mapping_error(&dev->pci->dev, saa->top_dma))
+               return -ENOMEM;
+       saa->bottom_dma = dma_map_page(&dev->pci->dev,
+                       virt_to_page(saa->bottom),
+                       0, PAGE_SIZE, DMA_FROM_DEVICE);
+       if (dma_mapping_error(&dev->pci->dev, saa->bottom_dma)) {
+               dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
+                               DMA_FROM_DEVICE);
+               return -ENOMEM;
+       }
+
+       saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000);
+       saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200);
+
+       /* Set HPI interface for video */
+       saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+       saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER);
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+       saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
+
+       /* Enable TS interface */
+       saa_writeb(SAA7134_TS_PARALLEL, 0xe6);
+
+       /* Reset TS interface */
+       saa_setb(SAA7134_TS_SERIAL1, 0x01);
+       saa_clearb(SAA7134_TS_SERIAL1, 0x01);
+
+       /* Set up transfer block size */
+       saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1);
+       saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1);
+       saa_writeb(SAA7134_TS_DMA1, 0);
+       saa_writeb(SAA7134_TS_DMA2, 0);
+
+       /* Enable video streaming mode */
+       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO);
+
+       saa_writel(SAA7134_RS_BA1(5), saa->top_dma);
+       saa_writel(SAA7134_RS_BA2(5), saa->bottom_dma);
+       saa_writel(SAA7134_RS_PITCH(5), 128);
+       saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX);
+
+       /* Enable TS FIFO */
+       saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
+
+       /* Enable DMA IRQ */
+       saa_setl(SAA7134_IRQ1,
+                       SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
+
+       return 0;
+}
+
+static int saa7134_go7007_stream_stop(struct go7007 *go)
+{
+       struct saa7134_go7007 *saa = go->hpi_context;
+       struct saa7134_dev *dev;
+
+       if (!saa)
+               return -EINVAL;
+       dev = saa->dev;
+       if (!dev)
+               return -EINVAL;
+
+       /* Shut down TS FIFO */
+       saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
+
+       /* Disable DMA IRQ */
+       saa_clearl(SAA7134_IRQ1,
+                       SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
+
+       /* Disable TS interface */
+       saa_clearb(SAA7134_TS_PARALLEL, 0x80);
+
+       dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
+                       DMA_FROM_DEVICE);
+       dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE,
+                       DMA_FROM_DEVICE);
+
+       return 0;
+}
+
+static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len)
+{
+       struct saa7134_go7007 *saa = go->hpi_context;
+       struct saa7134_dev *dev = saa->dev;
+       u16 status_reg;
+       int i;
+
+#ifdef GO7007_HPI_DEBUG
+       pr_debug("saa7134-go7007: DownloadBuffer sending %d bytes\n", len);
+#endif
+
+       while (len > 0) {
+               i = len > 64 ? 64 : len;
+               saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+               saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER);
+               saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+               saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+               while (i-- > 0) {
+                       saa_writeb(SAA7134_GPIO_GPSTATUS0, *data);
+                       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
+                       saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+                       ++data;
+                       --len;
+               }
+               for (i = 0; i < 100; ++i) {
+                       gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
+                       if (!(status_reg & 0x0002))
+                               break;
+               }
+               if (i == 100) {
+                       pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n",
+                              status_reg);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static struct go7007_hpi_ops saa7134_go7007_hpi_ops = {
+       .interface_reset        = saa7134_go7007_interface_reset,
+       .write_interrupt        = saa7134_go7007_write_interrupt,
+       .read_interrupt         = saa7134_go7007_read_interrupt,
+       .stream_start           = saa7134_go7007_stream_start,
+       .stream_stop            = saa7134_go7007_stream_stop,
+       .send_firmware          = saa7134_go7007_send_firmware,
+};
+MODULE_FIRMWARE("go7007/go7007tv.bin");
+
+/* --------------------------------------------------------------------------*/
+
+static int saa7134_go7007_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+#if 0
+       struct saa7134_go7007 *saa = to_state(sd);
+       struct saa7134_dev *dev = saa->dev;
+
+       return saa7134_s_std_internal(dev, NULL, norm);
+#else
+       return 0;
+#endif
+}
+
+static const struct v4l2_subdev_video_ops saa7134_go7007_video_ops = {
+       .s_std = saa7134_go7007_s_std,
+};
+
+static const struct v4l2_subdev_ops saa7134_go7007_sd_ops = {
+       .video = &saa7134_go7007_video_ops,
+};
+
+/* --------------------------------------------------------------------------*/
+
+
+/********************* Add/remove functions *********************/
+
+static int saa7134_go7007_init(struct saa7134_dev *dev)
+{
+       struct go7007 *go;
+       struct saa7134_go7007 *saa;
+       struct v4l2_subdev *sd;
+
+       pr_debug("saa7134-go7007: probing new SAA713X board\n");
+
+       go = go7007_alloc(&board_voyager, &dev->pci->dev);
+       if (go == NULL)
+               return -ENOMEM;
+
+       saa = kzalloc(sizeof(struct saa7134_go7007), GFP_KERNEL);
+       if (saa == NULL) {
+               kfree(go);
+               return -ENOMEM;
+       }
+
+       go->board_id = GO7007_BOARDID_PCI_VOYAGER;
+       snprintf(go->bus_info, sizeof(go->bus_info), "PCI:%s", pci_name(dev->pci));
+       strlcpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name));
+       go->hpi_ops = &saa7134_go7007_hpi_ops;
+       go->hpi_context = saa;
+       saa->dev = dev;
+
+       /* Init the subdevice interface */
+       sd = &saa->sd;
+       v4l2_subdev_init(sd, &saa7134_go7007_sd_ops);
+       v4l2_set_subdevdata(sd, saa);
+       strncpy(sd->name, "saa7134-go7007", sizeof(sd->name));
+
+       /* Allocate a couple pages for receiving the compressed stream */
+       saa->top = (u8 *)get_zeroed_page(GFP_KERNEL);
+       if (!saa->top)
+               goto allocfail;
+       saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL);
+       if (!saa->bottom)
+               goto allocfail;
+
+       /* Boot the GO7007 */
+       if (go7007_boot_encoder(go, go->board_info->flags &
+                                       GO7007_BOARD_USE_ONBOARD_I2C) < 0)
+               goto allocfail;
+
+       /* Do any final GO7007 initialization, then register the
+        * V4L2 and ALSA interfaces */
+       if (go7007_register_encoder(go, go->board_info->num_i2c_devs) < 0)
+               goto allocfail;
+
+       /* Register the subdevice interface with the go7007 device */
+       if (v4l2_device_register_subdev(&go->v4l2_dev, sd) < 0)
+               pr_info("saa7134-go7007: register subdev failed\n");
+
+       dev->empress_dev = &go->vdev;
+
+       go->status = STATUS_ONLINE;
+       return 0;
+
+allocfail:
+       if (saa->top)
+               free_page((unsigned long)saa->top);
+       if (saa->bottom)
+               free_page((unsigned long)saa->bottom);
+       kfree(saa);
+       kfree(go);
+       return -ENOMEM;
+}
+
+static int saa7134_go7007_fini(struct saa7134_dev *dev)
+{
+       struct go7007 *go;
+       struct saa7134_go7007 *saa;
+
+       if (NULL == dev->empress_dev)
+               return 0;
+
+       go = video_get_drvdata(dev->empress_dev);
+       if (go->audio_enabled)
+               go7007_snd_remove(go);
+
+       saa = go->hpi_context;
+       go->status = STATUS_SHUTDOWN;
+       free_page((unsigned long)saa->top);
+       free_page((unsigned long)saa->bottom);
+       v4l2_device_unregister_subdev(&saa->sd);
+       kfree(saa);
+       video_unregister_device(&go->vdev);
+
+       v4l2_device_put(&go->v4l2_dev);
+       dev->empress_dev = NULL;
+
+       return 0;
+}
+
+static struct saa7134_mpeg_ops saa7134_go7007_ops = {
+       .type          = SAA7134_MPEG_GO7007,
+       .init          = saa7134_go7007_init,
+       .fini          = saa7134_go7007_fini,
+       .irq_ts_done   = saa7134_go7007_irq_ts_done,
+};
+
+static int __init saa7134_go7007_mod_init(void)
+{
+       return saa7134_ts_register(&saa7134_go7007_ops);
+}
+
+static void __exit saa7134_go7007_mod_cleanup(void)
+{
+       saa7134_ts_unregister(&saa7134_go7007_ops);
+}
+
+module_init(saa7134_go7007_mod_init);
+module_exit(saa7134_go7007_mod_cleanup);
+
+MODULE_LICENSE("GPL v2");
index c06dbe17a87f03303dc72b2e2500dc037c87ad4d..4f0b1012e4f39d308b445ae198ce30231509a8f1 100644 (file)
@@ -43,7 +43,7 @@ MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
 
 /* ------------------------------------------------------------------ */
 
-#define VBI_LINE_COUNT     16
+#define VBI_LINE_COUNT     17
 #define VBI_LINE_LENGTH  2048
 #define VBI_SCALE       0x200
 
index 0cfa2ca6a32ad2c1e3a03a952792fdcb590cc146..fc4a427cb51fed9bea0cf9ec16bab44e5408ec70 100644 (file)
@@ -201,7 +201,7 @@ static struct saa7134_format formats[] = {
                .video_v_start = 24,    \
                .video_v_stop  = 311,   \
                .vbi_v_start_0 = 7,     \
-               .vbi_v_stop_0  = 22,    \
+               .vbi_v_stop_0  = 23,    \
                .vbi_v_start_1 = 319,   \
                .src_timing    = 4
 
index e47edd4b57ce30c2c8fee0fdba8153bc1f37eaed..1a82dd07205b9dfb2b1457939d0be7027937873a 100644 (file)
@@ -338,6 +338,7 @@ struct saa7134_card_ir {
 #define SAA7134_BOARD_ASUSTeK_PS3_100      190
 #define SAA7134_BOARD_HAWELL_HW_9004V1      191
 #define SAA7134_BOARD_AVERMEDIA_A706           192
+#define SAA7134_BOARD_WIS_VOYAGER           193
 
 #define SAA7134_MAXBOARDS 32
 #define SAA7134_INPUT_MAX 8
@@ -368,6 +369,7 @@ enum saa7134_mpeg_type {
        SAA7134_MPEG_UNUSED,
        SAA7134_MPEG_EMPRESS,
        SAA7134_MPEG_DVB,
+       SAA7134_MPEG_GO7007,
 };
 
 enum saa7134_mpeg_ts_type {
@@ -407,6 +409,7 @@ struct saa7134_board {
 #define card_has_radio(dev)   (NULL != saa7134_boards[dev->board].radio.name)
 #define card_is_empress(dev)  (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg)
 #define card_is_dvb(dev)      (SAA7134_MPEG_DVB     == saa7134_boards[dev->board].mpeg)
+#define card_is_go7007(dev)   (SAA7134_MPEG_GO7007  == saa7134_boards[dev->board].mpeg)
 #define card_has_mpeg(dev)    (SAA7134_MPEG_UNUSED  != saa7134_boards[dev->board].mpeg)
 #define card(dev)             (saa7134_boards[dev->board])
 #define card_in(dev,n)        (saa7134_boards[dev->board].inputs[n])
@@ -522,6 +525,8 @@ struct saa7134_mpeg_ops {
        int                        (*init)(struct saa7134_dev *dev);
        int                        (*fini)(struct saa7134_dev *dev);
        void                       (*signal_change)(struct saa7134_dev *dev);
+       void                       (*irq_ts_done)(struct saa7134_dev *dev,
+                                                 unsigned long status);
 };
 
 /* global device status */
index e042963d377d0c8dc9b7c6d94225f1ccb5983549..4f3b1dd18ba4db4335ab87c34b02fc1ea5f2e012 100644 (file)
@@ -680,7 +680,6 @@ static int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val)
 int saa7164_api_configure_dif(struct saa7164_port *port, u32 std)
 {
        struct saa7164_dev *dev = port->dev;
-       int ret = 0;
        u8 agc_disable;
 
        dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std);
@@ -733,7 +732,7 @@ int saa7164_api_configure_dif(struct saa7164_port *port, u32 std)
        saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */
        msleep(100);
 
-       return ret;
+       return 0;
 }
 
 /* Ensure the dif is in the correct state for the operating mode
index 1bf06970ca3e8e180cb6a2f20d732c983af820b2..cc1be8a7a4510259b9dcb271f02f85325c3d7826 100644 (file)
@@ -52,7 +52,7 @@ unsigned int saa_debug;
 module_param_named(debug, saa_debug, int, 0644);
 MODULE_PARM_DESC(debug, "enable debug messages");
 
-unsigned int fw_debug;
+static unsigned int fw_debug;
 module_param(fw_debug, int, 0644);
 MODULE_PARM_DESC(fw_debug, "Firmware debug level def:2");
 
@@ -72,7 +72,7 @@ static unsigned int card[]  = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET };
 module_param_array(card,  int, NULL, 0444);
 MODULE_PARM_DESC(card, "card type");
 
-unsigned int print_histogram = 64;
+static unsigned int print_histogram = 64;
 module_param(print_histogram, int, 0644);
 MODULE_PARM_DESC(print_histogram, "print histogram values once");
 
@@ -80,7 +80,7 @@ unsigned int crc_checking = 1;
 module_param(crc_checking, int, 0644);
 MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers");
 
-unsigned int guard_checking = 1;
+static unsigned int guard_checking = 1;
 module_param(guard_checking, int, 0644);
 MODULE_PARM_DESC(guard_checking,
        "enable dma sanity checking for buffer overruns");
index d9e06a6bf1ebc1a7de4b498361bac87f9cabe9d4..0fb91dc7ca73529eda55bb894287cbdc084e8fd0 100644 (file)
@@ -1,6 +1,7 @@
 config VIDEO_SOLO6X10
        tristate "Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264)"
        depends on PCI && VIDEO_DEV && SND && I2C
+       depends on HAS_DMA
        select BITREVERSE
        select FONT_SUPPORT
        select FONT_8x16
index 5ea9cac03968ee6d1b87d17f129cc4a8d37c85dc..11c98f0625e49bfaecf6e26cb5ade05b50796e25 100644 (file)
@@ -172,7 +172,7 @@ static void solo_vout_config(struct solo_dev *solo_dev)
 static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off,
                               u16 val, int reg_size)
 {
-       u16 *buf;
+       __le16 *buf;
        const int n = 64, size = n * sizeof(*buf);
        int i, ret = 0;
 
@@ -211,7 +211,7 @@ int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch,
 {
        const unsigned size = sizeof(u16) * 64;
        u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2;
-       u16 *buf;
+       __le16 *buf;
        int x, y;
        int ret = 0;
 
index af40b3aba41006cc4565555845c34a177dfe42df..da25ce4a69526187520ffa790f8fe6e50e04aba7 100644 (file)
@@ -100,7 +100,7 @@ unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en)
        return retval;
 }
 
-unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc)
+__be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc)
 {
        int read_cmd = loc | (EE_READ_CMD << ADDR_LEN);
        unsigned short retval = 0;
@@ -117,11 +117,11 @@ unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc)
 
        solo_eeprom_reg_write(solo_dev, ~EE_CS);
 
-       return retval;
+       return (__force __be16)retval;
 }
 
 int solo_eeprom_write(struct solo_dev *solo_dev, int loc,
-                     unsigned short data)
+                     __be16 data)
 {
        int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN);
        unsigned int retval;
@@ -130,7 +130,7 @@ int solo_eeprom_write(struct solo_dev *solo_dev, int loc,
        solo_eeprom_cmd(solo_dev, write_cmd);
 
        for (i = 15; i >= 0; i--) {
-               unsigned int dataval = (data >> i) & 1;
+               unsigned int dataval = ((__force unsigned)data >> i) & 1;
 
                solo_eeprom_reg_write(solo_dev, EE_ENB);
                solo_eeprom_reg_write(solo_dev,
index c6154b00fcbd9250e4b9b50bfb0ded1b56701731..72017b7f0a753d1161dcced8ffe5fa3cc3679503 100644 (file)
@@ -394,9 +394,9 @@ int solo_osd_print(struct solo_enc_dev *solo_enc);
 
 /* EEPROM commands */
 unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en);
-unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc);
+__be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc);
 int solo_eeprom_write(struct solo_dev *solo_dev, int loc,
-                     unsigned short data);
+                     __be16 data);
 
 /* JPEG Qp functions */
 void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch,
index 03130157db83c28001d2d3ac2986117869f8bc7b..f6f30abc088ba26e4ce547b7f2cf9bac12122be7 100644 (file)
@@ -1,6 +1,7 @@
 config STA2X11_VIP
        tristate "STA2X11 VIP Video For Linux"
        depends on STA2X11
+       depends on HAS_DMA
        select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT
        select VIDEOBUF2_DMA_CONTIG
        depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS
index 365bd21301baa494744238eea568fea16ec29e5d..22450f583da1750e743b27aeb1fa4b7ce7f805f8 100644 (file)
@@ -152,7 +152,7 @@ struct sta2x11_vip {
        int tcount, bcount;
        int overflow;
 
-       void *iomem;    /* I/O Memory */
+       void __iomem *iomem;    /* I/O Memory */
        struct vip_config *config;
 };
 
index 0dcb8cd7767698d101860c111f65751c3951461e..7b83151ed6c48dd665a021c43438e61b453f2cce 100644 (file)
@@ -1,8 +1,12 @@
+config DVB_AV7110_IR
+       bool
+
 config DVB_AV7110
        tristate "AV7110 cards"
        depends on DVB_CORE && PCI && I2C
        select TTPCI_EEPROM
        select VIDEO_SAA7146_VV
+       select DVB_AV7110_IR if INPUT_EVDEV=y || INPUT_EVDEV=DVB_AV7110
        depends on VIDEO_DEV    # dependencies of VIDEO_SAA7146_VV
        select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
index 98905963ff085c0b0e265b7bf26ae0d892aa6a83..49f71b1eaf14b529689f82e658d44a09b285ddbf 100644 (file)
@@ -5,7 +5,7 @@
 
 dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o
 
-ifdef CONFIG_INPUT_EVDEV
+ifdef CONFIG_DVB_AV7110_IR
 dvb-ttpci-objs += av7110_ir.o
 endif
 
index f38329d29daa8c41a83823deb0d2f87de7a079ff..c1f0617a69733df52b924668457a955ab39806f1 100644 (file)
@@ -235,7 +235,7 @@ static void recover_arm(struct av7110 *av7110)
 
        restart_feeds(av7110);
 
-#if IS_ENABLED(CONFIG_INPUT_EVDEV)
+#if IS_ENABLED(CONFIG_DVB_AV7110_IR)
        av7110_check_ir_config(av7110, true);
 #endif
 }
@@ -268,7 +268,7 @@ static int arm_thread(void *data)
                if (!av7110->arm_ready)
                        continue;
 
-#if IS_ENABLED(CONFIG_INPUT_EVDEV)
+#if IS_ENABLED(CONFIG_DVB_AV7110_IR)
                av7110_check_ir_config(av7110, false);
 #endif
 
@@ -2725,7 +2725,7 @@ static int av7110_attach(struct saa7146_dev* dev,
 
        mutex_init(&av7110->ioctl_mutex);
 
-#if IS_ENABLED(CONFIG_INPUT_EVDEV)
+#if IS_ENABLED(CONFIG_DVB_AV7110_IR)
        av7110_ir_init(av7110);
 #endif
        printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num);
@@ -2768,7 +2768,7 @@ static int av7110_detach(struct saa7146_dev* saa)
        struct av7110 *av7110 = saa->ext_priv;
        dprintk(4, "%p\n", av7110);
 
-#if IS_ENABLED(CONFIG_INPUT_EVDEV)
+#if IS_ENABLED(CONFIG_DVB_AV7110_IR)
        av7110_ir_exit(av7110);
 #endif
        if (budgetpatch || av7110->full_ts) {
diff --git a/drivers/media/pci/tw68/Kconfig b/drivers/media/pci/tw68/Kconfig
new file mode 100644 (file)
index 0000000..5425ba1
--- /dev/null
@@ -0,0 +1,10 @@
+config VIDEO_TW68
+       tristate "Techwell tw68x Video For Linux"
+       depends on VIDEO_DEV && PCI && VIDEO_V4L2
+       select I2C_ALGOBIT
+       select VIDEOBUF2_DMA_SG
+       ---help---
+         Support for Techwell tw68xx based frame grabber boards.
+
+         To compile this driver as a module, choose M here: the
+         module will be called tw68.
diff --git a/drivers/media/pci/tw68/Makefile b/drivers/media/pci/tw68/Makefile
new file mode 100644 (file)
index 0000000..3d02f28
--- /dev/null
@@ -0,0 +1,3 @@
+tw68-objs := tw68-core.o tw68-video.o tw68-risc.o
+
+obj-$(CONFIG_VIDEO_TW68) += tw68.o
diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c
new file mode 100644 (file)
index 0000000..a6fb48c
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ *  tw68-core.c
+ *  Core functions for the Techwell 68xx driver
+ *
+ *  Much of this code is derived from the cx88 and sa7134 drivers, which
+ *  were in turn derived from the bt87x driver.  The original work was by
+ *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
+ *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
+ *  acknowledged.  Full credit goes to them - any problems within this code
+ *  are mine.
+ *
+ *  Copyright (C) 2009  William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 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.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/sound.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm.h>
+
+#include <media/v4l2-dev.h>
+#include "tw68.h"
+#include "tw68-reg.h"
+
+MODULE_DESCRIPTION("v4l2 driver module for tw6800 based video capture cards");
+MODULE_AUTHOR("William M. Brack");
+MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
+MODULE_LICENSE("GPL");
+
+static unsigned int latency = UNSET;
+module_param(latency, int, 0444);
+MODULE_PARM_DESC(latency, "pci latency timer");
+
+static unsigned int video_nr[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
+module_param_array(video_nr, int, NULL, 0444);
+MODULE_PARM_DESC(video_nr, "video device number");
+
+static unsigned int card[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+static atomic_t tw68_instance = ATOMIC_INIT(0);
+
+/* ------------------------------------------------------------------ */
+
+/*
+ * Please add any new PCI IDs to: http://pci-ids.ucw.cz.  This keeps
+ * the PCI ID database up to date.  Note that the entries must be
+ * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs.
+ */
+static const struct pci_device_id tw68_pci_tbl[] = {
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6800)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6801)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6804)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_1)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_2)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_3)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_4)},
+       {0,}
+};
+
+/* ------------------------------------------------------------------ */
+
+
+/*
+ * The device is given a "soft reset". According to the specifications,
+ * after this "all register content remain unchanged", so we also write
+ * to all specified registers manually as well (mostly to manufacturer's
+ * specified reset values)
+ */
+static int tw68_hw_init1(struct tw68_dev *dev)
+{
+       /* Assure all interrupts are disabled */
+       tw_writel(TW68_INTMASK, 0);             /* 020 */
+       /* Clear any pending interrupts */
+       tw_writel(TW68_INTSTAT, 0xffffffff);    /* 01C */
+       /* Stop risc processor, set default buffer level */
+       tw_writel(TW68_DMAC, 0x1600);
+
+       tw_writeb(TW68_ACNTL, 0x80);    /* 218  soft reset */
+       msleep(100);
+
+       tw_writeb(TW68_INFORM, 0x40);   /* 208  mux0, 27mhz xtal */
+       tw_writeb(TW68_OPFORM, 0x04);   /* 20C  analog line-lock */
+       tw_writeb(TW68_HSYNC, 0);       /* 210  color-killer high sens */
+       tw_writeb(TW68_ACNTL, 0x42);    /* 218  int vref #2, chroma adc off */
+
+       tw_writeb(TW68_CROP_HI, 0x02);  /* 21C  Hactive m.s. bits */
+       tw_writeb(TW68_VDELAY_LO, 0x12);/* 220  Mfg specified reset value */
+       tw_writeb(TW68_VACTIVE_LO, 0xf0);
+       tw_writeb(TW68_HDELAY_LO, 0x0f);
+       tw_writeb(TW68_HACTIVE_LO, 0xd0);
+
+       tw_writeb(TW68_CNTRL1, 0xcd);   /* 230  Wide Chroma BPF B/W
+                                        *      Secam reduction, Adap comb for
+                                        *      NTSC, Op Mode 1 */
+
+       tw_writeb(TW68_VSCALE_LO, 0);   /* 234 */
+       tw_writeb(TW68_SCALE_HI, 0x11); /* 238 */
+       tw_writeb(TW68_HSCALE_LO, 0);   /* 23c */
+       tw_writeb(TW68_BRIGHT, 0);      /* 240 */
+       tw_writeb(TW68_CONTRAST, 0x5c); /* 244 */
+       tw_writeb(TW68_SHARPNESS, 0x51);/* 248 */
+       tw_writeb(TW68_SAT_U, 0x80);    /* 24C */
+       tw_writeb(TW68_SAT_V, 0x80);    /* 250 */
+       tw_writeb(TW68_HUE, 0x00);      /* 254 */
+
+       /* TODO - Check that none of these are set by control defaults */
+       tw_writeb(TW68_SHARP2, 0x53);   /* 258  Mfg specified reset val */
+       tw_writeb(TW68_VSHARP, 0x80);   /* 25C  Sharpness Coring val 8 */
+       tw_writeb(TW68_CORING, 0x44);   /* 260  CTI and Vert Peak coring */
+       tw_writeb(TW68_CNTRL2, 0x00);   /* 268  No power saving enabled */
+       tw_writeb(TW68_SDT, 0x07);      /* 270  Enable shadow reg, auto-det */
+       tw_writeb(TW68_SDTR, 0x7f);     /* 274  All stds recog, don't start */
+       tw_writeb(TW68_CLMPG, 0x50);    /* 280  Clamp end at 40 sys clocks */
+       tw_writeb(TW68_IAGC, 0x22);     /* 284  Mfg specified reset val */
+       tw_writeb(TW68_AGCGAIN, 0xf0);  /* 288  AGC gain when loop disabled */
+       tw_writeb(TW68_PEAKWT, 0xd8);   /* 28C  White peak threshold */
+       tw_writeb(TW68_CLMPL, 0x3c);    /* 290  Y channel clamp level */
+/*     tw_writeb(TW68_SYNCT, 0x38);*/  /* 294  Sync amplitude */
+       tw_writeb(TW68_SYNCT, 0x30);    /* 294  Sync amplitude */
+       tw_writeb(TW68_MISSCNT, 0x44);  /* 298  Horiz sync, VCR detect sens */
+       tw_writeb(TW68_PCLAMP, 0x28);   /* 29C  Clamp pos from PLL sync */
+       /* Bit DETV of VCNTL1 helps sync multi cams/chip board */
+       tw_writeb(TW68_VCNTL1, 0x04);   /* 2A0 */
+       tw_writeb(TW68_VCNTL2, 0);      /* 2A4 */
+       tw_writeb(TW68_CKILL, 0x68);    /* 2A8  Mfg specified reset val */
+       tw_writeb(TW68_COMB, 0x44);     /* 2AC  Mfg specified reset val */
+       tw_writeb(TW68_LDLY, 0x30);     /* 2B0  Max positive luma delay */
+       tw_writeb(TW68_MISC1, 0x14);    /* 2B4  Mfg specified reset val */
+       tw_writeb(TW68_LOOP, 0xa5);     /* 2B8  Mfg specified reset val */
+       tw_writeb(TW68_MISC2, 0xe0);    /* 2BC  Enable colour killer */
+       tw_writeb(TW68_MVSN, 0);        /* 2C0 */
+       tw_writeb(TW68_CLMD, 0x05);     /* 2CC  slice level auto, clamp med. */
+       tw_writeb(TW68_IDCNTL, 0);      /* 2D0  Writing zero to this register
+                                        *      selects NTSC ID detection,
+                                        *      but doesn't change the
+                                        *      sensitivity (which has a reset
+                                        *      value of 1E).  Since we are
+                                        *      not doing auto-detection, it
+                                        *      has no real effect */
+       tw_writeb(TW68_CLCNTL1, 0);     /* 2D4 */
+       tw_writel(TW68_VBIC, 0x03);     /* 010 */
+       tw_writel(TW68_CAP_CTL, 0x03);  /* 040  Enable both even & odd flds */
+       tw_writel(TW68_DMAC, 0x2000);   /* patch set had 0x2080 */
+       tw_writel(TW68_TESTREG, 0);     /* 02C */
+
+       /*
+        * Some common boards, especially inexpensive single-chip models,
+        * use the GPIO bits 0-3 to control an on-board video-output mux.
+        * For these boards, we need to set up the GPIO register into
+        * "normal" mode, set bits 0-3 as output, and then set those bits
+        * zero.
+        *
+        * Eventually, it would be nice if we could identify these boards
+        * uniquely, and only do this initialisation if the board has been
+        * identify.  For the moment, however, it shouldn't hurt anything
+        * to do these steps.
+        */
+       tw_writel(TW68_GPIOC, 0);       /* Set the GPIO to "normal", no ints */
+       tw_writel(TW68_GPOE, 0x0f);     /* Set bits 0-3 to "output" */
+       tw_writel(TW68_GPDATA, 0);      /* Set all bits to low state */
+
+       /* Initialize the device control structures */
+       mutex_init(&dev->lock);
+       spin_lock_init(&dev->slock);
+
+       /* Initialize any subsystems */
+       tw68_video_init1(dev);
+       return 0;
+}
+
+static irqreturn_t tw68_irq(int irq, void *dev_id)
+{
+       struct tw68_dev *dev = dev_id;
+       u32 status, orig;
+       int loop;
+
+       status = orig = tw_readl(TW68_INTSTAT) & dev->pci_irqmask;
+       /* Check if anything to do */
+       if (0 == status)
+               return IRQ_NONE;        /* Nope - return */
+       for (loop = 0; loop < 10; loop++) {
+               if (status & dev->board_virqmask)       /* video interrupt */
+                       tw68_irq_video_done(dev, status);
+               status = tw_readl(TW68_INTSTAT) & dev->pci_irqmask;
+               if (0 == status)
+                       return IRQ_HANDLED;
+       }
+       dev_dbg(&dev->pci->dev, "%s: **** INTERRUPT NOT HANDLED - clearing mask (orig 0x%08x, cur 0x%08x)",
+                       dev->name, orig, tw_readl(TW68_INTSTAT));
+       dev_dbg(&dev->pci->dev, "%s: pci_irqmask 0x%08x; board_virqmask 0x%08x ****\n",
+                       dev->name, dev->pci_irqmask, dev->board_virqmask);
+       tw_clearl(TW68_INTMASK, dev->pci_irqmask);
+       return IRQ_HANDLED;
+}
+
+static int tw68_initdev(struct pci_dev *pci_dev,
+                                    const struct pci_device_id *pci_id)
+{
+       struct tw68_dev *dev;
+       int vidnr = -1;
+       int err;
+
+       dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev), GFP_KERNEL);
+       if (NULL == dev)
+               return -ENOMEM;
+
+       dev->instance = v4l2_device_set_name(&dev->v4l2_dev, "tw68",
+                                               &tw68_instance);
+
+       err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
+       if (err)
+               return err;
+
+       /* pci init */
+       dev->pci = pci_dev;
+       if (pci_enable_device(pci_dev)) {
+               err = -EIO;
+               goto fail1;
+       }
+
+       dev->name = dev->v4l2_dev.name;
+
+       if (UNSET != latency) {
+               pr_info("%s: setting pci latency timer to %d\n",
+                      dev->name, latency);
+               pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
+       }
+
+       /* print pci info */
+       pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
+       pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER,  &dev->pci_lat);
+       pr_info("%s: found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n",
+               dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+               dev->pci_lat, (u64)pci_resource_start(pci_dev, 0));
+       pci_set_master(pci_dev);
+       if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) {
+               pr_info("%s: Oops: no 32bit PCI DMA ???\n", dev->name);
+               err = -EIO;
+               goto fail1;
+       }
+
+       switch (pci_id->device) {
+       case PCI_DEVICE_ID_6800:        /* TW6800 */
+               dev->vdecoder = TW6800;
+               dev->board_virqmask = TW68_VID_INTS;
+               break;
+       case PCI_DEVICE_ID_6801:        /* Video decoder for TW6802 */
+               dev->vdecoder = TW6801;
+               dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX;
+               break;
+       case PCI_DEVICE_ID_6804:        /* Video decoder for TW6804 */
+               dev->vdecoder = TW6804;
+               dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX;
+               break;
+       default:
+               dev->vdecoder = TWXXXX; /* To be announced */
+               dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX;
+               break;
+       }
+
+       /* get mmio */
+       if (!request_mem_region(pci_resource_start(pci_dev, 0),
+                               pci_resource_len(pci_dev, 0),
+                               dev->name)) {
+               err = -EBUSY;
+               pr_err("%s: can't get MMIO memory @ 0x%llx\n",
+                       dev->name,
+                       (unsigned long long)pci_resource_start(pci_dev, 0));
+               goto fail1;
+       }
+       dev->lmmio = ioremap(pci_resource_start(pci_dev, 0),
+                            pci_resource_len(pci_dev, 0));
+       dev->bmmio = (__u8 __iomem *)dev->lmmio;
+       if (NULL == dev->lmmio) {
+               err = -EIO;
+               pr_err("%s: can't ioremap() MMIO memory\n",
+                      dev->name);
+               goto fail2;
+       }
+       /* initialize hardware #1 */
+       /* Then do any initialisation wanted before interrupts are on */
+       tw68_hw_init1(dev);
+
+       /* get irq */
+       err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw68_irq,
+                         IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+       if (err < 0) {
+               pr_err("%s: can't get IRQ %d\n",
+                      dev->name, pci_dev->irq);
+               goto fail3;
+       }
+
+       /*
+        *  Now do remainder of initialisation, first for
+        *  things unique for this card, then for general board
+        */
+       if (dev->instance < TW68_MAXBOARDS)
+               vidnr = video_nr[dev->instance];
+       /* initialise video function first */
+       err = tw68_video_init2(dev, vidnr);
+       if (err < 0) {
+               pr_err("%s: can't register video device\n",
+                      dev->name);
+               goto fail4;
+       }
+       tw_setl(TW68_INTMASK, dev->pci_irqmask);
+
+       pr_info("%s: registered device %s\n",
+              dev->name, video_device_node_name(&dev->vdev));
+
+       return 0;
+
+fail4:
+       video_unregister_device(&dev->vdev);
+fail3:
+       iounmap(dev->lmmio);
+fail2:
+       release_mem_region(pci_resource_start(pci_dev, 0),
+                          pci_resource_len(pci_dev, 0));
+fail1:
+       v4l2_device_unregister(&dev->v4l2_dev);
+       return err;
+}
+
+static void tw68_finidev(struct pci_dev *pci_dev)
+{
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct tw68_dev *dev =
+               container_of(v4l2_dev, struct tw68_dev, v4l2_dev);
+
+       /* shutdown subsystems */
+       tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
+       tw_writel(TW68_INTMASK, 0);
+
+       /* unregister */
+       video_unregister_device(&dev->vdev);
+       v4l2_ctrl_handler_free(&dev->hdl);
+
+       /* release resources */
+       iounmap(dev->lmmio);
+       release_mem_region(pci_resource_start(pci_dev, 0),
+                          pci_resource_len(pci_dev, 0));
+
+       v4l2_device_unregister(&dev->v4l2_dev);
+}
+
+#ifdef CONFIG_PM
+
+static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state)
+{
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct tw68_dev *dev = container_of(v4l2_dev,
+                               struct tw68_dev, v4l2_dev);
+
+       tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
+       dev->pci_irqmask &= ~TW68_VID_INTS;
+       tw_writel(TW68_INTMASK, 0);
+
+       synchronize_irq(pci_dev->irq);
+
+       pci_save_state(pci_dev);
+       pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
+       vb2_discard_done(&dev->vidq);
+
+       return 0;
+}
+
+static int tw68_resume(struct pci_dev *pci_dev)
+{
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct tw68_dev *dev = container_of(v4l2_dev,
+                                           struct tw68_dev, v4l2_dev);
+       struct tw68_buf *buf;
+       unsigned long flags;
+
+       pci_set_power_state(pci_dev, PCI_D0);
+       pci_restore_state(pci_dev);
+
+       /* Do things that are done in tw68_initdev ,
+               except of initializing memory structures.*/
+
+       msleep(100);
+
+       tw68_set_tvnorm_hw(dev);
+
+       /*resume unfinished buffer(s)*/
+       spin_lock_irqsave(&dev->slock, flags);
+       buf = container_of(dev->active.next, struct tw68_buf, list);
+
+       tw68_video_start_dma(dev, buf);
+
+       spin_unlock_irqrestore(&dev->slock, flags);
+
+       return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+
+static struct pci_driver tw68_pci_driver = {
+       .name     = "tw68",
+       .id_table = tw68_pci_tbl,
+       .probe    = tw68_initdev,
+       .remove   = tw68_finidev,
+#ifdef CONFIG_PM
+       .suspend  = tw68_suspend,
+       .resume   = tw68_resume
+#endif
+};
+
+module_pci_driver(tw68_pci_driver);
diff --git a/drivers/media/pci/tw68/tw68-reg.h b/drivers/media/pci/tw68/tw68-reg.h
new file mode 100644 (file)
index 0000000..f60b3a8
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ *  tw68-reg.h - TW68xx register offsets
+ *
+ *  Much of this code is derived from the cx88 and sa7134 drivers, which
+ *  were in turn derived from the bt87x driver.  The original work was by
+ *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
+ *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
+ *  acknowledged.  Full credit goes to them - any problems within this code
+ *  are mine.
+ *
+ *  Copyright (C) William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 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.
+*/
+
+#ifndef _TW68_REG_H_
+#define _TW68_REG_H_
+
+/* ---------------------------------------------------------------------- */
+#define        TW68_DMAC               0x000
+#define        TW68_DMAP_SA            0x004
+#define        TW68_DMAP_EXE           0x008
+#define        TW68_DMAP_PP            0x00c
+#define        TW68_VBIC               0x010
+#define        TW68_SBUSC              0x014
+#define        TW68_SBUSSD             0x018
+#define        TW68_INTSTAT            0x01C
+#define        TW68_INTMASK            0x020
+#define        TW68_GPIOC              0x024
+#define        TW68_GPOE               0x028
+#define        TW68_TESTREG            0x02C
+#define        TW68_SBUSRD             0x030
+#define        TW68_SBUS_TRIG          0x034
+#define        TW68_CAP_CTL            0x040
+#define        TW68_SUBSYS             0x054
+#define        TW68_I2C_RST            0x064
+#define        TW68_VBIINST            0x06C
+/* define bits in FIFO and DMAP Control reg */
+#define        TW68_DMAP_EN            (1 << 0)
+#define        TW68_FIFO_EN            (1 << 1)
+/* define the Interrupt Status Register bits */
+#define        TW68_SBDONE             (1 << 0)
+#define        TW68_DMAPI              (1 << 1)
+#define        TW68_GPINT              (1 << 2)
+#define        TW68_FFOF               (1 << 3)
+#define        TW68_FDMIS              (1 << 4)
+#define        TW68_DMAPERR            (1 << 5)
+#define        TW68_PABORT             (1 << 6)
+#define        TW68_SBDONE2            (1 << 12)
+#define        TW68_SBERR2             (1 << 13)
+#define        TW68_PPERR              (1 << 14)
+#define        TW68_FFERR              (1 << 15)
+#define        TW68_DET50              (1 << 16)
+#define        TW68_FLOCK              (1 << 17)
+#define        TW68_CCVALID            (1 << 18)
+#define        TW68_VLOCK              (1 << 19)
+#define        TW68_FIELD              (1 << 20)
+#define        TW68_SLOCK              (1 << 21)
+#define        TW68_HLOCK              (1 << 22)
+#define        TW68_VDLOSS             (1 << 23)
+#define        TW68_SBERR              (1 << 24)
+/* define the i2c control register bits */
+#define        TW68_SBMODE             (0)
+#define        TW68_WREN               (1)
+#define        TW68_SSCLK              (6)
+#define        TW68_SSDAT              (7)
+#define        TW68_SBCLK              (8)
+#define        TW68_WDLEN              (16)
+#define        TW68_RDLEN              (20)
+#define        TW68_SBRW               (24)
+#define        TW68_SBDEV              (25)
+
+#define        TW68_SBMODE_B           (1 << TW68_SBMODE)
+#define        TW68_WREN_B             (1 << TW68_WREN)
+#define        TW68_SSCLK_B            (1 << TW68_SSCLK)
+#define        TW68_SSDAT_B            (1 << TW68_SSDAT)
+#define        TW68_SBRW_B             (1 << TW68_SBRW)
+
+#define        TW68_GPDATA             0x100
+#define        TW68_STATUS1            0x204
+#define        TW68_INFORM             0x208
+#define        TW68_OPFORM             0x20C
+#define        TW68_HSYNC              0x210
+#define        TW68_ACNTL              0x218
+#define        TW68_CROP_HI            0x21C
+#define        TW68_VDELAY_LO          0x220
+#define        TW68_VACTIVE_LO         0x224
+#define        TW68_HDELAY_LO          0x228
+#define        TW68_HACTIVE_LO         0x22C
+#define        TW68_CNTRL1             0x230
+#define        TW68_VSCALE_LO          0x234
+#define        TW68_SCALE_HI           0x238
+#define        TW68_HSCALE_LO          0x23C
+#define        TW68_BRIGHT             0x240
+#define        TW68_CONTRAST           0x244
+#define        TW68_SHARPNESS          0x248
+#define        TW68_SAT_U              0x24C
+#define        TW68_SAT_V              0x250
+#define        TW68_HUE                0x254
+#define        TW68_SHARP2             0x258
+#define        TW68_VSHARP             0x25C
+#define        TW68_CORING             0x260
+#define        TW68_VBICNTL            0x264
+#define        TW68_CNTRL2             0x268
+#define        TW68_CC_DATA            0x26C
+#define        TW68_SDT                0x270
+#define        TW68_SDTR               0x274
+#define        TW68_RESERV2            0x278
+#define        TW68_RESERV3            0x27C
+#define        TW68_CLMPG              0x280
+#define        TW68_IAGC               0x284
+#define        TW68_AGCGAIN            0x288
+#define        TW68_PEAKWT             0x28C
+#define        TW68_CLMPL              0x290
+#define        TW68_SYNCT              0x294
+#define        TW68_MISSCNT            0x298
+#define        TW68_PCLAMP             0x29C
+#define        TW68_VCNTL1             0x2A0
+#define        TW68_VCNTL2             0x2A4
+#define        TW68_CKILL              0x2A8
+#define        TW68_COMB               0x2AC
+#define        TW68_LDLY               0x2B0
+#define        TW68_MISC1              0x2B4
+#define        TW68_LOOP               0x2B8
+#define        TW68_MISC2              0x2BC
+#define        TW68_MVSN               0x2C0
+#define        TW68_STATUS2            0x2C4
+#define        TW68_HFREF              0x2C8
+#define        TW68_CLMD               0x2CC
+#define        TW68_IDCNTL             0x2D0
+#define        TW68_CLCNTL1            0x2D4
+
+/* Audio */
+#define        TW68_ACKI1              0x300
+#define        TW68_ACKI2              0x304
+#define        TW68_ACKI3              0x308
+#define        TW68_ACKN1              0x30C
+#define        TW68_ACKN2              0x310
+#define        TW68_ACKN3              0x314
+#define        TW68_SDIV               0x318
+#define        TW68_LRDIV              0x31C
+#define        TW68_ACCNTL             0x320
+
+#define        TW68_VSCTL              0x3B8
+#define        TW68_CHROMAGVAL         0x3BC
+
+#define        TW68_F2CROP_HI          0x3DC
+#define        TW68_F2VDELAY_LO        0x3E0
+#define        TW68_F2VACTIVE_LO       0x3E4
+#define        TW68_F2HDELAY_LO        0x3E8
+#define        TW68_F2HACTIVE_LO       0x3EC
+#define        TW68_F2CNT              0x3F0
+#define        TW68_F2VSCALE_LO        0x3F4
+#define        TW68_F2SCALE_HI         0x3F8
+#define        TW68_F2HSCALE_LO        0x3FC
+
+#define        RISC_INT_BIT            0x08000000
+#define        RISC_SYNCO              0xC0000000
+#define        RISC_SYNCE              0xD0000000
+#define        RISC_JUMP               0xB0000000
+#define        RISC_LINESTART          0x90000000
+#define        RISC_INLINE             0xA0000000
+
+#define VideoFormatNTSC                 0
+#define VideoFormatNTSCJapan    0
+#define VideoFormatPALBDGHI     1
+#define VideoFormatSECAM        2
+#define VideoFormatNTSC443      3
+#define VideoFormatPALM                 4
+#define VideoFormatPALN                 5
+#define VideoFormatPALNC        5
+#define VideoFormatPAL60        6
+#define VideoFormatAuto                 7
+
+#define ColorFormatRGB32        0x00
+#define ColorFormatRGB24        0x10
+#define ColorFormatRGB16        0x20
+#define ColorFormatRGB15        0x30
+#define ColorFormatYUY2                 0x40
+#define ColorFormatBSWAP         0x04
+#define ColorFormatWSWAP         0x08
+#define ColorFormatGamma         0x80
+#endif
diff --git a/drivers/media/pci/tw68/tw68-risc.c b/drivers/media/pci/tw68/tw68-risc.c
new file mode 100644 (file)
index 0000000..7439db2
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *  tw68_risc.c
+ *  Part of the device driver for Techwell 68xx based cards
+ *
+ *  Much of this code is derived from the cx88 and sa7134 drivers, which
+ *  were in turn derived from the bt87x driver.  The original work was by
+ *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
+ *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
+ *  acknowledged.  Full credit goes to them - any problems within this code
+ *  are mine.
+ *
+ *  Copyright (C) 2009  William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 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.
+ */
+
+#include "tw68.h"
+
+/**
+ *  @rp                pointer to current risc program position
+ *  @sglist    pointer to "scatter-gather list" of buffer pointers
+ *  @offset    offset to target memory buffer
+ *  @sync_line 0 -> no sync, 1 -> odd sync, 2 -> even sync
+ *  @bpl       number of bytes per scan line
+ *  @padding   number of bytes of padding to add
+ *  @lines     number of lines in field
+ *  @jump      insert a jump at the start
+ */
+static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist,
+                           unsigned int offset, u32 sync_line,
+                           unsigned int bpl, unsigned int padding,
+                           unsigned int lines, bool jump)
+{
+       struct scatterlist *sg;
+       unsigned int line, todo, done;
+
+       if (jump) {
+               *(rp++) = cpu_to_le32(RISC_JUMP);
+               *(rp++) = 0;
+       }
+
+       /* sync instruction */
+       if (sync_line == 1)
+               *(rp++) = cpu_to_le32(RISC_SYNCO);
+       else
+               *(rp++) = cpu_to_le32(RISC_SYNCE);
+       *(rp++) = 0;
+
+       /* scan lines */
+       sg = sglist;
+       for (line = 0; line < lines; line++) {
+               /* calculate next starting position */
+               while (offset && offset >= sg_dma_len(sg)) {
+                       offset -= sg_dma_len(sg);
+                       sg = sg_next(sg);
+               }
+               if (bpl <= sg_dma_len(sg) - offset) {
+                       /* fits into current chunk */
+                       *(rp++) = cpu_to_le32(RISC_LINESTART |
+                                             /* (offset<<12) |*/  bpl);
+                       *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+                       offset += bpl;
+               } else {
+                       /*
+                        * scanline needs to be split.  Put the start in
+                        * whatever memory remains using RISC_LINESTART,
+                        * then the remainder into following addresses
+                        * given by the scatter-gather list.
+                        */
+                       todo = bpl;     /* one full line to be done */
+                       /* first fragment */
+                       done = (sg_dma_len(sg) - offset);
+                       *(rp++) = cpu_to_le32(RISC_LINESTART |
+                                               (7 << 24) |
+                                               done);
+                       *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+                       todo -= done;
+                       sg = sg_next(sg);
+                       /* succeeding fragments have no offset */
+                       while (todo > sg_dma_len(sg)) {
+                               *(rp++) = cpu_to_le32(RISC_INLINE |
+                                               (done << 12) |
+                                               sg_dma_len(sg));
+                               *(rp++) = cpu_to_le32(sg_dma_address(sg));
+                               todo -= sg_dma_len(sg);
+                               sg = sg_next(sg);
+                               done += sg_dma_len(sg);
+                       }
+                       if (todo) {
+                               /* final chunk - offset 0, count 'todo' */
+                               *(rp++) = cpu_to_le32(RISC_INLINE |
+                                                       (done << 12) |
+                                                       todo);
+                               *(rp++) = cpu_to_le32(sg_dma_address(sg));
+                       }
+                       offset = todo;
+               }
+               offset += padding;
+       }
+
+       return rp;
+}
+
+/**
+ * tw68_risc_buffer
+ *
+ *     This routine is called by tw68-video.  It allocates
+ *     memory for the dma controller "program" and then fills in that
+ *     memory with the appropriate "instructions".
+ *
+ *     @pci_dev        structure with info about the pci
+ *                     slot which our device is in.
+ *     @risc           structure with info about the memory
+ *                     used for our controller program.
+ *     @sglist         scatter-gather list entry
+ *     @top_offset     offset within the risc program area for the
+ *                     first odd frame line
+ *     @bottom_offset  offset within the risc program area for the
+ *                     first even frame line
+ *     @bpl            number of data bytes per scan line
+ *     @padding        number of extra bytes to add at end of line
+ *     @lines          number of scan lines
+ */
+int tw68_risc_buffer(struct pci_dev *pci,
+                       struct tw68_buf *buf,
+                       struct scatterlist *sglist,
+                       unsigned int top_offset,
+                       unsigned int bottom_offset,
+                       unsigned int bpl,
+                       unsigned int padding,
+                       unsigned int lines)
+{
+       u32 instructions, fields;
+       __le32 *rp;
+
+       fields = 0;
+       if (UNSET != top_offset)
+               fields++;
+       if (UNSET != bottom_offset)
+               fields++;
+       /*
+        * estimate risc mem: worst case is one write per page border +
+        * one write per scan line + syncs + 2 jumps (all 2 dwords).
+        * Padding can cause next bpl to start close to a page border.
+        * First DMA region may be smaller than PAGE_SIZE
+        */
+       instructions  = fields * (1 + (((bpl + padding) * lines) /
+                        PAGE_SIZE) + lines) + 4;
+       buf->size = instructions * 8;
+       buf->cpu = pci_alloc_consistent(pci, buf->size, &buf->dma);
+       if (buf->cpu == NULL)
+               return -ENOMEM;
+
+       /* write risc instructions */
+       rp = buf->cpu;
+       if (UNSET != top_offset)        /* generates SYNCO */
+               rp = tw68_risc_field(rp, sglist, top_offset, 1,
+                                    bpl, padding, lines, true);
+       if (UNSET != bottom_offset)     /* generates SYNCE */
+               rp = tw68_risc_field(rp, sglist, bottom_offset, 2,
+                                    bpl, padding, lines, top_offset == UNSET);
+
+       /* save pointer to jmp instruction address */
+       buf->jmp = rp;
+       buf->cpu[1] = cpu_to_le32(buf->dma + 8);
+       /* assure risc buffer hasn't overflowed */
+       BUG_ON((buf->jmp - buf->cpu + 2) * sizeof(buf->cpu[0]) > buf->size);
+       return 0;
+}
+
+#if 0
+/* ------------------------------------------------------------------ */
+/* debug helper code                                                  */
+
+static void tw68_risc_decode(u32 risc, u32 addr)
+{
+#define        RISC_OP(reg)    (((reg) >> 28) & 7)
+       static struct instr_details {
+               char *name;
+               u8 has_data_type;
+               u8 has_byte_info;
+               u8 has_addr;
+       } instr[8] = {
+               [RISC_OP(RISC_SYNCO)]     = {"syncOdd", 0, 0, 0},
+               [RISC_OP(RISC_SYNCE)]     = {"syncEven", 0, 0, 0},
+               [RISC_OP(RISC_JUMP)]      = {"jump", 0, 0, 1},
+               [RISC_OP(RISC_LINESTART)] = {"lineStart", 1, 1, 1},
+               [RISC_OP(RISC_INLINE)]    = {"inline", 1, 1, 1},
+       };
+       u32 p;
+
+       p = RISC_OP(risc);
+       if (!(risc & 0x80000000) || !instr[p].name) {
+               pr_debug("0x%08x [ INVALID ]\n", risc);
+               return;
+       }
+       pr_debug("0x%08x %-9s IRQ=%d",
+               risc, instr[p].name, (risc >> 27) & 1);
+       if (instr[p].has_data_type)
+               pr_debug(" Type=%d", (risc >> 24) & 7);
+       if (instr[p].has_byte_info)
+               pr_debug(" Start=0x%03x Count=%03u",
+                       (risc >> 12) & 0xfff, risc & 0xfff);
+       if (instr[p].has_addr)
+               pr_debug(" StartAddr=0x%08x", addr);
+       pr_debug("\n");
+}
+
+void tw68_risc_program_dump(struct tw68_core *core, struct tw68_buf *buf)
+{
+       const __le32 *addr;
+
+       pr_debug("%s: risc_program_dump: risc=%p, buf->cpu=0x%p, buf->jmp=0x%p\n",
+                 core->name, buf, buf->cpu, buf->jmp);
+       for (addr = buf->cpu; addr <= buf->jmp; addr += 2)
+               tw68_risc_decode(*addr, *(addr+1));
+}
+#endif
diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c
new file mode 100644 (file)
index 0000000..5c94ac7
--- /dev/null
@@ -0,0 +1,1051 @@
+/*
+ *  tw68 functions to handle video data
+ *
+ *  Much of this code is derived from the cx88 and sa7134 drivers, which
+ *  were in turn derived from the bt87x driver.  The original work was by
+ *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
+ *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
+ *  acknowledged.  Full credit goes to them - any problems within this code
+ *  are mine.
+ *
+ *  Copyright (C) 2009  William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 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.
+ */
+
+#include <linux/module.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "tw68.h"
+#include "tw68-reg.h"
+
+/* ------------------------------------------------------------------ */
+/* data structs for video                                             */
+/*
+ * FIXME -
+ * Note that the saa7134 has formats, e.g. YUV420, which are classified
+ * as "planar".  These affect overlay mode, and are flagged with a field
+ * ".planar" in the format.  Do we need to implement this in this driver?
+ */
+static const struct tw68_format formats[] = {
+       {
+               .name           = "15 bpp RGB, le",
+               .fourcc         = V4L2_PIX_FMT_RGB555,
+               .depth          = 16,
+               .twformat       = ColorFormatRGB15,
+       }, {
+               .name           = "15 bpp RGB, be",
+               .fourcc         = V4L2_PIX_FMT_RGB555X,
+               .depth          = 16,
+               .twformat       = ColorFormatRGB15 | ColorFormatBSWAP,
+       }, {
+               .name           = "16 bpp RGB, le",
+               .fourcc         = V4L2_PIX_FMT_RGB565,
+               .depth          = 16,
+               .twformat       = ColorFormatRGB16,
+       }, {
+               .name           = "16 bpp RGB, be",
+               .fourcc         = V4L2_PIX_FMT_RGB565X,
+               .depth          = 16,
+               .twformat       = ColorFormatRGB16 | ColorFormatBSWAP,
+       }, {
+               .name           = "24 bpp RGB, le",
+               .fourcc         = V4L2_PIX_FMT_BGR24,
+               .depth          = 24,
+               .twformat       = ColorFormatRGB24,
+       }, {
+               .name           = "24 bpp RGB, be",
+               .fourcc         = V4L2_PIX_FMT_RGB24,
+               .depth          = 24,
+               .twformat       = ColorFormatRGB24 | ColorFormatBSWAP,
+       }, {
+               .name           = "32 bpp RGB, le",
+               .fourcc         = V4L2_PIX_FMT_BGR32,
+               .depth          = 32,
+               .twformat       = ColorFormatRGB32,
+       }, {
+               .name           = "32 bpp RGB, be",
+               .fourcc         = V4L2_PIX_FMT_RGB32,
+               .depth          = 32,
+               .twformat       = ColorFormatRGB32 | ColorFormatBSWAP |
+                                 ColorFormatWSWAP,
+       }, {
+               .name           = "4:2:2 packed, YUYV",
+               .fourcc         = V4L2_PIX_FMT_YUYV,
+               .depth          = 16,
+               .twformat       = ColorFormatYUY2,
+       }, {
+               .name           = "4:2:2 packed, UYVY",
+               .fourcc         = V4L2_PIX_FMT_UYVY,
+               .depth          = 16,
+               .twformat       = ColorFormatYUY2 | ColorFormatBSWAP,
+       }
+};
+#define FORMATS ARRAY_SIZE(formats)
+
+#define NORM_625_50                    \
+               .h_delay        = 3,    \
+               .h_delay0       = 133,  \
+               .h_start        = 0,    \
+               .h_stop         = 719,  \
+               .v_delay        = 24,   \
+               .vbi_v_start_0  = 7,    \
+               .vbi_v_stop_0   = 22,   \
+               .video_v_start  = 24,   \
+               .video_v_stop   = 311,  \
+               .vbi_v_start_1  = 319
+
+#define NORM_525_60                    \
+               .h_delay        = 8,    \
+               .h_delay0       = 138,  \
+               .h_start        = 0,    \
+               .h_stop         = 719,  \
+               .v_delay        = 22,   \
+               .vbi_v_start_0  = 10,   \
+               .vbi_v_stop_0   = 21,   \
+               .video_v_start  = 22,   \
+               .video_v_stop   = 262,  \
+               .vbi_v_start_1  = 273
+
+/*
+ * The following table is searched by tw68_s_std, first for a specific
+ * match, then for an entry which contains the desired id.  The table
+ * entries should therefore be ordered in ascending order of specificity.
+ */
+static const struct tw68_tvnorm tvnorms[] = {
+       {
+               .name           = "PAL", /* autodetect */
+               .id             = V4L2_STD_PAL,
+               NORM_625_50,
+
+               .sync_control   = 0x18,
+               .luma_control   = 0x40,
+               .chroma_ctrl1   = 0x81,
+               .chroma_gain    = 0x2a,
+               .chroma_ctrl2   = 0x06,
+               .vgate_misc     = 0x1c,
+               .format         = VideoFormatPALBDGHI,
+       }, {
+               .name           = "NTSC",
+               .id             = V4L2_STD_NTSC,
+               NORM_525_60,
+
+               .sync_control   = 0x59,
+               .luma_control   = 0x40,
+               .chroma_ctrl1   = 0x89,
+               .chroma_gain    = 0x2a,
+               .chroma_ctrl2   = 0x0e,
+               .vgate_misc     = 0x18,
+               .format         = VideoFormatNTSC,
+       }, {
+               .name           = "SECAM",
+               .id             = V4L2_STD_SECAM,
+               NORM_625_50,
+
+               .sync_control   = 0x18,
+               .luma_control   = 0x1b,
+               .chroma_ctrl1   = 0xd1,
+               .chroma_gain    = 0x80,
+               .chroma_ctrl2   = 0x00,
+               .vgate_misc     = 0x1c,
+               .format         = VideoFormatSECAM,
+       }, {
+               .name           = "PAL-M",
+               .id             = V4L2_STD_PAL_M,
+               NORM_525_60,
+
+               .sync_control   = 0x59,
+               .luma_control   = 0x40,
+               .chroma_ctrl1   = 0xb9,
+               .chroma_gain    = 0x2a,
+               .chroma_ctrl2   = 0x0e,
+               .vgate_misc     = 0x18,
+               .format         = VideoFormatPALM,
+       }, {
+               .name           = "PAL-Nc",
+               .id             = V4L2_STD_PAL_Nc,
+               NORM_625_50,
+
+               .sync_control   = 0x18,
+               .luma_control   = 0x40,
+               .chroma_ctrl1   = 0xa1,
+               .chroma_gain    = 0x2a,
+               .chroma_ctrl2   = 0x06,
+               .vgate_misc     = 0x1c,
+               .format         = VideoFormatPALNC,
+       }, {
+               .name           = "PAL-60",
+               .id             = V4L2_STD_PAL_60,
+               .h_delay        = 186,
+               .h_start        = 0,
+               .h_stop         = 719,
+               .v_delay        = 26,
+               .video_v_start  = 23,
+               .video_v_stop   = 262,
+               .vbi_v_start_0  = 10,
+               .vbi_v_stop_0   = 21,
+               .vbi_v_start_1  = 273,
+
+               .sync_control   = 0x18,
+               .luma_control   = 0x40,
+               .chroma_ctrl1   = 0x81,
+               .chroma_gain    = 0x2a,
+               .chroma_ctrl2   = 0x06,
+               .vgate_misc     = 0x1c,
+               .format         = VideoFormatPAL60,
+       }
+};
+#define TVNORMS ARRAY_SIZE(tvnorms)
+
+static const struct tw68_format *format_by_fourcc(unsigned int fourcc)
+{
+       unsigned int i;
+
+       for (i = 0; i < FORMATS; i++)
+               if (formats[i].fourcc == fourcc)
+                       return formats+i;
+       return NULL;
+}
+
+
+/* ------------------------------------------------------------------ */
+/*
+ * Note that the cropping rectangles are described in terms of a single
+ * frame, i.e. line positions are only 1/2 the interlaced equivalent
+ */
+static void set_tvnorm(struct tw68_dev *dev, const struct tw68_tvnorm *norm)
+{
+       if (norm != dev->tvnorm) {
+               dev->width = 720;
+               dev->height = (norm->id & V4L2_STD_525_60) ? 480 : 576;
+               dev->tvnorm = norm;
+               tw68_set_tvnorm_hw(dev);
+       }
+}
+
+/*
+ * tw68_set_scale
+ *
+ * Scaling and Cropping for video decoding
+ *
+ * We are working with 3 values for horizontal and vertical - scale,
+ * delay and active.
+ *
+ * HACTIVE represent the actual number of pixels in the "usable" image,
+ * before scaling.  HDELAY represents the number of pixels skipped
+ * between the start of the horizontal sync and the start of the image.
+ * HSCALE is calculated using the formula
+ *     HSCALE = (HACTIVE / (#pixels desired)) * 256
+ *
+ * The vertical registers are similar, except based upon the total number
+ * of lines in the image, and the first line of the image (i.e. ignoring
+ * vertical sync and VBI).
+ *
+ * Note that the number of bytes reaching the FIFO (and hence needing
+ * to be processed by the DMAP program) is completely dependent upon
+ * these values, especially HSCALE.
+ *
+ * Parameters:
+ *     @dev            pointer to the device structure, needed for
+ *                     getting current norm (as well as debug print)
+ *     @width          actual image width (from user buffer)
+ *     @height         actual image height
+ *     @field          indicates Top, Bottom or Interlaced
+ */
+static int tw68_set_scale(struct tw68_dev *dev, unsigned int width,
+                         unsigned int height, enum v4l2_field field)
+{
+       const struct tw68_tvnorm *norm = dev->tvnorm;
+       /* set individually for debugging clarity */
+       int hactive, hdelay, hscale;
+       int vactive, vdelay, vscale;
+       int comb;
+
+       if (V4L2_FIELD_HAS_BOTH(field)) /* if field is interlaced */
+               height /= 2;            /* we must set for 1-frame */
+
+       pr_debug("%s: width=%d, height=%d, both=%d\n"
+                "  tvnorm h_delay=%d, h_start=%d, h_stop=%d, "
+                "v_delay=%d, v_start=%d, v_stop=%d\n" , __func__,
+               width, height, V4L2_FIELD_HAS_BOTH(field),
+               norm->h_delay, norm->h_start, norm->h_stop,
+               norm->v_delay, norm->video_v_start,
+               norm->video_v_stop);
+
+       switch (dev->vdecoder) {
+       case TW6800:
+               hdelay = norm->h_delay0;
+               break;
+       default:
+               hdelay = norm->h_delay;
+               break;
+       }
+
+       hdelay += norm->h_start;
+       hactive = norm->h_stop - norm->h_start + 1;
+
+       hscale = (hactive * 256) / (width);
+
+       vdelay = norm->v_delay;
+       vactive = ((norm->id & V4L2_STD_525_60) ? 524 : 624) / 2 - norm->video_v_start;
+       vscale = (vactive * 256) / height;
+
+       pr_debug("%s: %dx%d [%s%s,%s]\n", __func__,
+               width, height,
+               V4L2_FIELD_HAS_TOP(field)    ? "T" : "",
+               V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
+               v4l2_norm_to_name(dev->tvnorm->id));
+       pr_debug("%s: hactive=%d, hdelay=%d, hscale=%d; "
+               "vactive=%d, vdelay=%d, vscale=%d\n", __func__,
+               hactive, hdelay, hscale, vactive, vdelay, vscale);
+
+       comb =  ((vdelay & 0x300)  >> 2) |
+               ((vactive & 0x300) >> 4) |
+               ((hdelay & 0x300)  >> 6) |
+               ((hactive & 0x300) >> 8);
+       pr_debug("%s: setting CROP_HI=%02x, VDELAY_LO=%02x, "
+               "VACTIVE_LO=%02x, HDELAY_LO=%02x, HACTIVE_LO=%02x\n",
+               __func__, comb, vdelay, vactive, hdelay, hactive);
+       tw_writeb(TW68_CROP_HI, comb);
+       tw_writeb(TW68_VDELAY_LO, vdelay & 0xff);
+       tw_writeb(TW68_VACTIVE_LO, vactive & 0xff);
+       tw_writeb(TW68_HDELAY_LO, hdelay & 0xff);
+       tw_writeb(TW68_HACTIVE_LO, hactive & 0xff);
+
+       comb = ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8);
+       pr_debug("%s: setting SCALE_HI=%02x, VSCALE_LO=%02x, "
+               "HSCALE_LO=%02x\n", __func__, comb, vscale, hscale);
+       tw_writeb(TW68_SCALE_HI, comb);
+       tw_writeb(TW68_VSCALE_LO, vscale);
+       tw_writeb(TW68_HSCALE_LO, hscale);
+
+       return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf)
+{
+       /* Set cropping and scaling */
+       tw68_set_scale(dev, dev->width, dev->height, dev->field);
+       /*
+        *  Set start address for RISC program.  Note that if the DMAP
+        *  processor is currently running, it must be stopped before
+        *  a new address can be set.
+        */
+       tw_clearl(TW68_DMAC, TW68_DMAP_EN);
+       tw_writel(TW68_DMAP_SA, buf->dma);
+       /* Clear any pending interrupts */
+       tw_writel(TW68_INTSTAT, dev->board_virqmask);
+       /* Enable the risc engine and the fifo */
+       tw_andorl(TW68_DMAC, 0xff, dev->fmt->twformat |
+               ColorFormatGamma | TW68_DMAP_EN | TW68_FIFO_EN);
+       dev->pci_irqmask |= dev->board_virqmask;
+       tw_setl(TW68_INTMASK, dev->pci_irqmask);
+       return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+/* calc max # of buffers from size (must not exceed the 4MB virtual
+ * address space per DMA channel) */
+static int tw68_buffer_count(unsigned int size, unsigned int count)
+{
+       unsigned int maxcount;
+
+       maxcount = (4 * 1024 * 1024) / roundup(size, PAGE_SIZE);
+       if (count > maxcount)
+               count = maxcount;
+       return count;
+}
+
+/* ------------------------------------------------------------- */
+/* vb2 queue operations                                          */
+
+static int tw68_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *num_buffers, unsigned int *num_planes,
+                          unsigned int sizes[], void *alloc_ctxs[])
+{
+       struct tw68_dev *dev = vb2_get_drv_priv(q);
+       unsigned tot_bufs = q->num_buffers + *num_buffers;
+
+       sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3;
+       /*
+        * We allow create_bufs, but only if the sizeimage is the same as the
+        * current sizeimage. The tw68_buffer_count calculation becomes quite
+        * difficult otherwise.
+        */
+       if (fmt && fmt->fmt.pix.sizeimage < sizes[0])
+               return -EINVAL;
+       *num_planes = 1;
+       if (tot_bufs < 2)
+               tot_bufs = 2;
+       tot_bufs = tw68_buffer_count(sizes[0], tot_bufs);
+       *num_buffers = tot_bufs - q->num_buffers;
+
+       return 0;
+}
+
+/*
+ * The risc program for each buffers works as follows: it starts with a simple
+ * 'JUMP to addr + 8', which is effectively a NOP. Then the program to DMA the
+ * buffer follows and at the end we have a JUMP back to the start + 8 (skipping
+ * the initial JUMP).
+ *
+ * This is the program of the first buffer to be queued if the active list is
+ * empty and it just keeps DMAing this buffer without generating any interrupts.
+ *
+ * If a new buffer is added then the initial JUMP in the program generates an
+ * interrupt as well which signals that the previous buffer has been DMAed
+ * successfully and that it can be returned to userspace.
+ *
+ * It also sets the final jump of the previous buffer to the start of the new
+ * buffer, thus chaining the new buffer into the DMA chain. This is a single
+ * atomic u32 write, so there is no race condition.
+ *
+ * The end-result of all this that you only get an interrupt when a buffer
+ * is ready, so the control flow is very easy.
+ */
+static void tw68_buf_queue(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct tw68_dev *dev = vb2_get_drv_priv(vq);
+       struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
+       struct tw68_buf *prev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->slock, flags);
+
+       /* append a 'JUMP to start of buffer' to the buffer risc program */
+       buf->jmp[0] = cpu_to_le32(RISC_JUMP);
+       buf->jmp[1] = cpu_to_le32(buf->dma + 8);
+
+       if (!list_empty(&dev->active)) {
+               prev = list_entry(dev->active.prev, struct tw68_buf, list);
+               buf->cpu[0] |= cpu_to_le32(RISC_INT_BIT);
+               prev->jmp[1] = cpu_to_le32(buf->dma);
+       }
+       list_add_tail(&buf->list, &dev->active);
+       spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+/*
+ * buffer_prepare
+ *
+ * Set the ancilliary information into the buffer structure.  This
+ * includes generating the necessary risc program if it hasn't already
+ * been done for the current buffer format.
+ * The structure fh contains the details of the format requested by the
+ * user - type, width, height and #fields.  This is compared with the
+ * last format set for the current buffer.  If they differ, the risc
+ * code (which controls the filling of the buffer) is (re-)generated.
+ */
+static int tw68_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct tw68_dev *dev = vb2_get_drv_priv(vq);
+       struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0);
+       unsigned size, bpl;
+       int rc;
+
+       size = (dev->width * dev->height * dev->fmt->depth) >> 3;
+       if (vb2_plane_size(vb, 0) < size)
+               return -EINVAL;
+       vb2_set_plane_payload(vb, 0, size);
+
+       rc = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
+       if (!rc)
+               return -EIO;
+
+       bpl = (dev->width * dev->fmt->depth) >> 3;
+       switch (dev->field) {
+       case V4L2_FIELD_TOP:
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                0, UNSET, bpl, 0, dev->height);
+               break;
+       case V4L2_FIELD_BOTTOM:
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                UNSET, 0, bpl, 0, dev->height);
+               break;
+       case V4L2_FIELD_SEQ_TB:
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                0, bpl * (dev->height >> 1),
+                                bpl, 0, dev->height >> 1);
+               break;
+       case V4L2_FIELD_SEQ_BT:
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                bpl * (dev->height >> 1), 0,
+                                bpl, 0, dev->height >> 1);
+               break;
+       case V4L2_FIELD_INTERLACED:
+       default:
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                0, bpl, bpl, bpl, dev->height >> 1);
+               break;
+       }
+       return 0;
+}
+
+static void tw68_buf_finish(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct tw68_dev *dev = vb2_get_drv_priv(vq);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0);
+       struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
+
+       dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
+
+       pci_free_consistent(dev->pci, buf->size, buf->cpu, buf->dma);
+}
+
+static int tw68_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct tw68_dev *dev = vb2_get_drv_priv(q);
+       struct tw68_buf *buf =
+               container_of(dev->active.next, struct tw68_buf, list);
+
+       dev->seqnr = 0;
+       tw68_video_start_dma(dev, buf);
+       return 0;
+}
+
+static void tw68_stop_streaming(struct vb2_queue *q)
+{
+       struct tw68_dev *dev = vb2_get_drv_priv(q);
+
+       /* Stop risc & fifo */
+       tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
+       while (!list_empty(&dev->active)) {
+               struct tw68_buf *buf =
+                       container_of(dev->active.next, struct tw68_buf, list);
+
+               list_del(&buf->list);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+       }
+}
+
+static struct vb2_ops tw68_video_qops = {
+       .queue_setup    = tw68_queue_setup,
+       .buf_queue      = tw68_buf_queue,
+       .buf_prepare    = tw68_buf_prepare,
+       .buf_finish     = tw68_buf_finish,
+       .start_streaming = tw68_start_streaming,
+       .stop_streaming = tw68_stop_streaming,
+       .wait_prepare   = vb2_ops_wait_prepare,
+       .wait_finish    = vb2_ops_wait_finish,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int tw68_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct tw68_dev *dev =
+               container_of(ctrl->handler, struct tw68_dev, hdl);
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               tw_writeb(TW68_BRIGHT, ctrl->val);
+               break;
+       case V4L2_CID_HUE:
+               tw_writeb(TW68_HUE, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               tw_writeb(TW68_CONTRAST, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               tw_writeb(TW68_SAT_U, ctrl->val);
+               tw_writeb(TW68_SAT_V, ctrl->val);
+               break;
+       case V4L2_CID_COLOR_KILLER:
+               if (ctrl->val)
+                       tw_andorb(TW68_MISC2, 0xe0, 0xe0);
+               else
+                       tw_andorb(TW68_MISC2, 0xe0, 0x00);
+               break;
+       case V4L2_CID_CHROMA_AGC:
+               if (ctrl->val)
+                       tw_andorb(TW68_LOOP, 0x30, 0x20);
+               else
+                       tw_andorb(TW68_LOOP, 0x30, 0x00);
+               break;
+       }
+       return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+/*
+ * Note that this routine returns what is stored in the fh structure, and
+ * does not interrogate any of the device registers.
+ */
+static int tw68_g_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+
+       f->fmt.pix.width        = dev->width;
+       f->fmt.pix.height       = dev->height;
+       f->fmt.pix.field        = dev->field;
+       f->fmt.pix.pixelformat  = dev->fmt->fourcc;
+       f->fmt.pix.bytesperline =
+               (f->fmt.pix.width * (dev->fmt->depth)) >> 3;
+       f->fmt.pix.sizeimage =
+               f->fmt.pix.height * f->fmt.pix.bytesperline;
+       f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
+       f->fmt.pix.priv = 0;
+       return 0;
+}
+
+static int tw68_try_fmt_vid_cap(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+       const struct tw68_format *fmt;
+       enum v4l2_field field;
+       unsigned int maxh;
+
+       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+       if (NULL == fmt)
+               return -EINVAL;
+
+       field = f->fmt.pix.field;
+       maxh  = (dev->tvnorm->id & V4L2_STD_525_60) ? 480 : 576;
+
+       switch (field) {
+       case V4L2_FIELD_TOP:
+       case V4L2_FIELD_BOTTOM:
+               break;
+       case V4L2_FIELD_INTERLACED:
+       case V4L2_FIELD_SEQ_BT:
+       case V4L2_FIELD_SEQ_TB:
+               maxh = maxh * 2;
+               break;
+       default:
+               field = (f->fmt.pix.height > maxh / 2)
+                       ? V4L2_FIELD_INTERLACED
+                       : V4L2_FIELD_BOTTOM;
+               break;
+       }
+
+       f->fmt.pix.field = field;
+       if (f->fmt.pix.width  < 48)
+               f->fmt.pix.width  = 48;
+       if (f->fmt.pix.height < 32)
+               f->fmt.pix.height = 32;
+       if (f->fmt.pix.width > 720)
+               f->fmt.pix.width = 720;
+       if (f->fmt.pix.height > maxh)
+               f->fmt.pix.height = maxh;
+       f->fmt.pix.width &= ~0x03;
+       f->fmt.pix.bytesperline =
+               (f->fmt.pix.width * (fmt->depth)) >> 3;
+       f->fmt.pix.sizeimage =
+               f->fmt.pix.height * f->fmt.pix.bytesperline;
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+       return 0;
+}
+
+/*
+ * Note that tw68_s_fmt_vid_cap sets the information into the fh structure,
+ * and it will be used for all future new buffers.  However, there could be
+ * some number of buffers on the "active" chain which will be filled before
+ * the change takes place.
+ */
+static int tw68_s_fmt_vid_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+       int err;
+
+       err = tw68_try_fmt_vid_cap(file, priv, f);
+       if (0 != err)
+               return err;
+
+       dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+       dev->width = f->fmt.pix.width;
+       dev->height = f->fmt.pix.height;
+       dev->field = f->fmt.pix.field;
+       return 0;
+}
+
+static int tw68_enum_input(struct file *file, void *priv,
+                                       struct v4l2_input *i)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+       unsigned int n;
+
+       n = i->index;
+       if (n >= TW68_INPUT_MAX)
+               return -EINVAL;
+       i->index = n;
+       i->type = V4L2_INPUT_TYPE_CAMERA;
+       snprintf(i->name, sizeof(i->name), "Composite %d", n);
+
+       /* If the query is for the current input, get live data */
+       if (n == dev->input) {
+               int v1 = tw_readb(TW68_STATUS1);
+               int v2 = tw_readb(TW68_MVSN);
+
+               if (0 != (v1 & (1 << 7)))
+                       i->status |= V4L2_IN_ST_NO_SYNC;
+               if (0 != (v1 & (1 << 6)))
+                       i->status |= V4L2_IN_ST_NO_H_LOCK;
+               if (0 != (v1 & (1 << 2)))
+                       i->status |= V4L2_IN_ST_NO_SIGNAL;
+               if (0 != (v1 & 1 << 1))
+                       i->status |= V4L2_IN_ST_NO_COLOR;
+               if (0 != (v2 & (1 << 2)))
+                       i->status |= V4L2_IN_ST_MACROVISION;
+       }
+       i->std = video_devdata(file)->tvnorms;
+       return 0;
+}
+
+static int tw68_g_input(struct file *file, void *priv, unsigned int *i)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+
+       *i = dev->input;
+       return 0;
+}
+
+static int tw68_s_input(struct file *file, void *priv, unsigned int i)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+
+       if (i >= TW68_INPUT_MAX)
+               return -EINVAL;
+       dev->input = i;
+       tw_andorb(TW68_INFORM, 0x03 << 2, dev->input << 2);
+       return 0;
+}
+
+static int tw68_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *cap)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+
+       strcpy(cap->driver, "tw68");
+       strlcpy(cap->card, "Techwell Capture Card",
+               sizeof(cap->card));
+       sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+       cap->device_caps =
+               V4L2_CAP_VIDEO_CAPTURE |
+               V4L2_CAP_READWRITE |
+               V4L2_CAP_STREAMING;
+
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int tw68_s_std(struct file *file, void *priv, v4l2_std_id id)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+       unsigned int i;
+
+       if (vb2_is_busy(&dev->vidq))
+               return -EBUSY;
+
+       /* Look for match on complete norm id (may have mult bits) */
+       for (i = 0; i < TVNORMS; i++) {
+               if (id == tvnorms[i].id)
+                       break;
+       }
+
+       /* If no exact match, look for norm which contains this one */
+       if (i == TVNORMS) {
+               for (i = 0; i < TVNORMS; i++)
+                       if (id & tvnorms[i].id)
+                               break;
+       }
+       /* If still not matched, give up */
+       if (i == TVNORMS)
+               return -EINVAL;
+
+       set_tvnorm(dev, &tvnorms[i]);   /* do the actual setting */
+       return 0;
+}
+
+static int tw68_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+
+       *id = dev->tvnorm->id;
+       return 0;
+}
+
+static int tw68_enum_fmt_vid_cap(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+{
+       if (f->index >= FORMATS)
+               return -EINVAL;
+
+       strlcpy(f->description, formats[f->index].name,
+               sizeof(f->description));
+
+       f->pixelformat = formats[f->index].fourcc;
+
+       return 0;
+}
+
+/*
+ * Used strictly for internal development and debugging, this routine
+ * prints out the current register contents for the tw68xx device.
+ */
+static void tw68_dump_regs(struct tw68_dev *dev)
+{
+       unsigned char line[80];
+       int i, j, k;
+       unsigned char *cptr;
+
+       pr_info("Full dump of TW68 registers:\n");
+       /* First we do the PCI regs, 8 4-byte regs per line */
+       for (i = 0; i < 0x100; i += 32) {
+               cptr = line;
+               cptr += sprintf(cptr, "%03x  ", i);
+               /* j steps through the next 4 words */
+               for (j = i; j < i + 16; j += 4)
+                       cptr += sprintf(cptr, "%08x ", tw_readl(j));
+               *cptr++ = ' ';
+               for (; j < i + 32; j += 4)
+                       cptr += sprintf(cptr, "%08x ", tw_readl(j));
+               *cptr++ = '\n';
+               *cptr = 0;
+               pr_info("%s", line);
+       }
+       /* Next the control regs, which are single-byte, address mod 4 */
+       while (i < 0x400) {
+               cptr = line;
+               cptr += sprintf(cptr, "%03x ", i);
+               /* Print out 4 groups of 4 bytes */
+               for (j = 0; j < 4; j++) {
+                       for (k = 0; k < 4; k++) {
+                               cptr += sprintf(cptr, "%02x ",
+                                       tw_readb(i));
+                               i += 4;
+                       }
+                       *cptr++ = ' ';
+               }
+               *cptr++ = '\n';
+               *cptr = 0;
+               pr_info("%s", line);
+       }
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+
+       tw68_dump_regs(dev);
+       return v4l2_ctrl_log_status(file, priv);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+                             struct v4l2_dbg_register *reg)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+
+       if (reg->size == 1)
+               reg->val = tw_readb(reg->reg);
+       else
+               reg->val = tw_readl(reg->reg);
+       return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+                               const struct v4l2_dbg_register *reg)
+{
+       struct tw68_dev *dev = video_drvdata(file);
+
+       if (reg->size == 1)
+               tw_writeb(reg->reg, reg->val);
+       else
+               tw_writel(reg->reg & 0xffff, reg->val);
+       return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops tw68_ctrl_ops = {
+       .s_ctrl = tw68_s_ctrl,
+};
+
+static const struct v4l2_file_operations video_fops = {
+       .owner                  = THIS_MODULE,
+       .open                   = v4l2_fh_open,
+       .release                = vb2_fop_release,
+       .read                   = vb2_fop_read,
+       .poll                   = vb2_fop_poll,
+       .mmap                   = vb2_fop_mmap,
+       .unlocked_ioctl         = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+       .vidioc_querycap                = tw68_querycap,
+       .vidioc_enum_fmt_vid_cap        = tw68_enum_fmt_vid_cap,
+       .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs             = vb2_ioctl_create_bufs,
+       .vidioc_querybuf                = vb2_ioctl_querybuf,
+       .vidioc_qbuf                    = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
+       .vidioc_s_std                   = tw68_s_std,
+       .vidioc_g_std                   = tw68_g_std,
+       .vidioc_enum_input              = tw68_enum_input,
+       .vidioc_g_input                 = tw68_g_input,
+       .vidioc_s_input                 = tw68_s_input,
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
+       .vidioc_g_fmt_vid_cap           = tw68_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap         = tw68_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap           = tw68_s_fmt_vid_cap,
+       .vidioc_log_status              = vidioc_log_status,
+       .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register              = vidioc_g_register,
+       .vidioc_s_register              = vidioc_s_register,
+#endif
+};
+
+static struct video_device tw68_video_template = {
+       .name                   = "tw68_video",
+       .fops                   = &video_fops,
+       .ioctl_ops              = &video_ioctl_ops,
+       .release                = video_device_release_empty,
+       .tvnorms                = TW68_NORMS,
+};
+
+/* ------------------------------------------------------------------ */
+/* exported stuff                                                     */
+void tw68_set_tvnorm_hw(struct tw68_dev *dev)
+{
+       tw_andorb(TW68_SDT, 0x07, dev->tvnorm->format);
+}
+
+int tw68_video_init1(struct tw68_dev *dev)
+{
+       struct v4l2_ctrl_handler *hdl = &dev->hdl;
+
+       v4l2_ctrl_handler_init(hdl, 6);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, -128, 127, 1, 20);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 100);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 255, 1, 128);
+       /* NTSC only */
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_HUE, -128, 127, 1, 0);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
+       if (hdl->error) {
+               v4l2_ctrl_handler_free(hdl);
+               return hdl->error;
+       }
+       dev->v4l2_dev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_setup(hdl);
+       return 0;
+}
+
+int tw68_video_init2(struct tw68_dev *dev, int video_nr)
+{
+       int ret;
+
+       set_tvnorm(dev, &tvnorms[0]);
+
+       dev->fmt      = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+       dev->width    = 720;
+       dev->height   = 576;
+       dev->field    = V4L2_FIELD_INTERLACED;
+
+       INIT_LIST_HEAD(&dev->active);
+       dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | VB2_DMABUF;
+       dev->vidq.ops = &tw68_video_qops;
+       dev->vidq.mem_ops = &vb2_dma_sg_memops;
+       dev->vidq.drv_priv = dev;
+       dev->vidq.gfp_flags = __GFP_DMA32;
+       dev->vidq.buf_struct_size = sizeof(struct tw68_buf);
+       dev->vidq.lock = &dev->lock;
+       dev->vidq.min_buffers_needed = 2;
+       ret = vb2_queue_init(&dev->vidq);
+       if (ret)
+               return ret;
+       dev->vdev = tw68_video_template;
+       dev->vdev.v4l2_dev = &dev->v4l2_dev;
+       dev->vdev.lock = &dev->lock;
+       dev->vdev.queue = &dev->vidq;
+       video_set_drvdata(&dev->vdev, dev);
+       return video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr);
+}
+
+/*
+ * tw68_irq_video_done
+ */
+void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status)
+{
+       __u32 reg;
+
+       /* reset interrupts handled by this routine */
+       tw_writel(TW68_INTSTAT, status);
+       /*
+        * Check most likely first
+        *
+        * DMAPI shows we have reached the end of the risc code
+        * for the current buffer.
+        */
+       if (status & TW68_DMAPI) {
+               struct tw68_buf *buf;
+
+               spin_lock(&dev->slock);
+               buf = list_entry(dev->active.next, struct tw68_buf, list);
+               list_del(&buf->list);
+               spin_unlock(&dev->slock);
+               v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+               buf->vb.v4l2_buf.field = dev->field;
+               buf->vb.v4l2_buf.sequence = dev->seqnr++;
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+               status &= ~(TW68_DMAPI);
+               if (0 == status)
+                       return;
+       }
+       if (status & (TW68_VLOCK | TW68_HLOCK))
+               dev_dbg(&dev->pci->dev, "Lost sync\n");
+       if (status & TW68_PABORT)
+               dev_err(&dev->pci->dev, "PABORT interrupt\n");
+       if (status & TW68_DMAPERR)
+               dev_err(&dev->pci->dev, "DMAPERR interrupt\n");
+       /*
+        * On TW6800, FDMIS is apparently generated if video input is switched
+        * during operation.  Therefore, it is not enabled for that chip.
+        */
+       if (status & TW68_FDMIS)
+               dev_dbg(&dev->pci->dev, "FDMIS interrupt\n");
+       if (status & TW68_FFOF) {
+               /* probably a logic error */
+               reg = tw_readl(TW68_DMAC) & TW68_FIFO_EN;
+               tw_clearl(TW68_DMAC, TW68_FIFO_EN);
+               dev_dbg(&dev->pci->dev, "FFOF interrupt\n");
+               tw_setl(TW68_DMAC, reg);
+       }
+       if (status & TW68_FFERR)
+               dev_dbg(&dev->pci->dev, "FFERR interrupt\n");
+}
diff --git a/drivers/media/pci/tw68/tw68.h b/drivers/media/pci/tw68/tw68.h
new file mode 100644 (file)
index 0000000..2c8abe2
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ *  tw68 driver common header file
+ *
+ *  Much of this code is derived from the cx88 and sa7134 drivers, which
+ *  were in turn derived from the bt87x driver.  The original work was by
+ *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
+ *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
+ *  acknowledged.  Full credit goes to them - any problems within this code
+ *  are mine.
+ *
+ *  Copyright (C) 2009  William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 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.
+ */
+
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/videodev2.h>
+#include <linux/notifier.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "tw68-reg.h"
+
+#define        UNSET   (-1U)
+
+/* system vendor and device ID's */
+#define        PCI_VENDOR_ID_TECHWELL  0x1797
+#define        PCI_DEVICE_ID_6800      0x6800
+#define        PCI_DEVICE_ID_6801      0x6801
+#define        PCI_DEVICE_ID_AUDIO2    0x6802
+#define        PCI_DEVICE_ID_TS3       0x6803
+#define        PCI_DEVICE_ID_6804      0x6804
+#define        PCI_DEVICE_ID_AUDIO5    0x6805
+#define        PCI_DEVICE_ID_TS6       0x6806
+
+/* tw6816 based cards */
+#define        PCI_DEVICE_ID_6816_1   0x6810
+#define        PCI_DEVICE_ID_6816_2   0x6811
+#define        PCI_DEVICE_ID_6816_3   0x6812
+#define        PCI_DEVICE_ID_6816_4   0x6813
+
+#define TW68_NORMS ( \
+       V4L2_STD_NTSC    | V4L2_STD_PAL       | V4L2_STD_SECAM    | \
+       V4L2_STD_PAL_M   | V4L2_STD_PAL_Nc    | V4L2_STD_PAL_60)
+
+#define        TW68_VID_INTS   (TW68_FFERR | TW68_PABORT | TW68_DMAPERR | \
+                        TW68_FFOF   | TW68_DMAPI)
+/* TW6800 chips have trouble with these, so we don't set them for that chip */
+#define        TW68_VID_INTSX  (TW68_FDMIS | TW68_HLOCK | TW68_VLOCK)
+
+#define        TW68_I2C_INTS   (TW68_SBERR | TW68_SBDONE | TW68_SBERR2  | \
+                        TW68_SBDONE2)
+
+enum tw68_decoder_type {
+       TW6800,
+       TW6801,
+       TW6804,
+       TWXXXX,
+};
+
+/* ----------------------------------------------------------- */
+/* static data                                                 */
+
+struct tw68_tvnorm {
+       char            *name;
+       v4l2_std_id     id;
+
+       /* video decoder */
+       u32     sync_control;
+       u32     luma_control;
+       u32     chroma_ctrl1;
+       u32     chroma_gain;
+       u32     chroma_ctrl2;
+       u32     vgate_misc;
+
+       /* video scaler */
+       u32     h_delay;
+       u32     h_delay0;       /* for TW6800 */
+       u32     h_start;
+       u32     h_stop;
+       u32     v_delay;
+       u32     video_v_start;
+       u32     video_v_stop;
+       u32     vbi_v_start_0;
+       u32     vbi_v_stop_0;
+       u32     vbi_v_start_1;
+
+       /* Techwell specific */
+       u32     format;
+};
+
+struct tw68_format {
+       char    *name;
+       u32     fourcc;
+       u32     depth;
+       u32     twformat;
+};
+
+/* ----------------------------------------------------------- */
+/* card configuration                                    */
+
+#define TW68_BOARD_NOAUTO              UNSET
+#define TW68_BOARD_UNKNOWN             0
+#define        TW68_BOARD_GENERIC_6802         1
+
+#define        TW68_MAXBOARDS                  16
+#define        TW68_INPUT_MAX                  4
+
+/* ----------------------------------------------------------- */
+/* device / file handle status                                 */
+
+#define        BUFFER_TIMEOUT  msecs_to_jiffies(500)   /* 0.5 seconds */
+
+struct tw68_dev;       /* forward delclaration */
+
+/* buffer for one video/vbi/ts frame */
+struct tw68_buf {
+       struct vb2_buffer vb;
+       struct list_head list;
+
+       unsigned int   size;
+       __le32         *cpu;
+       __le32         *jmp;
+       dma_addr_t     dma;
+};
+
+struct tw68_fmt {
+       char                    *name;
+       u32                     fourcc; /* v4l2 format id */
+       int                     depth;
+       int                     flags;
+       u32                     twformat;
+};
+
+/* global device status */
+struct tw68_dev {
+       struct mutex            lock;
+       spinlock_t              slock;
+       u16                     instance;
+       struct v4l2_device      v4l2_dev;
+
+       /* various device info */
+       enum tw68_decoder_type  vdecoder;
+       struct video_device     vdev;
+       struct v4l2_ctrl_handler hdl;
+
+       /* pci i/o */
+       char                    *name;
+       struct pci_dev          *pci;
+       unsigned char           pci_rev, pci_lat;
+       u32                     __iomem *lmmio;
+       u8                      __iomem *bmmio;
+       u32                     pci_irqmask;
+       /* The irq mask to be used will depend upon the chip type */
+       u32                     board_virqmask;
+
+       /* video capture */
+       const struct tw68_format *fmt;
+       unsigned                width, height;
+       unsigned                seqnr;
+       unsigned                field;
+       struct vb2_queue        vidq;
+       struct list_head        active;
+
+       /* various v4l controls */
+       const struct tw68_tvnorm *tvnorm;       /* video */
+
+       int                     input;
+};
+
+/* ----------------------------------------------------------- */
+
+#define tw_readl(reg)          readl(dev->lmmio + ((reg) >> 2))
+#define        tw_readb(reg)           readb(dev->bmmio + (reg))
+#define tw_writel(reg, value)  writel((value), dev->lmmio + ((reg) >> 2))
+#define        tw_writeb(reg, value)   writeb((value), dev->bmmio + (reg))
+
+#define tw_andorl(reg, mask, value) \
+               writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
+               ((value) & (mask)), dev->lmmio+((reg)>>2))
+#define        tw_andorb(reg, mask, value) \
+               writeb((readb(dev->bmmio + (reg)) & ~(mask)) |\
+               ((value) & (mask)), dev->bmmio+(reg))
+#define tw_setl(reg, bit)      tw_andorl((reg), (bit), (bit))
+#define        tw_setb(reg, bit)       tw_andorb((reg), (bit), (bit))
+#define        tw_clearl(reg, bit)     \
+               writel((readl(dev->lmmio + ((reg) >> 2)) & ~(bit)), \
+               dev->lmmio + ((reg) >> 2))
+#define        tw_clearb(reg, bit)     \
+               writeb((readb(dev->bmmio+(reg)) & ~(bit)), \
+               dev->bmmio + (reg))
+
+#define tw_wait(us) { udelay(us); }
+
+/* ----------------------------------------------------------- */
+/* tw68-video.c                                                */
+
+void tw68_set_tvnorm_hw(struct tw68_dev *dev);
+
+int tw68_video_init1(struct tw68_dev *dev);
+int tw68_video_init2(struct tw68_dev *dev, int video_nr);
+void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status);
+int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf);
+
+/* ----------------------------------------------------------- */
+/* tw68-risc.c                                                 */
+
+int tw68_risc_buffer(struct pci_dev *pci, struct tw68_buf *buf,
+       struct scatterlist *sglist, unsigned int top_offset,
+       unsigned int bottom_offset, unsigned int bpl,
+       unsigned int padding, unsigned int lines);
index bf34b93f23ee84fee373057778b1692059b58a84..b6801e035ea451c59800a95456425c249e14dd83 100644 (file)
@@ -682,7 +682,7 @@ set_videobus_dir (struct zoran *zr,
        switch (zr->card.type) {
        case LML33:
        case LML33R10:
-               if (lml33dpath == 0)
+               if (!lml33dpath)
                        GPIO(zr, 5, val);
                else
                        GPIO(zr, 5, 1);
index 6d86646d97433e2436435d52884e2e92ed1c2e33..bee9074ebc138b28a0b12bc38c398d720eadb463 100644 (file)
@@ -56,7 +56,8 @@ config VIDEO_VIU
 
 config VIDEO_TIMBERDALE
        tristate "Support for timberdale Video In/LogiWIN"
-       depends on MFD_TIMBERDALE && VIDEO_V4L2 && I2C && DMADEVICES
+       depends on VIDEO_V4L2 && I2C && DMADEVICES
+       depends on MFD_TIMBERDALE || COMPILE_TEST
        select DMA_ENGINE
        select TIMB_DMA
        select VIDEO_ADV7180
@@ -74,7 +75,8 @@ config VIDEO_VINO
 
 config VIDEO_M32R_AR
        tristate "AR devices"
-       depends on M32R && VIDEO_V4L2
+       depends on VIDEO_V4L2
+       depends on M32R || COMPILE_TEST
        ---help---
          This is a video4linux driver for the Renesas AR (Artificial Retina)
          camera module.
@@ -94,6 +96,7 @@ config VIDEO_M32R_AR_M64278
 config VIDEO_OMAP3
        tristate "OMAP 3 Camera support"
        depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
+       depends on HAS_DMA
        select ARM_DMA_USE_IOMMU
        select OMAP_IOMMU
        select VIDEOBUF2_DMA_CONTIG
@@ -109,7 +112,9 @@ config VIDEO_OMAP3_DEBUG
 config VIDEO_S3C_CAMIF
        tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
        depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
-       depends on (ARCH_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME
+       depends on PM_RUNTIME
+       depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        ---help---
          This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera
@@ -140,6 +145,7 @@ if V4L_MEM2MEM_DRIVERS
 config VIDEO_CODA
        tristate "Chips&Media Coda multi-standard codec IP"
        depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC
+       depends on HAS_DMA
        select SRAM
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
@@ -151,6 +157,7 @@ config VIDEO_CODA
 config VIDEO_MEM2MEM_DEINTERLACE
        tristate "Deinterlace support"
        depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
        help
@@ -158,7 +165,9 @@ config VIDEO_MEM2MEM_DEINTERLACE
 
 config VIDEO_SAMSUNG_S5P_G2D
        tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
-       depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS)
+       depends on VIDEO_DEV && VIDEO_V4L2
+       depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
        default n
@@ -168,7 +177,9 @@ config VIDEO_SAMSUNG_S5P_G2D
 
 config VIDEO_SAMSUNG_S5P_JPEG
        tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver"
-       depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS)
+       depends on VIDEO_DEV && VIDEO_V4L2
+       depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
        ---help---
@@ -177,7 +188,9 @@ config VIDEO_SAMSUNG_S5P_JPEG
 
 config VIDEO_SAMSUNG_S5P_MFC
        tristate "Samsung S5P MFC Video Codec"
-       depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS)
+       depends on VIDEO_DEV && VIDEO_V4L2
+       depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        default n
        help
@@ -185,7 +198,9 @@ config VIDEO_SAMSUNG_S5P_MFC
 
 config VIDEO_MX2_EMMAPRP
        tristate "MX2 eMMa-PrP support"
-       depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27
+       depends on VIDEO_DEV && VIDEO_V4L2
+       depends on SOC_IMX27 || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
        help
@@ -195,7 +210,9 @@ config VIDEO_MX2_EMMAPRP
 
 config VIDEO_SAMSUNG_EXYNOS_GSC
        tristate "Samsung Exynos G-Scaler driver"
-       depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_EXYNOS5
+       depends on VIDEO_DEV && VIDEO_V4L2
+       depends on ARCH_EXYNOS5 || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
        help
@@ -204,6 +221,7 @@ config VIDEO_SAMSUNG_EXYNOS_GSC
 config VIDEO_SH_VEU
        tristate "SuperH VEU mem2mem video processing driver"
        depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
        help
@@ -213,6 +231,7 @@ config VIDEO_SH_VEU
 config VIDEO_RENESAS_VSP1
        tristate "Renesas VSP1 Video Processing Engine"
        depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+       depends on ARCH_SHMOBILE || COMPILE_TEST
        select VIDEOBUF2_DMA_CONTIG
        ---help---
          This is a V4L2 driver for the Renesas VSP1 video processing engine.
@@ -222,7 +241,9 @@ config VIDEO_RENESAS_VSP1
 
 config VIDEO_TI_VPE
        tristate "TI VPE (Video Processing Engine) driver"
-       depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX
+       depends on VIDEO_DEV && VIDEO_V4L2
+       depends on SOC_DRA7XX || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
        default n
@@ -243,19 +264,8 @@ menuconfig V4L_TEST_DRIVERS
        depends on MEDIA_CAMERA_SUPPORT
 
 if V4L_TEST_DRIVERS
-config VIDEO_VIVI
-       tristate "Virtual Video Driver"
-       depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
-       select FONT_SUPPORT
-       select FONT_8x16
-       select VIDEOBUF2_VMALLOC
-       default n
-       ---help---
-         Enables a virtual video driver. This device shows a color bar
-         and a timestamp, as a real device would generate by using V4L2
-         api.
-         Say Y here if you want to test video apps or debug V4L devices.
-         In doubt, say N.
+
+source "drivers/media/platform/vivid/Kconfig"
 
 config VIDEO_MEM2MEM_TESTDEV
        tristate "Virtual test device for mem2mem framework"
index e5269da91906bd0dea8760e361bb1d599d48b626..579046bc276fd23298b8c298ef1a1d0da8c1fdea 100644 (file)
@@ -15,14 +15,14 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
 obj-$(CONFIG_VIDEO_OMAP3)      += omap3isp/
 
 obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
-obj-$(CONFIG_VIDEO_VIVI) += vivi.o
 
+obj-$(CONFIG_VIDEO_VIVID)              += vivid/
 obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
 
 obj-$(CONFIG_VIDEO_TI_VPE)             += ti-vpe/
 
 obj-$(CONFIG_VIDEO_MX2_EMMAPRP)                += mx2_emmaprp.o
-obj-$(CONFIG_VIDEO_CODA)               += coda.o
+obj-$(CONFIG_VIDEO_CODA)               += coda/
 
 obj-$(CONFIG_VIDEO_SH_VEU)             += sh_veu.o
 
@@ -47,8 +47,6 @@ obj-$(CONFIG_SOC_CAMERA)              += soc_camera/
 
 obj-$(CONFIG_VIDEO_RENESAS_VSP1)       += vsp1/
 
-obj-y  += davinci/
-
-obj-$(CONFIG_ARCH_OMAP)        += omap/
+obj-y  += omap/
 
 ccflags-y += -I$(srctree)/drivers/media/i2c
index cc239972fa2c32283c4a9f523b2a6ba28d494adf..68fa90151b8f40f22146244ba1935cf7489ebdf6 100644 (file)
@@ -1,6 +1,7 @@
 config VIDEO_BLACKFIN_CAPTURE
        tristate "Blackfin Video Capture Driver"
        depends on VIDEO_V4L2 && BLACKFIN && I2C
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        help
          V4L2 bridge driver for Blackfin video capture device.
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
deleted file mode 100644 (file)
index 3a6d1d2..0000000
+++ /dev/null
@@ -1,3933 +0,0 @@
-/*
- * Coda multi-standard codec IP
- *
- * Copyright (C) 2012 Vista Silicon S.L.
- *    Javier Martin, <javier.martin@vista-silicon.com>
- *    Xavier Duret
- *
- * 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.
- */
-
-#include <linux/clk.h>
-#include <linux/debugfs.h>
-#include <linux/delay.h>
-#include <linux/firmware.h>
-#include <linux/genalloc.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/kfifo.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/of.h>
-#include <linux/platform_data/coda.h>
-#include <linux/reset.h>
-
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "coda.h"
-
-#define CODA_NAME              "coda"
-
-#define CODADX6_MAX_INSTANCES  4
-
-#define CODA_PARA_BUF_SIZE     (10 * 1024)
-#define CODA_ISRAM_SIZE        (2048 * 2)
-
-#define CODA7_PS_BUF_SIZE      0x28000
-#define CODA9_PS_SAVE_SIZE     (512 * 1024)
-
-#define CODA_MAX_FRAMEBUFFERS  8
-
-#define CODA_MAX_FRAME_SIZE    0x100000
-#define FMO_SLICE_SAVE_BUF_SIZE         (32)
-#define CODA_DEFAULT_GAMMA             4096
-#define CODA9_DEFAULT_GAMMA            24576   /* 0.75 * 32768 */
-
-#define MIN_W 176
-#define MIN_H 144
-
-#define S_ALIGN                1 /* multiple of 2 */
-#define W_ALIGN                1 /* multiple of 2 */
-#define H_ALIGN                1 /* multiple of 2 */
-
-#define fh_to_ctx(__fh)        container_of(__fh, struct coda_ctx, fh)
-
-static int coda_debug;
-module_param(coda_debug, int, 0644);
-MODULE_PARM_DESC(coda_debug, "Debug level (0-1)");
-
-enum {
-       V4L2_M2M_SRC = 0,
-       V4L2_M2M_DST = 1,
-};
-
-enum coda_inst_type {
-       CODA_INST_ENCODER,
-       CODA_INST_DECODER,
-};
-
-enum coda_product {
-       CODA_DX6 = 0xf001,
-       CODA_7541 = 0xf012,
-       CODA_960 = 0xf020,
-};
-
-struct coda_fmt {
-       char *name;
-       u32 fourcc;
-};
-
-struct coda_codec {
-       u32 mode;
-       u32 src_fourcc;
-       u32 dst_fourcc;
-       u32 max_w;
-       u32 max_h;
-};
-
-struct coda_devtype {
-       char                    *firmware;
-       enum coda_product       product;
-       struct coda_codec       *codecs;
-       unsigned int            num_codecs;
-       size_t                  workbuf_size;
-       size_t                  tempbuf_size;
-       size_t                  iram_size;
-};
-
-/* Per-queue, driver-specific private data */
-struct coda_q_data {
-       unsigned int            width;
-       unsigned int            height;
-       unsigned int            bytesperline;
-       unsigned int            sizeimage;
-       unsigned int            fourcc;
-       struct v4l2_rect        rect;
-};
-
-struct coda_aux_buf {
-       void                    *vaddr;
-       dma_addr_t              paddr;
-       u32                     size;
-       struct debugfs_blob_wrapper blob;
-       struct dentry           *dentry;
-};
-
-struct coda_dev {
-       struct v4l2_device      v4l2_dev;
-       struct video_device     vfd;
-       struct platform_device  *plat_dev;
-       const struct coda_devtype *devtype;
-
-       void __iomem            *regs_base;
-       struct clk              *clk_per;
-       struct clk              *clk_ahb;
-       struct reset_control    *rstc;
-
-       struct coda_aux_buf     codebuf;
-       struct coda_aux_buf     tempbuf;
-       struct coda_aux_buf     workbuf;
-       struct gen_pool         *iram_pool;
-       struct coda_aux_buf     iram;
-
-       spinlock_t              irqlock;
-       struct mutex            dev_mutex;
-       struct mutex            coda_mutex;
-       struct workqueue_struct *workqueue;
-       struct v4l2_m2m_dev     *m2m_dev;
-       struct vb2_alloc_ctx    *alloc_ctx;
-       struct list_head        instances;
-       unsigned long           instance_mask;
-       struct dentry           *debugfs_root;
-};
-
-struct coda_params {
-       u8                      rot_mode;
-       u8                      h264_intra_qp;
-       u8                      h264_inter_qp;
-       u8                      h264_min_qp;
-       u8                      h264_max_qp;
-       u8                      h264_deblk_enabled;
-       u8                      h264_deblk_alpha;
-       u8                      h264_deblk_beta;
-       u8                      mpeg4_intra_qp;
-       u8                      mpeg4_inter_qp;
-       u8                      gop_size;
-       int                     intra_refresh;
-       int                     codec_mode;
-       int                     codec_mode_aux;
-       enum v4l2_mpeg_video_multi_slice_mode slice_mode;
-       u32                     framerate;
-       u16                     bitrate;
-       u32                     slice_max_bits;
-       u32                     slice_max_mb;
-};
-
-struct coda_iram_info {
-       u32             axi_sram_use;
-       phys_addr_t     buf_bit_use;
-       phys_addr_t     buf_ip_ac_dc_use;
-       phys_addr_t     buf_dbk_y_use;
-       phys_addr_t     buf_dbk_c_use;
-       phys_addr_t     buf_ovl_use;
-       phys_addr_t     buf_btp_use;
-       phys_addr_t     search_ram_paddr;
-       int             search_ram_size;
-       int             remaining;
-       phys_addr_t     next_paddr;
-};
-
-struct gdi_tiled_map {
-       int xy2ca_map[16];
-       int xy2ba_map[16];
-       int xy2ra_map[16];
-       int rbc2axi_map[32];
-       int xy2rbc_config;
-       int map_type;
-#define GDI_LINEAR_FRAME_MAP 0
-};
-
-struct coda_timestamp {
-       struct list_head        list;
-       u32                     sequence;
-       struct v4l2_timecode    timecode;
-       struct timeval          timestamp;
-};
-
-struct coda_ctx {
-       struct coda_dev                 *dev;
-       struct mutex                    buffer_mutex;
-       struct list_head                list;
-       struct work_struct              pic_run_work;
-       struct work_struct              seq_end_work;
-       struct completion               completion;
-       int                             aborting;
-       int                             initialized;
-       int                             streamon_out;
-       int                             streamon_cap;
-       u32                             isequence;
-       u32                             qsequence;
-       u32                             osequence;
-       u32                             sequence_offset;
-       struct coda_q_data              q_data[2];
-       enum coda_inst_type             inst_type;
-       struct coda_codec               *codec;
-       enum v4l2_colorspace            colorspace;
-       struct coda_params              params;
-       struct v4l2_ctrl_handler        ctrls;
-       struct v4l2_fh                  fh;
-       int                             gopcounter;
-       int                             runcounter;
-       char                            vpu_header[3][64];
-       int                             vpu_header_size[3];
-       struct kfifo                    bitstream_fifo;
-       struct mutex                    bitstream_mutex;
-       struct coda_aux_buf             bitstream;
-       bool                            hold;
-       struct coda_aux_buf             parabuf;
-       struct coda_aux_buf             psbuf;
-       struct coda_aux_buf             slicebuf;
-       struct coda_aux_buf             internal_frames[CODA_MAX_FRAMEBUFFERS];
-       u32                             frame_types[CODA_MAX_FRAMEBUFFERS];
-       struct coda_timestamp           frame_timestamps[CODA_MAX_FRAMEBUFFERS];
-       u32                             frame_errors[CODA_MAX_FRAMEBUFFERS];
-       struct list_head                timestamp_list;
-       struct coda_aux_buf             workbuf;
-       int                             num_internal_frames;
-       int                             idx;
-       int                             reg_idx;
-       struct coda_iram_info           iram_info;
-       struct gdi_tiled_map            tiled_map;
-       u32                             bit_stream_param;
-       u32                             frm_dis_flg;
-       u32                             frame_mem_ctrl;
-       int                             display_idx;
-       struct dentry                   *debugfs_entry;
-};
-
-static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff,
-                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 };
-static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 };
-
-static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg)
-{
-       v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-                "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
-       writel(data, dev->regs_base + reg);
-}
-
-static inline unsigned int coda_read(struct coda_dev *dev, u32 reg)
-{
-       u32 data;
-       data = readl(dev->regs_base + reg);
-       v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-                "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
-       return data;
-}
-
-static inline unsigned long coda_isbusy(struct coda_dev *dev)
-{
-       return coda_read(dev, CODA_REG_BIT_BUSY);
-}
-
-static inline int coda_is_initialized(struct coda_dev *dev)
-{
-       return (coda_read(dev, CODA_REG_BIT_CUR_PC) != 0);
-}
-
-static int coda_wait_timeout(struct coda_dev *dev)
-{
-       unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-
-       while (coda_isbusy(dev)) {
-               if (time_after(jiffies, timeout))
-                       return -ETIMEDOUT;
-       }
-       return 0;
-}
-
-static void coda_command_async(struct coda_ctx *ctx, int cmd)
-{
-       struct coda_dev *dev = ctx->dev;
-
-       if (dev->devtype->product == CODA_960 ||
-           dev->devtype->product == CODA_7541) {
-               /* Restore context related registers to CODA */
-               coda_write(dev, ctx->bit_stream_param,
-                               CODA_REG_BIT_BIT_STREAM_PARAM);
-               coda_write(dev, ctx->frm_dis_flg,
-                               CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
-               coda_write(dev, ctx->frame_mem_ctrl,
-                               CODA_REG_BIT_FRAME_MEM_CTRL);
-               coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR);
-       }
-
-       if (dev->devtype->product == CODA_960) {
-               coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR);
-               coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
-       }
-
-       coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
-
-       coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX);
-       coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD);
-       coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD);
-
-       coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND);
-}
-
-static int coda_command_sync(struct coda_ctx *ctx, int cmd)
-{
-       struct coda_dev *dev = ctx->dev;
-
-       coda_command_async(ctx, cmd);
-       return coda_wait_timeout(dev);
-}
-
-static int coda_hw_reset(struct coda_ctx *ctx)
-{
-       struct coda_dev *dev = ctx->dev;
-       unsigned long timeout;
-       unsigned int idx;
-       int ret;
-
-       if (!dev->rstc)
-               return -ENOENT;
-
-       idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX);
-
-       timeout = jiffies + msecs_to_jiffies(100);
-       coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL);
-       while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) {
-               if (time_after(jiffies, timeout))
-                       return -ETIME;
-               cpu_relax();
-       }
-
-       ret = reset_control_reset(dev->rstc);
-       if (ret < 0)
-               return ret;
-
-       coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL);
-       coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
-       coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN);
-       ret = coda_wait_timeout(dev);
-       coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX);
-
-       return ret;
-}
-
-static struct coda_q_data *get_q_data(struct coda_ctx *ctx,
-                                        enum v4l2_buf_type type)
-{
-       switch (type) {
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               return &(ctx->q_data[V4L2_M2M_SRC]);
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               return &(ctx->q_data[V4L2_M2M_DST]);
-       default:
-               return NULL;
-       }
-}
-
-/*
- * Array of all formats supported by any version of Coda:
- */
-static struct coda_fmt coda_formats[] = {
-       {
-               .name = "YUV 4:2:0 Planar, YCbCr",
-               .fourcc = V4L2_PIX_FMT_YUV420,
-       },
-       {
-               .name = "YUV 4:2:0 Planar, YCrCb",
-               .fourcc = V4L2_PIX_FMT_YVU420,
-       },
-       {
-               .name = "H264 Encoded Stream",
-               .fourcc = V4L2_PIX_FMT_H264,
-       },
-       {
-               .name = "MPEG4 Encoded Stream",
-               .fourcc = V4L2_PIX_FMT_MPEG4,
-       },
-};
-
-#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \
-       { mode, src_fourcc, dst_fourcc, max_w, max_h }
-
-/*
- * Arrays of codecs supported by each given version of Coda:
- *  i.MX27 -> codadx6
- *  i.MX5x -> coda7
- *  i.MX6  -> coda960
- * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants
- */
-static struct coda_codec codadx6_codecs[] = {
-       CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264,  720, 576),
-       CODA_CODEC(CODADX6_MODE_ENCODE_MP4,  V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576),
-};
-
-static struct coda_codec coda7_codecs[] = {
-       CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264,   1280, 720),
-       CODA_CODEC(CODA7_MODE_ENCODE_MP4,  V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4,  1280, 720),
-       CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264,   V4L2_PIX_FMT_YUV420, 1920, 1080),
-       CODA_CODEC(CODA7_MODE_DECODE_MP4,  V4L2_PIX_FMT_MPEG4,  V4L2_PIX_FMT_YUV420, 1920, 1080),
-};
-
-static struct coda_codec coda9_codecs[] = {
-       CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264,   1920, 1080),
-       CODA_CODEC(CODA9_MODE_ENCODE_MP4,  V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4,  1920, 1080),
-       CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264,   V4L2_PIX_FMT_YUV420, 1920, 1080),
-       CODA_CODEC(CODA9_MODE_DECODE_MP4,  V4L2_PIX_FMT_MPEG4,  V4L2_PIX_FMT_YUV420, 1920, 1080),
-};
-
-static bool coda_format_is_yuv(u32 fourcc)
-{
-       switch (fourcc) {
-       case V4L2_PIX_FMT_YUV420:
-       case V4L2_PIX_FMT_YVU420:
-               return true;
-       default:
-               return false;
-       }
-}
-
-/*
- * Normalize all supported YUV 4:2:0 formats to the value used in the codec
- * tables.
- */
-static u32 coda_format_normalize_yuv(u32 fourcc)
-{
-       return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc;
-}
-
-static struct coda_codec *coda_find_codec(struct coda_dev *dev, int src_fourcc,
-                                         int dst_fourcc)
-{
-       struct coda_codec *codecs = dev->devtype->codecs;
-       int num_codecs = dev->devtype->num_codecs;
-       int k;
-
-       src_fourcc = coda_format_normalize_yuv(src_fourcc);
-       dst_fourcc = coda_format_normalize_yuv(dst_fourcc);
-       if (src_fourcc == dst_fourcc)
-               return NULL;
-
-       for (k = 0; k < num_codecs; k++) {
-               if (codecs[k].src_fourcc == src_fourcc &&
-                   codecs[k].dst_fourcc == dst_fourcc)
-                       break;
-       }
-
-       if (k == num_codecs)
-               return NULL;
-
-       return &codecs[k];
-}
-
-static void coda_get_max_dimensions(struct coda_dev *dev,
-                                   struct coda_codec *codec,
-                                   int *max_w, int *max_h)
-{
-       struct coda_codec *codecs = dev->devtype->codecs;
-       int num_codecs = dev->devtype->num_codecs;
-       unsigned int w, h;
-       int k;
-
-       if (codec) {
-               w = codec->max_w;
-               h = codec->max_h;
-       } else {
-               for (k = 0, w = 0, h = 0; k < num_codecs; k++) {
-                       w = max(w, codecs[k].max_w);
-                       h = max(h, codecs[k].max_h);
-               }
-       }
-
-       if (max_w)
-               *max_w = w;
-       if (max_h)
-               *max_h = h;
-}
-
-static char *coda_product_name(int product)
-{
-       static char buf[9];
-
-       switch (product) {
-       case CODA_DX6:
-               return "CodaDx6";
-       case CODA_7541:
-               return "CODA7541";
-       case CODA_960:
-               return "CODA960";
-       default:
-               snprintf(buf, sizeof(buf), "(0x%04x)", product);
-               return buf;
-       }
-}
-
-/*
- * V4L2 ioctl() operations.
- */
-static int coda_querycap(struct file *file, void *priv,
-                        struct v4l2_capability *cap)
-{
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-
-       strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver));
-       strlcpy(cap->card, coda_product_name(ctx->dev->devtype->product),
-               sizeof(cap->card));
-       strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info));
-       /*
-        * This is only a mem-to-mem video device. The capture and output
-        * device capability flags are left only for backward compatibility
-        * and are scheduled for removal.
-        */
-       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
-                          V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
-       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
-       return 0;
-}
-
-static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
-                       enum v4l2_buf_type type, int src_fourcc)
-{
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-       struct coda_codec *codecs = ctx->dev->devtype->codecs;
-       struct coda_fmt *formats = coda_formats;
-       struct coda_fmt *fmt;
-       int num_codecs = ctx->dev->devtype->num_codecs;
-       int num_formats = ARRAY_SIZE(coda_formats);
-       int i, k, num = 0;
-
-       for (i = 0; i < num_formats; i++) {
-               /* Both uncompressed formats are always supported */
-               if (coda_format_is_yuv(formats[i].fourcc) &&
-                   !coda_format_is_yuv(src_fourcc)) {
-                       if (num == f->index)
-                               break;
-                       ++num;
-                       continue;
-               }
-               /* Compressed formats may be supported, check the codec list */
-               for (k = 0; k < num_codecs; k++) {
-                       /* if src_fourcc is set, only consider matching codecs */
-                       if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
-                           formats[i].fourcc == codecs[k].dst_fourcc &&
-                           (!src_fourcc || src_fourcc == codecs[k].src_fourcc))
-                               break;
-                       if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
-                           formats[i].fourcc == codecs[k].src_fourcc)
-                               break;
-               }
-               if (k < num_codecs) {
-                       if (num == f->index)
-                               break;
-                       ++num;
-               }
-       }
-
-       if (i < num_formats) {
-               fmt = &formats[i];
-               strlcpy(f->description, fmt->name, sizeof(f->description));
-               f->pixelformat = fmt->fourcc;
-               if (!coda_format_is_yuv(fmt->fourcc))
-                       f->flags |= V4L2_FMT_FLAG_COMPRESSED;
-               return 0;
-       }
-
-       /* Format not found */
-       return -EINVAL;
-}
-
-static int coda_enum_fmt_vid_cap(struct file *file, void *priv,
-                                struct v4l2_fmtdesc *f)
-{
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-       struct vb2_queue *src_vq;
-       struct coda_q_data *q_data_src;
-
-       /* If the source format is already fixed, only list matching formats */
-       src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-       if (vb2_is_streaming(src_vq)) {
-               q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-
-               return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                               q_data_src->fourcc);
-       }
-
-       return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
-}
-
-static int coda_enum_fmt_vid_out(struct file *file, void *priv,
-                                struct v4l2_fmtdesc *f)
-{
-       return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);
-}
-
-static int coda_g_fmt(struct file *file, void *priv,
-                     struct v4l2_format *f)
-{
-       struct coda_q_data *q_data;
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-
-       q_data = get_q_data(ctx, f->type);
-       if (!q_data)
-               return -EINVAL;
-
-       f->fmt.pix.field        = V4L2_FIELD_NONE;
-       f->fmt.pix.pixelformat  = q_data->fourcc;
-       f->fmt.pix.width        = q_data->width;
-       f->fmt.pix.height       = q_data->height;
-       f->fmt.pix.bytesperline = q_data->bytesperline;
-
-       f->fmt.pix.sizeimage    = q_data->sizeimage;
-       f->fmt.pix.colorspace   = ctx->colorspace;
-
-       return 0;
-}
-
-static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec,
-                       struct v4l2_format *f)
-{
-       struct coda_dev *dev = ctx->dev;
-       struct coda_q_data *q_data;
-       unsigned int max_w, max_h;
-       enum v4l2_field field;
-
-       field = f->fmt.pix.field;
-       if (field == V4L2_FIELD_ANY)
-               field = V4L2_FIELD_NONE;
-       else if (V4L2_FIELD_NONE != field)
-               return -EINVAL;
-
-       /* V4L2 specification suggests the driver corrects the format struct
-        * if any of the dimensions is unsupported */
-       f->fmt.pix.field = field;
-
-       coda_get_max_dimensions(dev, codec, &max_w, &max_h);
-       v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN,
-                             &f->fmt.pix.height, MIN_H, max_h, H_ALIGN,
-                             S_ALIGN);
-
-       switch (f->fmt.pix.pixelformat) {
-       case V4L2_PIX_FMT_YUV420:
-       case V4L2_PIX_FMT_YVU420:
-       case V4L2_PIX_FMT_H264:
-       case V4L2_PIX_FMT_MPEG4:
-       case V4L2_PIX_FMT_JPEG:
-               break;
-       default:
-               q_data = get_q_data(ctx, f->type);
-               if (!q_data)
-                       return -EINVAL;
-               f->fmt.pix.pixelformat = q_data->fourcc;
-       }
-
-       switch (f->fmt.pix.pixelformat) {
-       case V4L2_PIX_FMT_YUV420:
-       case V4L2_PIX_FMT_YVU420:
-               /* Frame stride must be multiple of 8, but 16 for h.264 */
-               f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
-               f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
-                                       f->fmt.pix.height * 3 / 2;
-               break;
-       case V4L2_PIX_FMT_H264:
-       case V4L2_PIX_FMT_MPEG4:
-       case V4L2_PIX_FMT_JPEG:
-               f->fmt.pix.bytesperline = 0;
-               f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE;
-               break;
-       default:
-               BUG();
-       }
-
-       return 0;
-}
-
-static int coda_try_fmt_vid_cap(struct file *file, void *priv,
-                               struct v4l2_format *f)
-{
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-       struct coda_codec *codec;
-       struct vb2_queue *src_vq;
-       int ret;
-
-       /*
-        * If the source format is already fixed, try to find a codec that
-        * converts to the given destination format
-        */
-       src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-       if (vb2_is_streaming(src_vq)) {
-               struct coda_q_data *q_data_src;
-
-               q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-               codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
-                                       f->fmt.pix.pixelformat);
-               if (!codec)
-                       return -EINVAL;
-       } else {
-               /* Otherwise determine codec by encoded format, if possible */
-               codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
-                                       f->fmt.pix.pixelformat);
-       }
-
-       f->fmt.pix.colorspace = ctx->colorspace;
-
-       ret = coda_try_fmt(ctx, codec, f);
-       if (ret < 0)
-               return ret;
-
-       /* The h.264 decoder only returns complete 16x16 macroblocks */
-       if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) {
-               f->fmt.pix.width = f->fmt.pix.width;
-               f->fmt.pix.height = round_up(f->fmt.pix.height, 16);
-               f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
-               f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
-                                      f->fmt.pix.height * 3 / 2;
-       }
-
-       return 0;
-}
-
-static int coda_try_fmt_vid_out(struct file *file, void *priv,
-                               struct v4l2_format *f)
-{
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-       struct coda_codec *codec;
-
-       /* Determine codec by encoded format, returns NULL if raw or invalid */
-       codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat,
-                               V4L2_PIX_FMT_YUV420);
-
-       if (!f->fmt.pix.colorspace)
-               f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
-
-       return coda_try_fmt(ctx, codec, f);
-}
-
-static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
-{
-       struct coda_q_data *q_data;
-       struct vb2_queue *vq;
-
-       vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
-       if (!vq)
-               return -EINVAL;
-
-       q_data = get_q_data(ctx, f->type);
-       if (!q_data)
-               return -EINVAL;
-
-       if (vb2_is_busy(vq)) {
-               v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
-               return -EBUSY;
-       }
-
-       q_data->fourcc = f->fmt.pix.pixelformat;
-       q_data->width = f->fmt.pix.width;
-       q_data->height = f->fmt.pix.height;
-       q_data->bytesperline = f->fmt.pix.bytesperline;
-       q_data->sizeimage = f->fmt.pix.sizeimage;
-       q_data->rect.left = 0;
-       q_data->rect.top = 0;
-       q_data->rect.width = f->fmt.pix.width;
-       q_data->rect.height = f->fmt.pix.height;
-
-       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-               "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
-               f->type, q_data->width, q_data->height, q_data->fourcc);
-
-       return 0;
-}
-
-static int coda_s_fmt_vid_cap(struct file *file, void *priv,
-                             struct v4l2_format *f)
-{
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-       int ret;
-
-       ret = coda_try_fmt_vid_cap(file, priv, f);
-       if (ret)
-               return ret;
-
-       return coda_s_fmt(ctx, f);
-}
-
-static int coda_s_fmt_vid_out(struct file *file, void *priv,
-                             struct v4l2_format *f)
-{
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-       int ret;
-
-       ret = coda_try_fmt_vid_out(file, priv, f);
-       if (ret)
-               return ret;
-
-       ret = coda_s_fmt(ctx, f);
-       if (ret)
-               ctx->colorspace = f->fmt.pix.colorspace;
-
-       return ret;
-}
-
-static int coda_qbuf(struct file *file, void *priv,
-                    struct v4l2_buffer *buf)
-{
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-
-       return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
-}
-
-static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx,
-                                     struct v4l2_buffer *buf)
-{
-       struct vb2_queue *src_vq;
-
-       src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-
-       return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) &&
-               (buf->sequence == (ctx->qsequence - 1)));
-}
-
-static int coda_dqbuf(struct file *file, void *priv,
-                     struct v4l2_buffer *buf)
-{
-       struct coda_ctx *ctx = fh_to_ctx(priv);
-       int ret;
-
-       ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf);
-
-       /* If this is the last capture buffer, emit an end-of-stream event */
-       if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
-           coda_buf_is_end_of_stream(ctx, buf)) {
-               const struct v4l2_event eos_event = {
-                       .type = V4L2_EVENT_EOS
-               };
-
-               v4l2_event_queue_fh(&ctx->fh, &eos_event);
-       }
-
-       return ret;
-}
-
-static int coda_g_selection(struct file *file, void *fh,
-                           struct v4l2_selection *s)
-{
-       struct coda_ctx *ctx = fh_to_ctx(fh);
-       struct coda_q_data *q_data;
-       struct v4l2_rect r, *rsel;
-
-       q_data = get_q_data(ctx, s->type);
-       if (!q_data)
-               return -EINVAL;
-
-       r.left = 0;
-       r.top = 0;
-       r.width = q_data->width;
-       r.height = q_data->height;
-       rsel = &q_data->rect;
-
-       switch (s->target) {
-       case V4L2_SEL_TGT_CROP_DEFAULT:
-       case V4L2_SEL_TGT_CROP_BOUNDS:
-               rsel = &r;
-               /* fallthrough */
-       case V4L2_SEL_TGT_CROP:
-               if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
-                       return -EINVAL;
-               break;
-       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
-       case V4L2_SEL_TGT_COMPOSE_PADDED:
-               rsel = &r;
-               /* fallthrough */
-       case V4L2_SEL_TGT_COMPOSE:
-       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
-               if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       s->r = *rsel;
-
-       return 0;
-}
-
-static int coda_try_decoder_cmd(struct file *file, void *fh,
-                               struct v4l2_decoder_cmd *dc)
-{
-       if (dc->cmd != V4L2_DEC_CMD_STOP)
-               return -EINVAL;
-
-       if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK)
-               return -EINVAL;
-
-       if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0))
-               return -EINVAL;
-
-       return 0;
-}
-
-static int coda_decoder_cmd(struct file *file, void *fh,
-                           struct v4l2_decoder_cmd *dc)
-{
-       struct coda_ctx *ctx = fh_to_ctx(fh);
-       struct coda_dev *dev = ctx->dev;
-       int ret;
-
-       ret = coda_try_decoder_cmd(file, fh, dc);
-       if (ret < 0)
-               return ret;
-
-       /* Ignore decoder stop command silently in encoder context */
-       if (ctx->inst_type != CODA_INST_DECODER)
-               return 0;
-
-       /* Set the strem-end flag on this context */
-       ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
-
-       if ((dev->devtype->product == CODA_960) &&
-           coda_isbusy(dev) &&
-           (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) {
-               /* If this context is currently running, update the hardware flag */
-               coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM);
-       }
-       ctx->hold = false;
-       v4l2_m2m_try_schedule(ctx->fh.m2m_ctx);
-
-       return 0;
-}
-
-static int coda_subscribe_event(struct v4l2_fh *fh,
-                               const struct v4l2_event_subscription *sub)
-{
-       switch (sub->type) {
-       case V4L2_EVENT_EOS:
-               return v4l2_event_subscribe(fh, sub, 0, NULL);
-       default:
-               return v4l2_ctrl_subscribe_event(fh, sub);
-       }
-}
-
-static const struct v4l2_ioctl_ops coda_ioctl_ops = {
-       .vidioc_querycap        = coda_querycap,
-
-       .vidioc_enum_fmt_vid_cap = coda_enum_fmt_vid_cap,
-       .vidioc_g_fmt_vid_cap   = coda_g_fmt,
-       .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap,
-       .vidioc_s_fmt_vid_cap   = coda_s_fmt_vid_cap,
-
-       .vidioc_enum_fmt_vid_out = coda_enum_fmt_vid_out,
-       .vidioc_g_fmt_vid_out   = coda_g_fmt,
-       .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out,
-       .vidioc_s_fmt_vid_out   = coda_s_fmt_vid_out,
-
-       .vidioc_reqbufs         = v4l2_m2m_ioctl_reqbufs,
-       .vidioc_querybuf        = v4l2_m2m_ioctl_querybuf,
-
-       .vidioc_qbuf            = coda_qbuf,
-       .vidioc_expbuf          = v4l2_m2m_ioctl_expbuf,
-       .vidioc_dqbuf           = coda_dqbuf,
-       .vidioc_create_bufs     = v4l2_m2m_ioctl_create_bufs,
-
-       .vidioc_streamon        = v4l2_m2m_ioctl_streamon,
-       .vidioc_streamoff       = v4l2_m2m_ioctl_streamoff,
-
-       .vidioc_g_selection     = coda_g_selection,
-
-       .vidioc_try_decoder_cmd = coda_try_decoder_cmd,
-       .vidioc_decoder_cmd     = coda_decoder_cmd,
-
-       .vidioc_subscribe_event = coda_subscribe_event,
-       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static int coda_start_decoding(struct coda_ctx *ctx);
-
-static inline int coda_get_bitstream_payload(struct coda_ctx *ctx)
-{
-       return kfifo_len(&ctx->bitstream_fifo);
-}
-
-static void coda_kfifo_sync_from_device(struct coda_ctx *ctx)
-{
-       struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
-       struct coda_dev *dev = ctx->dev;
-       u32 rd_ptr;
-
-       rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
-       kfifo->out = (kfifo->in & ~kfifo->mask) |
-                     (rd_ptr - ctx->bitstream.paddr);
-       if (kfifo->out > kfifo->in)
-               kfifo->out -= kfifo->mask + 1;
-}
-
-static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx)
-{
-       struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
-       struct coda_dev *dev = ctx->dev;
-       u32 rd_ptr, wr_ptr;
-
-       rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask);
-       coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
-       wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
-       coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
-}
-
-static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx)
-{
-       struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
-       struct coda_dev *dev = ctx->dev;
-       u32 wr_ptr;
-
-       wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
-       coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
-}
-
-static int coda_bitstream_queue(struct coda_ctx *ctx, struct vb2_buffer *src_buf)
-{
-       u32 src_size = vb2_get_plane_payload(src_buf, 0);
-       u32 n;
-
-       n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0), src_size);
-       if (n < src_size)
-               return -ENOSPC;
-
-       dma_sync_single_for_device(&ctx->dev->plat_dev->dev, ctx->bitstream.paddr,
-                                  ctx->bitstream.size, DMA_TO_DEVICE);
-
-       src_buf->v4l2_buf.sequence = ctx->qsequence++;
-
-       return 0;
-}
-
-static bool coda_bitstream_try_queue(struct coda_ctx *ctx,
-                                    struct vb2_buffer *src_buf)
-{
-       int ret;
-
-       if (coda_get_bitstream_payload(ctx) +
-           vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size)
-               return false;
-
-       if (vb2_plane_vaddr(src_buf, 0) == NULL) {
-               v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n");
-               return true;
-       }
-
-       ret = coda_bitstream_queue(ctx, src_buf);
-       if (ret < 0) {
-               v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n");
-               return false;
-       }
-       /* Sync read pointer to device */
-       if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev))
-               coda_kfifo_sync_to_device_write(ctx);
-
-       ctx->hold = false;
-
-       return true;
-}
-
-static void coda_fill_bitstream(struct coda_ctx *ctx)
-{
-       struct vb2_buffer *src_buf;
-       struct coda_timestamp *ts;
-
-       while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) {
-               src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
-
-               if (coda_bitstream_try_queue(ctx, src_buf)) {
-                       /*
-                        * Source buffer is queued in the bitstream ringbuffer;
-                        * queue the timestamp and mark source buffer as done
-                        */
-                       src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
-
-                       ts = kmalloc(sizeof(*ts), GFP_KERNEL);
-                       if (ts) {
-                               ts->sequence = src_buf->v4l2_buf.sequence;
-                               ts->timecode = src_buf->v4l2_buf.timecode;
-                               ts->timestamp = src_buf->v4l2_buf.timestamp;
-                               list_add_tail(&ts->list, &ctx->timestamp_list);
-                       }
-
-                       v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
-               } else {
-                       break;
-               }
-       }
-}
-
-static void coda_set_gdi_regs(struct coda_ctx *ctx)
-{
-       struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
-       struct coda_dev *dev = ctx->dev;
-       int i;
-
-       for (i = 0; i < 16; i++)
-               coda_write(dev, tiled_map->xy2ca_map[i],
-                               CODA9_GDI_XY2_CAS_0 + 4 * i);
-       for (i = 0; i < 4; i++)
-               coda_write(dev, tiled_map->xy2ba_map[i],
-                               CODA9_GDI_XY2_BA_0 + 4 * i);
-       for (i = 0; i < 16; i++)
-               coda_write(dev, tiled_map->xy2ra_map[i],
-                               CODA9_GDI_XY2_RAS_0 + 4 * i);
-       coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG);
-       for (i = 0; i < 32; i++)
-               coda_write(dev, tiled_map->rbc2axi_map[i],
-                               CODA9_GDI_RBC2_AXI_0 + 4 * i);
-}
-
-/*
- * Mem-to-mem operations.
- */
-static int coda_prepare_decode(struct coda_ctx *ctx)
-{
-       struct vb2_buffer *dst_buf;
-       struct coda_dev *dev = ctx->dev;
-       struct coda_q_data *q_data_dst;
-       u32 stridey, height;
-       u32 picture_y, picture_cb, picture_cr;
-
-       dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-
-       if (ctx->params.rot_mode & CODA_ROT_90) {
-               stridey = q_data_dst->height;
-               height = q_data_dst->width;
-       } else {
-               stridey = q_data_dst->width;
-               height = q_data_dst->height;
-       }
-
-       /* Try to copy source buffer contents into the bitstream ringbuffer */
-       mutex_lock(&ctx->bitstream_mutex);
-       coda_fill_bitstream(ctx);
-       mutex_unlock(&ctx->bitstream_mutex);
-
-       if (coda_get_bitstream_payload(ctx) < 512 &&
-           (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
-               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-                        "bitstream payload: %d, skipping\n",
-                        coda_get_bitstream_payload(ctx));
-               v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
-               return -EAGAIN;
-       }
-
-       /* Run coda_start_decoding (again) if not yet initialized */
-       if (!ctx->initialized) {
-               int ret = coda_start_decoding(ctx);
-               if (ret < 0) {
-                       v4l2_err(&dev->v4l2_dev, "failed to start decoding\n");
-                       v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
-                       return -EAGAIN;
-               } else {
-                       ctx->initialized = 1;
-               }
-       }
-
-       if (dev->devtype->product == CODA_960)
-               coda_set_gdi_regs(ctx);
-
-       /* Set rotator output */
-       picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
-       if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) {
-               /* Switch Cr and Cb for YVU420 format */
-               picture_cr = picture_y + stridey * height;
-               picture_cb = picture_cr + stridey / 2 * height / 2;
-       } else {
-               picture_cb = picture_y + stridey * height;
-               picture_cr = picture_cb + stridey / 2 * height / 2;
-       }
-
-       if (dev->devtype->product == CODA_960) {
-               /*
-                * The CODA960 seems to have an internal list of buffers with
-                * 64 entries that includes the registered frame buffers as
-                * well as the rotator buffer output.
-                * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames.
-                */
-               coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->v4l2_buf.index,
-                               CODA9_CMD_DEC_PIC_ROT_INDEX);
-               coda_write(dev, picture_y, CODA9_CMD_DEC_PIC_ROT_ADDR_Y);
-               coda_write(dev, picture_cb, CODA9_CMD_DEC_PIC_ROT_ADDR_CB);
-               coda_write(dev, picture_cr, CODA9_CMD_DEC_PIC_ROT_ADDR_CR);
-               coda_write(dev, stridey, CODA9_CMD_DEC_PIC_ROT_STRIDE);
-       } else {
-               coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y);
-               coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB);
-               coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR);
-               coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE);
-       }
-       coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
-                       CODA_CMD_DEC_PIC_ROT_MODE);
-
-       switch (dev->devtype->product) {
-       case CODA_DX6:
-               /* TBD */
-       case CODA_7541:
-               coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION);
-               break;
-       case CODA_960:
-               coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION); /* 'hardcode to use interrupt disable mode'? */
-               break;
-       }
-
-       coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM);
-
-       coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START);
-       coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE);
-
-       return 0;
-}
-
-static void coda_prepare_encode(struct coda_ctx *ctx)
-{
-       struct coda_q_data *q_data_src, *q_data_dst;
-       struct vb2_buffer *src_buf, *dst_buf;
-       struct coda_dev *dev = ctx->dev;
-       int force_ipicture;
-       int quant_param = 0;
-       u32 picture_y, picture_cb, picture_cr;
-       u32 pic_stream_buffer_addr, pic_stream_buffer_size;
-       u32 dst_fourcc;
-
-       src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
-       dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-       dst_fourcc = q_data_dst->fourcc;
-
-       src_buf->v4l2_buf.sequence = ctx->osequence;
-       dst_buf->v4l2_buf.sequence = ctx->osequence;
-       ctx->osequence++;
-
-       /*
-        * Workaround coda firmware BUG that only marks the first
-        * frame as IDR. This is a problem for some decoders that can't
-        * recover when a frame is lost.
-        */
-       if (src_buf->v4l2_buf.sequence % ctx->params.gop_size) {
-               src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
-               src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
-       } else {
-               src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
-               src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
-       }
-
-       if (dev->devtype->product == CODA_960)
-               coda_set_gdi_regs(ctx);
-
-       /*
-        * Copy headers at the beginning of the first frame for H.264 only.
-        * In MPEG4 they are already copied by the coda.
-        */
-       if (src_buf->v4l2_buf.sequence == 0) {
-               pic_stream_buffer_addr =
-                       vb2_dma_contig_plane_dma_addr(dst_buf, 0) +
-                       ctx->vpu_header_size[0] +
-                       ctx->vpu_header_size[1] +
-                       ctx->vpu_header_size[2];
-               pic_stream_buffer_size = CODA_MAX_FRAME_SIZE -
-                       ctx->vpu_header_size[0] -
-                       ctx->vpu_header_size[1] -
-                       ctx->vpu_header_size[2];
-               memcpy(vb2_plane_vaddr(dst_buf, 0),
-                      &ctx->vpu_header[0][0], ctx->vpu_header_size[0]);
-               memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0],
-                      &ctx->vpu_header[1][0], ctx->vpu_header_size[1]);
-               memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0] +
-                       ctx->vpu_header_size[1], &ctx->vpu_header[2][0],
-                       ctx->vpu_header_size[2]);
-       } else {
-               pic_stream_buffer_addr =
-                       vb2_dma_contig_plane_dma_addr(dst_buf, 0);
-               pic_stream_buffer_size = CODA_MAX_FRAME_SIZE;
-       }
-
-       if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) {
-               force_ipicture = 1;
-               switch (dst_fourcc) {
-               case V4L2_PIX_FMT_H264:
-                       quant_param = ctx->params.h264_intra_qp;
-                       break;
-               case V4L2_PIX_FMT_MPEG4:
-                       quant_param = ctx->params.mpeg4_intra_qp;
-                       break;
-               default:
-                       v4l2_warn(&ctx->dev->v4l2_dev,
-                               "cannot set intra qp, fmt not supported\n");
-                       break;
-               }
-       } else {
-               force_ipicture = 0;
-               switch (dst_fourcc) {
-               case V4L2_PIX_FMT_H264:
-                       quant_param = ctx->params.h264_inter_qp;
-                       break;
-               case V4L2_PIX_FMT_MPEG4:
-                       quant_param = ctx->params.mpeg4_inter_qp;
-                       break;
-               default:
-                       v4l2_warn(&ctx->dev->v4l2_dev,
-                               "cannot set inter qp, fmt not supported\n");
-                       break;
-               }
-       }
-
-       /* submit */
-       coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, CODA_CMD_ENC_PIC_ROT_MODE);
-       coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS);
-
-
-       picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0);
-       switch (q_data_src->fourcc) {
-       case V4L2_PIX_FMT_YVU420:
-               /* Switch Cb and Cr for YVU420 format */
-               picture_cr = picture_y + q_data_src->bytesperline *
-                               q_data_src->height;
-               picture_cb = picture_cr + q_data_src->bytesperline / 2 *
-                               q_data_src->height / 2;
-               break;
-       case V4L2_PIX_FMT_YUV420:
-       default:
-               picture_cb = picture_y + q_data_src->bytesperline *
-                               q_data_src->height;
-               picture_cr = picture_cb + q_data_src->bytesperline / 2 *
-                               q_data_src->height / 2;
-               break;
-       }
-
-       if (dev->devtype->product == CODA_960) {
-               coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX);
-               coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE);
-               coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC);
-
-               coda_write(dev, picture_y, CODA9_CMD_ENC_PIC_SRC_ADDR_Y);
-               coda_write(dev, picture_cb, CODA9_CMD_ENC_PIC_SRC_ADDR_CB);
-               coda_write(dev, picture_cr, CODA9_CMD_ENC_PIC_SRC_ADDR_CR);
-       } else {
-               coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y);
-               coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB);
-               coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR);
-       }
-       coda_write(dev, force_ipicture << 1 & 0x2,
-                  CODA_CMD_ENC_PIC_OPTION);
-
-       coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START);
-       coda_write(dev, pic_stream_buffer_size / 1024,
-                  CODA_CMD_ENC_PIC_BB_SIZE);
-
-       if (!ctx->streamon_out) {
-               /* After streamoff on the output side, set the stream end flag */
-               ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
-               coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM);
-       }
-}
-
-static void coda_device_run(void *m2m_priv)
-{
-       struct coda_ctx *ctx = m2m_priv;
-       struct coda_dev *dev = ctx->dev;
-
-       queue_work(dev->workqueue, &ctx->pic_run_work);
-}
-
-static void coda_free_framebuffers(struct coda_ctx *ctx);
-static void coda_free_context_buffers(struct coda_ctx *ctx);
-
-static void coda_seq_end_work(struct work_struct *work)
-{
-       struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work);
-       struct coda_dev *dev = ctx->dev;
-
-       mutex_lock(&ctx->buffer_mutex);
-       mutex_lock(&dev->coda_mutex);
-
-       v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-                "%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx, __func__);
-       if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) {
-               v4l2_err(&dev->v4l2_dev,
-                        "CODA_COMMAND_SEQ_END failed\n");
-       }
-
-       kfifo_init(&ctx->bitstream_fifo,
-               ctx->bitstream.vaddr, ctx->bitstream.size);
-
-       coda_free_framebuffers(ctx);
-       coda_free_context_buffers(ctx);
-
-       mutex_unlock(&dev->coda_mutex);
-       mutex_unlock(&ctx->buffer_mutex);
-}
-
-static void coda_finish_decode(struct coda_ctx *ctx);
-static void coda_finish_encode(struct coda_ctx *ctx);
-
-static void coda_pic_run_work(struct work_struct *work)
-{
-       struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work);
-       struct coda_dev *dev = ctx->dev;
-       int ret;
-
-       mutex_lock(&ctx->buffer_mutex);
-       mutex_lock(&dev->coda_mutex);
-
-       if (ctx->inst_type == CODA_INST_DECODER) {
-               ret = coda_prepare_decode(ctx);
-               if (ret < 0) {
-                       mutex_unlock(&dev->coda_mutex);
-                       mutex_unlock(&ctx->buffer_mutex);
-                       /* job_finish scheduled by prepare_decode */
-                       return;
-               }
-       } else {
-               coda_prepare_encode(ctx);
-       }
-
-       if (dev->devtype->product != CODA_DX6)
-               coda_write(dev, ctx->iram_info.axi_sram_use,
-                               CODA7_REG_BIT_AXI_SRAM_USE);
-
-       if (ctx->inst_type == CODA_INST_DECODER)
-               coda_kfifo_sync_to_device_full(ctx);
-       coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
-
-       if (!wait_for_completion_timeout(&ctx->completion, msecs_to_jiffies(1000))) {
-               dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n");
-
-               ctx->hold = true;
-
-               coda_hw_reset(ctx);
-       } else if (!ctx->aborting) {
-               if (ctx->inst_type == CODA_INST_DECODER)
-                       coda_finish_decode(ctx);
-               else
-                       coda_finish_encode(ctx);
-       }
-
-       if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out))
-               queue_work(dev->workqueue, &ctx->seq_end_work);
-
-       mutex_unlock(&dev->coda_mutex);
-       mutex_unlock(&ctx->buffer_mutex);
-
-       v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
-}
-
-static int coda_job_ready(void *m2m_priv)
-{
-       struct coda_ctx *ctx = m2m_priv;
-
-       /*
-        * For both 'P' and 'key' frame cases 1 picture
-        * and 1 frame are needed. In the decoder case,
-        * the compressed frame can be in the bitstream.
-        */
-       if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) &&
-           ctx->inst_type != CODA_INST_DECODER) {
-               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                        "not ready: not enough video buffers.\n");
-               return 0;
-       }
-
-       if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
-               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                        "not ready: not enough video capture buffers.\n");
-               return 0;
-       }
-
-       if (ctx->hold ||
-           ((ctx->inst_type == CODA_INST_DECODER) &&
-            (coda_get_bitstream_payload(ctx) < 512) &&
-            !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
-               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                        "%d: not ready: not enough bitstream data.\n",
-                        ctx->idx);
-               return 0;
-       }
-
-       if (ctx->aborting) {
-               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                        "not ready: aborting\n");
-               return 0;
-       }
-
-       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                       "job ready\n");
-       return 1;
-}
-
-static void coda_job_abort(void *priv)
-{
-       struct coda_ctx *ctx = priv;
-
-       ctx->aborting = 1;
-
-       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                "Aborting task\n");
-}
-
-static void coda_lock(void *m2m_priv)
-{
-       struct coda_ctx *ctx = m2m_priv;
-       struct coda_dev *pcdev = ctx->dev;
-       mutex_lock(&pcdev->dev_mutex);
-}
-
-static void coda_unlock(void *m2m_priv)
-{
-       struct coda_ctx *ctx = m2m_priv;
-       struct coda_dev *pcdev = ctx->dev;
-       mutex_unlock(&pcdev->dev_mutex);
-}
-
-static struct v4l2_m2m_ops coda_m2m_ops = {
-       .device_run     = coda_device_run,
-       .job_ready      = coda_job_ready,
-       .job_abort      = coda_job_abort,
-       .lock           = coda_lock,
-       .unlock         = coda_unlock,
-};
-
-static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type)
-{
-       struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
-       int luma_map, chro_map, i;
-
-       memset(tiled_map, 0, sizeof(*tiled_map));
-
-       luma_map = 64;
-       chro_map = 64;
-       tiled_map->map_type = tiled_map_type;
-       for (i = 0; i < 16; i++)
-               tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map;
-       for (i = 0; i < 4; i++)
-               tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map;
-       for (i = 0; i < 16; i++)
-               tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map;
-
-       if (tiled_map_type == GDI_LINEAR_FRAME_MAP) {
-               tiled_map->xy2rbc_config = 0;
-       } else {
-               dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n",
-                       tiled_map_type);
-               return;
-       }
-}
-
-static void set_default_params(struct coda_ctx *ctx)
-{
-       int max_w;
-       int max_h;
-
-       ctx->codec = &ctx->dev->devtype->codecs[0];
-       max_w = ctx->codec->max_w;
-       max_h = ctx->codec->max_h;
-
-       ctx->params.codec_mode = CODA_MODE_INVALID;
-       ctx->colorspace = V4L2_COLORSPACE_REC709;
-       ctx->params.framerate = 30;
-       ctx->aborting = 0;
-
-       /* Default formats for output and input queues */
-       ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc;
-       ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc;
-       ctx->q_data[V4L2_M2M_SRC].width = max_w;
-       ctx->q_data[V4L2_M2M_SRC].height = max_h;
-       ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w;
-       ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2;
-       ctx->q_data[V4L2_M2M_DST].width = max_w;
-       ctx->q_data[V4L2_M2M_DST].height = max_h;
-       ctx->q_data[V4L2_M2M_DST].bytesperline = 0;
-       ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE;
-       ctx->q_data[V4L2_M2M_SRC].rect.width = max_w;
-       ctx->q_data[V4L2_M2M_SRC].rect.height = max_h;
-       ctx->q_data[V4L2_M2M_DST].rect.width = max_w;
-       ctx->q_data[V4L2_M2M_DST].rect.height = max_h;
-
-       if (ctx->dev->devtype->product == CODA_960)
-               coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP);
-}
-
-/*
- * Queue operations
- */
-static int coda_queue_setup(struct vb2_queue *vq,
-                               const struct v4l2_format *fmt,
-                               unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
-{
-       struct coda_ctx *ctx = vb2_get_drv_priv(vq);
-       struct coda_q_data *q_data;
-       unsigned int size;
-
-       q_data = get_q_data(ctx, vq->type);
-       size = q_data->sizeimage;
-
-       *nplanes = 1;
-       sizes[0] = size;
-
-       alloc_ctxs[0] = ctx->dev->alloc_ctx;
-
-       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                "get %d buffer(s) of size %d each.\n", *nbuffers, size);
-
-       return 0;
-}
-
-static int coda_buf_prepare(struct vb2_buffer *vb)
-{
-       struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-       struct coda_q_data *q_data;
-
-       q_data = get_q_data(ctx, vb->vb2_queue->type);
-
-       if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
-               v4l2_warn(&ctx->dev->v4l2_dev,
-                         "%s data will not fit into plane (%lu < %lu)\n",
-                         __func__, vb2_plane_size(vb, 0),
-                         (long)q_data->sizeimage);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static void coda_buf_queue(struct vb2_buffer *vb)
-{
-       struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-       struct coda_dev *dev = ctx->dev;
-       struct coda_q_data *q_data;
-
-       q_data = get_q_data(ctx, vb->vb2_queue->type);
-
-       /*
-        * In the decoder case, immediately try to copy the buffer into the
-        * bitstream ringbuffer and mark it as ready to be dequeued.
-        */
-       if (q_data->fourcc == V4L2_PIX_FMT_H264 &&
-           vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-               /*
-                * For backwards compatibility, queuing an empty buffer marks
-                * the stream end
-                */
-               if (vb2_get_plane_payload(vb, 0) == 0) {
-                       ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
-                       if ((dev->devtype->product == CODA_960) &&
-                           coda_isbusy(dev) &&
-                           (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) {
-                               /* if this decoder instance is running, set the stream end flag */
-                               coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM);
-                       }
-               }
-               mutex_lock(&ctx->bitstream_mutex);
-               v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
-               coda_fill_bitstream(ctx);
-               mutex_unlock(&ctx->bitstream_mutex);
-       } else {
-               v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
-       }
-}
-
-static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value)
-{
-       struct coda_dev *dev = ctx->dev;
-       u32 *p = ctx->parabuf.vaddr;
-
-       if (dev->devtype->product == CODA_DX6)
-               p[index] = value;
-       else
-               p[index ^ 1] = value;
-}
-
-static int coda_alloc_aux_buf(struct coda_dev *dev,
-                             struct coda_aux_buf *buf, size_t size,
-                             const char *name, struct dentry *parent)
-{
-       buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr,
-                                       GFP_KERNEL);
-       if (!buf->vaddr)
-               return -ENOMEM;
-
-       buf->size = size;
-
-       if (name && parent) {
-               buf->blob.data = buf->vaddr;
-               buf->blob.size = size;
-               buf->dentry = debugfs_create_blob(name, 0644, parent, &buf->blob);
-               if (!buf->dentry)
-                       dev_warn(&dev->plat_dev->dev,
-                                "failed to create debugfs entry %s\n", name);
-       }
-
-       return 0;
-}
-
-static inline int coda_alloc_context_buf(struct coda_ctx *ctx,
-                                        struct coda_aux_buf *buf, size_t size,
-                                        const char *name)
-{
-       return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry);
-}
-
-static void coda_free_aux_buf(struct coda_dev *dev,
-                             struct coda_aux_buf *buf)
-{
-       if (buf->vaddr) {
-               dma_free_coherent(&dev->plat_dev->dev, buf->size,
-                                 buf->vaddr, buf->paddr);
-               buf->vaddr = NULL;
-               buf->size = 0;
-       }
-       debugfs_remove(buf->dentry);
-}
-
-static void coda_free_framebuffers(struct coda_ctx *ctx)
-{
-       int i;
-
-       for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++)
-               coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]);
-}
-
-static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc)
-{
-       struct coda_dev *dev = ctx->dev;
-       int width, height;
-       dma_addr_t paddr;
-       int ysize;
-       int ret;
-       int i;
-
-       if (ctx->codec && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 ||
-            ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264)) {
-               width = round_up(q_data->width, 16);
-               height = round_up(q_data->height, 16);
-       } else {
-               width = round_up(q_data->width, 8);
-               height = q_data->height;
-       }
-       ysize = width * height;
-
-       /* Allocate frame buffers */
-       for (i = 0; i < ctx->num_internal_frames; i++) {
-               size_t size;
-               char *name;
-
-               size = ysize + ysize / 2;
-               if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
-                   dev->devtype->product != CODA_DX6)
-                       size += ysize / 4;
-               name = kasprintf(GFP_KERNEL, "fb%d", i);
-               ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i],
-                                            size, name);
-               kfree(name);
-               if (ret < 0) {
-                       coda_free_framebuffers(ctx);
-                       return ret;
-               }
-       }
-
-       /* Register frame buffers in the parameter buffer */
-       for (i = 0; i < ctx->num_internal_frames; i++) {
-               paddr = ctx->internal_frames[i].paddr;
-               coda_parabuf_write(ctx, i * 3 + 0, paddr); /* Y */
-               coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); /* Cb */
-               coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize/4); /* Cr */
-
-               /* mvcol buffer for h.264 */
-               if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
-                   dev->devtype->product != CODA_DX6)
-                       coda_parabuf_write(ctx, 96 + i,
-                                          ctx->internal_frames[i].paddr +
-                                          ysize + ysize/4 + ysize/4);
-       }
-
-       /* mvcol buffer for mpeg4 */
-       if ((dev->devtype->product != CODA_DX6) &&
-           (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4))
-               coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr +
-                                           ysize + ysize/4 + ysize/4);
-
-       return 0;
-}
-
-static int coda_h264_padding(int size, char *p)
-{
-       int nal_size;
-       int diff;
-
-       diff = size - (size & ~0x7);
-       if (diff == 0)
-               return 0;
-
-       nal_size = coda_filler_size[diff];
-       memcpy(p, coda_filler_nal, nal_size);
-
-       /* Add rbsp stop bit and trailing at the end */
-       *(p + nal_size - 1) = 0x80;
-
-       return nal_size;
-}
-
-static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size)
-{
-       phys_addr_t ret;
-
-       size = round_up(size, 1024);
-       if (size > iram->remaining)
-               return 0;
-       iram->remaining -= size;
-
-       ret = iram->next_paddr;
-       iram->next_paddr += size;
-
-       return ret;
-}
-
-static void coda_setup_iram(struct coda_ctx *ctx)
-{
-       struct coda_iram_info *iram_info = &ctx->iram_info;
-       struct coda_dev *dev = ctx->dev;
-       int mb_width;
-       int dbk_bits;
-       int bit_bits;
-       int ip_bits;
-
-       memset(iram_info, 0, sizeof(*iram_info));
-       iram_info->next_paddr = dev->iram.paddr;
-       iram_info->remaining = dev->iram.size;
-
-       switch (dev->devtype->product) {
-       case CODA_7541:
-               dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE;
-               bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
-               ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
-               break;
-       case CODA_960:
-               dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE;
-               bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
-               ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
-               break;
-       default: /* CODA_DX6 */
-               return;
-       }
-
-       if (ctx->inst_type == CODA_INST_ENCODER) {
-               struct coda_q_data *q_data_src;
-
-               q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-               mb_width = DIV_ROUND_UP(q_data_src->width, 16);
-
-               /* Prioritize in case IRAM is too small for everything */
-               if (dev->devtype->product == CODA_7541) {
-                       iram_info->search_ram_size = round_up(mb_width * 16 *
-                                                             36 + 2048, 1024);
-                       iram_info->search_ram_paddr = coda_iram_alloc(iram_info,
-                                                       iram_info->search_ram_size);
-                       if (!iram_info->search_ram_paddr) {
-                               pr_err("IRAM is smaller than the search ram size\n");
-                               goto out;
-                       }
-                       iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE |
-                                                  CODA7_USE_ME_ENABLE;
-               }
-
-               /* Only H.264BP and H.263P3 are considered */
-               iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, 64 * mb_width);
-               iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, 64 * mb_width);
-               if (!iram_info->buf_dbk_c_use)
-                       goto out;
-               iram_info->axi_sram_use |= dbk_bits;
-
-               iram_info->buf_bit_use = coda_iram_alloc(iram_info, 128 * mb_width);
-               if (!iram_info->buf_bit_use)
-                       goto out;
-               iram_info->axi_sram_use |= bit_bits;
-
-               iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, 128 * mb_width);
-               if (!iram_info->buf_ip_ac_dc_use)
-                       goto out;
-               iram_info->axi_sram_use |= ip_bits;
-
-               /* OVL and BTP disabled for encoder */
-       } else if (ctx->inst_type == CODA_INST_DECODER) {
-               struct coda_q_data *q_data_dst;
-
-               q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-               mb_width = DIV_ROUND_UP(q_data_dst->width, 16);
-
-               iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, 128 * mb_width);
-               iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, 128 * mb_width);
-               if (!iram_info->buf_dbk_c_use)
-                       goto out;
-               iram_info->axi_sram_use |= dbk_bits;
-
-               iram_info->buf_bit_use = coda_iram_alloc(iram_info, 128 * mb_width);
-               if (!iram_info->buf_bit_use)
-                       goto out;
-               iram_info->axi_sram_use |= bit_bits;
-
-               iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, 128 * mb_width);
-               if (!iram_info->buf_ip_ac_dc_use)
-                       goto out;
-               iram_info->axi_sram_use |= ip_bits;
-
-               /* OVL and BTP unused as there is no VC1 support yet */
-       }
-
-out:
-       if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE))
-               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                        "IRAM smaller than needed\n");
-
-       if (dev->devtype->product == CODA_7541) {
-               /* TODO - Enabling these causes picture errors on CODA7541 */
-               if (ctx->inst_type == CODA_INST_DECODER) {
-                       /* fw 1.4.50 */
-                       iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
-                                                    CODA7_USE_IP_ENABLE);
-               } else {
-                       /* fw 13.4.29 */
-                       iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
-                                                    CODA7_USE_HOST_DBK_ENABLE |
-                                                    CODA7_USE_IP_ENABLE |
-                                                    CODA7_USE_DBK_ENABLE);
-               }
-       }
-}
-
-static void coda_free_context_buffers(struct coda_ctx *ctx)
-{
-       struct coda_dev *dev = ctx->dev;
-
-       coda_free_aux_buf(dev, &ctx->slicebuf);
-       coda_free_aux_buf(dev, &ctx->psbuf);
-       if (dev->devtype->product != CODA_DX6)
-               coda_free_aux_buf(dev, &ctx->workbuf);
-}
-
-static int coda_alloc_context_buffers(struct coda_ctx *ctx,
-                                     struct coda_q_data *q_data)
-{
-       struct coda_dev *dev = ctx->dev;
-       size_t size;
-       int ret;
-
-       if (dev->devtype->product == CODA_DX6)
-               return 0;
-
-       if (ctx->psbuf.vaddr) {
-               v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n");
-               return -EBUSY;
-       }
-       if (ctx->slicebuf.vaddr) {
-               v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n");
-               return -EBUSY;
-       }
-       if (ctx->workbuf.vaddr) {
-               v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n");
-               ret = -EBUSY;
-               return -ENOMEM;
-       }
-
-       if (q_data->fourcc == V4L2_PIX_FMT_H264) {
-               /* worst case slice size */
-               size = (DIV_ROUND_UP(q_data->width, 16) *
-                       DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512;
-               ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, "slicebuf");
-               if (ret < 0) {
-                       v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte slice buffer",
-                                ctx->slicebuf.size);
-                       return ret;
-               }
-       }
-
-       if (dev->devtype->product == CODA_7541) {
-               ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE, "psbuf");
-               if (ret < 0) {
-                       v4l2_err(&dev->v4l2_dev, "failed to allocate psmem buffer");
-                       goto err;
-               }
-       }
-
-       size = dev->devtype->workbuf_size;
-       if (dev->devtype->product == CODA_960 &&
-           q_data->fourcc == V4L2_PIX_FMT_H264)
-               size += CODA9_PS_SAVE_SIZE;
-       ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf");
-       if (ret < 0) {
-               v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte context buffer",
-                        ctx->workbuf.size);
-               goto err;
-       }
-
-       return 0;
-
-err:
-       coda_free_context_buffers(ctx);
-       return ret;
-}
-
-static int coda_start_decoding(struct coda_ctx *ctx)
-{
-       struct coda_q_data *q_data_src, *q_data_dst;
-       u32 bitstream_buf, bitstream_size;
-       struct coda_dev *dev = ctx->dev;
-       int width, height;
-       u32 src_fourcc;
-       u32 val;
-       int ret;
-
-       /* Start decoding */
-       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-       bitstream_buf = ctx->bitstream.paddr;
-       bitstream_size = ctx->bitstream.size;
-       src_fourcc = q_data_src->fourcc;
-
-       coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
-
-       /* Update coda bitstream read and write pointers from kfifo */
-       coda_kfifo_sync_to_device_full(ctx);
-
-       ctx->display_idx = -1;
-       ctx->frm_dis_flg = 0;
-       coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
-
-       coda_write(dev, CODA_BIT_DEC_SEQ_INIT_ESCAPE,
-                       CODA_REG_BIT_BIT_STREAM_PARAM);
-
-       coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START);
-       coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE);
-       val = 0;
-       if ((dev->devtype->product == CODA_7541) ||
-           (dev->devtype->product == CODA_960))
-               val |= CODA_REORDER_ENABLE;
-       coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION);
-
-       ctx->params.codec_mode = ctx->codec->mode;
-       if (dev->devtype->product == CODA_960 &&
-           src_fourcc == V4L2_PIX_FMT_MPEG4)
-               ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4;
-       else
-               ctx->params.codec_mode_aux = 0;
-       if (src_fourcc == V4L2_PIX_FMT_H264) {
-               if (dev->devtype->product == CODA_7541) {
-                       coda_write(dev, ctx->psbuf.paddr,
-                                       CODA_CMD_DEC_SEQ_PS_BB_START);
-                       coda_write(dev, (CODA7_PS_BUF_SIZE / 1024),
-                                       CODA_CMD_DEC_SEQ_PS_BB_SIZE);
-               }
-               if (dev->devtype->product == CODA_960) {
-                       coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN);
-                       coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE);
-               }
-       }
-       if (dev->devtype->product != CODA_960) {
-               coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE);
-       }
-
-       if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) {
-               v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
-               coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
-               return -ETIMEDOUT;
-       }
-
-       /* Update kfifo out pointer from coda bitstream read pointer */
-       coda_kfifo_sync_from_device(ctx);
-
-       coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
-
-       if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) {
-               v4l2_err(&dev->v4l2_dev,
-                       "CODA_COMMAND_SEQ_INIT failed, error code = %d\n",
-                       coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON));
-               return -EAGAIN;
-       }
-
-       val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE);
-       if (dev->devtype->product == CODA_DX6) {
-               width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK;
-               height = val & CODADX6_PICHEIGHT_MASK;
-       } else {
-               width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK;
-               height = val & CODA7_PICHEIGHT_MASK;
-       }
-
-       if (width > q_data_dst->width || height > q_data_dst->height) {
-               v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n",
-                        width, height, q_data_dst->width, q_data_dst->height);
-               return -EINVAL;
-       }
-
-       width = round_up(width, 16);
-       height = round_up(height, 16);
-
-       v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s instance %d now: %dx%d\n",
-                __func__, ctx->idx, width, height);
-
-       ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED);
-       if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) {
-               v4l2_err(&dev->v4l2_dev,
-                        "not enough framebuffers to decode (%d < %d)\n",
-                        CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames);
-               return -EINVAL;
-       }
-
-       if (src_fourcc == V4L2_PIX_FMT_H264) {
-               u32 left_right;
-               u32 top_bottom;
-
-               left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT);
-               top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM);
-
-               q_data_dst->rect.left = (left_right >> 10) & 0x3ff;
-               q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff;
-               q_data_dst->rect.width = width - q_data_dst->rect.left -
-                                        (left_right & 0x3ff);
-               q_data_dst->rect.height = height - q_data_dst->rect.top -
-                                         (top_bottom & 0x3ff);
-       }
-
-       ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc);
-       if (ret < 0)
-               return ret;
-
-       /* Tell the decoder how many frame buffers we allocated. */
-       coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
-       coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE);
-
-       if (dev->devtype->product != CODA_DX6) {
-               /* Set secondary AXI IRAM */
-               coda_setup_iram(ctx);
-
-               coda_write(dev, ctx->iram_info.buf_bit_use,
-                               CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
-               coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
-                               CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
-               coda_write(dev, ctx->iram_info.buf_dbk_y_use,
-                               CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
-               coda_write(dev, ctx->iram_info.buf_dbk_c_use,
-                               CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
-               coda_write(dev, ctx->iram_info.buf_ovl_use,
-                               CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
-               if (dev->devtype->product == CODA_960)
-                       coda_write(dev, ctx->iram_info.buf_btp_use,
-                                       CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
-       }
-
-       if (dev->devtype->product == CODA_960) {
-               coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY);
-
-               coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE);
-               coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET |
-                               32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET |
-                               8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET |
-                               8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET,
-                               CODA9_CMD_SET_FRAME_CACHE_CONFIG);
-       }
-
-       if (src_fourcc == V4L2_PIX_FMT_H264) {
-               coda_write(dev, ctx->slicebuf.paddr,
-                               CODA_CMD_SET_FRAME_SLICE_BB_START);
-               coda_write(dev, ctx->slicebuf.size / 1024,
-                               CODA_CMD_SET_FRAME_SLICE_BB_SIZE);
-       }
-
-       if (dev->devtype->product == CODA_7541) {
-               int max_mb_x = 1920 / 16;
-               int max_mb_y = 1088 / 16;
-               int max_mb_num = max_mb_x * max_mb_y;
-
-               coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
-                               CODA7_CMD_SET_FRAME_MAX_DEC_SIZE);
-       } else if (dev->devtype->product == CODA_960) {
-               int max_mb_x = 1920 / 16;
-               int max_mb_y = 1088 / 16;
-               int max_mb_num = max_mb_x * max_mb_y;
-
-               coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
-                               CODA9_CMD_SET_FRAME_MAX_DEC_SIZE);
-       }
-
-       if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) {
-               v4l2_err(&ctx->dev->v4l2_dev,
-                        "CODA_COMMAND_SET_FRAME_BUF timeout\n");
-               return -ETIMEDOUT;
-       }
-
-       return 0;
-}
-
-static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf,
-                             int header_code, u8 *header, int *size)
-{
-       struct coda_dev *dev = ctx->dev;
-       size_t bufsize;
-       int ret;
-       int i;
-
-       if (dev->devtype->product == CODA_960)
-               memset(vb2_plane_vaddr(buf, 0), 0, 64);
-
-       coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0),
-                  CODA_CMD_ENC_HEADER_BB_START);
-       bufsize = vb2_plane_size(buf, 0);
-       if (dev->devtype->product == CODA_960)
-               bufsize /= 1024;
-       coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE);
-       coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE);
-       ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER);
-       if (ret < 0) {
-               v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
-               return ret;
-       }
-
-       if (dev->devtype->product == CODA_960) {
-               for (i = 63; i > 0; i--)
-                       if (((char *)vb2_plane_vaddr(buf, 0))[i] != 0)
-                               break;
-               *size = i + 1;
-       } else {
-               *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) -
-                       coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
-       }
-       memcpy(header, vb2_plane_vaddr(buf, 0), *size);
-
-       return 0;
-}
-
-static int coda_start_encoding(struct coda_ctx *ctx);
-
-static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
-{
-       struct coda_ctx *ctx = vb2_get_drv_priv(q);
-       struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev;
-       struct coda_dev *dev = ctx->dev;
-       struct coda_q_data *q_data_src, *q_data_dst;
-       u32 dst_fourcc;
-       int ret = 0;
-
-       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-       if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-               if (q_data_src->fourcc == V4L2_PIX_FMT_H264) {
-                       if (coda_get_bitstream_payload(ctx) < 512)
-                               return -EINVAL;
-               } else {
-                       if (count < 1)
-                               return -EINVAL;
-               }
-
-               ctx->streamon_out = 1;
-
-               if (coda_format_is_yuv(q_data_src->fourcc))
-                       ctx->inst_type = CODA_INST_ENCODER;
-               else
-                       ctx->inst_type = CODA_INST_DECODER;
-       } else {
-               if (count < 1)
-                       return -EINVAL;
-
-               ctx->streamon_cap = 1;
-       }
-
-       /* Don't start the coda unless both queues are on */
-       if (!(ctx->streamon_out & ctx->streamon_cap))
-               return 0;
-
-       /* Allow decoder device_run with no new buffers queued */
-       if (ctx->inst_type == CODA_INST_DECODER)
-               v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true);
-
-       ctx->gopcounter = ctx->params.gop_size - 1;
-       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-       dst_fourcc = q_data_dst->fourcc;
-
-       ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
-                                    q_data_dst->fourcc);
-       if (!ctx->codec) {
-               v4l2_err(v4l2_dev, "couldn't tell instance type.\n");
-               return -EINVAL;
-       }
-
-       /* Allocate per-instance buffers */
-       ret = coda_alloc_context_buffers(ctx, q_data_src);
-       if (ret < 0)
-               return ret;
-
-       if (ctx->inst_type == CODA_INST_DECODER) {
-               mutex_lock(&dev->coda_mutex);
-               ret = coda_start_decoding(ctx);
-               mutex_unlock(&dev->coda_mutex);
-               if (ret == -EAGAIN)
-                       return 0;
-               else if (ret < 0)
-                       return ret;
-       } else {
-               ret = coda_start_encoding(ctx);
-       }
-
-       ctx->initialized = 1;
-       return ret;
-}
-
-static int coda_start_encoding(struct coda_ctx *ctx)
-{
-       struct coda_dev *dev = ctx->dev;
-       struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
-       struct coda_q_data *q_data_src, *q_data_dst;
-       u32 bitstream_buf, bitstream_size;
-       struct vb2_buffer *buf;
-       int gamma, ret, value;
-       u32 dst_fourcc;
-
-       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-       dst_fourcc = q_data_dst->fourcc;
-
-       buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-       bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0);
-       bitstream_size = q_data_dst->sizeimage;
-
-       if (!coda_is_initialized(dev)) {
-               v4l2_err(v4l2_dev, "coda is not initialized.\n");
-               return -EFAULT;
-       }
-
-       mutex_lock(&dev->coda_mutex);
-
-       coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
-       coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
-       coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
-       switch (dev->devtype->product) {
-       case CODA_DX6:
-               coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN |
-                       CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
-               break;
-       case CODA_960:
-               coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
-               /* fallthrough */
-       case CODA_7541:
-               coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN |
-                       CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
-               break;
-       }
-
-       value = coda_read(dev, CODA_REG_BIT_FRAME_MEM_CTRL);
-       value &= ~(1 << 2 | 0x7 << 9);
-       ctx->frame_mem_ctrl = value;
-       coda_write(dev, value, CODA_REG_BIT_FRAME_MEM_CTRL);
-
-       if (dev->devtype->product == CODA_DX6) {
-               /* Configure the coda */
-               coda_write(dev, dev->iram.paddr, CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR);
-       }
-
-       /* Could set rotation here if needed */
-       switch (dev->devtype->product) {
-       case CODA_DX6:
-               value = (q_data_src->width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET;
-               value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
-               break;
-       case CODA_7541:
-               if (dst_fourcc == V4L2_PIX_FMT_H264) {
-                       value = (round_up(q_data_src->width, 16) &
-                                CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET;
-                       value |= (round_up(q_data_src->height, 16) &
-                                 CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
-                       break;
-               }
-               /* fallthrough */
-       case CODA_960:
-               value = (q_data_src->width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET;
-               value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
-       }
-       coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE);
-       coda_write(dev, ctx->params.framerate,
-                  CODA_CMD_ENC_SEQ_SRC_F_RATE);
-
-       ctx->params.codec_mode = ctx->codec->mode;
-       switch (dst_fourcc) {
-       case V4L2_PIX_FMT_MPEG4:
-               if (dev->devtype->product == CODA_960)
-                       coda_write(dev, CODA9_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD);
-               else
-                       coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD);
-               coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA);
-               break;
-       case V4L2_PIX_FMT_H264:
-               if (dev->devtype->product == CODA_960)
-                       coda_write(dev, CODA9_STD_H264, CODA_CMD_ENC_SEQ_COD_STD);
-               else
-                       coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD);
-               if (ctx->params.h264_deblk_enabled) {
-                       value = ((ctx->params.h264_deblk_alpha &
-                                 CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) <<
-                                CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) |
-                               ((ctx->params.h264_deblk_beta &
-                                 CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) <<
-                                CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET);
-               } else {
-                       value = 1 << CODA_264PARAM_DISABLEDEBLK_OFFSET;
-               }
-               coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA);
-               break;
-       default:
-               v4l2_err(v4l2_dev,
-                        "dst format (0x%08x) invalid.\n", dst_fourcc);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       switch (ctx->params.slice_mode) {
-       case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
-               value = 0;
-               break;
-       case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB:
-               value  = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET;
-               value |= (1 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET;
-               value |=  1 & CODA_SLICING_MODE_MASK;
-               break;
-       case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
-               value  = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET;
-               value |= (0 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET;
-               value |=  1 & CODA_SLICING_MODE_MASK;
-               break;
-       }
-       coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE);
-       value = ctx->params.gop_size & CODA_GOP_SIZE_MASK;
-       coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE);
-
-       if (ctx->params.bitrate) {
-               /* Rate control enabled */
-               value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) << CODA_RATECONTROL_BITRATE_OFFSET;
-               value |=  1 & CODA_RATECONTROL_ENABLE_MASK;
-               if (dev->devtype->product == CODA_960)
-                       value |= BIT(31); /* disable autoskip */
-       } else {
-               value = 0;
-       }
-       coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA);
-
-       coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE);
-       coda_write(dev, ctx->params.intra_refresh,
-                  CODA_CMD_ENC_SEQ_INTRA_REFRESH);
-
-       coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START);
-       coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE);
-
-
-       value = 0;
-       if (dev->devtype->product == CODA_960)
-               gamma = CODA9_DEFAULT_GAMMA;
-       else
-               gamma = CODA_DEFAULT_GAMMA;
-       if (gamma > 0) {
-               coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET,
-                          CODA_CMD_ENC_SEQ_RC_GAMMA);
-       }
-
-       if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) {
-               coda_write(dev,
-                          ctx->params.h264_min_qp << CODA_QPMIN_OFFSET |
-                          ctx->params.h264_max_qp << CODA_QPMAX_OFFSET,
-                          CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX);
-       }
-       if (dev->devtype->product == CODA_960) {
-               if (ctx->params.h264_max_qp)
-                       value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET;
-               if (CODA_DEFAULT_GAMMA > 0)
-                       value |= 1 << CODA9_OPTION_GAMMA_OFFSET;
-       } else {
-               if (CODA_DEFAULT_GAMMA > 0) {
-                       if (dev->devtype->product == CODA_DX6)
-                               value |= 1 << CODADX6_OPTION_GAMMA_OFFSET;
-                       else
-                               value |= 1 << CODA7_OPTION_GAMMA_OFFSET;
-               }
-               if (ctx->params.h264_min_qp)
-                       value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET;
-               if (ctx->params.h264_max_qp)
-                       value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET;
-       }
-       coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION);
-
-       coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE);
-
-       coda_setup_iram(ctx);
-
-       if (dst_fourcc == V4L2_PIX_FMT_H264) {
-               switch (dev->devtype->product) {
-               case CODA_DX6:
-                       value = FMO_SLICE_SAVE_BUF_SIZE << 7;
-                       coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);
-                       break;
-               case CODA_7541:
-                       coda_write(dev, ctx->iram_info.search_ram_paddr,
-                                       CODA7_CMD_ENC_SEQ_SEARCH_BASE);
-                       coda_write(dev, ctx->iram_info.search_ram_size,
-                                       CODA7_CMD_ENC_SEQ_SEARCH_SIZE);
-                       break;
-               case CODA_960:
-                       coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION);
-                       coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT);
-               }
-       }
-
-       ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT);
-       if (ret < 0) {
-               v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
-               goto out;
-       }
-
-       if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) {
-               v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n");
-               ret = -EFAULT;
-               goto out;
-       }
-
-       if (dev->devtype->product == CODA_960)
-               ctx->num_internal_frames = 4;
-       else
-               ctx->num_internal_frames = 2;
-       ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
-       if (ret < 0) {
-               v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
-               goto out;
-       }
-
-       coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
-       coda_write(dev, q_data_src->bytesperline,
-                       CODA_CMD_SET_FRAME_BUF_STRIDE);
-       if (dev->devtype->product == CODA_7541) {
-               coda_write(dev, q_data_src->bytesperline,
-                               CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
-       }
-       if (dev->devtype->product != CODA_DX6) {
-               coda_write(dev, ctx->iram_info.buf_bit_use,
-                               CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
-               coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
-                               CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
-               coda_write(dev, ctx->iram_info.buf_dbk_y_use,
-                               CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
-               coda_write(dev, ctx->iram_info.buf_dbk_c_use,
-                               CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
-               coda_write(dev, ctx->iram_info.buf_ovl_use,
-                               CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
-               if (dev->devtype->product == CODA_960) {
-                       coda_write(dev, ctx->iram_info.buf_btp_use,
-                                       CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
-
-                       /* FIXME */
-                       coda_write(dev, ctx->internal_frames[2].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_A);
-                       coda_write(dev, ctx->internal_frames[3].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_B);
-               }
-       }
-
-       ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF);
-       if (ret < 0) {
-               v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n");
-               goto out;
-       }
-
-       /* Save stream headers */
-       buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-       switch (dst_fourcc) {
-       case V4L2_PIX_FMT_H264:
-               /*
-                * Get SPS in the first frame and copy it to an
-                * intermediate buffer.
-                */
-               ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS,
-                                        &ctx->vpu_header[0][0],
-                                        &ctx->vpu_header_size[0]);
-               if (ret < 0)
-                       goto out;
-
-               /*
-                * Get PPS in the first frame and copy it to an
-                * intermediate buffer.
-                */
-               ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS,
-                                        &ctx->vpu_header[1][0],
-                                        &ctx->vpu_header_size[1]);
-               if (ret < 0)
-                       goto out;
-
-               /*
-                * Length of H.264 headers is variable and thus it might not be
-                * aligned for the coda to append the encoded frame. In that is
-                * the case a filler NAL must be added to header 2.
-                */
-               ctx->vpu_header_size[2] = coda_h264_padding(
-                                       (ctx->vpu_header_size[0] +
-                                        ctx->vpu_header_size[1]),
-                                        ctx->vpu_header[2]);
-               break;
-       case V4L2_PIX_FMT_MPEG4:
-               /*
-                * Get VOS in the first frame and copy it to an
-                * intermediate buffer
-                */
-               ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS,
-                                        &ctx->vpu_header[0][0],
-                                        &ctx->vpu_header_size[0]);
-               if (ret < 0)
-                       goto out;
-
-               ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS,
-                                        &ctx->vpu_header[1][0],
-                                        &ctx->vpu_header_size[1]);
-               if (ret < 0)
-                       goto out;
-
-               ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL,
-                                        &ctx->vpu_header[2][0],
-                                        &ctx->vpu_header_size[2]);
-               if (ret < 0)
-                       goto out;
-               break;
-       default:
-               /* No more formats need to save headers at the moment */
-               break;
-       }
-
-out:
-       mutex_unlock(&dev->coda_mutex);
-       return ret;
-}
-
-static void coda_stop_streaming(struct vb2_queue *q)
-{
-       struct coda_ctx *ctx = vb2_get_drv_priv(q);
-       struct coda_dev *dev = ctx->dev;
-
-       if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-                        "%s: output\n", __func__);
-               ctx->streamon_out = 0;
-
-               if (ctx->inst_type == CODA_INST_DECODER &&
-                   coda_isbusy(dev) && ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX)) {
-                       /* if this decoder instance is running, set the stream end flag */
-                       if (dev->devtype->product == CODA_960) {
-                               u32 val = coda_read(dev, CODA_REG_BIT_BIT_STREAM_PARAM);
-
-                               val |= CODA_BIT_STREAM_END_FLAG;
-                               coda_write(dev, val, CODA_REG_BIT_BIT_STREAM_PARAM);
-                               ctx->bit_stream_param = val;
-                       }
-               }
-               ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
-
-               ctx->isequence = 0;
-       } else {
-               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-                        "%s: capture\n", __func__);
-               ctx->streamon_cap = 0;
-
-               ctx->osequence = 0;
-               ctx->sequence_offset = 0;
-       }
-
-       if (!ctx->streamon_out && !ctx->streamon_cap) {
-               struct coda_timestamp *ts;
-
-               while (!list_empty(&ctx->timestamp_list)) {
-                       ts = list_first_entry(&ctx->timestamp_list,
-                                             struct coda_timestamp, list);
-                       list_del(&ts->list);
-                       kfree(ts);
-               }
-               kfifo_init(&ctx->bitstream_fifo,
-                       ctx->bitstream.vaddr, ctx->bitstream.size);
-               ctx->runcounter = 0;
-       }
-}
-
-static struct vb2_ops coda_qops = {
-       .queue_setup            = coda_queue_setup,
-       .buf_prepare            = coda_buf_prepare,
-       .buf_queue              = coda_buf_queue,
-       .start_streaming        = coda_start_streaming,
-       .stop_streaming         = coda_stop_streaming,
-       .wait_prepare           = vb2_ops_wait_prepare,
-       .wait_finish            = vb2_ops_wait_finish,
-};
-
-static int coda_s_ctrl(struct v4l2_ctrl *ctrl)
-{
-       struct coda_ctx *ctx =
-                       container_of(ctrl->handler, struct coda_ctx, ctrls);
-
-       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val);
-
-       switch (ctrl->id) {
-       case V4L2_CID_HFLIP:
-               if (ctrl->val)
-                       ctx->params.rot_mode |= CODA_MIR_HOR;
-               else
-                       ctx->params.rot_mode &= ~CODA_MIR_HOR;
-               break;
-       case V4L2_CID_VFLIP:
-               if (ctrl->val)
-                       ctx->params.rot_mode |= CODA_MIR_VER;
-               else
-                       ctx->params.rot_mode &= ~CODA_MIR_VER;
-               break;
-       case V4L2_CID_MPEG_VIDEO_BITRATE:
-               ctx->params.bitrate = ctrl->val / 1000;
-               break;
-       case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
-               ctx->params.gop_size = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
-               ctx->params.h264_intra_qp = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
-               ctx->params.h264_inter_qp = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
-               ctx->params.h264_min_qp = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
-               ctx->params.h264_max_qp = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
-               ctx->params.h264_deblk_alpha = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
-               ctx->params.h264_deblk_beta = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
-               ctx->params.h264_deblk_enabled = (ctrl->val ==
-                               V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
-               break;
-       case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
-               ctx->params.mpeg4_intra_qp = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
-               ctx->params.mpeg4_inter_qp = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
-               ctx->params.slice_mode = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
-               ctx->params.slice_max_mb = ctrl->val;
-               break;
-       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
-               ctx->params.slice_max_bits = ctrl->val * 8;
-               break;
-       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
-               break;
-       case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
-               ctx->params.intra_refresh = ctrl->val;
-               break;
-       default:
-               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                       "Invalid control, id=%d, val=%d\n",
-                       ctrl->id, ctrl->val);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static struct v4l2_ctrl_ops coda_ctrl_ops = {
-       .s_ctrl = coda_s_ctrl,
-};
-
-static int coda_ctrls_setup(struct coda_ctx *ctx)
-{
-       v4l2_ctrl_handler_init(&ctx->ctrls, 9);
-
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_HFLIP, 0, 1, 1, 0);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_VFLIP, 0, 1, 1, 0);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25);
-       if (ctx->dev->devtype->product != CODA_960) {
-               v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-                       V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12);
-       }
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, 0, 15, 1, 0);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, 0, 15, 1, 0);
-       v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
-               V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, 0x0,
-               V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2);
-       v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
-               V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0,
-               V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, 500);
-       v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_HEADER_MODE,
-               V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
-               (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE),
-               V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);
-       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-               V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, 1920 * 1088 / 256, 1, 0);
-
-       if (ctx->ctrls.error) {
-               v4l2_err(&ctx->dev->v4l2_dev, "control initialization error (%d)",
-                       ctx->ctrls.error);
-               return -EINVAL;
-       }
-
-       return v4l2_ctrl_handler_setup(&ctx->ctrls);
-}
-
-static int coda_queue_init(void *priv, struct vb2_queue *src_vq,
-                     struct vb2_queue *dst_vq)
-{
-       struct coda_ctx *ctx = priv;
-       int ret;
-
-       src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-       src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
-       src_vq->drv_priv = ctx;
-       src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
-       src_vq->ops = &coda_qops;
-       src_vq->mem_ops = &vb2_dma_contig_memops;
-       src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
-       src_vq->lock = &ctx->dev->dev_mutex;
-
-       ret = vb2_queue_init(src_vq);
-       if (ret)
-               return ret;
-
-       dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
-       dst_vq->drv_priv = ctx;
-       dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
-       dst_vq->ops = &coda_qops;
-       dst_vq->mem_ops = &vb2_dma_contig_memops;
-       dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
-       dst_vq->lock = &ctx->dev->dev_mutex;
-
-       return vb2_queue_init(dst_vq);
-}
-
-static int coda_next_free_instance(struct coda_dev *dev)
-{
-       int idx = ffz(dev->instance_mask);
-
-       if ((idx < 0) ||
-           (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES))
-               return -EBUSY;
-
-       return idx;
-}
-
-static int coda_open(struct file *file)
-{
-       struct coda_dev *dev = video_drvdata(file);
-       struct coda_ctx *ctx = NULL;
-       char *name;
-       int ret;
-       int idx;
-
-       ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
-       if (!ctx)
-               return -ENOMEM;
-
-       idx = coda_next_free_instance(dev);
-       if (idx < 0) {
-               ret = idx;
-               goto err_coda_max;
-       }
-       set_bit(idx, &dev->instance_mask);
-
-       name = kasprintf(GFP_KERNEL, "context%d", idx);
-       ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root);
-       kfree(name);
-
-       init_completion(&ctx->completion);
-       INIT_WORK(&ctx->pic_run_work, coda_pic_run_work);
-       INIT_WORK(&ctx->seq_end_work, coda_seq_end_work);
-       v4l2_fh_init(&ctx->fh, video_devdata(file));
-       file->private_data = &ctx->fh;
-       v4l2_fh_add(&ctx->fh);
-       ctx->dev = dev;
-       ctx->idx = idx;
-       switch (dev->devtype->product) {
-       case CODA_7541:
-       case CODA_960:
-               ctx->reg_idx = 0;
-               break;
-       default:
-               ctx->reg_idx = idx;
-       }
-
-       /* Power up and upload firmware if necessary */
-       ret = pm_runtime_get_sync(&dev->plat_dev->dev);
-       if (ret < 0) {
-               v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret);
-               goto err_pm_get;
-       }
-
-       ret = clk_prepare_enable(dev->clk_per);
-       if (ret)
-               goto err_clk_per;
-
-       ret = clk_prepare_enable(dev->clk_ahb);
-       if (ret)
-               goto err_clk_ahb;
-
-       set_default_params(ctx);
-       ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
-                                        &coda_queue_init);
-       if (IS_ERR(ctx->fh.m2m_ctx)) {
-               ret = PTR_ERR(ctx->fh.m2m_ctx);
-
-               v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n",
-                        __func__, ret);
-               goto err_ctx_init;
-       }
-
-       ret = coda_ctrls_setup(ctx);
-       if (ret) {
-               v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n");
-               goto err_ctrls_setup;
-       }
-
-       ctx->fh.ctrl_handler = &ctx->ctrls;
-
-       ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE,
-                                    "parabuf");
-       if (ret < 0) {
-               v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf");
-               goto err_dma_alloc;
-       }
-
-       ctx->bitstream.size = CODA_MAX_FRAME_SIZE;
-       ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev,
-                       ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL);
-       if (!ctx->bitstream.vaddr) {
-               v4l2_err(&dev->v4l2_dev, "failed to allocate bitstream ringbuffer");
-               ret = -ENOMEM;
-               goto err_dma_writecombine;
-       }
-       kfifo_init(&ctx->bitstream_fifo,
-               ctx->bitstream.vaddr, ctx->bitstream.size);
-       mutex_init(&ctx->bitstream_mutex);
-       mutex_init(&ctx->buffer_mutex);
-       INIT_LIST_HEAD(&ctx->timestamp_list);
-
-       coda_lock(ctx);
-       list_add(&ctx->list, &dev->instances);
-       coda_unlock(ctx);
-
-       v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n",
-                ctx->idx, ctx);
-
-       return 0;
-
-err_dma_writecombine:
-       coda_free_context_buffers(ctx);
-       if (ctx->dev->devtype->product == CODA_DX6)
-               coda_free_aux_buf(dev, &ctx->workbuf);
-       coda_free_aux_buf(dev, &ctx->parabuf);
-err_dma_alloc:
-       v4l2_ctrl_handler_free(&ctx->ctrls);
-err_ctrls_setup:
-       v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
-err_ctx_init:
-       clk_disable_unprepare(dev->clk_ahb);
-err_clk_ahb:
-       clk_disable_unprepare(dev->clk_per);
-err_clk_per:
-       pm_runtime_put_sync(&dev->plat_dev->dev);
-err_pm_get:
-       v4l2_fh_del(&ctx->fh);
-       v4l2_fh_exit(&ctx->fh);
-       clear_bit(ctx->idx, &dev->instance_mask);
-err_coda_max:
-       kfree(ctx);
-       return ret;
-}
-
-static int coda_release(struct file *file)
-{
-       struct coda_dev *dev = video_drvdata(file);
-       struct coda_ctx *ctx = fh_to_ctx(file->private_data);
-
-       v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n",
-                ctx);
-
-       debugfs_remove_recursive(ctx->debugfs_entry);
-
-       /* If this instance is running, call .job_abort and wait for it to end */
-       v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
-
-       /* In case the instance was not running, we still need to call SEQ_END */
-       if (ctx->initialized) {
-               queue_work(dev->workqueue, &ctx->seq_end_work);
-               flush_work(&ctx->seq_end_work);
-       }
-
-       coda_free_framebuffers(ctx);
-
-       coda_lock(ctx);
-       list_del(&ctx->list);
-       coda_unlock(ctx);
-
-       dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size,
-               ctx->bitstream.vaddr, ctx->bitstream.paddr);
-       coda_free_context_buffers(ctx);
-       if (ctx->dev->devtype->product == CODA_DX6)
-               coda_free_aux_buf(dev, &ctx->workbuf);
-
-       coda_free_aux_buf(dev, &ctx->parabuf);
-       v4l2_ctrl_handler_free(&ctx->ctrls);
-       clk_disable_unprepare(dev->clk_ahb);
-       clk_disable_unprepare(dev->clk_per);
-       pm_runtime_put_sync(&dev->plat_dev->dev);
-       v4l2_fh_del(&ctx->fh);
-       v4l2_fh_exit(&ctx->fh);
-       clear_bit(ctx->idx, &dev->instance_mask);
-       kfree(ctx);
-
-       return 0;
-}
-
-static const struct v4l2_file_operations coda_fops = {
-       .owner          = THIS_MODULE,
-       .open           = coda_open,
-       .release        = coda_release,
-       .poll           = v4l2_m2m_fop_poll,
-       .unlocked_ioctl = video_ioctl2,
-       .mmap           = v4l2_m2m_fop_mmap,
-};
-
-static void coda_finish_decode(struct coda_ctx *ctx)
-{
-       struct coda_dev *dev = ctx->dev;
-       struct coda_q_data *q_data_src;
-       struct coda_q_data *q_data_dst;
-       struct vb2_buffer *dst_buf;
-       struct coda_timestamp *ts;
-       int width, height;
-       int decoded_idx;
-       int display_idx;
-       u32 src_fourcc;
-       int success;
-       u32 err_mb;
-       u32 val;
-
-       dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-
-       /* Update kfifo out pointer from coda bitstream read pointer */
-       coda_kfifo_sync_from_device(ctx);
-
-       /*
-        * in stream-end mode, the read pointer can overshoot the write pointer
-        * by up to 512 bytes
-        */
-       if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) {
-               if (coda_get_bitstream_payload(ctx) >= 0x100000 - 512)
-                       kfifo_init(&ctx->bitstream_fifo,
-                               ctx->bitstream.vaddr, ctx->bitstream.size);
-       }
-
-       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-       src_fourcc = q_data_src->fourcc;
-
-       val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS);
-       if (val != 1)
-               pr_err("DEC_PIC_SUCCESS = %d\n", val);
-
-       success = val & 0x1;
-       if (!success)
-               v4l2_err(&dev->v4l2_dev, "decode failed\n");
-
-       if (src_fourcc == V4L2_PIX_FMT_H264) {
-               if (val & (1 << 3))
-                       v4l2_err(&dev->v4l2_dev,
-                                "insufficient PS buffer space (%d bytes)\n",
-                                ctx->psbuf.size);
-               if (val & (1 << 2))
-                       v4l2_err(&dev->v4l2_dev,
-                                "insufficient slice buffer space (%d bytes)\n",
-                                ctx->slicebuf.size);
-       }
-
-       val = coda_read(dev, CODA_RET_DEC_PIC_SIZE);
-       width = (val >> 16) & 0xffff;
-       height = val & 0xffff;
-
-       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-
-       /* frame crop information */
-       if (src_fourcc == V4L2_PIX_FMT_H264) {
-               u32 left_right;
-               u32 top_bottom;
-
-               left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT);
-               top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM);
-
-               if (left_right == 0xffffffff && top_bottom == 0xffffffff) {
-                       /* Keep current crop information */
-               } else {
-                       struct v4l2_rect *rect = &q_data_dst->rect;
-
-                       rect->left = left_right >> 16 & 0xffff;
-                       rect->top = top_bottom >> 16 & 0xffff;
-                       rect->width = width - rect->left -
-                                     (left_right & 0xffff);
-                       rect->height = height - rect->top -
-                                      (top_bottom & 0xffff);
-               }
-       } else {
-               /* no cropping */
-       }
-
-       err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB);
-       if (err_mb > 0)
-               v4l2_err(&dev->v4l2_dev,
-                        "errors in %d macroblocks\n", err_mb);
-
-       if (dev->devtype->product == CODA_7541) {
-               val = coda_read(dev, CODA_RET_DEC_PIC_OPTION);
-               if (val == 0) {
-                       /* not enough bitstream data */
-                       v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-                                "prescan failed: %d\n", val);
-                       ctx->hold = true;
-                       return;
-               }
-       }
-
-       ctx->frm_dis_flg = coda_read(dev, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
-
-       /*
-        * The previous display frame was copied out by the rotator,
-        * now it can be overwritten again
-        */
-       if (ctx->display_idx >= 0 &&
-           ctx->display_idx < ctx->num_internal_frames) {
-               ctx->frm_dis_flg &= ~(1 << ctx->display_idx);
-               coda_write(dev, ctx->frm_dis_flg,
-                               CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
-       }
-
-       /*
-        * The index of the last decoded frame, not necessarily in
-        * display order, and the index of the next display frame.
-        * The latter could have been decoded in a previous run.
-        */
-       decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX);
-       display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX);
-
-       if (decoded_idx == -1) {
-               /* no frame was decoded, but we might have a display frame */
-               if (display_idx >= 0 && display_idx < ctx->num_internal_frames)
-                       ctx->sequence_offset++;
-               else if (ctx->display_idx < 0)
-                       ctx->hold = true;
-       } else if (decoded_idx == -2) {
-               /* no frame was decoded, we still return the remaining buffers */
-       } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) {
-               v4l2_err(&dev->v4l2_dev,
-                        "decoded frame index out of range: %d\n", decoded_idx);
-       } else {
-               ts = list_first_entry(&ctx->timestamp_list,
-                                     struct coda_timestamp, list);
-               list_del(&ts->list);
-               val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1;
-               val -= ctx->sequence_offset;
-               if (val != (ts->sequence & 0xffff)) {
-                       v4l2_err(&dev->v4l2_dev,
-                                "sequence number mismatch (%d(%d) != %d)\n",
-                                val, ctx->sequence_offset, ts->sequence);
-               }
-               ctx->frame_timestamps[decoded_idx] = *ts;
-               kfree(ts);
-
-               val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7;
-               if (val == 0)
-                       ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME;
-               else if (val == 1)
-                       ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_PFRAME;
-               else
-                       ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_BFRAME;
-
-               ctx->frame_errors[decoded_idx] = err_mb;
-       }
-
-       if (display_idx == -1) {
-               /*
-                * no more frames to be decoded, but there could still
-                * be rotator output to dequeue
-                */
-               ctx->hold = true;
-       } else if (display_idx == -3) {
-               /* possibly prescan failure */
-       } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) {
-               v4l2_err(&dev->v4l2_dev,
-                        "presentation frame index out of range: %d\n",
-                        display_idx);
-       }
-
-       /* If a frame was copied out, return it */
-       if (ctx->display_idx >= 0 &&
-           ctx->display_idx < ctx->num_internal_frames) {
-               dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
-               dst_buf->v4l2_buf.sequence = ctx->osequence++;
-
-               dst_buf->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME |
-                                            V4L2_BUF_FLAG_PFRAME |
-                                            V4L2_BUF_FLAG_BFRAME);
-               dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx];
-               ts = &ctx->frame_timestamps[ctx->display_idx];
-               dst_buf->v4l2_buf.timecode = ts->timecode;
-               dst_buf->v4l2_buf.timestamp = ts->timestamp;
-
-               vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2);
-
-               v4l2_m2m_buf_done(dst_buf, ctx->frame_errors[display_idx] ?
-                                 VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
-
-               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-                       "job finished: decoding frame (%d) (%s)\n",
-                       dst_buf->v4l2_buf.sequence,
-                       (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
-                       "KEYFRAME" : "PFRAME");
-       } else {
-               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-                       "job finished: no frame decoded\n");
-       }
-
-       /* The rotator will copy the current display frame next time */
-       ctx->display_idx = display_idx;
-}
-
-static void coda_finish_encode(struct coda_ctx *ctx)
-{
-       struct vb2_buffer *src_buf, *dst_buf;
-       struct coda_dev *dev = ctx->dev;
-       u32 wr_ptr, start_ptr;
-
-       src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
-       dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
-
-       /* Get results from the coda */
-       start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);
-       wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
-
-       /* Calculate bytesused field */
-       if (dst_buf->v4l2_buf.sequence == 0) {
-               vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr +
-                                       ctx->vpu_header_size[0] +
-                                       ctx->vpu_header_size[1] +
-                                       ctx->vpu_header_size[2]);
-       } else {
-               vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr);
-       }
-
-       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n",
-                wr_ptr - start_ptr);
-
-       coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM);
-       coda_read(dev, CODA_RET_ENC_PIC_FLAG);
-
-       if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) {
-               dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
-               dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
-       } else {
-               dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
-               dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
-       }
-
-       dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
-       dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
-       dst_buf->v4l2_buf.flags |=
-               src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
-       dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
-
-       v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
-
-       dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
-       v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
-
-       ctx->gopcounter--;
-       if (ctx->gopcounter < 0)
-               ctx->gopcounter = ctx->params.gop_size - 1;
-
-       v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
-               "job finished: encoding frame (%d) (%s)\n",
-               dst_buf->v4l2_buf.sequence,
-               (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
-               "KEYFRAME" : "PFRAME");
-}
-
-static irqreturn_t coda_irq_handler(int irq, void *data)
-{
-       struct coda_dev *dev = data;
-       struct coda_ctx *ctx;
-
-       /* read status register to attend the IRQ */
-       coda_read(dev, CODA_REG_BIT_INT_STATUS);
-       coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET,
-                     CODA_REG_BIT_INT_CLEAR);
-
-       ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
-       if (ctx == NULL) {
-               v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n");
-               mutex_unlock(&dev->coda_mutex);
-               return IRQ_HANDLED;
-       }
-
-       if (ctx->aborting) {
-               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                        "task has been aborted\n");
-       }
-
-       if (coda_isbusy(ctx->dev)) {
-               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-                        "coda is still busy!!!!\n");
-               return IRQ_NONE;
-       }
-
-       complete(&ctx->completion);
-
-       return IRQ_HANDLED;
-}
-
-static u32 coda_supported_firmwares[] = {
-       CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5),
-       CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50),
-       CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5),
-};
-
-static bool coda_firmware_supported(u32 vernum)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++)
-               if (vernum == coda_supported_firmwares[i])
-                       return true;
-       return false;
-}
-
-static int coda_hw_init(struct coda_dev *dev)
-{
-       u32 data;
-       u16 *p;
-       int i, ret;
-
-       ret = clk_prepare_enable(dev->clk_per);
-       if (ret)
-               goto err_clk_per;
-
-       ret = clk_prepare_enable(dev->clk_ahb);
-       if (ret)
-               goto err_clk_ahb;
-
-       if (dev->rstc)
-               reset_control_reset(dev->rstc);
-
-       /*
-        * Copy the first CODA_ISRAM_SIZE in the internal SRAM.
-        * The 16-bit chars in the code buffer are in memory access
-        * order, re-sort them to CODA order for register download.
-        * Data in this SRAM survives a reboot.
-        */
-       p = (u16 *)dev->codebuf.vaddr;
-       if (dev->devtype->product == CODA_DX6) {
-               for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++)  {
-                       data = CODA_DOWN_ADDRESS_SET(i) |
-                               CODA_DOWN_DATA_SET(p[i ^ 1]);
-                       coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
-               }
-       } else {
-               for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) {
-                       data = CODA_DOWN_ADDRESS_SET(i) |
-                               CODA_DOWN_DATA_SET(p[round_down(i, 4) +
-                                                       3 - (i % 4)]);
-                       coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
-               }
-       }
-
-       /* Clear registers */
-       for (i = 0; i < 64; i++)
-               coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4);
-
-       /* Tell the BIT where to find everything it needs */
-       if (dev->devtype->product == CODA_960 ||
-           dev->devtype->product == CODA_7541) {
-               coda_write(dev, dev->tempbuf.paddr,
-                               CODA_REG_BIT_TEMP_BUF_ADDR);
-               coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
-       } else {
-               coda_write(dev, dev->workbuf.paddr,
-                             CODA_REG_BIT_WORK_BUF_ADDR);
-       }
-       coda_write(dev, dev->codebuf.paddr,
-                     CODA_REG_BIT_CODE_BUF_ADDR);
-       coda_write(dev, 0, CODA_REG_BIT_CODE_RUN);
-
-       /* Set default values */
-       switch (dev->devtype->product) {
-       case CODA_DX6:
-               coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL);
-               break;
-       default:
-               coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL);
-       }
-       if (dev->devtype->product == CODA_960)
-               coda_write(dev, 1 << 12, CODA_REG_BIT_FRAME_MEM_CTRL);
-       else
-               coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL);
-
-       if (dev->devtype->product != CODA_DX6)
-               coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE);
-
-       coda_write(dev, CODA_INT_INTERRUPT_ENABLE,
-                     CODA_REG_BIT_INT_ENABLE);
-
-       /* Reset VPU and start processor */
-       data = coda_read(dev, CODA_REG_BIT_CODE_RESET);
-       data |= CODA_REG_RESET_ENABLE;
-       coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
-       udelay(10);
-       data &= ~CODA_REG_RESET_ENABLE;
-       coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
-       coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN);
-
-       clk_disable_unprepare(dev->clk_ahb);
-       clk_disable_unprepare(dev->clk_per);
-
-       return 0;
-
-err_clk_ahb:
-       clk_disable_unprepare(dev->clk_per);
-err_clk_per:
-       return ret;
-}
-
-static int coda_check_firmware(struct coda_dev *dev)
-{
-       u16 product, major, minor, release;
-       u32 data;
-       int ret;
-
-       ret = clk_prepare_enable(dev->clk_per);
-       if (ret)
-               goto err_clk_per;
-
-       ret = clk_prepare_enable(dev->clk_ahb);
-       if (ret)
-               goto err_clk_ahb;
-
-       coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM);
-       coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
-       coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX);
-       coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD);
-       coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND);
-       if (coda_wait_timeout(dev)) {
-               v4l2_err(&dev->v4l2_dev, "firmware get command error\n");
-               ret = -EIO;
-               goto err_run_cmd;
-       }
-
-       if (dev->devtype->product == CODA_960) {
-               data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV);
-               v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n",
-                         data);
-       }
-
-       /* Check we are compatible with the loaded firmware */
-       data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM);
-       product = CODA_FIRMWARE_PRODUCT(data);
-       major = CODA_FIRMWARE_MAJOR(data);
-       minor = CODA_FIRMWARE_MINOR(data);
-       release = CODA_FIRMWARE_RELEASE(data);
-
-       clk_disable_unprepare(dev->clk_per);
-       clk_disable_unprepare(dev->clk_ahb);
-
-       if (product != dev->devtype->product) {
-               v4l2_err(&dev->v4l2_dev, "Wrong firmware. Hw: %s, Fw: %s,"
-                        " Version: %u.%u.%u\n",
-                        coda_product_name(dev->devtype->product),
-                        coda_product_name(product), major, minor, release);
-               return -EINVAL;
-       }
-
-       v4l2_info(&dev->v4l2_dev, "Initialized %s.\n",
-                 coda_product_name(product));
-
-       if (coda_firmware_supported(data)) {
-               v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n",
-                         major, minor, release);
-       } else {
-               v4l2_warn(&dev->v4l2_dev, "Unsupported firmware version: "
-                         "%u.%u.%u\n", major, minor, release);
-       }
-
-       return 0;
-
-err_run_cmd:
-       clk_disable_unprepare(dev->clk_ahb);
-err_clk_ahb:
-       clk_disable_unprepare(dev->clk_per);
-err_clk_per:
-       return ret;
-}
-
-static void coda_fw_callback(const struct firmware *fw, void *context)
-{
-       struct coda_dev *dev = context;
-       struct platform_device *pdev = dev->plat_dev;
-       int ret;
-
-       if (!fw) {
-               v4l2_err(&dev->v4l2_dev, "firmware request failed\n");
-               return;
-       }
-
-       /* allocate auxiliary per-device code buffer for the BIT processor */
-       ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf",
-                                dev->debugfs_root);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to allocate code buffer\n");
-               return;
-       }
-
-       /* Copy the whole firmware image to the code buffer */
-       memcpy(dev->codebuf.vaddr, fw->data, fw->size);
-       release_firmware(fw);
-
-       if (pm_runtime_enabled(&pdev->dev) && pdev->dev.pm_domain) {
-               /*
-                * Enabling power temporarily will cause coda_hw_init to be
-                * called via coda_runtime_resume by the pm domain.
-                */
-               ret = pm_runtime_get_sync(&dev->plat_dev->dev);
-               if (ret < 0) {
-                       v4l2_err(&dev->v4l2_dev, "failed to power on: %d\n",
-                                ret);
-                       return;
-               }
-
-               ret = coda_check_firmware(dev);
-               if (ret < 0)
-                       return;
-
-               pm_runtime_put_sync(&dev->plat_dev->dev);
-       } else {
-               /*
-                * If runtime pm is disabled or pm_domain is not set,
-                * initialize once manually.
-                */
-               ret = coda_hw_init(dev);
-               if (ret < 0) {
-                       v4l2_err(&dev->v4l2_dev, "HW initialization failed\n");
-                       return;
-               }
-
-               ret = coda_check_firmware(dev);
-               if (ret < 0)
-                       return;
-       }
-
-       dev->vfd.fops   = &coda_fops,
-       dev->vfd.ioctl_ops      = &coda_ioctl_ops;
-       dev->vfd.release        = video_device_release_empty,
-       dev->vfd.lock   = &dev->dev_mutex;
-       dev->vfd.v4l2_dev       = &dev->v4l2_dev;
-       dev->vfd.vfl_dir        = VFL_DIR_M2M;
-       snprintf(dev->vfd.name, sizeof(dev->vfd.name), "%s", CODA_NAME);
-       video_set_drvdata(&dev->vfd, dev);
-
-       dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-       if (IS_ERR(dev->alloc_ctx)) {
-               v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n");
-               return;
-       }
-
-       dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops);
-       if (IS_ERR(dev->m2m_dev)) {
-               v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
-               goto rel_ctx;
-       }
-
-       ret = video_register_device(&dev->vfd, VFL_TYPE_GRABBER, 0);
-       if (ret) {
-               v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
-               goto rel_m2m;
-       }
-       v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video%d\n",
-                 dev->vfd.num);
-
-       return;
-
-rel_m2m:
-       v4l2_m2m_release(dev->m2m_dev);
-rel_ctx:
-       vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
-}
-
-static int coda_firmware_request(struct coda_dev *dev)
-{
-       char *fw = dev->devtype->firmware;
-
-       dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw,
-               coda_product_name(dev->devtype->product));
-
-       return request_firmware_nowait(THIS_MODULE, true,
-               fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback);
-}
-
-enum coda_platform {
-       CODA_IMX27,
-       CODA_IMX53,
-       CODA_IMX6Q,
-       CODA_IMX6DL,
-};
-
-static const struct coda_devtype coda_devdata[] = {
-       [CODA_IMX27] = {
-               .firmware     = "v4l-codadx6-imx27.bin",
-               .product      = CODA_DX6,
-               .codecs       = codadx6_codecs,
-               .num_codecs   = ARRAY_SIZE(codadx6_codecs),
-               .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024,
-               .iram_size    = 0xb000,
-       },
-       [CODA_IMX53] = {
-               .firmware     = "v4l-coda7541-imx53.bin",
-               .product      = CODA_7541,
-               .codecs       = coda7_codecs,
-               .num_codecs   = ARRAY_SIZE(coda7_codecs),
-               .workbuf_size = 128 * 1024,
-               .tempbuf_size = 304 * 1024,
-               .iram_size    = 0x14000,
-       },
-       [CODA_IMX6Q] = {
-               .firmware     = "v4l-coda960-imx6q.bin",
-               .product      = CODA_960,
-               .codecs       = coda9_codecs,
-               .num_codecs   = ARRAY_SIZE(coda9_codecs),
-               .workbuf_size = 80 * 1024,
-               .tempbuf_size = 204 * 1024,
-               .iram_size    = 0x21000,
-       },
-       [CODA_IMX6DL] = {
-               .firmware     = "v4l-coda960-imx6dl.bin",
-               .product      = CODA_960,
-               .codecs       = coda9_codecs,
-               .num_codecs   = ARRAY_SIZE(coda9_codecs),
-               .workbuf_size = 80 * 1024,
-               .tempbuf_size = 204 * 1024,
-               .iram_size    = 0x20000,
-       },
-};
-
-static struct platform_device_id coda_platform_ids[] = {
-       { .name = "coda-imx27", .driver_data = CODA_IMX27 },
-       { .name = "coda-imx53", .driver_data = CODA_IMX53 },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, coda_platform_ids);
-
-#ifdef CONFIG_OF
-static const struct of_device_id coda_dt_ids[] = {
-       { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] },
-       { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] },
-       { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] },
-       { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, coda_dt_ids);
-#endif
-
-static int coda_probe(struct platform_device *pdev)
-{
-       const struct of_device_id *of_id =
-                       of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev);
-       const struct platform_device_id *pdev_id;
-       struct coda_platform_data *pdata = pdev->dev.platform_data;
-       struct device_node *np = pdev->dev.of_node;
-       struct gen_pool *pool;
-       struct coda_dev *dev;
-       struct resource *res;
-       int ret, irq;
-
-       dev = devm_kzalloc(&pdev->dev, sizeof *dev, GFP_KERNEL);
-       if (!dev) {
-               dev_err(&pdev->dev, "Not enough memory for %s\n",
-                       CODA_NAME);
-               return -ENOMEM;
-       }
-
-       spin_lock_init(&dev->irqlock);
-       INIT_LIST_HEAD(&dev->instances);
-
-       dev->plat_dev = pdev;
-       dev->clk_per = devm_clk_get(&pdev->dev, "per");
-       if (IS_ERR(dev->clk_per)) {
-               dev_err(&pdev->dev, "Could not get per clock\n");
-               return PTR_ERR(dev->clk_per);
-       }
-
-       dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
-       if (IS_ERR(dev->clk_ahb)) {
-               dev_err(&pdev->dev, "Could not get ahb clock\n");
-               return PTR_ERR(dev->clk_ahb);
-       }
-
-       /* Get  memory for physical registers */
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(dev->regs_base))
-               return PTR_ERR(dev->regs_base);
-
-       /* IRQ */
-       irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
-               dev_err(&pdev->dev, "failed to get irq resource\n");
-               return irq;
-       }
-
-       ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler,
-                       IRQF_ONESHOT, dev_name(&pdev->dev), dev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
-               return ret;
-       }
-
-       dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL);
-       if (IS_ERR(dev->rstc)) {
-               ret = PTR_ERR(dev->rstc);
-               if (ret == -ENOENT || ret == -ENOSYS) {
-                       dev->rstc = NULL;
-               } else {
-                       dev_err(&pdev->dev, "failed get reset control: %d\n", ret);
-                       return ret;
-               }
-       }
-
-       /* Get IRAM pool from device tree or platform data */
-       pool = of_get_named_gen_pool(np, "iram", 0);
-       if (!pool && pdata)
-               pool = dev_get_gen_pool(pdata->iram_dev);
-       if (!pool) {
-               dev_err(&pdev->dev, "iram pool not available\n");
-               return -ENOMEM;
-       }
-       dev->iram_pool = pool;
-
-       ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
-       if (ret)
-               return ret;
-
-       mutex_init(&dev->dev_mutex);
-       mutex_init(&dev->coda_mutex);
-
-       pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
-
-       if (of_id) {
-               dev->devtype = of_id->data;
-       } else if (pdev_id) {
-               dev->devtype = &coda_devdata[pdev_id->driver_data];
-       } else {
-               v4l2_device_unregister(&dev->v4l2_dev);
-               return -EINVAL;
-       }
-
-       dev->debugfs_root = debugfs_create_dir("coda", NULL);
-       if (!dev->debugfs_root)
-               dev_warn(&pdev->dev, "failed to create debugfs root\n");
-
-       /* allocate auxiliary per-device buffers for the BIT processor */
-       if (dev->devtype->product == CODA_DX6) {
-               ret = coda_alloc_aux_buf(dev, &dev->workbuf,
-                                        dev->devtype->workbuf_size, "workbuf",
-                                        dev->debugfs_root);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to allocate work buffer\n");
-                       v4l2_device_unregister(&dev->v4l2_dev);
-                       return ret;
-               }
-       }
-
-       if (dev->devtype->tempbuf_size) {
-               ret = coda_alloc_aux_buf(dev, &dev->tempbuf,
-                                        dev->devtype->tempbuf_size, "tempbuf",
-                                        dev->debugfs_root);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to allocate temp buffer\n");
-                       v4l2_device_unregister(&dev->v4l2_dev);
-                       return ret;
-               }
-       }
-
-       dev->iram.size = dev->devtype->iram_size;
-       dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size,
-                                            &dev->iram.paddr);
-       if (!dev->iram.vaddr) {
-               dev_err(&pdev->dev, "unable to alloc iram\n");
-               return -ENOMEM;
-       }
-
-       dev->iram.blob.data = dev->iram.vaddr;
-       dev->iram.blob.size = dev->iram.size;
-       dev->iram.dentry = debugfs_create_blob("iram", 0644, dev->debugfs_root,
-                                              &dev->iram.blob);
-
-       dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
-       if (!dev->workqueue) {
-               dev_err(&pdev->dev, "unable to alloc workqueue\n");
-               return -ENOMEM;
-       }
-
-       platform_set_drvdata(pdev, dev);
-
-       pm_runtime_enable(&pdev->dev);
-
-       return coda_firmware_request(dev);
-}
-
-static int coda_remove(struct platform_device *pdev)
-{
-       struct coda_dev *dev = platform_get_drvdata(pdev);
-
-       video_unregister_device(&dev->vfd);
-       if (dev->m2m_dev)
-               v4l2_m2m_release(dev->m2m_dev);
-       pm_runtime_disable(&pdev->dev);
-       if (dev->alloc_ctx)
-               vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
-       v4l2_device_unregister(&dev->v4l2_dev);
-       destroy_workqueue(dev->workqueue);
-       if (dev->iram.vaddr)
-               gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr,
-                             dev->iram.size);
-       coda_free_aux_buf(dev, &dev->codebuf);
-       coda_free_aux_buf(dev, &dev->tempbuf);
-       coda_free_aux_buf(dev, &dev->workbuf);
-       debugfs_remove_recursive(dev->debugfs_root);
-       return 0;
-}
-
-#ifdef CONFIG_PM_RUNTIME
-static int coda_runtime_resume(struct device *dev)
-{
-       struct coda_dev *cdev = dev_get_drvdata(dev);
-       int ret = 0;
-
-       if (dev->pm_domain) {
-               ret = coda_hw_init(cdev);
-               if (ret)
-                       v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n");
-       }
-
-       return ret;
-}
-#endif
-
-static const struct dev_pm_ops coda_pm_ops = {
-       SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL)
-};
-
-static struct platform_driver coda_driver = {
-       .probe  = coda_probe,
-       .remove = coda_remove,
-       .driver = {
-               .name   = CODA_NAME,
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(coda_dt_ids),
-               .pm     = &coda_pm_ops,
-       },
-       .id_table = coda_platform_ids,
-};
-
-module_platform_driver(coda_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
-MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver");
diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile
new file mode 100644 (file)
index 0000000..3543291
--- /dev/null
@@ -0,0 +1,3 @@
+coda-objs := coda-common.o coda-bit.o coda-h264.o
+
+obj-$(CONFIG_VIDEO_CODA) += coda.o
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
new file mode 100644 (file)
index 0000000..9b8ea8b
--- /dev/null
@@ -0,0 +1,1861 @@
+/*
+ * Coda multi-standard codec IP - BIT processor functions
+ *
+ * Copyright (C) 2012 Vista Silicon S.L.
+ *    Javier Martin, <javier.martin@vista-silicon.com>
+ *    Xavier Duret
+ * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "coda.h"
+
+#define CODA7_PS_BUF_SIZE      0x28000
+#define CODA9_PS_SAVE_SIZE     (512 * 1024)
+
+#define CODA_DEFAULT_GAMMA     4096
+#define CODA9_DEFAULT_GAMMA    24576   /* 0.75 * 32768 */
+
+static inline int coda_is_initialized(struct coda_dev *dev)
+{
+       return coda_read(dev, CODA_REG_BIT_CUR_PC) != 0;
+}
+
+static inline unsigned long coda_isbusy(struct coda_dev *dev)
+{
+       return coda_read(dev, CODA_REG_BIT_BUSY);
+}
+
+static int coda_wait_timeout(struct coda_dev *dev)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+       while (coda_isbusy(dev)) {
+               if (time_after(jiffies, timeout))
+                       return -ETIMEDOUT;
+       }
+       return 0;
+}
+
+static void coda_command_async(struct coda_ctx *ctx, int cmd)
+{
+       struct coda_dev *dev = ctx->dev;
+
+       if (dev->devtype->product == CODA_960 ||
+           dev->devtype->product == CODA_7541) {
+               /* Restore context related registers to CODA */
+               coda_write(dev, ctx->bit_stream_param,
+                               CODA_REG_BIT_BIT_STREAM_PARAM);
+               coda_write(dev, ctx->frm_dis_flg,
+                               CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+               coda_write(dev, ctx->frame_mem_ctrl,
+                               CODA_REG_BIT_FRAME_MEM_CTRL);
+               coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR);
+       }
+
+       if (dev->devtype->product == CODA_960) {
+               coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR);
+               coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
+       }
+
+       coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
+
+       coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX);
+       coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD);
+       coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD);
+
+       coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND);
+}
+
+static int coda_command_sync(struct coda_ctx *ctx, int cmd)
+{
+       struct coda_dev *dev = ctx->dev;
+
+       coda_command_async(ctx, cmd);
+       return coda_wait_timeout(dev);
+}
+
+int coda_hw_reset(struct coda_ctx *ctx)
+{
+       struct coda_dev *dev = ctx->dev;
+       unsigned long timeout;
+       unsigned int idx;
+       int ret;
+
+       if (!dev->rstc)
+               return -ENOENT;
+
+       idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX);
+
+       if (dev->devtype->product == CODA_960) {
+               timeout = jiffies + msecs_to_jiffies(100);
+               coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL);
+               while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) {
+                       if (time_after(jiffies, timeout))
+                               return -ETIME;
+                       cpu_relax();
+               }
+       }
+
+       ret = reset_control_reset(dev->rstc);
+       if (ret < 0)
+               return ret;
+
+       if (dev->devtype->product == CODA_960)
+               coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL);
+       coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
+       coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN);
+       ret = coda_wait_timeout(dev);
+       coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX);
+
+       return ret;
+}
+
+static void coda_kfifo_sync_from_device(struct coda_ctx *ctx)
+{
+       struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+       struct coda_dev *dev = ctx->dev;
+       u32 rd_ptr;
+
+       rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+       kfifo->out = (kfifo->in & ~kfifo->mask) |
+                     (rd_ptr - ctx->bitstream.paddr);
+       if (kfifo->out > kfifo->in)
+               kfifo->out -= kfifo->mask + 1;
+}
+
+static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx)
+{
+       struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+       struct coda_dev *dev = ctx->dev;
+       u32 rd_ptr, wr_ptr;
+
+       rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask);
+       coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+       wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
+       coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+}
+
+static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx)
+{
+       struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+       struct coda_dev *dev = ctx->dev;
+       u32 wr_ptr;
+
+       wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
+       coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+}
+
+static int coda_bitstream_queue(struct coda_ctx *ctx,
+                               struct vb2_buffer *src_buf)
+{
+       u32 src_size = vb2_get_plane_payload(src_buf, 0);
+       u32 n;
+
+       n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0),
+                    src_size);
+       if (n < src_size)
+               return -ENOSPC;
+
+       dma_sync_single_for_device(&ctx->dev->plat_dev->dev,
+                                  ctx->bitstream.paddr, ctx->bitstream.size,
+                                  DMA_TO_DEVICE);
+
+       src_buf->v4l2_buf.sequence = ctx->qsequence++;
+
+       return 0;
+}
+
+static bool coda_bitstream_try_queue(struct coda_ctx *ctx,
+                                    struct vb2_buffer *src_buf)
+{
+       int ret;
+
+       if (coda_get_bitstream_payload(ctx) +
+           vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size)
+               return false;
+
+       if (vb2_plane_vaddr(src_buf, 0) == NULL) {
+               v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n");
+               return true;
+       }
+
+       ret = coda_bitstream_queue(ctx, src_buf);
+       if (ret < 0) {
+               v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n");
+               return false;
+       }
+       /* Sync read pointer to device */
+       if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev))
+               coda_kfifo_sync_to_device_write(ctx);
+
+       ctx->hold = false;
+
+       return true;
+}
+
+void coda_fill_bitstream(struct coda_ctx *ctx)
+{
+       struct vb2_buffer *src_buf;
+       struct coda_timestamp *ts;
+
+       while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) {
+               src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+
+               if (coda_bitstream_try_queue(ctx, src_buf)) {
+                       /*
+                        * Source buffer is queued in the bitstream ringbuffer;
+                        * queue the timestamp and mark source buffer as done
+                        */
+                       src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+
+                       ts = kmalloc(sizeof(*ts), GFP_KERNEL);
+                       if (ts) {
+                               ts->sequence = src_buf->v4l2_buf.sequence;
+                               ts->timecode = src_buf->v4l2_buf.timecode;
+                               ts->timestamp = src_buf->v4l2_buf.timestamp;
+                               list_add_tail(&ts->list, &ctx->timestamp_list);
+                       }
+
+                       v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+               } else {
+                       break;
+               }
+       }
+}
+
+void coda_bit_stream_end_flag(struct coda_ctx *ctx)
+{
+       struct coda_dev *dev = ctx->dev;
+
+       ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+
+       /* If this context is currently running, update the hardware flag */
+       if ((dev->devtype->product == CODA_960) &&
+           coda_isbusy(dev) &&
+           (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) {
+               coda_write(dev, ctx->bit_stream_param,
+                          CODA_REG_BIT_BIT_STREAM_PARAM);
+       }
+}
+
+static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value)
+{
+       struct coda_dev *dev = ctx->dev;
+       u32 *p = ctx->parabuf.vaddr;
+
+       if (dev->devtype->product == CODA_DX6)
+               p[index] = value;
+       else
+               p[index ^ 1] = value;
+}
+
+static void coda_free_framebuffers(struct coda_ctx *ctx)
+{
+       int i;
+
+       for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++)
+               coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]);
+}
+
+static int coda_alloc_framebuffers(struct coda_ctx *ctx,
+                                  struct coda_q_data *q_data, u32 fourcc)
+{
+       struct coda_dev *dev = ctx->dev;
+       int width, height;
+       dma_addr_t paddr;
+       int ysize;
+       int ret;
+       int i;
+
+       if (ctx->codec && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 ||
+            ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264)) {
+               width = round_up(q_data->width, 16);
+               height = round_up(q_data->height, 16);
+       } else {
+               width = round_up(q_data->width, 8);
+               height = q_data->height;
+       }
+       ysize = width * height;
+
+       /* Allocate frame buffers */
+       for (i = 0; i < ctx->num_internal_frames; i++) {
+               size_t size;
+               char *name;
+
+               size = ysize + ysize / 2;
+               if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
+                   dev->devtype->product != CODA_DX6)
+                       size += ysize / 4;
+               name = kasprintf(GFP_KERNEL, "fb%d", i);
+               ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i],
+                                            size, name);
+               kfree(name);
+               if (ret < 0) {
+                       coda_free_framebuffers(ctx);
+                       return ret;
+               }
+       }
+
+       /* Register frame buffers in the parameter buffer */
+       for (i = 0; i < ctx->num_internal_frames; i++) {
+               paddr = ctx->internal_frames[i].paddr;
+               /* Start addresses of Y, Cb, Cr planes */
+               coda_parabuf_write(ctx, i * 3 + 0, paddr);
+               coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize);
+               coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize / 4);
+
+               /* mvcol buffer for h.264 */
+               if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
+                   dev->devtype->product != CODA_DX6)
+                       coda_parabuf_write(ctx, 96 + i,
+                                          ctx->internal_frames[i].paddr +
+                                          ysize + ysize/4 + ysize/4);
+       }
+
+       /* mvcol buffer for mpeg4 */
+       if ((dev->devtype->product != CODA_DX6) &&
+           (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4))
+               coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr +
+                                           ysize + ysize/4 + ysize/4);
+
+       return 0;
+}
+
+static void coda_free_context_buffers(struct coda_ctx *ctx)
+{
+       struct coda_dev *dev = ctx->dev;
+
+       coda_free_aux_buf(dev, &ctx->slicebuf);
+       coda_free_aux_buf(dev, &ctx->psbuf);
+       if (dev->devtype->product != CODA_DX6)
+               coda_free_aux_buf(dev, &ctx->workbuf);
+}
+
+static int coda_alloc_context_buffers(struct coda_ctx *ctx,
+                                     struct coda_q_data *q_data)
+{
+       struct coda_dev *dev = ctx->dev;
+       size_t size;
+       int ret;
+
+       if (dev->devtype->product == CODA_DX6)
+               return 0;
+
+       if (ctx->psbuf.vaddr) {
+               v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n");
+               return -EBUSY;
+       }
+       if (ctx->slicebuf.vaddr) {
+               v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n");
+               return -EBUSY;
+       }
+       if (ctx->workbuf.vaddr) {
+               v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n");
+               ret = -EBUSY;
+               return -ENOMEM;
+       }
+
+       if (q_data->fourcc == V4L2_PIX_FMT_H264) {
+               /* worst case slice size */
+               size = (DIV_ROUND_UP(q_data->width, 16) *
+                       DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512;
+               ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size,
+                                            "slicebuf");
+               if (ret < 0) {
+                       v4l2_err(&dev->v4l2_dev,
+                                "failed to allocate %d byte slice buffer",
+                                ctx->slicebuf.size);
+                       return ret;
+               }
+       }
+
+       if (dev->devtype->product == CODA_7541) {
+               ret = coda_alloc_context_buf(ctx, &ctx->psbuf,
+                                            CODA7_PS_BUF_SIZE, "psbuf");
+               if (ret < 0) {
+                       v4l2_err(&dev->v4l2_dev,
+                                "failed to allocate psmem buffer");
+                       goto err;
+               }
+       }
+
+       size = dev->devtype->workbuf_size;
+       if (dev->devtype->product == CODA_960 &&
+           q_data->fourcc == V4L2_PIX_FMT_H264)
+               size += CODA9_PS_SAVE_SIZE;
+       ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf");
+       if (ret < 0) {
+               v4l2_err(&dev->v4l2_dev,
+                        "failed to allocate %d byte context buffer",
+                        ctx->workbuf.size);
+               goto err;
+       }
+
+       return 0;
+
+err:
+       coda_free_context_buffers(ctx);
+       return ret;
+}
+
+static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf,
+                             int header_code, u8 *header, int *size)
+{
+       struct coda_dev *dev = ctx->dev;
+       size_t bufsize;
+       int ret;
+       int i;
+
+       if (dev->devtype->product == CODA_960)
+               memset(vb2_plane_vaddr(buf, 0), 0, 64);
+
+       coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0),
+                  CODA_CMD_ENC_HEADER_BB_START);
+       bufsize = vb2_plane_size(buf, 0);
+       if (dev->devtype->product == CODA_960)
+               bufsize /= 1024;
+       coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE);
+       coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE);
+       ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER);
+       if (ret < 0) {
+               v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
+               return ret;
+       }
+
+       if (dev->devtype->product == CODA_960) {
+               for (i = 63; i > 0; i--)
+                       if (((char *)vb2_plane_vaddr(buf, 0))[i] != 0)
+                               break;
+               *size = i + 1;
+       } else {
+               *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) -
+                       coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
+       }
+       memcpy(header, vb2_plane_vaddr(buf, 0), *size);
+
+       return 0;
+}
+
+static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size)
+{
+       phys_addr_t ret;
+
+       size = round_up(size, 1024);
+       if (size > iram->remaining)
+               return 0;
+       iram->remaining -= size;
+
+       ret = iram->next_paddr;
+       iram->next_paddr += size;
+
+       return ret;
+}
+
+static void coda_setup_iram(struct coda_ctx *ctx)
+{
+       struct coda_iram_info *iram_info = &ctx->iram_info;
+       struct coda_dev *dev = ctx->dev;
+       int w64, w128;
+       int mb_width;
+       int dbk_bits;
+       int bit_bits;
+       int ip_bits;
+
+       memset(iram_info, 0, sizeof(*iram_info));
+       iram_info->next_paddr = dev->iram.paddr;
+       iram_info->remaining = dev->iram.size;
+
+       if (!dev->iram.vaddr)
+               return;
+
+       switch (dev->devtype->product) {
+       case CODA_7541:
+               dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE;
+               bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
+               ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
+               break;
+       case CODA_960:
+               dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE;
+               bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
+               ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
+               break;
+       default: /* CODA_DX6 */
+               return;
+       }
+
+       if (ctx->inst_type == CODA_INST_ENCODER) {
+               struct coda_q_data *q_data_src;
+
+               q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+               mb_width = DIV_ROUND_UP(q_data_src->width, 16);
+               w128 = mb_width * 128;
+               w64 = mb_width * 64;
+
+               /* Prioritize in case IRAM is too small for everything */
+               if (dev->devtype->product == CODA_7541) {
+                       iram_info->search_ram_size = round_up(mb_width * 16 *
+                                                             36 + 2048, 1024);
+                       iram_info->search_ram_paddr = coda_iram_alloc(iram_info,
+                                               iram_info->search_ram_size);
+                       if (!iram_info->search_ram_paddr) {
+                               pr_err("IRAM is smaller than the search ram size\n");
+                               goto out;
+                       }
+                       iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE |
+                                                  CODA7_USE_ME_ENABLE;
+               }
+
+               /* Only H.264BP and H.263P3 are considered */
+               iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w64);
+               iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w64);
+               if (!iram_info->buf_dbk_c_use)
+                       goto out;
+               iram_info->axi_sram_use |= dbk_bits;
+
+               iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128);
+               if (!iram_info->buf_bit_use)
+                       goto out;
+               iram_info->axi_sram_use |= bit_bits;
+
+               iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128);
+               if (!iram_info->buf_ip_ac_dc_use)
+                       goto out;
+               iram_info->axi_sram_use |= ip_bits;
+
+               /* OVL and BTP disabled for encoder */
+       } else if (ctx->inst_type == CODA_INST_DECODER) {
+               struct coda_q_data *q_data_dst;
+
+               q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+               mb_width = DIV_ROUND_UP(q_data_dst->width, 16);
+               w128 = mb_width * 128;
+
+               iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, w128);
+               iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, w128);
+               if (!iram_info->buf_dbk_c_use)
+                       goto out;
+               iram_info->axi_sram_use |= dbk_bits;
+
+               iram_info->buf_bit_use = coda_iram_alloc(iram_info, w128);
+               if (!iram_info->buf_bit_use)
+                       goto out;
+               iram_info->axi_sram_use |= bit_bits;
+
+               iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, w128);
+               if (!iram_info->buf_ip_ac_dc_use)
+                       goto out;
+               iram_info->axi_sram_use |= ip_bits;
+
+               /* OVL and BTP unused as there is no VC1 support yet */
+       }
+
+out:
+       if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE))
+               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                        "IRAM smaller than needed\n");
+
+       if (dev->devtype->product == CODA_7541) {
+               /* TODO - Enabling these causes picture errors on CODA7541 */
+               if (ctx->inst_type == CODA_INST_DECODER) {
+                       /* fw 1.4.50 */
+                       iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
+                                                    CODA7_USE_IP_ENABLE);
+               } else {
+                       /* fw 13.4.29 */
+                       iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
+                                                    CODA7_USE_HOST_DBK_ENABLE |
+                                                    CODA7_USE_IP_ENABLE |
+                                                    CODA7_USE_DBK_ENABLE);
+               }
+       }
+}
+
+static u32 coda_supported_firmwares[] = {
+       CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5),
+       CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50),
+       CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5),
+};
+
+static bool coda_firmware_supported(u32 vernum)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++)
+               if (vernum == coda_supported_firmwares[i])
+                       return true;
+       return false;
+}
+
+int coda_check_firmware(struct coda_dev *dev)
+{
+       u16 product, major, minor, release;
+       u32 data;
+       int ret;
+
+       ret = clk_prepare_enable(dev->clk_per);
+       if (ret)
+               goto err_clk_per;
+
+       ret = clk_prepare_enable(dev->clk_ahb);
+       if (ret)
+               goto err_clk_ahb;
+
+       coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM);
+       coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
+       coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX);
+       coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD);
+       coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND);
+       if (coda_wait_timeout(dev)) {
+               v4l2_err(&dev->v4l2_dev, "firmware get command error\n");
+               ret = -EIO;
+               goto err_run_cmd;
+       }
+
+       if (dev->devtype->product == CODA_960) {
+               data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV);
+               v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n",
+                         data);
+       }
+
+       /* Check we are compatible with the loaded firmware */
+       data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM);
+       product = CODA_FIRMWARE_PRODUCT(data);
+       major = CODA_FIRMWARE_MAJOR(data);
+       minor = CODA_FIRMWARE_MINOR(data);
+       release = CODA_FIRMWARE_RELEASE(data);
+
+       clk_disable_unprepare(dev->clk_per);
+       clk_disable_unprepare(dev->clk_ahb);
+
+       if (product != dev->devtype->product) {
+               v4l2_err(&dev->v4l2_dev,
+                        "Wrong firmware. Hw: %s, Fw: %s, Version: %u.%u.%u\n",
+                        coda_product_name(dev->devtype->product),
+                        coda_product_name(product), major, minor, release);
+               return -EINVAL;
+       }
+
+       v4l2_info(&dev->v4l2_dev, "Initialized %s.\n",
+                 coda_product_name(product));
+
+       if (coda_firmware_supported(data)) {
+               v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n",
+                         major, minor, release);
+       } else {
+               v4l2_warn(&dev->v4l2_dev,
+                         "Unsupported firmware version: %u.%u.%u\n",
+                         major, minor, release);
+       }
+
+       return 0;
+
+err_run_cmd:
+       clk_disable_unprepare(dev->clk_ahb);
+err_clk_ahb:
+       clk_disable_unprepare(dev->clk_per);
+err_clk_per:
+       return ret;
+}
+
+/*
+ * Encoder context operations
+ */
+
+static int coda_start_encoding(struct coda_ctx *ctx)
+{
+       struct coda_dev *dev = ctx->dev;
+       struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+       struct coda_q_data *q_data_src, *q_data_dst;
+       u32 bitstream_buf, bitstream_size;
+       struct vb2_buffer *buf;
+       int gamma, ret, value;
+       u32 dst_fourcc;
+
+       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+       dst_fourcc = q_data_dst->fourcc;
+
+       /* Allocate per-instance buffers */
+       ret = coda_alloc_context_buffers(ctx, q_data_src);
+       if (ret < 0)
+               return ret;
+
+       buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+       bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0);
+       bitstream_size = q_data_dst->sizeimage;
+
+       if (!coda_is_initialized(dev)) {
+               v4l2_err(v4l2_dev, "coda is not initialized.\n");
+               return -EFAULT;
+       }
+
+       mutex_lock(&dev->coda_mutex);
+
+       coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
+       coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+       coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+       switch (dev->devtype->product) {
+       case CODA_DX6:
+               coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN |
+                       CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
+               break;
+       case CODA_960:
+               coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
+               /* fallthrough */
+       case CODA_7541:
+               coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN |
+                       CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
+               break;
+       }
+
+       value = coda_read(dev, CODA_REG_BIT_FRAME_MEM_CTRL);
+       value &= ~(1 << 2 | 0x7 << 9);
+       ctx->frame_mem_ctrl = value;
+       coda_write(dev, value, CODA_REG_BIT_FRAME_MEM_CTRL);
+
+       if (dev->devtype->product == CODA_DX6) {
+               /* Configure the coda */
+               coda_write(dev, dev->iram.paddr,
+                          CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR);
+       }
+
+       /* Could set rotation here if needed */
+       switch (dev->devtype->product) {
+       case CODA_DX6:
+               value = (q_data_src->width & CODADX6_PICWIDTH_MASK)
+                       << CODADX6_PICWIDTH_OFFSET;
+               value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK)
+                        << CODA_PICHEIGHT_OFFSET;
+               break;
+       case CODA_7541:
+               if (dst_fourcc == V4L2_PIX_FMT_H264) {
+                       value = (round_up(q_data_src->width, 16) &
+                                CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET;
+                       value |= (round_up(q_data_src->height, 16) &
+                                CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
+                       break;
+               }
+               /* fallthrough */
+       case CODA_960:
+               value = (q_data_src->width & CODA7_PICWIDTH_MASK)
+                       << CODA7_PICWIDTH_OFFSET;
+               value |= (q_data_src->height & CODA7_PICHEIGHT_MASK)
+                        << CODA_PICHEIGHT_OFFSET;
+       }
+       coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE);
+       coda_write(dev, ctx->params.framerate,
+                  CODA_CMD_ENC_SEQ_SRC_F_RATE);
+
+       ctx->params.codec_mode = ctx->codec->mode;
+       switch (dst_fourcc) {
+       case V4L2_PIX_FMT_MPEG4:
+               if (dev->devtype->product == CODA_960)
+                       coda_write(dev, CODA9_STD_MPEG4,
+                                  CODA_CMD_ENC_SEQ_COD_STD);
+               else
+                       coda_write(dev, CODA_STD_MPEG4,
+                                  CODA_CMD_ENC_SEQ_COD_STD);
+               coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA);
+               break;
+       case V4L2_PIX_FMT_H264:
+               if (dev->devtype->product == CODA_960)
+                       coda_write(dev, CODA9_STD_H264,
+                                  CODA_CMD_ENC_SEQ_COD_STD);
+               else
+                       coda_write(dev, CODA_STD_H264,
+                                  CODA_CMD_ENC_SEQ_COD_STD);
+               if (ctx->params.h264_deblk_enabled) {
+                       value = ((ctx->params.h264_deblk_alpha &
+                                 CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) <<
+                                CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) |
+                               ((ctx->params.h264_deblk_beta &
+                                 CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) <<
+                                CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET);
+               } else {
+                       value = 1 << CODA_264PARAM_DISABLEDEBLK_OFFSET;
+               }
+               coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA);
+               break;
+       default:
+               v4l2_err(v4l2_dev,
+                        "dst format (0x%08x) invalid.\n", dst_fourcc);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       switch (ctx->params.slice_mode) {
+       case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
+               value = 0;
+               break;
+       case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB:
+               value  = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK)
+                        << CODA_SLICING_SIZE_OFFSET;
+               value |= (1 & CODA_SLICING_UNIT_MASK)
+                        << CODA_SLICING_UNIT_OFFSET;
+               value |=  1 & CODA_SLICING_MODE_MASK;
+               break;
+       case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
+               value  = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK)
+                        << CODA_SLICING_SIZE_OFFSET;
+               value |= (0 & CODA_SLICING_UNIT_MASK)
+                        << CODA_SLICING_UNIT_OFFSET;
+               value |=  1 & CODA_SLICING_MODE_MASK;
+               break;
+       }
+       coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE);
+       value = ctx->params.gop_size & CODA_GOP_SIZE_MASK;
+       coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE);
+
+       if (ctx->params.bitrate) {
+               /* Rate control enabled */
+               value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK)
+                       << CODA_RATECONTROL_BITRATE_OFFSET;
+               value |=  1 & CODA_RATECONTROL_ENABLE_MASK;
+               if (dev->devtype->product == CODA_960)
+                       value |= BIT(31); /* disable autoskip */
+       } else {
+               value = 0;
+       }
+       coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA);
+
+       coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE);
+       coda_write(dev, ctx->params.intra_refresh,
+                  CODA_CMD_ENC_SEQ_INTRA_REFRESH);
+
+       coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START);
+       coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE);
+
+
+       value = 0;
+       if (dev->devtype->product == CODA_960)
+               gamma = CODA9_DEFAULT_GAMMA;
+       else
+               gamma = CODA_DEFAULT_GAMMA;
+       if (gamma > 0) {
+               coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET,
+                          CODA_CMD_ENC_SEQ_RC_GAMMA);
+       }
+
+       if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) {
+               coda_write(dev,
+                          ctx->params.h264_min_qp << CODA_QPMIN_OFFSET |
+                          ctx->params.h264_max_qp << CODA_QPMAX_OFFSET,
+                          CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX);
+       }
+       if (dev->devtype->product == CODA_960) {
+               if (ctx->params.h264_max_qp)
+                       value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET;
+               if (CODA_DEFAULT_GAMMA > 0)
+                       value |= 1 << CODA9_OPTION_GAMMA_OFFSET;
+       } else {
+               if (CODA_DEFAULT_GAMMA > 0) {
+                       if (dev->devtype->product == CODA_DX6)
+                               value |= 1 << CODADX6_OPTION_GAMMA_OFFSET;
+                       else
+                               value |= 1 << CODA7_OPTION_GAMMA_OFFSET;
+               }
+               if (ctx->params.h264_min_qp)
+                       value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET;
+               if (ctx->params.h264_max_qp)
+                       value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET;
+       }
+       coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION);
+
+       coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE);
+
+       coda_setup_iram(ctx);
+
+       if (dst_fourcc == V4L2_PIX_FMT_H264) {
+               switch (dev->devtype->product) {
+               case CODA_DX6:
+                       value = FMO_SLICE_SAVE_BUF_SIZE << 7;
+                       coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);
+                       break;
+               case CODA_7541:
+                       coda_write(dev, ctx->iram_info.search_ram_paddr,
+                                       CODA7_CMD_ENC_SEQ_SEARCH_BASE);
+                       coda_write(dev, ctx->iram_info.search_ram_size,
+                                       CODA7_CMD_ENC_SEQ_SEARCH_SIZE);
+                       break;
+               case CODA_960:
+                       coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION);
+                       coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT);
+               }
+       }
+
+       ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT);
+       if (ret < 0) {
+               v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
+               goto out;
+       }
+
+       if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) {
+               v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n");
+               ret = -EFAULT;
+               goto out;
+       }
+
+       if (dev->devtype->product == CODA_960)
+               ctx->num_internal_frames = 4;
+       else
+               ctx->num_internal_frames = 2;
+       ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
+       if (ret < 0) {
+               v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
+               goto out;
+       }
+
+       coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
+       coda_write(dev, q_data_src->bytesperline,
+                       CODA_CMD_SET_FRAME_BUF_STRIDE);
+       if (dev->devtype->product == CODA_7541) {
+               coda_write(dev, q_data_src->bytesperline,
+                               CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
+       }
+       if (dev->devtype->product != CODA_DX6) {
+               coda_write(dev, ctx->iram_info.buf_bit_use,
+                               CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
+               coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
+                               CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
+               coda_write(dev, ctx->iram_info.buf_dbk_y_use,
+                               CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
+               coda_write(dev, ctx->iram_info.buf_dbk_c_use,
+                               CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
+               coda_write(dev, ctx->iram_info.buf_ovl_use,
+                               CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
+               if (dev->devtype->product == CODA_960) {
+                       coda_write(dev, ctx->iram_info.buf_btp_use,
+                                       CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
+
+                       /* FIXME */
+                       coda_write(dev, ctx->internal_frames[2].paddr,
+                                  CODA9_CMD_SET_FRAME_SUBSAMP_A);
+                       coda_write(dev, ctx->internal_frames[3].paddr,
+                                  CODA9_CMD_SET_FRAME_SUBSAMP_B);
+               }
+       }
+
+       ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF);
+       if (ret < 0) {
+               v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n");
+               goto out;
+       }
+
+       /* Save stream headers */
+       buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+       switch (dst_fourcc) {
+       case V4L2_PIX_FMT_H264:
+               /*
+                * Get SPS in the first frame and copy it to an
+                * intermediate buffer.
+                */
+               ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS,
+                                        &ctx->vpu_header[0][0],
+                                        &ctx->vpu_header_size[0]);
+               if (ret < 0)
+                       goto out;
+
+               /*
+                * Get PPS in the first frame and copy it to an
+                * intermediate buffer.
+                */
+               ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS,
+                                        &ctx->vpu_header[1][0],
+                                        &ctx->vpu_header_size[1]);
+               if (ret < 0)
+                       goto out;
+
+               /*
+                * Length of H.264 headers is variable and thus it might not be
+                * aligned for the coda to append the encoded frame. In that is
+                * the case a filler NAL must be added to header 2.
+                */
+               ctx->vpu_header_size[2] = coda_h264_padding(
+                                       (ctx->vpu_header_size[0] +
+                                        ctx->vpu_header_size[1]),
+                                        ctx->vpu_header[2]);
+               break;
+       case V4L2_PIX_FMT_MPEG4:
+               /*
+                * Get VOS in the first frame and copy it to an
+                * intermediate buffer
+                */
+               ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS,
+                                        &ctx->vpu_header[0][0],
+                                        &ctx->vpu_header_size[0]);
+               if (ret < 0)
+                       goto out;
+
+               ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS,
+                                        &ctx->vpu_header[1][0],
+                                        &ctx->vpu_header_size[1]);
+               if (ret < 0)
+                       goto out;
+
+               ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL,
+                                        &ctx->vpu_header[2][0],
+                                        &ctx->vpu_header_size[2]);
+               if (ret < 0)
+                       goto out;
+               break;
+       default:
+               /* No more formats need to save headers at the moment */
+               break;
+       }
+
+out:
+       mutex_unlock(&dev->coda_mutex);
+       return ret;
+}
+
+static int coda_prepare_encode(struct coda_ctx *ctx)
+{
+       struct coda_q_data *q_data_src, *q_data_dst;
+       struct vb2_buffer *src_buf, *dst_buf;
+       struct coda_dev *dev = ctx->dev;
+       int force_ipicture;
+       int quant_param = 0;
+       u32 picture_y, picture_cb, picture_cr;
+       u32 pic_stream_buffer_addr, pic_stream_buffer_size;
+       u32 dst_fourcc;
+
+       src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+       dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+       dst_fourcc = q_data_dst->fourcc;
+
+       src_buf->v4l2_buf.sequence = ctx->osequence;
+       dst_buf->v4l2_buf.sequence = ctx->osequence;
+       ctx->osequence++;
+
+       /*
+        * Workaround coda firmware BUG that only marks the first
+        * frame as IDR. This is a problem for some decoders that can't
+        * recover when a frame is lost.
+        */
+       if (src_buf->v4l2_buf.sequence % ctx->params.gop_size) {
+               src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+               src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+       } else {
+               src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+               src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
+       }
+
+       if (dev->devtype->product == CODA_960)
+               coda_set_gdi_regs(ctx);
+
+       /*
+        * Copy headers at the beginning of the first frame for H.264 only.
+        * In MPEG4 they are already copied by the coda.
+        */
+       if (src_buf->v4l2_buf.sequence == 0) {
+               pic_stream_buffer_addr =
+                       vb2_dma_contig_plane_dma_addr(dst_buf, 0) +
+                       ctx->vpu_header_size[0] +
+                       ctx->vpu_header_size[1] +
+                       ctx->vpu_header_size[2];
+               pic_stream_buffer_size = CODA_MAX_FRAME_SIZE -
+                       ctx->vpu_header_size[0] -
+                       ctx->vpu_header_size[1] -
+                       ctx->vpu_header_size[2];
+               memcpy(vb2_plane_vaddr(dst_buf, 0),
+                      &ctx->vpu_header[0][0], ctx->vpu_header_size[0]);
+               memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0],
+                      &ctx->vpu_header[1][0], ctx->vpu_header_size[1]);
+               memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0] +
+                       ctx->vpu_header_size[1], &ctx->vpu_header[2][0],
+                       ctx->vpu_header_size[2]);
+       } else {
+               pic_stream_buffer_addr =
+                       vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+               pic_stream_buffer_size = CODA_MAX_FRAME_SIZE;
+       }
+
+       if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) {
+               force_ipicture = 1;
+               switch (dst_fourcc) {
+               case V4L2_PIX_FMT_H264:
+                       quant_param = ctx->params.h264_intra_qp;
+                       break;
+               case V4L2_PIX_FMT_MPEG4:
+                       quant_param = ctx->params.mpeg4_intra_qp;
+                       break;
+               default:
+                       v4l2_warn(&ctx->dev->v4l2_dev,
+                               "cannot set intra qp, fmt not supported\n");
+                       break;
+               }
+       } else {
+               force_ipicture = 0;
+               switch (dst_fourcc) {
+               case V4L2_PIX_FMT_H264:
+                       quant_param = ctx->params.h264_inter_qp;
+                       break;
+               case V4L2_PIX_FMT_MPEG4:
+                       quant_param = ctx->params.mpeg4_inter_qp;
+                       break;
+               default:
+                       v4l2_warn(&ctx->dev->v4l2_dev,
+                               "cannot set inter qp, fmt not supported\n");
+                       break;
+               }
+       }
+
+       /* submit */
+       coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
+                  CODA_CMD_ENC_PIC_ROT_MODE);
+       coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS);
+
+
+       picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+       switch (q_data_src->fourcc) {
+       case V4L2_PIX_FMT_YVU420:
+               /* Switch Cb and Cr for YVU420 format */
+               picture_cr = picture_y + q_data_src->bytesperline *
+                               q_data_src->height;
+               picture_cb = picture_cr + q_data_src->bytesperline / 2 *
+                               q_data_src->height / 2;
+               break;
+       case V4L2_PIX_FMT_YUV420:
+       default:
+               picture_cb = picture_y + q_data_src->bytesperline *
+                               q_data_src->height;
+               picture_cr = picture_cb + q_data_src->bytesperline / 2 *
+                               q_data_src->height / 2;
+               break;
+       }
+
+       if (dev->devtype->product == CODA_960) {
+               coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX);
+               coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE);
+               coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC);
+
+               coda_write(dev, picture_y, CODA9_CMD_ENC_PIC_SRC_ADDR_Y);
+               coda_write(dev, picture_cb, CODA9_CMD_ENC_PIC_SRC_ADDR_CB);
+               coda_write(dev, picture_cr, CODA9_CMD_ENC_PIC_SRC_ADDR_CR);
+       } else {
+               coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y);
+               coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB);
+               coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR);
+       }
+       coda_write(dev, force_ipicture << 1 & 0x2,
+                  CODA_CMD_ENC_PIC_OPTION);
+
+       coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START);
+       coda_write(dev, pic_stream_buffer_size / 1024,
+                  CODA_CMD_ENC_PIC_BB_SIZE);
+
+       if (!ctx->streamon_out) {
+               /* After streamoff on the output side, set stream end flag */
+               ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+               coda_write(dev, ctx->bit_stream_param,
+                          CODA_REG_BIT_BIT_STREAM_PARAM);
+       }
+
+       if (dev->devtype->product != CODA_DX6)
+               coda_write(dev, ctx->iram_info.axi_sram_use,
+                               CODA7_REG_BIT_AXI_SRAM_USE);
+
+       coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
+
+       return 0;
+}
+
+static void coda_finish_encode(struct coda_ctx *ctx)
+{
+       struct vb2_buffer *src_buf, *dst_buf;
+       struct coda_dev *dev = ctx->dev;
+       u32 wr_ptr, start_ptr;
+
+       src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+       dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+       /* Get results from the coda */
+       start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);
+       wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+
+       /* Calculate bytesused field */
+       if (dst_buf->v4l2_buf.sequence == 0) {
+               vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr +
+                                       ctx->vpu_header_size[0] +
+                                       ctx->vpu_header_size[1] +
+                                       ctx->vpu_header_size[2]);
+       } else {
+               vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr);
+       }
+
+       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n",
+                wr_ptr - start_ptr);
+
+       coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM);
+       coda_read(dev, CODA_RET_ENC_PIC_FLAG);
+
+       if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) {
+               dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+               dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
+       } else {
+               dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+               dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+       }
+
+       dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
+       dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+       dst_buf->v4l2_buf.flags |=
+               src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+       dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
+
+       v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+
+       dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+       v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+
+       ctx->gopcounter--;
+       if (ctx->gopcounter < 0)
+               ctx->gopcounter = ctx->params.gop_size - 1;
+
+       v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+               "job finished: encoding frame (%d) (%s)\n",
+               dst_buf->v4l2_buf.sequence,
+               (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
+               "KEYFRAME" : "PFRAME");
+}
+
+static void coda_seq_end_work(struct work_struct *work)
+{
+       struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work);
+       struct coda_dev *dev = ctx->dev;
+
+       mutex_lock(&ctx->buffer_mutex);
+       mutex_lock(&dev->coda_mutex);
+
+       v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+                "%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx,
+                __func__);
+       if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) {
+               v4l2_err(&dev->v4l2_dev,
+                        "CODA_COMMAND_SEQ_END failed\n");
+       }
+
+       kfifo_init(&ctx->bitstream_fifo,
+               ctx->bitstream.vaddr, ctx->bitstream.size);
+
+       coda_free_framebuffers(ctx);
+       coda_free_context_buffers(ctx);
+
+       mutex_unlock(&dev->coda_mutex);
+       mutex_unlock(&ctx->buffer_mutex);
+}
+
+static void coda_bit_release(struct coda_ctx *ctx)
+{
+       coda_free_framebuffers(ctx);
+       coda_free_context_buffers(ctx);
+}
+
+const struct coda_context_ops coda_bit_encode_ops = {
+       .queue_init = coda_encoder_queue_init,
+       .start_streaming = coda_start_encoding,
+       .prepare_run = coda_prepare_encode,
+       .finish_run = coda_finish_encode,
+       .seq_end_work = coda_seq_end_work,
+       .release = coda_bit_release,
+};
+
+/*
+ * Decoder context operations
+ */
+
+static int __coda_start_decoding(struct coda_ctx *ctx)
+{
+       struct coda_q_data *q_data_src, *q_data_dst;
+       u32 bitstream_buf, bitstream_size;
+       struct coda_dev *dev = ctx->dev;
+       int width, height;
+       u32 src_fourcc;
+       u32 val;
+       int ret;
+
+       /* Start decoding */
+       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+       bitstream_buf = ctx->bitstream.paddr;
+       bitstream_size = ctx->bitstream.size;
+       src_fourcc = q_data_src->fourcc;
+
+       /* Allocate per-instance buffers */
+       ret = coda_alloc_context_buffers(ctx, q_data_src);
+       if (ret < 0)
+               return ret;
+
+       coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
+
+       /* Update coda bitstream read and write pointers from kfifo */
+       coda_kfifo_sync_to_device_full(ctx);
+
+       ctx->display_idx = -1;
+       ctx->frm_dis_flg = 0;
+       coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+
+       coda_write(dev, CODA_BIT_DEC_SEQ_INIT_ESCAPE,
+                       CODA_REG_BIT_BIT_STREAM_PARAM);
+
+       coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START);
+       coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE);
+       val = 0;
+       if ((dev->devtype->product == CODA_7541) ||
+           (dev->devtype->product == CODA_960))
+               val |= CODA_REORDER_ENABLE;
+       coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION);
+
+       ctx->params.codec_mode = ctx->codec->mode;
+       if (dev->devtype->product == CODA_960 &&
+           src_fourcc == V4L2_PIX_FMT_MPEG4)
+               ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4;
+       else
+               ctx->params.codec_mode_aux = 0;
+       if (src_fourcc == V4L2_PIX_FMT_H264) {
+               if (dev->devtype->product == CODA_7541) {
+                       coda_write(dev, ctx->psbuf.paddr,
+                                       CODA_CMD_DEC_SEQ_PS_BB_START);
+                       coda_write(dev, (CODA7_PS_BUF_SIZE / 1024),
+                                       CODA_CMD_DEC_SEQ_PS_BB_SIZE);
+               }
+               if (dev->devtype->product == CODA_960) {
+                       coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN);
+                       coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE);
+               }
+       }
+       if (dev->devtype->product != CODA_960)
+               coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE);
+
+       if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) {
+               v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
+               coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+               return -ETIMEDOUT;
+       }
+
+       /* Update kfifo out pointer from coda bitstream read pointer */
+       coda_kfifo_sync_from_device(ctx);
+
+       coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+
+       if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) {
+               v4l2_err(&dev->v4l2_dev,
+                       "CODA_COMMAND_SEQ_INIT failed, error code = %d\n",
+                       coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON));
+               return -EAGAIN;
+       }
+
+       val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE);
+       if (dev->devtype->product == CODA_DX6) {
+               width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK;
+               height = val & CODADX6_PICHEIGHT_MASK;
+       } else {
+               width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK;
+               height = val & CODA7_PICHEIGHT_MASK;
+       }
+
+       if (width > q_data_dst->width || height > q_data_dst->height) {
+               v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n",
+                        width, height, q_data_dst->width, q_data_dst->height);
+               return -EINVAL;
+       }
+
+       width = round_up(width, 16);
+       height = round_up(height, 16);
+
+       v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s instance %d now: %dx%d\n",
+                __func__, ctx->idx, width, height);
+
+       ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED);
+       if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) {
+               v4l2_err(&dev->v4l2_dev,
+                        "not enough framebuffers to decode (%d < %d)\n",
+                        CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames);
+               return -EINVAL;
+       }
+
+       if (src_fourcc == V4L2_PIX_FMT_H264) {
+               u32 left_right;
+               u32 top_bottom;
+
+               left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT);
+               top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM);
+
+               q_data_dst->rect.left = (left_right >> 10) & 0x3ff;
+               q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff;
+               q_data_dst->rect.width = width - q_data_dst->rect.left -
+                                        (left_right & 0x3ff);
+               q_data_dst->rect.height = height - q_data_dst->rect.top -
+                                         (top_bottom & 0x3ff);
+       }
+
+       ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc);
+       if (ret < 0) {
+               v4l2_err(&dev->v4l2_dev, "failed to allocate framebuffers\n");
+               return ret;
+       }
+
+       /* Tell the decoder how many frame buffers we allocated. */
+       coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
+       coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE);
+
+       if (dev->devtype->product != CODA_DX6) {
+               /* Set secondary AXI IRAM */
+               coda_setup_iram(ctx);
+
+               coda_write(dev, ctx->iram_info.buf_bit_use,
+                               CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
+               coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
+                               CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
+               coda_write(dev, ctx->iram_info.buf_dbk_y_use,
+                               CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
+               coda_write(dev, ctx->iram_info.buf_dbk_c_use,
+                               CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
+               coda_write(dev, ctx->iram_info.buf_ovl_use,
+                               CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
+               if (dev->devtype->product == CODA_960)
+                       coda_write(dev, ctx->iram_info.buf_btp_use,
+                                       CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
+       }
+
+       if (dev->devtype->product == CODA_960) {
+               coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY);
+
+               coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE);
+               coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET |
+                               32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET |
+                               8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET |
+                               8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET,
+                               CODA9_CMD_SET_FRAME_CACHE_CONFIG);
+       }
+
+       if (src_fourcc == V4L2_PIX_FMT_H264) {
+               coda_write(dev, ctx->slicebuf.paddr,
+                               CODA_CMD_SET_FRAME_SLICE_BB_START);
+               coda_write(dev, ctx->slicebuf.size / 1024,
+                               CODA_CMD_SET_FRAME_SLICE_BB_SIZE);
+       }
+
+       if (dev->devtype->product == CODA_7541) {
+               int max_mb_x = 1920 / 16;
+               int max_mb_y = 1088 / 16;
+               int max_mb_num = max_mb_x * max_mb_y;
+
+               coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
+                               CODA7_CMD_SET_FRAME_MAX_DEC_SIZE);
+       } else if (dev->devtype->product == CODA_960) {
+               int max_mb_x = 1920 / 16;
+               int max_mb_y = 1088 / 16;
+               int max_mb_num = max_mb_x * max_mb_y;
+
+               coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
+                               CODA9_CMD_SET_FRAME_MAX_DEC_SIZE);
+       }
+
+       if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) {
+               v4l2_err(&ctx->dev->v4l2_dev,
+                        "CODA_COMMAND_SET_FRAME_BUF timeout\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int coda_start_decoding(struct coda_ctx *ctx)
+{
+       struct coda_dev *dev = ctx->dev;
+       int ret;
+
+       mutex_lock(&dev->coda_mutex);
+       ret = __coda_start_decoding(ctx);
+       mutex_unlock(&dev->coda_mutex);
+
+       return ret;
+}
+
+static int coda_prepare_decode(struct coda_ctx *ctx)
+{
+       struct vb2_buffer *dst_buf;
+       struct coda_dev *dev = ctx->dev;
+       struct coda_q_data *q_data_dst;
+       u32 stridey, height;
+       u32 picture_y, picture_cb, picture_cr;
+
+       dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+       if (ctx->params.rot_mode & CODA_ROT_90) {
+               stridey = q_data_dst->height;
+               height = q_data_dst->width;
+       } else {
+               stridey = q_data_dst->width;
+               height = q_data_dst->height;
+       }
+
+       /* Try to copy source buffer contents into the bitstream ringbuffer */
+       mutex_lock(&ctx->bitstream_mutex);
+       coda_fill_bitstream(ctx);
+       mutex_unlock(&ctx->bitstream_mutex);
+
+       if (coda_get_bitstream_payload(ctx) < 512 &&
+           (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
+               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+                        "bitstream payload: %d, skipping\n",
+                        coda_get_bitstream_payload(ctx));
+               v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+               return -EAGAIN;
+       }
+
+       /* Run coda_start_decoding (again) if not yet initialized */
+       if (!ctx->initialized) {
+               int ret = __coda_start_decoding(ctx);
+
+               if (ret < 0) {
+                       v4l2_err(&dev->v4l2_dev, "failed to start decoding\n");
+                       v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+                       return -EAGAIN;
+               } else {
+                       ctx->initialized = 1;
+               }
+       }
+
+       if (dev->devtype->product == CODA_960)
+               coda_set_gdi_regs(ctx);
+
+       /* Set rotator output */
+       picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+       if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) {
+               /* Switch Cr and Cb for YVU420 format */
+               picture_cr = picture_y + stridey * height;
+               picture_cb = picture_cr + stridey / 2 * height / 2;
+       } else {
+               picture_cb = picture_y + stridey * height;
+               picture_cr = picture_cb + stridey / 2 * height / 2;
+       }
+
+       if (dev->devtype->product == CODA_960) {
+               /*
+                * The CODA960 seems to have an internal list of buffers with
+                * 64 entries that includes the registered frame buffers as
+                * well as the rotator buffer output.
+                * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames.
+                */
+               coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->v4l2_buf.index,
+                               CODA9_CMD_DEC_PIC_ROT_INDEX);
+               coda_write(dev, picture_y, CODA9_CMD_DEC_PIC_ROT_ADDR_Y);
+               coda_write(dev, picture_cb, CODA9_CMD_DEC_PIC_ROT_ADDR_CB);
+               coda_write(dev, picture_cr, CODA9_CMD_DEC_PIC_ROT_ADDR_CR);
+               coda_write(dev, stridey, CODA9_CMD_DEC_PIC_ROT_STRIDE);
+       } else {
+               coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y);
+               coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB);
+               coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR);
+               coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE);
+       }
+       coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
+                       CODA_CMD_DEC_PIC_ROT_MODE);
+
+       switch (dev->devtype->product) {
+       case CODA_DX6:
+               /* TBD */
+       case CODA_7541:
+               coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION);
+               break;
+       case CODA_960:
+               /* 'hardcode to use interrupt disable mode'? */
+               coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION);
+               break;
+       }
+
+       coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM);
+
+       coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START);
+       coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE);
+
+       if (dev->devtype->product != CODA_DX6)
+               coda_write(dev, ctx->iram_info.axi_sram_use,
+                               CODA7_REG_BIT_AXI_SRAM_USE);
+
+       coda_kfifo_sync_to_device_full(ctx);
+
+       coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
+
+       return 0;
+}
+
+static void coda_finish_decode(struct coda_ctx *ctx)
+{
+       struct coda_dev *dev = ctx->dev;
+       struct coda_q_data *q_data_src;
+       struct coda_q_data *q_data_dst;
+       struct vb2_buffer *dst_buf;
+       struct coda_timestamp *ts;
+       int width, height;
+       int decoded_idx;
+       int display_idx;
+       u32 src_fourcc;
+       int success;
+       u32 err_mb;
+       u32 val;
+
+       /* Update kfifo out pointer from coda bitstream read pointer */
+       coda_kfifo_sync_from_device(ctx);
+
+       /*
+        * in stream-end mode, the read pointer can overshoot the write pointer
+        * by up to 512 bytes
+        */
+       if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) {
+               if (coda_get_bitstream_payload(ctx) >= CODA_MAX_FRAME_SIZE - 512)
+                       kfifo_init(&ctx->bitstream_fifo,
+                               ctx->bitstream.vaddr, ctx->bitstream.size);
+       }
+
+       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+       src_fourcc = q_data_src->fourcc;
+
+       val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS);
+       if (val != 1)
+               pr_err("DEC_PIC_SUCCESS = %d\n", val);
+
+       success = val & 0x1;
+       if (!success)
+               v4l2_err(&dev->v4l2_dev, "decode failed\n");
+
+       if (src_fourcc == V4L2_PIX_FMT_H264) {
+               if (val & (1 << 3))
+                       v4l2_err(&dev->v4l2_dev,
+                                "insufficient PS buffer space (%d bytes)\n",
+                                ctx->psbuf.size);
+               if (val & (1 << 2))
+                       v4l2_err(&dev->v4l2_dev,
+                                "insufficient slice buffer space (%d bytes)\n",
+                                ctx->slicebuf.size);
+       }
+
+       val = coda_read(dev, CODA_RET_DEC_PIC_SIZE);
+       width = (val >> 16) & 0xffff;
+       height = val & 0xffff;
+
+       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+       /* frame crop information */
+       if (src_fourcc == V4L2_PIX_FMT_H264) {
+               u32 left_right;
+               u32 top_bottom;
+
+               left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT);
+               top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM);
+
+               if (left_right == 0xffffffff && top_bottom == 0xffffffff) {
+                       /* Keep current crop information */
+               } else {
+                       struct v4l2_rect *rect = &q_data_dst->rect;
+
+                       rect->left = left_right >> 16 & 0xffff;
+                       rect->top = top_bottom >> 16 & 0xffff;
+                       rect->width = width - rect->left -
+                                     (left_right & 0xffff);
+                       rect->height = height - rect->top -
+                                      (top_bottom & 0xffff);
+               }
+       } else {
+               /* no cropping */
+       }
+
+       err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB);
+       if (err_mb > 0)
+               v4l2_err(&dev->v4l2_dev,
+                        "errors in %d macroblocks\n", err_mb);
+
+       if (dev->devtype->product == CODA_7541) {
+               val = coda_read(dev, CODA_RET_DEC_PIC_OPTION);
+               if (val == 0) {
+                       /* not enough bitstream data */
+                       v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+                                "prescan failed: %d\n", val);
+                       ctx->hold = true;
+                       return;
+               }
+       }
+
+       ctx->frm_dis_flg = coda_read(dev,
+                                    CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+
+       /*
+        * The previous display frame was copied out by the rotator,
+        * now it can be overwritten again
+        */
+       if (ctx->display_idx >= 0 &&
+           ctx->display_idx < ctx->num_internal_frames) {
+               ctx->frm_dis_flg &= ~(1 << ctx->display_idx);
+               coda_write(dev, ctx->frm_dis_flg,
+                               CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+       }
+
+       /*
+        * The index of the last decoded frame, not necessarily in
+        * display order, and the index of the next display frame.
+        * The latter could have been decoded in a previous run.
+        */
+       decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX);
+       display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX);
+
+       if (decoded_idx == -1) {
+               /* no frame was decoded, but we might have a display frame */
+               if (display_idx >= 0 && display_idx < ctx->num_internal_frames)
+                       ctx->sequence_offset++;
+               else if (ctx->display_idx < 0)
+                       ctx->hold = true;
+       } else if (decoded_idx == -2) {
+               /* no frame was decoded, we still return remaining buffers */
+       } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) {
+               v4l2_err(&dev->v4l2_dev,
+                        "decoded frame index out of range: %d\n", decoded_idx);
+       } else {
+               val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1;
+               val -= ctx->sequence_offset;
+               mutex_lock(&ctx->bitstream_mutex);
+               if (!list_empty(&ctx->timestamp_list)) {
+                       ts = list_first_entry(&ctx->timestamp_list,
+                                             struct coda_timestamp, list);
+                       list_del(&ts->list);
+                       if (val != (ts->sequence & 0xffff)) {
+                               v4l2_err(&dev->v4l2_dev,
+                                        "sequence number mismatch (%d(%d) != %d)\n",
+                                        val, ctx->sequence_offset,
+                                        ts->sequence);
+                       }
+                       ctx->frame_timestamps[decoded_idx] = *ts;
+                       kfree(ts);
+               } else {
+                       v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n");
+                       memset(&ctx->frame_timestamps[decoded_idx], 0,
+                              sizeof(struct coda_timestamp));
+                       ctx->frame_timestamps[decoded_idx].sequence = val;
+               }
+               mutex_unlock(&ctx->bitstream_mutex);
+
+               val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7;
+               if (val == 0)
+                       ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME;
+               else if (val == 1)
+                       ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_PFRAME;
+               else
+                       ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_BFRAME;
+
+               ctx->frame_errors[decoded_idx] = err_mb;
+       }
+
+       if (display_idx == -1) {
+               /*
+                * no more frames to be decoded, but there could still
+                * be rotator output to dequeue
+                */
+               ctx->hold = true;
+       } else if (display_idx == -3) {
+               /* possibly prescan failure */
+       } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) {
+               v4l2_err(&dev->v4l2_dev,
+                        "presentation frame index out of range: %d\n",
+                        display_idx);
+       }
+
+       /* If a frame was copied out, return it */
+       if (ctx->display_idx >= 0 &&
+           ctx->display_idx < ctx->num_internal_frames) {
+               dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+               dst_buf->v4l2_buf.sequence = ctx->osequence++;
+
+               dst_buf->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME |
+                                            V4L2_BUF_FLAG_PFRAME |
+                                            V4L2_BUF_FLAG_BFRAME);
+               dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx];
+               ts = &ctx->frame_timestamps[ctx->display_idx];
+               dst_buf->v4l2_buf.timecode = ts->timecode;
+               dst_buf->v4l2_buf.timestamp = ts->timestamp;
+
+               vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2);
+
+               v4l2_m2m_buf_done(dst_buf, ctx->frame_errors[display_idx] ?
+                                 VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
+               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+                       "job finished: decoding frame (%d) (%s)\n",
+                       dst_buf->v4l2_buf.sequence,
+                       (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
+                       "KEYFRAME" : "PFRAME");
+       } else {
+               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+                       "job finished: no frame decoded\n");
+       }
+
+       /* The rotator will copy the current display frame next time */
+       ctx->display_idx = display_idx;
+}
+
+const struct coda_context_ops coda_bit_decode_ops = {
+       .queue_init = coda_decoder_queue_init,
+       .start_streaming = coda_start_decoding,
+       .prepare_run = coda_prepare_decode,
+       .finish_run = coda_finish_decode,
+       .seq_end_work = coda_seq_end_work,
+       .release = coda_bit_release,
+};
+
+irqreturn_t coda_irq_handler(int irq, void *data)
+{
+       struct coda_dev *dev = data;
+       struct coda_ctx *ctx;
+
+       /* read status register to attend the IRQ */
+       coda_read(dev, CODA_REG_BIT_INT_STATUS);
+       coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET,
+                     CODA_REG_BIT_INT_CLEAR);
+
+       ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+       if (ctx == NULL) {
+               v4l2_err(&dev->v4l2_dev,
+                        "Instance released before the end of transaction\n");
+               mutex_unlock(&dev->coda_mutex);
+               return IRQ_HANDLED;
+       }
+
+       if (ctx->aborting) {
+               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                        "task has been aborted\n");
+       }
+
+       if (coda_isbusy(ctx->dev)) {
+               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                        "coda is still busy!!!!\n");
+               return IRQ_NONE;
+       }
+
+       complete(&ctx->completion);
+
+       return IRQ_HANDLED;
+}
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
new file mode 100644 (file)
index 0000000..ced4760
--- /dev/null
@@ -0,0 +1,2052 @@
+/*
+ * Coda multi-standard codec IP
+ *
+ * Copyright (C) 2012 Vista Silicon S.L.
+ *    Javier Martin, <javier.martin@vista-silicon.com>
+ *    Xavier Duret
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kfifo.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/of.h>
+#include <linux/platform_data/coda.h>
+#include <linux/reset.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "coda.h"
+
+#define CODA_NAME              "coda"
+
+#define CODADX6_MAX_INSTANCES  4
+
+#define CODA_PARA_BUF_SIZE     (10 * 1024)
+#define CODA_ISRAM_SIZE        (2048 * 2)
+
+#define MIN_W 176
+#define MIN_H 144
+
+#define S_ALIGN                1 /* multiple of 2 */
+#define W_ALIGN                1 /* multiple of 2 */
+#define H_ALIGN                1 /* multiple of 2 */
+
+#define fh_to_ctx(__fh)        container_of(__fh, struct coda_ctx, fh)
+
+int coda_debug;
+module_param(coda_debug, int, 0644);
+MODULE_PARM_DESC(coda_debug, "Debug level (0-2)");
+
+struct coda_fmt {
+       char *name;
+       u32 fourcc;
+};
+
+void coda_write(struct coda_dev *dev, u32 data, u32 reg)
+{
+       v4l2_dbg(2, coda_debug, &dev->v4l2_dev,
+                "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
+       writel(data, dev->regs_base + reg);
+}
+
+unsigned int coda_read(struct coda_dev *dev, u32 reg)
+{
+       u32 data;
+
+       data = readl(dev->regs_base + reg);
+       v4l2_dbg(2, coda_debug, &dev->v4l2_dev,
+                "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
+       return data;
+}
+
+/*
+ * Array of all formats supported by any version of Coda:
+ */
+static const struct coda_fmt coda_formats[] = {
+       {
+               .name = "YUV 4:2:0 Planar, YCbCr",
+               .fourcc = V4L2_PIX_FMT_YUV420,
+       },
+       {
+               .name = "YUV 4:2:0 Planar, YCrCb",
+               .fourcc = V4L2_PIX_FMT_YVU420,
+       },
+       {
+               .name = "H264 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_H264,
+       },
+       {
+               .name = "MPEG4 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_MPEG4,
+       },
+};
+
+#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \
+       { mode, src_fourcc, dst_fourcc, max_w, max_h }
+
+/*
+ * Arrays of codecs supported by each given version of Coda:
+ *  i.MX27 -> codadx6
+ *  i.MX5x -> coda7
+ *  i.MX6  -> coda960
+ * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants
+ */
+static const struct coda_codec codadx6_codecs[] = {
+       CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264,  720, 576),
+       CODA_CODEC(CODADX6_MODE_ENCODE_MP4,  V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576),
+};
+
+static const struct coda_codec coda7_codecs[] = {
+       CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264,   1280, 720),
+       CODA_CODEC(CODA7_MODE_ENCODE_MP4,  V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4,  1280, 720),
+       CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264,   V4L2_PIX_FMT_YUV420, 1920, 1088),
+       CODA_CODEC(CODA7_MODE_DECODE_MP4,  V4L2_PIX_FMT_MPEG4,  V4L2_PIX_FMT_YUV420, 1920, 1088),
+};
+
+static const struct coda_codec coda9_codecs[] = {
+       CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264,   1920, 1088),
+       CODA_CODEC(CODA9_MODE_ENCODE_MP4,  V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4,  1920, 1088),
+       CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264,   V4L2_PIX_FMT_YUV420, 1920, 1088),
+       CODA_CODEC(CODA9_MODE_DECODE_MP4,  V4L2_PIX_FMT_MPEG4,  V4L2_PIX_FMT_YUV420, 1920, 1088),
+};
+
+static bool coda_format_is_yuv(u32 fourcc)
+{
+       switch (fourcc) {
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_YVU420:
+               return true;
+       default:
+               return false;
+       }
+}
+
+/*
+ * Normalize all supported YUV 4:2:0 formats to the value used in the codec
+ * tables.
+ */
+static u32 coda_format_normalize_yuv(u32 fourcc)
+{
+       return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc;
+}
+
+static const struct coda_codec *coda_find_codec(struct coda_dev *dev,
+                                               int src_fourcc, int dst_fourcc)
+{
+       const struct coda_codec *codecs = dev->devtype->codecs;
+       int num_codecs = dev->devtype->num_codecs;
+       int k;
+
+       src_fourcc = coda_format_normalize_yuv(src_fourcc);
+       dst_fourcc = coda_format_normalize_yuv(dst_fourcc);
+       if (src_fourcc == dst_fourcc)
+               return NULL;
+
+       for (k = 0; k < num_codecs; k++) {
+               if (codecs[k].src_fourcc == src_fourcc &&
+                   codecs[k].dst_fourcc == dst_fourcc)
+                       break;
+       }
+
+       if (k == num_codecs)
+               return NULL;
+
+       return &codecs[k];
+}
+
+static void coda_get_max_dimensions(struct coda_dev *dev,
+                                   const struct coda_codec *codec,
+                                   int *max_w, int *max_h)
+{
+       const struct coda_codec *codecs = dev->devtype->codecs;
+       int num_codecs = dev->devtype->num_codecs;
+       unsigned int w, h;
+       int k;
+
+       if (codec) {
+               w = codec->max_w;
+               h = codec->max_h;
+       } else {
+               for (k = 0, w = 0, h = 0; k < num_codecs; k++) {
+                       w = max(w, codecs[k].max_w);
+                       h = max(h, codecs[k].max_h);
+               }
+       }
+
+       if (max_w)
+               *max_w = w;
+       if (max_h)
+               *max_h = h;
+}
+
+const char *coda_product_name(int product)
+{
+       static char buf[9];
+
+       switch (product) {
+       case CODA_DX6:
+               return "CodaDx6";
+       case CODA_7541:
+               return "CODA7541";
+       case CODA_960:
+               return "CODA960";
+       default:
+               snprintf(buf, sizeof(buf), "(0x%04x)", product);
+               return buf;
+       }
+}
+
+/*
+ * V4L2 ioctl() operations.
+ */
+static int coda_querycap(struct file *file, void *priv,
+                        struct v4l2_capability *cap)
+{
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+
+       strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver));
+       strlcpy(cap->card, coda_product_name(ctx->dev->devtype->product),
+               sizeof(cap->card));
+       strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info));
+       cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+       return 0;
+}
+
+static int coda_enum_fmt(struct file *file, void *priv,
+                        struct v4l2_fmtdesc *f)
+{
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+       const struct coda_codec *codecs = ctx->dev->devtype->codecs;
+       const struct coda_fmt *formats = coda_formats;
+       const struct coda_fmt *fmt;
+       int num_codecs = ctx->dev->devtype->num_codecs;
+       int num_formats = ARRAY_SIZE(coda_formats);
+       int i, k, num = 0;
+       bool yuv;
+
+       if (ctx->inst_type == CODA_INST_ENCODER)
+               yuv = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
+       else
+               yuv = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+       for (i = 0; i < num_formats; i++) {
+               /* Skip either raw or compressed formats */
+               if (yuv != coda_format_is_yuv(formats[i].fourcc))
+                       continue;
+               /* All uncompressed formats are always supported */
+               if (yuv) {
+                       if (num == f->index)
+                               break;
+                       ++num;
+                       continue;
+               }
+               /* Compressed formats may be supported, check the codec list */
+               for (k = 0; k < num_codecs; k++) {
+                       if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+                           formats[i].fourcc == codecs[k].dst_fourcc)
+                               break;
+                       if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+                           formats[i].fourcc == codecs[k].src_fourcc)
+                               break;
+               }
+               if (k < num_codecs) {
+                       if (num == f->index)
+                               break;
+                       ++num;
+               }
+       }
+
+       if (i < num_formats) {
+               fmt = &formats[i];
+               strlcpy(f->description, fmt->name, sizeof(f->description));
+               f->pixelformat = fmt->fourcc;
+               if (!yuv)
+                       f->flags |= V4L2_FMT_FLAG_COMPRESSED;
+               return 0;
+       }
+
+       /* Format not found */
+       return -EINVAL;
+}
+
+static int coda_g_fmt(struct file *file, void *priv,
+                     struct v4l2_format *f)
+{
+       struct coda_q_data *q_data;
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+
+       q_data = get_q_data(ctx, f->type);
+       if (!q_data)
+               return -EINVAL;
+
+       f->fmt.pix.field        = V4L2_FIELD_NONE;
+       f->fmt.pix.pixelformat  = q_data->fourcc;
+       f->fmt.pix.width        = q_data->width;
+       f->fmt.pix.height       = q_data->height;
+       f->fmt.pix.bytesperline = q_data->bytesperline;
+
+       f->fmt.pix.sizeimage    = q_data->sizeimage;
+       f->fmt.pix.colorspace   = ctx->colorspace;
+
+       return 0;
+}
+
+static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec,
+                       struct v4l2_format *f)
+{
+       struct coda_dev *dev = ctx->dev;
+       struct coda_q_data *q_data;
+       unsigned int max_w, max_h;
+       enum v4l2_field field;
+
+       field = f->fmt.pix.field;
+       if (field == V4L2_FIELD_ANY)
+               field = V4L2_FIELD_NONE;
+       else if (V4L2_FIELD_NONE != field)
+               return -EINVAL;
+
+       /* V4L2 specification suggests the driver corrects the format struct
+        * if any of the dimensions is unsupported */
+       f->fmt.pix.field = field;
+
+       coda_get_max_dimensions(dev, codec, &max_w, &max_h);
+       v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN,
+                             &f->fmt.pix.height, MIN_H, max_h, H_ALIGN,
+                             S_ALIGN);
+
+       switch (f->fmt.pix.pixelformat) {
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_YVU420:
+       case V4L2_PIX_FMT_H264:
+       case V4L2_PIX_FMT_MPEG4:
+       case V4L2_PIX_FMT_JPEG:
+               break;
+       default:
+               q_data = get_q_data(ctx, f->type);
+               if (!q_data)
+                       return -EINVAL;
+               f->fmt.pix.pixelformat = q_data->fourcc;
+       }
+
+       switch (f->fmt.pix.pixelformat) {
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_YVU420:
+               /* Frame stride must be multiple of 8, but 16 for h.264 */
+               f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
+               f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+                                       f->fmt.pix.height * 3 / 2;
+               break;
+       case V4L2_PIX_FMT_H264:
+       case V4L2_PIX_FMT_MPEG4:
+       case V4L2_PIX_FMT_JPEG:
+               f->fmt.pix.bytesperline = 0;
+               f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE;
+               break;
+       default:
+               BUG();
+       }
+
+       return 0;
+}
+
+static int coda_try_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+       const struct coda_codec *codec = NULL;
+       struct vb2_queue *src_vq;
+       int ret;
+
+       /*
+        * If the source format is already fixed, try to find a codec that
+        * converts to the given destination format
+        */
+       src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+       if (vb2_is_streaming(src_vq)) {
+               struct coda_q_data *q_data_src;
+
+               q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+               codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
+                                       f->fmt.pix.pixelformat);
+               if (!codec)
+                       return -EINVAL;
+
+               f->fmt.pix.width = q_data_src->width;
+               f->fmt.pix.height = q_data_src->height;
+       } else {
+               /* Otherwise determine codec by encoded format, if possible */
+               codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
+                                       f->fmt.pix.pixelformat);
+       }
+
+       f->fmt.pix.colorspace = ctx->colorspace;
+
+       ret = coda_try_fmt(ctx, codec, f);
+       if (ret < 0)
+               return ret;
+
+       /* The h.264 decoder only returns complete 16x16 macroblocks */
+       if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) {
+               f->fmt.pix.width = f->fmt.pix.width;
+               f->fmt.pix.height = round_up(f->fmt.pix.height, 16);
+               f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
+               f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+                                      f->fmt.pix.height * 3 / 2;
+       }
+
+       return 0;
+}
+
+static int coda_try_fmt_vid_out(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+       const struct coda_codec *codec = NULL;
+
+       /* Determine codec by encoded format, returns NULL if raw or invalid */
+       if (ctx->inst_type == CODA_INST_DECODER) {
+               codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat,
+                                       V4L2_PIX_FMT_YUV420);
+               if (!codec)
+                       codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_H264,
+                                               V4L2_PIX_FMT_YUV420);
+               if (!codec)
+                       return -EINVAL;
+       }
+
+       if (!f->fmt.pix.colorspace)
+               f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+
+       return coda_try_fmt(ctx, codec, f);
+}
+
+static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
+{
+       struct coda_q_data *q_data;
+       struct vb2_queue *vq;
+
+       vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+       if (!vq)
+               return -EINVAL;
+
+       q_data = get_q_data(ctx, f->type);
+       if (!q_data)
+               return -EINVAL;
+
+       if (vb2_is_busy(vq)) {
+               v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+               return -EBUSY;
+       }
+
+       q_data->fourcc = f->fmt.pix.pixelformat;
+       q_data->width = f->fmt.pix.width;
+       q_data->height = f->fmt.pix.height;
+       q_data->bytesperline = f->fmt.pix.bytesperline;
+       q_data->sizeimage = f->fmt.pix.sizeimage;
+       q_data->rect.left = 0;
+       q_data->rect.top = 0;
+       q_data->rect.width = f->fmt.pix.width;
+       q_data->rect.height = f->fmt.pix.height;
+
+       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+               "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
+               f->type, q_data->width, q_data->height, q_data->fourcc);
+
+       return 0;
+}
+
+static int coda_s_fmt_vid_cap(struct file *file, void *priv,
+                             struct v4l2_format *f)
+{
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+
+       ret = coda_try_fmt_vid_cap(file, priv, f);
+       if (ret)
+               return ret;
+
+       return coda_s_fmt(ctx, f);
+}
+
+static int coda_s_fmt_vid_out(struct file *file, void *priv,
+                             struct v4l2_format *f)
+{
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+       struct v4l2_format f_cap;
+       int ret;
+
+       ret = coda_try_fmt_vid_out(file, priv, f);
+       if (ret)
+               return ret;
+
+       ret = coda_s_fmt(ctx, f);
+       if (ret)
+               return ret;
+
+       ctx->colorspace = f->fmt.pix.colorspace;
+
+       f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       coda_g_fmt(file, priv, &f_cap);
+       f_cap.fmt.pix.width = f->fmt.pix.width;
+       f_cap.fmt.pix.height = f->fmt.pix.height;
+
+       ret = coda_try_fmt_vid_cap(file, priv, &f_cap);
+       if (ret)
+               return ret;
+
+       return coda_s_fmt(ctx, &f_cap);
+}
+
+static int coda_qbuf(struct file *file, void *priv,
+                    struct v4l2_buffer *buf)
+{
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+
+       return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
+}
+
+static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx,
+                                     struct v4l2_buffer *buf)
+{
+       struct vb2_queue *src_vq;
+
+       src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+       return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) &&
+               (buf->sequence == (ctx->qsequence - 1)));
+}
+
+static int coda_dqbuf(struct file *file, void *priv,
+                     struct v4l2_buffer *buf)
+{
+       struct coda_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+
+       ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf);
+
+       /* If this is the last capture buffer, emit an end-of-stream event */
+       if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           coda_buf_is_end_of_stream(ctx, buf)) {
+               const struct v4l2_event eos_event = {
+                       .type = V4L2_EVENT_EOS
+               };
+
+               v4l2_event_queue_fh(&ctx->fh, &eos_event);
+       }
+
+       return ret;
+}
+
+static int coda_g_selection(struct file *file, void *fh,
+                           struct v4l2_selection *s)
+{
+       struct coda_ctx *ctx = fh_to_ctx(fh);
+       struct coda_q_data *q_data;
+       struct v4l2_rect r, *rsel;
+
+       q_data = get_q_data(ctx, s->type);
+       if (!q_data)
+               return -EINVAL;
+
+       r.left = 0;
+       r.top = 0;
+       r.width = q_data->width;
+       r.height = q_data->height;
+       rsel = &q_data->rect;
+
+       switch (s->target) {
+       case V4L2_SEL_TGT_CROP_DEFAULT:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
+               rsel = &r;
+               /* fallthrough */
+       case V4L2_SEL_TGT_CROP:
+               if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+                       return -EINVAL;
+               break;
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+       case V4L2_SEL_TGT_COMPOSE_PADDED:
+               rsel = &r;
+               /* fallthrough */
+       case V4L2_SEL_TGT_COMPOSE:
+       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+               if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       s->r = *rsel;
+
+       return 0;
+}
+
+static int coda_try_decoder_cmd(struct file *file, void *fh,
+                               struct v4l2_decoder_cmd *dc)
+{
+       if (dc->cmd != V4L2_DEC_CMD_STOP)
+               return -EINVAL;
+
+       if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK)
+               return -EINVAL;
+
+       if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int coda_decoder_cmd(struct file *file, void *fh,
+                           struct v4l2_decoder_cmd *dc)
+{
+       struct coda_ctx *ctx = fh_to_ctx(fh);
+       int ret;
+
+       ret = coda_try_decoder_cmd(file, fh, dc);
+       if (ret < 0)
+               return ret;
+
+       /* Ignore decoder stop command silently in encoder context */
+       if (ctx->inst_type != CODA_INST_DECODER)
+               return 0;
+
+       /* Set the stream-end flag on this context */
+       coda_bit_stream_end_flag(ctx);
+       ctx->hold = false;
+       v4l2_m2m_try_schedule(ctx->fh.m2m_ctx);
+
+       return 0;
+}
+
+static int coda_subscribe_event(struct v4l2_fh *fh,
+                               const struct v4l2_event_subscription *sub)
+{
+       switch (sub->type) {
+       case V4L2_EVENT_EOS:
+               return v4l2_event_subscribe(fh, sub, 0, NULL);
+       default:
+               return v4l2_ctrl_subscribe_event(fh, sub);
+       }
+}
+
+static const struct v4l2_ioctl_ops coda_ioctl_ops = {
+       .vidioc_querycap        = coda_querycap,
+
+       .vidioc_enum_fmt_vid_cap = coda_enum_fmt,
+       .vidioc_g_fmt_vid_cap   = coda_g_fmt,
+       .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap   = coda_s_fmt_vid_cap,
+
+       .vidioc_enum_fmt_vid_out = coda_enum_fmt,
+       .vidioc_g_fmt_vid_out   = coda_g_fmt,
+       .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out,
+       .vidioc_s_fmt_vid_out   = coda_s_fmt_vid_out,
+
+       .vidioc_reqbufs         = v4l2_m2m_ioctl_reqbufs,
+       .vidioc_querybuf        = v4l2_m2m_ioctl_querybuf,
+
+       .vidioc_qbuf            = coda_qbuf,
+       .vidioc_expbuf          = v4l2_m2m_ioctl_expbuf,
+       .vidioc_dqbuf           = coda_dqbuf,
+       .vidioc_create_bufs     = v4l2_m2m_ioctl_create_bufs,
+
+       .vidioc_streamon        = v4l2_m2m_ioctl_streamon,
+       .vidioc_streamoff       = v4l2_m2m_ioctl_streamoff,
+
+       .vidioc_g_selection     = coda_g_selection,
+
+       .vidioc_try_decoder_cmd = coda_try_decoder_cmd,
+       .vidioc_decoder_cmd     = coda_decoder_cmd,
+
+       .vidioc_subscribe_event = coda_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+void coda_set_gdi_regs(struct coda_ctx *ctx)
+{
+       struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
+       struct coda_dev *dev = ctx->dev;
+       int i;
+
+       for (i = 0; i < 16; i++)
+               coda_write(dev, tiled_map->xy2ca_map[i],
+                               CODA9_GDI_XY2_CAS_0 + 4 * i);
+       for (i = 0; i < 4; i++)
+               coda_write(dev, tiled_map->xy2ba_map[i],
+                               CODA9_GDI_XY2_BA_0 + 4 * i);
+       for (i = 0; i < 16; i++)
+               coda_write(dev, tiled_map->xy2ra_map[i],
+                               CODA9_GDI_XY2_RAS_0 + 4 * i);
+       coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG);
+       for (i = 0; i < 32; i++)
+               coda_write(dev, tiled_map->rbc2axi_map[i],
+                               CODA9_GDI_RBC2_AXI_0 + 4 * i);
+}
+
+/*
+ * Mem-to-mem operations.
+ */
+
+static void coda_device_run(void *m2m_priv)
+{
+       struct coda_ctx *ctx = m2m_priv;
+       struct coda_dev *dev = ctx->dev;
+
+       queue_work(dev->workqueue, &ctx->pic_run_work);
+}
+
+static void coda_pic_run_work(struct work_struct *work)
+{
+       struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work);
+       struct coda_dev *dev = ctx->dev;
+       int ret;
+
+       mutex_lock(&ctx->buffer_mutex);
+       mutex_lock(&dev->coda_mutex);
+
+       ret = ctx->ops->prepare_run(ctx);
+       if (ret < 0 && ctx->inst_type == CODA_INST_DECODER) {
+               mutex_unlock(&dev->coda_mutex);
+               mutex_unlock(&ctx->buffer_mutex);
+               /* job_finish scheduled by prepare_decode */
+               return;
+       }
+
+       if (!wait_for_completion_timeout(&ctx->completion,
+                                        msecs_to_jiffies(1000))) {
+               dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n");
+
+               ctx->hold = true;
+
+               coda_hw_reset(ctx);
+       } else if (!ctx->aborting) {
+               ctx->ops->finish_run(ctx);
+       }
+
+       if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out))
+               queue_work(dev->workqueue, &ctx->seq_end_work);
+
+       mutex_unlock(&dev->coda_mutex);
+       mutex_unlock(&ctx->buffer_mutex);
+
+       v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+static int coda_job_ready(void *m2m_priv)
+{
+       struct coda_ctx *ctx = m2m_priv;
+
+       /*
+        * For both 'P' and 'key' frame cases 1 picture
+        * and 1 frame are needed. In the decoder case,
+        * the compressed frame can be in the bitstream.
+        */
+       if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) &&
+           ctx->inst_type != CODA_INST_DECODER) {
+               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                        "not ready: not enough video buffers.\n");
+               return 0;
+       }
+
+       if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
+               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                        "not ready: not enough video capture buffers.\n");
+               return 0;
+       }
+
+       if (ctx->hold ||
+           ((ctx->inst_type == CODA_INST_DECODER) &&
+            (coda_get_bitstream_payload(ctx) < 512) &&
+            !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
+               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                        "%d: not ready: not enough bitstream data.\n",
+                        ctx->idx);
+               return 0;
+       }
+
+       if (ctx->aborting) {
+               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                        "not ready: aborting\n");
+               return 0;
+       }
+
+       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                       "job ready\n");
+       return 1;
+}
+
+static void coda_job_abort(void *priv)
+{
+       struct coda_ctx *ctx = priv;
+
+       ctx->aborting = 1;
+
+       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                "Aborting task\n");
+}
+
+static void coda_lock(void *m2m_priv)
+{
+       struct coda_ctx *ctx = m2m_priv;
+       struct coda_dev *pcdev = ctx->dev;
+
+       mutex_lock(&pcdev->dev_mutex);
+}
+
+static void coda_unlock(void *m2m_priv)
+{
+       struct coda_ctx *ctx = m2m_priv;
+       struct coda_dev *pcdev = ctx->dev;
+
+       mutex_unlock(&pcdev->dev_mutex);
+}
+
+static const struct v4l2_m2m_ops coda_m2m_ops = {
+       .device_run     = coda_device_run,
+       .job_ready      = coda_job_ready,
+       .job_abort      = coda_job_abort,
+       .lock           = coda_lock,
+       .unlock         = coda_unlock,
+};
+
+static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type)
+{
+       struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
+       int luma_map, chro_map, i;
+
+       memset(tiled_map, 0, sizeof(*tiled_map));
+
+       luma_map = 64;
+       chro_map = 64;
+       tiled_map->map_type = tiled_map_type;
+       for (i = 0; i < 16; i++)
+               tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map;
+       for (i = 0; i < 4; i++)
+               tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map;
+       for (i = 0; i < 16; i++)
+               tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map;
+
+       if (tiled_map_type == GDI_LINEAR_FRAME_MAP) {
+               tiled_map->xy2rbc_config = 0;
+       } else {
+               dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n",
+                       tiled_map_type);
+               return;
+       }
+}
+
+static void set_default_params(struct coda_ctx *ctx)
+{
+       u32 src_fourcc, dst_fourcc;
+       int max_w;
+       int max_h;
+
+       if (ctx->inst_type == CODA_INST_ENCODER) {
+               src_fourcc = V4L2_PIX_FMT_YUV420;
+               dst_fourcc = V4L2_PIX_FMT_H264;
+       } else {
+               src_fourcc = V4L2_PIX_FMT_H264;
+               dst_fourcc = V4L2_PIX_FMT_YUV420;
+       }
+       ctx->codec = coda_find_codec(ctx->dev, src_fourcc, dst_fourcc);
+       max_w = ctx->codec->max_w;
+       max_h = ctx->codec->max_h;
+
+       ctx->params.codec_mode = ctx->codec->mode;
+       ctx->colorspace = V4L2_COLORSPACE_REC709;
+       ctx->params.framerate = 30;
+       ctx->aborting = 0;
+
+       /* Default formats for output and input queues */
+       ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc;
+       ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc;
+       ctx->q_data[V4L2_M2M_SRC].width = max_w;
+       ctx->q_data[V4L2_M2M_SRC].height = max_h;
+       ctx->q_data[V4L2_M2M_DST].width = max_w;
+       ctx->q_data[V4L2_M2M_DST].height = max_h;
+       if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) {
+               ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w;
+               ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2;
+               ctx->q_data[V4L2_M2M_DST].bytesperline = 0;
+               ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE;
+       } else {
+               ctx->q_data[V4L2_M2M_SRC].bytesperline = 0;
+               ctx->q_data[V4L2_M2M_SRC].sizeimage = CODA_MAX_FRAME_SIZE;
+               ctx->q_data[V4L2_M2M_DST].bytesperline = max_w;
+               ctx->q_data[V4L2_M2M_DST].sizeimage = (max_w * max_h * 3) / 2;
+       }
+       ctx->q_data[V4L2_M2M_SRC].rect.width = max_w;
+       ctx->q_data[V4L2_M2M_SRC].rect.height = max_h;
+       ctx->q_data[V4L2_M2M_DST].rect.width = max_w;
+       ctx->q_data[V4L2_M2M_DST].rect.height = max_h;
+
+       if (ctx->dev->devtype->product == CODA_960)
+               coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP);
+}
+
+/*
+ * Queue operations
+ */
+static int coda_queue_setup(struct vb2_queue *vq,
+                               const struct v4l2_format *fmt,
+                               unsigned int *nbuffers, unsigned int *nplanes,
+                               unsigned int sizes[], void *alloc_ctxs[])
+{
+       struct coda_ctx *ctx = vb2_get_drv_priv(vq);
+       struct coda_q_data *q_data;
+       unsigned int size;
+
+       q_data = get_q_data(ctx, vq->type);
+       size = q_data->sizeimage;
+
+       *nplanes = 1;
+       sizes[0] = size;
+
+       alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                "get %d buffer(s) of size %d each.\n", *nbuffers, size);
+
+       return 0;
+}
+
+static int coda_buf_prepare(struct vb2_buffer *vb)
+{
+       struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+       struct coda_q_data *q_data;
+
+       q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+       if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+               v4l2_warn(&ctx->dev->v4l2_dev,
+                         "%s data will not fit into plane (%lu < %lu)\n",
+                         __func__, vb2_plane_size(vb, 0),
+                         (long)q_data->sizeimage);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void coda_buf_queue(struct vb2_buffer *vb)
+{
+       struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+       struct coda_q_data *q_data;
+
+       q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+       /*
+        * In the decoder case, immediately try to copy the buffer into the
+        * bitstream ringbuffer and mark it as ready to be dequeued.
+        */
+       if (q_data->fourcc == V4L2_PIX_FMT_H264 &&
+           vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               /*
+                * For backwards compatibility, queuing an empty buffer marks
+                * the stream end
+                */
+               if (vb2_get_plane_payload(vb, 0) == 0)
+                       coda_bit_stream_end_flag(ctx);
+               mutex_lock(&ctx->bitstream_mutex);
+               v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
+               if (vb2_is_streaming(vb->vb2_queue))
+                       coda_fill_bitstream(ctx);
+               mutex_unlock(&ctx->bitstream_mutex);
+       } else {
+               v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
+       }
+}
+
+int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf,
+                      size_t size, const char *name, struct dentry *parent)
+{
+       buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr,
+                                       GFP_KERNEL);
+       if (!buf->vaddr) {
+               v4l2_err(&dev->v4l2_dev,
+                        "Failed to allocate %s buffer of size %u\n",
+                        name, size);
+               return -ENOMEM;
+       }
+
+       buf->size = size;
+
+       if (name && parent) {
+               buf->blob.data = buf->vaddr;
+               buf->blob.size = size;
+               buf->dentry = debugfs_create_blob(name, 0644, parent,
+                                                 &buf->blob);
+               if (!buf->dentry)
+                       dev_warn(&dev->plat_dev->dev,
+                                "failed to create debugfs entry %s\n", name);
+       }
+
+       return 0;
+}
+
+void coda_free_aux_buf(struct coda_dev *dev,
+                      struct coda_aux_buf *buf)
+{
+       if (buf->vaddr) {
+               dma_free_coherent(&dev->plat_dev->dev, buf->size,
+                                 buf->vaddr, buf->paddr);
+               buf->vaddr = NULL;
+               buf->size = 0;
+       }
+       debugfs_remove(buf->dentry);
+}
+
+static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct coda_ctx *ctx = vb2_get_drv_priv(q);
+       struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev;
+       struct coda_q_data *q_data_src, *q_data_dst;
+       struct vb2_buffer *buf;
+       u32 dst_fourcc;
+       int ret = 0;
+
+       q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+       if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               if (q_data_src->fourcc == V4L2_PIX_FMT_H264) {
+                       /* copy the buffers that where queued before streamon */
+                       mutex_lock(&ctx->bitstream_mutex);
+                       coda_fill_bitstream(ctx);
+                       mutex_unlock(&ctx->bitstream_mutex);
+
+                       if (coda_get_bitstream_payload(ctx) < 512) {
+                               ret = -EINVAL;
+                               goto err;
+                       }
+               } else {
+                       if (count < 1) {
+                               ret = -EINVAL;
+                               goto err;
+                       }
+               }
+
+               ctx->streamon_out = 1;
+       } else {
+               if (count < 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               ctx->streamon_cap = 1;
+       }
+
+       /* Don't start the coda unless both queues are on */
+       if (!(ctx->streamon_out & ctx->streamon_cap))
+               return 0;
+
+       /* Allow decoder device_run with no new buffers queued */
+       if (ctx->inst_type == CODA_INST_DECODER)
+               v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true);
+
+       ctx->gopcounter = ctx->params.gop_size - 1;
+       q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+       dst_fourcc = q_data_dst->fourcc;
+
+       ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
+                                    q_data_dst->fourcc);
+       if (!ctx->codec) {
+               v4l2_err(v4l2_dev, "couldn't tell instance type.\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = ctx->ops->start_streaming(ctx);
+       if (ctx->inst_type == CODA_INST_DECODER) {
+               if (ret == -EAGAIN)
+                       return 0;
+               else if (ret < 0)
+                       goto err;
+       }
+
+       ctx->initialized = 1;
+       return ret;
+
+err:
+       if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
+                       v4l2_m2m_buf_done(buf, VB2_BUF_STATE_DEQUEUED);
+       } else {
+               while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
+                       v4l2_m2m_buf_done(buf, VB2_BUF_STATE_DEQUEUED);
+       }
+       return ret;
+}
+
+static void coda_stop_streaming(struct vb2_queue *q)
+{
+       struct coda_ctx *ctx = vb2_get_drv_priv(q);
+       struct coda_dev *dev = ctx->dev;
+       struct vb2_buffer *buf;
+
+       if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+                        "%s: output\n", __func__);
+               ctx->streamon_out = 0;
+
+               coda_bit_stream_end_flag(ctx);
+
+               ctx->isequence = 0;
+
+               while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
+                       v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+       } else {
+               v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+                        "%s: capture\n", __func__);
+               ctx->streamon_cap = 0;
+
+               ctx->osequence = 0;
+               ctx->sequence_offset = 0;
+
+               while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
+                       v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+       }
+
+       if (!ctx->streamon_out && !ctx->streamon_cap) {
+               struct coda_timestamp *ts;
+
+               mutex_lock(&ctx->bitstream_mutex);
+               while (!list_empty(&ctx->timestamp_list)) {
+                       ts = list_first_entry(&ctx->timestamp_list,
+                                             struct coda_timestamp, list);
+                       list_del(&ts->list);
+                       kfree(ts);
+               }
+               mutex_unlock(&ctx->bitstream_mutex);
+               kfifo_init(&ctx->bitstream_fifo,
+                       ctx->bitstream.vaddr, ctx->bitstream.size);
+               ctx->runcounter = 0;
+       }
+}
+
+static const struct vb2_ops coda_qops = {
+       .queue_setup            = coda_queue_setup,
+       .buf_prepare            = coda_buf_prepare,
+       .buf_queue              = coda_buf_queue,
+       .start_streaming        = coda_start_streaming,
+       .stop_streaming         = coda_stop_streaming,
+       .wait_prepare           = vb2_ops_wait_prepare,
+       .wait_finish            = vb2_ops_wait_finish,
+};
+
+static int coda_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct coda_ctx *ctx =
+                       container_of(ctrl->handler, struct coda_ctx, ctrls);
+
+       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val);
+
+       switch (ctrl->id) {
+       case V4L2_CID_HFLIP:
+               if (ctrl->val)
+                       ctx->params.rot_mode |= CODA_MIR_HOR;
+               else
+                       ctx->params.rot_mode &= ~CODA_MIR_HOR;
+               break;
+       case V4L2_CID_VFLIP:
+               if (ctrl->val)
+                       ctx->params.rot_mode |= CODA_MIR_VER;
+               else
+                       ctx->params.rot_mode &= ~CODA_MIR_VER;
+               break;
+       case V4L2_CID_MPEG_VIDEO_BITRATE:
+               ctx->params.bitrate = ctrl->val / 1000;
+               break;
+       case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+               ctx->params.gop_size = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+               ctx->params.h264_intra_qp = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+               ctx->params.h264_inter_qp = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+               ctx->params.h264_min_qp = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+               ctx->params.h264_max_qp = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+               ctx->params.h264_deblk_alpha = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+               ctx->params.h264_deblk_beta = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+               ctx->params.h264_deblk_enabled = (ctrl->val ==
+                               V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+               ctx->params.mpeg4_intra_qp = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+               ctx->params.mpeg4_inter_qp = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+               ctx->params.slice_mode = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+               ctx->params.slice_max_mb = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+               ctx->params.slice_max_bits = ctrl->val * 8;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+               break;
+       case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+               ctx->params.intra_refresh = ctrl->val;
+               break;
+       default:
+               v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                       "Invalid control, id=%d, val=%d\n",
+                       ctrl->id, ctrl->val);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops coda_ctrl_ops = {
+       .s_ctrl = coda_s_ctrl,
+};
+
+static int coda_ctrls_setup(struct coda_ctx *ctx)
+{
+       v4l2_ctrl_handler_init(&ctx->ctrls, 9);
+
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_HFLIP, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_VFLIP, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25);
+       if (ctx->dev->devtype->product != CODA_960) {
+               v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+                       V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12);
+       }
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, 0, 15, 1, 0);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, 0, 15, 1, 0);
+       v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+               V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, 0x0,
+               V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2);
+       v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+               V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0,
+               V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1,
+               500);
+       v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+               V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+               (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE),
+               V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);
+       v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+               V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0,
+               1920 * 1088 / 256, 1, 0);
+
+       if (ctx->ctrls.error) {
+               v4l2_err(&ctx->dev->v4l2_dev,
+                       "control initialization error (%d)",
+                       ctx->ctrls.error);
+               return -EINVAL;
+       }
+
+       return v4l2_ctrl_handler_setup(&ctx->ctrls);
+}
+
+static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq)
+{
+       vq->drv_priv = ctx;
+       vq->ops = &coda_qops;
+       vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+       vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       vq->lock = &ctx->dev->dev_mutex;
+
+       return vb2_queue_init(vq);
+}
+
+int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq,
+                           struct vb2_queue *dst_vq)
+{
+       int ret;
+
+       src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+       src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+       src_vq->mem_ops = &vb2_dma_contig_memops;
+
+       ret = coda_queue_init(priv, src_vq);
+       if (ret)
+               return ret;
+
+       dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+       dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+       return coda_queue_init(priv, dst_vq);
+}
+
+int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq,
+                           struct vb2_queue *dst_vq)
+{
+       int ret;
+
+       src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+       src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+       src_vq->mem_ops = &vb2_dma_contig_memops;
+
+       ret = coda_queue_init(priv, src_vq);
+       if (ret)
+               return ret;
+
+       dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+       dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+       return coda_queue_init(priv, dst_vq);
+}
+
+static int coda_next_free_instance(struct coda_dev *dev)
+{
+       int idx = ffz(dev->instance_mask);
+
+       if ((idx < 0) ||
+           (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES))
+               return -EBUSY;
+
+       return idx;
+}
+
+static int coda_open(struct file *file, enum coda_inst_type inst_type,
+                    const struct coda_context_ops *ctx_ops)
+{
+       struct coda_dev *dev = video_drvdata(file);
+       struct coda_ctx *ctx = NULL;
+       char *name;
+       int ret;
+       int idx;
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       idx = coda_next_free_instance(dev);
+       if (idx < 0) {
+               ret = idx;
+               goto err_coda_max;
+       }
+       set_bit(idx, &dev->instance_mask);
+
+       name = kasprintf(GFP_KERNEL, "context%d", idx);
+       ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root);
+       kfree(name);
+
+       ctx->inst_type = inst_type;
+       ctx->ops = ctx_ops;
+       init_completion(&ctx->completion);
+       INIT_WORK(&ctx->pic_run_work, coda_pic_run_work);
+       INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work);
+       v4l2_fh_init(&ctx->fh, video_devdata(file));
+       file->private_data = &ctx->fh;
+       v4l2_fh_add(&ctx->fh);
+       ctx->dev = dev;
+       ctx->idx = idx;
+       switch (dev->devtype->product) {
+       case CODA_7541:
+       case CODA_960:
+               ctx->reg_idx = 0;
+               break;
+       default:
+               ctx->reg_idx = idx;
+       }
+
+       /* Power up and upload firmware if necessary */
+       ret = pm_runtime_get_sync(&dev->plat_dev->dev);
+       if (ret < 0) {
+               v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret);
+               goto err_pm_get;
+       }
+
+       ret = clk_prepare_enable(dev->clk_per);
+       if (ret)
+               goto err_clk_per;
+
+       ret = clk_prepare_enable(dev->clk_ahb);
+       if (ret)
+               goto err_clk_ahb;
+
+       set_default_params(ctx);
+       ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
+                                           ctx->ops->queue_init);
+       if (IS_ERR(ctx->fh.m2m_ctx)) {
+               ret = PTR_ERR(ctx->fh.m2m_ctx);
+
+               v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n",
+                        __func__, ret);
+               goto err_ctx_init;
+       }
+
+       ret = coda_ctrls_setup(ctx);
+       if (ret) {
+               v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n");
+               goto err_ctrls_setup;
+       }
+
+       ctx->fh.ctrl_handler = &ctx->ctrls;
+
+       ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE,
+                                    "parabuf");
+       if (ret < 0) {
+               v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf");
+               goto err_dma_alloc;
+       }
+
+       ctx->bitstream.size = CODA_MAX_FRAME_SIZE;
+       ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev,
+                       ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL);
+       if (!ctx->bitstream.vaddr) {
+               v4l2_err(&dev->v4l2_dev,
+                        "failed to allocate bitstream ringbuffer");
+               ret = -ENOMEM;
+               goto err_dma_writecombine;
+       }
+       kfifo_init(&ctx->bitstream_fifo,
+               ctx->bitstream.vaddr, ctx->bitstream.size);
+       mutex_init(&ctx->bitstream_mutex);
+       mutex_init(&ctx->buffer_mutex);
+       INIT_LIST_HEAD(&ctx->timestamp_list);
+
+       coda_lock(ctx);
+       list_add(&ctx->list, &dev->instances);
+       coda_unlock(ctx);
+
+       v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n",
+                ctx->idx, ctx);
+
+       return 0;
+
+err_dma_writecombine:
+       if (ctx->dev->devtype->product == CODA_DX6)
+               coda_free_aux_buf(dev, &ctx->workbuf);
+       coda_free_aux_buf(dev, &ctx->parabuf);
+err_dma_alloc:
+       v4l2_ctrl_handler_free(&ctx->ctrls);
+err_ctrls_setup:
+       v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+err_ctx_init:
+       clk_disable_unprepare(dev->clk_ahb);
+err_clk_ahb:
+       clk_disable_unprepare(dev->clk_per);
+err_clk_per:
+       pm_runtime_put_sync(&dev->plat_dev->dev);
+err_pm_get:
+       v4l2_fh_del(&ctx->fh);
+       v4l2_fh_exit(&ctx->fh);
+       clear_bit(ctx->idx, &dev->instance_mask);
+err_coda_max:
+       kfree(ctx);
+       return ret;
+}
+
+static int coda_encoder_open(struct file *file)
+{
+       return coda_open(file, CODA_INST_ENCODER, &coda_bit_encode_ops);
+}
+
+static int coda_decoder_open(struct file *file)
+{
+       return coda_open(file, CODA_INST_DECODER, &coda_bit_decode_ops);
+}
+
+static int coda_release(struct file *file)
+{
+       struct coda_dev *dev = video_drvdata(file);
+       struct coda_ctx *ctx = fh_to_ctx(file->private_data);
+
+       v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n",
+                ctx);
+
+       debugfs_remove_recursive(ctx->debugfs_entry);
+
+       /* If this instance is running, call .job_abort and wait for it to end */
+       v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+       /* In case the instance was not running, we still need to call SEQ_END */
+       if (ctx->initialized) {
+               queue_work(dev->workqueue, &ctx->seq_end_work);
+               flush_work(&ctx->seq_end_work);
+       }
+
+       coda_lock(ctx);
+       list_del(&ctx->list);
+       coda_unlock(ctx);
+
+       dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size,
+               ctx->bitstream.vaddr, ctx->bitstream.paddr);
+       if (ctx->dev->devtype->product == CODA_DX6)
+               coda_free_aux_buf(dev, &ctx->workbuf);
+
+       coda_free_aux_buf(dev, &ctx->parabuf);
+       v4l2_ctrl_handler_free(&ctx->ctrls);
+       clk_disable_unprepare(dev->clk_ahb);
+       clk_disable_unprepare(dev->clk_per);
+       pm_runtime_put_sync(&dev->plat_dev->dev);
+       v4l2_fh_del(&ctx->fh);
+       v4l2_fh_exit(&ctx->fh);
+       clear_bit(ctx->idx, &dev->instance_mask);
+       if (ctx->ops->release)
+               ctx->ops->release(ctx);
+       kfree(ctx);
+
+       return 0;
+}
+
+static const struct v4l2_file_operations coda_encoder_fops = {
+       .owner          = THIS_MODULE,
+       .open           = coda_encoder_open,
+       .release        = coda_release,
+       .poll           = v4l2_m2m_fop_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = v4l2_m2m_fop_mmap,
+};
+
+static const struct v4l2_file_operations coda_decoder_fops = {
+       .owner          = THIS_MODULE,
+       .open           = coda_decoder_open,
+       .release        = coda_release,
+       .poll           = v4l2_m2m_fop_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = v4l2_m2m_fop_mmap,
+};
+
+static int coda_hw_init(struct coda_dev *dev)
+{
+       u32 data;
+       u16 *p;
+       int i, ret;
+
+       ret = clk_prepare_enable(dev->clk_per);
+       if (ret)
+               goto err_clk_per;
+
+       ret = clk_prepare_enable(dev->clk_ahb);
+       if (ret)
+               goto err_clk_ahb;
+
+       if (dev->rstc)
+               reset_control_reset(dev->rstc);
+
+       /*
+        * Copy the first CODA_ISRAM_SIZE in the internal SRAM.
+        * The 16-bit chars in the code buffer are in memory access
+        * order, re-sort them to CODA order for register download.
+        * Data in this SRAM survives a reboot.
+        */
+       p = (u16 *)dev->codebuf.vaddr;
+       if (dev->devtype->product == CODA_DX6) {
+               for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++)  {
+                       data = CODA_DOWN_ADDRESS_SET(i) |
+                               CODA_DOWN_DATA_SET(p[i ^ 1]);
+                       coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
+               }
+       } else {
+               for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) {
+                       data = CODA_DOWN_ADDRESS_SET(i) |
+                               CODA_DOWN_DATA_SET(p[round_down(i, 4) +
+                                                       3 - (i % 4)]);
+                       coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
+               }
+       }
+
+       /* Clear registers */
+       for (i = 0; i < 64; i++)
+               coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4);
+
+       /* Tell the BIT where to find everything it needs */
+       if (dev->devtype->product == CODA_960 ||
+           dev->devtype->product == CODA_7541) {
+               coda_write(dev, dev->tempbuf.paddr,
+                               CODA_REG_BIT_TEMP_BUF_ADDR);
+               coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+       } else {
+               coda_write(dev, dev->workbuf.paddr,
+                             CODA_REG_BIT_WORK_BUF_ADDR);
+       }
+       coda_write(dev, dev->codebuf.paddr,
+                     CODA_REG_BIT_CODE_BUF_ADDR);
+       coda_write(dev, 0, CODA_REG_BIT_CODE_RUN);
+
+       /* Set default values */
+       switch (dev->devtype->product) {
+       case CODA_DX6:
+               coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH,
+                          CODA_REG_BIT_STREAM_CTRL);
+               break;
+       default:
+               coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH,
+                          CODA_REG_BIT_STREAM_CTRL);
+       }
+       if (dev->devtype->product == CODA_960)
+               coda_write(dev, 1 << 12, CODA_REG_BIT_FRAME_MEM_CTRL);
+       else
+               coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL);
+
+       if (dev->devtype->product != CODA_DX6)
+               coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE);
+
+       coda_write(dev, CODA_INT_INTERRUPT_ENABLE,
+                     CODA_REG_BIT_INT_ENABLE);
+
+       /* Reset VPU and start processor */
+       data = coda_read(dev, CODA_REG_BIT_CODE_RESET);
+       data |= CODA_REG_RESET_ENABLE;
+       coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
+       udelay(10);
+       data &= ~CODA_REG_RESET_ENABLE;
+       coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
+       coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN);
+
+       clk_disable_unprepare(dev->clk_ahb);
+       clk_disable_unprepare(dev->clk_per);
+
+       return 0;
+
+err_clk_ahb:
+       clk_disable_unprepare(dev->clk_per);
+err_clk_per:
+       return ret;
+}
+
+static int coda_register_device(struct coda_dev *dev, struct video_device *vfd)
+{
+       vfd->release    = video_device_release_empty,
+       vfd->lock       = &dev->dev_mutex;
+       vfd->v4l2_dev   = &dev->v4l2_dev;
+       vfd->vfl_dir    = VFL_DIR_M2M;
+       video_set_drvdata(vfd, dev);
+
+       /* Not applicable, use the selection API instead */
+       v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP);
+       v4l2_disable_ioctl(vfd, VIDIOC_G_CROP);
+       v4l2_disable_ioctl(vfd, VIDIOC_S_CROP);
+
+       return video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+}
+
+static void coda_fw_callback(const struct firmware *fw, void *context)
+{
+       struct coda_dev *dev = context;
+       struct platform_device *pdev = dev->plat_dev;
+       int ret;
+
+       if (!fw) {
+               v4l2_err(&dev->v4l2_dev, "firmware request failed\n");
+               goto put_pm;
+       }
+
+       /* allocate auxiliary per-device code buffer for the BIT processor */
+       ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf",
+                                dev->debugfs_root);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to allocate code buffer\n");
+               goto put_pm;
+       }
+
+       /* Copy the whole firmware image to the code buffer */
+       memcpy(dev->codebuf.vaddr, fw->data, fw->size);
+       release_firmware(fw);
+
+       ret = coda_hw_init(dev);
+       if (ret < 0) {
+               v4l2_err(&dev->v4l2_dev, "HW initialization failed\n");
+               goto put_pm;
+       }
+
+       ret = coda_check_firmware(dev);
+       if (ret < 0)
+               goto put_pm;
+
+       dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+       if (IS_ERR(dev->alloc_ctx)) {
+               v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n");
+               goto put_pm;
+       }
+
+       dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops);
+       if (IS_ERR(dev->m2m_dev)) {
+               v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+               goto rel_ctx;
+       }
+
+       dev->vfd[0].fops      = &coda_encoder_fops,
+       dev->vfd[0].ioctl_ops = &coda_ioctl_ops;
+       snprintf(dev->vfd[0].name, sizeof(dev->vfd[0].name), "coda-encoder");
+       ret = coda_register_device(dev, &dev->vfd[0]);
+       if (ret) {
+               v4l2_err(&dev->v4l2_dev,
+                        "Failed to register encoder video device\n");
+               goto rel_m2m;
+       }
+
+       dev->vfd[1].fops      = &coda_decoder_fops,
+       dev->vfd[1].ioctl_ops = &coda_ioctl_ops;
+       snprintf(dev->vfd[1].name, sizeof(dev->vfd[1].name), "coda-decoder");
+       ret = coda_register_device(dev, &dev->vfd[1]);
+       if (ret) {
+               v4l2_err(&dev->v4l2_dev,
+                        "Failed to register decoder video device\n");
+               goto rel_m2m;
+       }
+
+       v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video[%d-%d]\n",
+                 dev->vfd[0].num, dev->vfd[1].num);
+
+       pm_runtime_put_sync(&pdev->dev);
+       return;
+
+rel_m2m:
+       v4l2_m2m_release(dev->m2m_dev);
+rel_ctx:
+       vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+put_pm:
+       pm_runtime_put_sync(&pdev->dev);
+}
+
+static int coda_firmware_request(struct coda_dev *dev)
+{
+       char *fw = dev->devtype->firmware;
+
+       dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw,
+               coda_product_name(dev->devtype->product));
+
+       return request_firmware_nowait(THIS_MODULE, true,
+               fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback);
+}
+
+enum coda_platform {
+       CODA_IMX27,
+       CODA_IMX53,
+       CODA_IMX6Q,
+       CODA_IMX6DL,
+};
+
+static const struct coda_devtype coda_devdata[] = {
+       [CODA_IMX27] = {
+               .firmware     = "v4l-codadx6-imx27.bin",
+               .product      = CODA_DX6,
+               .codecs       = codadx6_codecs,
+               .num_codecs   = ARRAY_SIZE(codadx6_codecs),
+               .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024,
+               .iram_size    = 0xb000,
+       },
+       [CODA_IMX53] = {
+               .firmware     = "v4l-coda7541-imx53.bin",
+               .product      = CODA_7541,
+               .codecs       = coda7_codecs,
+               .num_codecs   = ARRAY_SIZE(coda7_codecs),
+               .workbuf_size = 128 * 1024,
+               .tempbuf_size = 304 * 1024,
+               .iram_size    = 0x14000,
+       },
+       [CODA_IMX6Q] = {
+               .firmware     = "v4l-coda960-imx6q.bin",
+               .product      = CODA_960,
+               .codecs       = coda9_codecs,
+               .num_codecs   = ARRAY_SIZE(coda9_codecs),
+               .workbuf_size = 80 * 1024,
+               .tempbuf_size = 204 * 1024,
+               .iram_size    = 0x21000,
+       },
+       [CODA_IMX6DL] = {
+               .firmware     = "v4l-coda960-imx6dl.bin",
+               .product      = CODA_960,
+               .codecs       = coda9_codecs,
+               .num_codecs   = ARRAY_SIZE(coda9_codecs),
+               .workbuf_size = 80 * 1024,
+               .tempbuf_size = 204 * 1024,
+               .iram_size    = 0x20000,
+       },
+};
+
+static struct platform_device_id coda_platform_ids[] = {
+       { .name = "coda-imx27", .driver_data = CODA_IMX27 },
+       { .name = "coda-imx53", .driver_data = CODA_IMX53 },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, coda_platform_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id coda_dt_ids[] = {
+       { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] },
+       { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] },
+       { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] },
+       { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, coda_dt_ids);
+#endif
+
+static int coda_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *of_id =
+                       of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev);
+       const struct platform_device_id *pdev_id;
+       struct coda_platform_data *pdata = pdev->dev.platform_data;
+       struct device_node *np = pdev->dev.of_node;
+       struct gen_pool *pool;
+       struct coda_dev *dev;
+       struct resource *res;
+       int ret, irq;
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev) {
+               dev_err(&pdev->dev, "Not enough memory for %s\n",
+                       CODA_NAME);
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&dev->irqlock);
+       INIT_LIST_HEAD(&dev->instances);
+
+       dev->plat_dev = pdev;
+       dev->clk_per = devm_clk_get(&pdev->dev, "per");
+       if (IS_ERR(dev->clk_per)) {
+               dev_err(&pdev->dev, "Could not get per clock\n");
+               return PTR_ERR(dev->clk_per);
+       }
+
+       dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+       if (IS_ERR(dev->clk_ahb)) {
+               dev_err(&pdev->dev, "Could not get ahb clock\n");
+               return PTR_ERR(dev->clk_ahb);
+       }
+
+       /* Get  memory for physical registers */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(dev->regs_base))
+               return PTR_ERR(dev->regs_base);
+
+       /* IRQ */
+       irq = platform_get_irq_byname(pdev, "bit");
+       if (irq < 0)
+               irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "failed to get irq resource\n");
+               return irq;
+       }
+
+       ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler,
+                       IRQF_ONESHOT, dev_name(&pdev->dev), dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
+               return ret;
+       }
+
+       dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL);
+       if (IS_ERR(dev->rstc)) {
+               ret = PTR_ERR(dev->rstc);
+               if (ret == -ENOENT || ret == -ENOSYS) {
+                       dev->rstc = NULL;
+               } else {
+                       dev_err(&pdev->dev, "failed get reset control: %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       /* Get IRAM pool from device tree or platform data */
+       pool = of_get_named_gen_pool(np, "iram", 0);
+       if (!pool && pdata)
+               pool = dev_get_gen_pool(pdata->iram_dev);
+       if (!pool) {
+               dev_err(&pdev->dev, "iram pool not available\n");
+               return -ENOMEM;
+       }
+       dev->iram_pool = pool;
+
+       ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+       if (ret)
+               return ret;
+
+       mutex_init(&dev->dev_mutex);
+       mutex_init(&dev->coda_mutex);
+
+       pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
+
+       if (of_id) {
+               dev->devtype = of_id->data;
+       } else if (pdev_id) {
+               dev->devtype = &coda_devdata[pdev_id->driver_data];
+       } else {
+               v4l2_device_unregister(&dev->v4l2_dev);
+               return -EINVAL;
+       }
+
+       dev->debugfs_root = debugfs_create_dir("coda", NULL);
+       if (!dev->debugfs_root)
+               dev_warn(&pdev->dev, "failed to create debugfs root\n");
+
+       /* allocate auxiliary per-device buffers for the BIT processor */
+       if (dev->devtype->product == CODA_DX6) {
+               ret = coda_alloc_aux_buf(dev, &dev->workbuf,
+                                        dev->devtype->workbuf_size, "workbuf",
+                                        dev->debugfs_root);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to allocate work buffer\n");
+                       v4l2_device_unregister(&dev->v4l2_dev);
+                       return ret;
+               }
+       }
+
+       if (dev->devtype->tempbuf_size) {
+               ret = coda_alloc_aux_buf(dev, &dev->tempbuf,
+                                        dev->devtype->tempbuf_size, "tempbuf",
+                                        dev->debugfs_root);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to allocate temp buffer\n");
+                       v4l2_device_unregister(&dev->v4l2_dev);
+                       return ret;
+               }
+       }
+
+       dev->iram.size = dev->devtype->iram_size;
+       dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size,
+                                            &dev->iram.paddr);
+       if (!dev->iram.vaddr) {
+               dev_warn(&pdev->dev, "unable to alloc iram\n");
+       } else {
+               dev->iram.blob.data = dev->iram.vaddr;
+               dev->iram.blob.size = dev->iram.size;
+               dev->iram.dentry = debugfs_create_blob("iram", 0644,
+                                                      dev->debugfs_root,
+                                                      &dev->iram.blob);
+       }
+
+       dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+       if (!dev->workqueue) {
+               dev_err(&pdev->dev, "unable to alloc workqueue\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(pdev, dev);
+
+       /*
+        * Start activated so we can directly call coda_hw_init in
+        * coda_fw_callback regardless of whether CONFIG_PM_RUNTIME is
+        * enabled or whether the device is associated with a PM domain.
+        */
+       pm_runtime_get_noresume(&pdev->dev);
+       pm_runtime_set_active(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+
+       return coda_firmware_request(dev);
+}
+
+static int coda_remove(struct platform_device *pdev)
+{
+       struct coda_dev *dev = platform_get_drvdata(pdev);
+
+       video_unregister_device(&dev->vfd[0]);
+       video_unregister_device(&dev->vfd[1]);
+       if (dev->m2m_dev)
+               v4l2_m2m_release(dev->m2m_dev);
+       pm_runtime_disable(&pdev->dev);
+       if (dev->alloc_ctx)
+               vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+       v4l2_device_unregister(&dev->v4l2_dev);
+       destroy_workqueue(dev->workqueue);
+       if (dev->iram.vaddr)
+               gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr,
+                             dev->iram.size);
+       coda_free_aux_buf(dev, &dev->codebuf);
+       coda_free_aux_buf(dev, &dev->tempbuf);
+       coda_free_aux_buf(dev, &dev->workbuf);
+       debugfs_remove_recursive(dev->debugfs_root);
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int coda_runtime_resume(struct device *dev)
+{
+       struct coda_dev *cdev = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (dev->pm_domain && cdev->codebuf.vaddr) {
+               ret = coda_hw_init(cdev);
+               if (ret)
+                       v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n");
+       }
+
+       return ret;
+}
+#endif
+
+static const struct dev_pm_ops coda_pm_ops = {
+       SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL)
+};
+
+static struct platform_driver coda_driver = {
+       .probe  = coda_probe,
+       .remove = coda_remove,
+       .driver = {
+               .name   = CODA_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(coda_dt_ids),
+               .pm     = &coda_pm_ops,
+       },
+       .id_table = coda_platform_ids,
+};
+
+module_platform_driver(coda_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
+MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver");
diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/coda/coda-h264.c
new file mode 100644 (file)
index 0000000..456773a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Coda multi-standard codec IP - H.264 helper functions
+ *
+ * Copyright (C) 2012 Vista Silicon S.L.
+ *    Javier Martin, <javier.martin@vista-silicon.com>
+ *    Xavier Duret
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 };
+static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 };
+
+int coda_h264_padding(int size, char *p)
+{
+       int nal_size;
+       int diff;
+
+       diff = size - (size & ~0x7);
+       if (diff == 0)
+               return 0;
+
+       nal_size = coda_filler_size[diff];
+       memcpy(p, coda_filler_nal, nal_size);
+
+       /* Add rbsp stop bit and trailing at the end */
+       *(p + nal_size - 1) = 0x80;
+
+       return nal_size;
+}
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
new file mode 100644 (file)
index 0000000..bbc18c0
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Coda multi-standard codec IP
+ *
+ * Copyright (C) 2012 Vista Silicon S.L.
+ *    Javier Martin, <javier.martin@vista-silicon.com>
+ *    Xavier Duret
+ * Copyright (C) 2012-2014 Philipp Zabel, Pengutronix
+ *
+ * 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.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/irqreturn.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/videobuf2-core.h>
+
+#include "coda_regs.h"
+
+#define CODA_MAX_FRAMEBUFFERS  8
+#define CODA_MAX_FRAME_SIZE    0x100000
+#define FMO_SLICE_SAVE_BUF_SIZE        (32)
+
+enum {
+       V4L2_M2M_SRC = 0,
+       V4L2_M2M_DST = 1,
+};
+
+enum coda_inst_type {
+       CODA_INST_ENCODER,
+       CODA_INST_DECODER,
+};
+
+enum coda_product {
+       CODA_DX6 = 0xf001,
+       CODA_7541 = 0xf012,
+       CODA_960 = 0xf020,
+};
+
+struct coda_devtype {
+       char                    *firmware;
+       enum coda_product       product;
+       const struct coda_codec *codecs;
+       unsigned int            num_codecs;
+       size_t                  workbuf_size;
+       size_t                  tempbuf_size;
+       size_t                  iram_size;
+};
+
+struct coda_aux_buf {
+       void                    *vaddr;
+       dma_addr_t              paddr;
+       u32                     size;
+       struct debugfs_blob_wrapper blob;
+       struct dentry           *dentry;
+};
+
+struct coda_dev {
+       struct v4l2_device      v4l2_dev;
+       struct video_device     vfd[2];
+       struct platform_device  *plat_dev;
+       const struct coda_devtype *devtype;
+
+       void __iomem            *regs_base;
+       struct clk              *clk_per;
+       struct clk              *clk_ahb;
+       struct reset_control    *rstc;
+
+       struct coda_aux_buf     codebuf;
+       struct coda_aux_buf     tempbuf;
+       struct coda_aux_buf     workbuf;
+       struct gen_pool         *iram_pool;
+       struct coda_aux_buf     iram;
+
+       spinlock_t              irqlock;
+       struct mutex            dev_mutex;
+       struct mutex            coda_mutex;
+       struct workqueue_struct *workqueue;
+       struct v4l2_m2m_dev     *m2m_dev;
+       struct vb2_alloc_ctx    *alloc_ctx;
+       struct list_head        instances;
+       unsigned long           instance_mask;
+       struct dentry           *debugfs_root;
+};
+
+struct coda_codec {
+       u32 mode;
+       u32 src_fourcc;
+       u32 dst_fourcc;
+       u32 max_w;
+       u32 max_h;
+};
+
+struct coda_huff_tab;
+
+struct coda_params {
+       u8                      rot_mode;
+       u8                      h264_intra_qp;
+       u8                      h264_inter_qp;
+       u8                      h264_min_qp;
+       u8                      h264_max_qp;
+       u8                      h264_deblk_enabled;
+       u8                      h264_deblk_alpha;
+       u8                      h264_deblk_beta;
+       u8                      mpeg4_intra_qp;
+       u8                      mpeg4_inter_qp;
+       u8                      gop_size;
+       int                     intra_refresh;
+       int                     codec_mode;
+       int                     codec_mode_aux;
+       enum v4l2_mpeg_video_multi_slice_mode slice_mode;
+       u32                     framerate;
+       u16                     bitrate;
+       u32                     slice_max_bits;
+       u32                     slice_max_mb;
+};
+
+struct coda_timestamp {
+       struct list_head        list;
+       u32                     sequence;
+       struct v4l2_timecode    timecode;
+       struct timeval          timestamp;
+};
+
+/* Per-queue, driver-specific private data */
+struct coda_q_data {
+       unsigned int            width;
+       unsigned int            height;
+       unsigned int            bytesperline;
+       unsigned int            sizeimage;
+       unsigned int            fourcc;
+       struct v4l2_rect        rect;
+};
+
+struct coda_iram_info {
+       u32             axi_sram_use;
+       phys_addr_t     buf_bit_use;
+       phys_addr_t     buf_ip_ac_dc_use;
+       phys_addr_t     buf_dbk_y_use;
+       phys_addr_t     buf_dbk_c_use;
+       phys_addr_t     buf_ovl_use;
+       phys_addr_t     buf_btp_use;
+       phys_addr_t     search_ram_paddr;
+       int             search_ram_size;
+       int             remaining;
+       phys_addr_t     next_paddr;
+};
+
+struct gdi_tiled_map {
+       int xy2ca_map[16];
+       int xy2ba_map[16];
+       int xy2ra_map[16];
+       int rbc2axi_map[32];
+       int xy2rbc_config;
+       int map_type;
+#define GDI_LINEAR_FRAME_MAP 0
+};
+
+struct coda_ctx;
+
+struct coda_context_ops {
+       int (*queue_init)(void *priv, struct vb2_queue *src_vq,
+                         struct vb2_queue *dst_vq);
+       int (*start_streaming)(struct coda_ctx *ctx);
+       int (*prepare_run)(struct coda_ctx *ctx);
+       void (*finish_run)(struct coda_ctx *ctx);
+       void (*seq_end_work)(struct work_struct *work);
+       void (*release)(struct coda_ctx *ctx);
+};
+
+struct coda_ctx {
+       struct coda_dev                 *dev;
+       struct mutex                    buffer_mutex;
+       struct list_head                list;
+       struct work_struct              pic_run_work;
+       struct work_struct              seq_end_work;
+       struct completion               completion;
+       const struct coda_context_ops   *ops;
+       int                             aborting;
+       int                             initialized;
+       int                             streamon_out;
+       int                             streamon_cap;
+       u32                             isequence;
+       u32                             qsequence;
+       u32                             osequence;
+       u32                             sequence_offset;
+       struct coda_q_data              q_data[2];
+       enum coda_inst_type             inst_type;
+       const struct coda_codec         *codec;
+       enum v4l2_colorspace            colorspace;
+       struct coda_params              params;
+       struct v4l2_ctrl_handler        ctrls;
+       struct v4l2_fh                  fh;
+       int                             gopcounter;
+       int                             runcounter;
+       char                            vpu_header[3][64];
+       int                             vpu_header_size[3];
+       struct kfifo                    bitstream_fifo;
+       struct mutex                    bitstream_mutex;
+       struct coda_aux_buf             bitstream;
+       bool                            hold;
+       struct coda_aux_buf             parabuf;
+       struct coda_aux_buf             psbuf;
+       struct coda_aux_buf             slicebuf;
+       struct coda_aux_buf             internal_frames[CODA_MAX_FRAMEBUFFERS];
+       u32                             frame_types[CODA_MAX_FRAMEBUFFERS];
+       struct coda_timestamp           frame_timestamps[CODA_MAX_FRAMEBUFFERS];
+       u32                             frame_errors[CODA_MAX_FRAMEBUFFERS];
+       struct list_head                timestamp_list;
+       struct coda_aux_buf             workbuf;
+       int                             num_internal_frames;
+       int                             idx;
+       int                             reg_idx;
+       struct coda_iram_info           iram_info;
+       struct gdi_tiled_map            tiled_map;
+       u32                             bit_stream_param;
+       u32                             frm_dis_flg;
+       u32                             frame_mem_ctrl;
+       int                             display_idx;
+       struct dentry                   *debugfs_entry;
+};
+
+extern int coda_debug;
+
+void coda_write(struct coda_dev *dev, u32 data, u32 reg);
+unsigned int coda_read(struct coda_dev *dev, u32 reg);
+
+int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf,
+                      size_t size, const char *name, struct dentry *parent);
+void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf);
+
+static inline int coda_alloc_context_buf(struct coda_ctx *ctx,
+                                        struct coda_aux_buf *buf, size_t size,
+                                        const char *name)
+{
+       return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry);
+}
+
+int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq,
+                           struct vb2_queue *dst_vq);
+int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq,
+                           struct vb2_queue *dst_vq);
+
+int coda_hw_reset(struct coda_ctx *ctx);
+
+void coda_fill_bitstream(struct coda_ctx *ctx);
+
+void coda_set_gdi_regs(struct coda_ctx *ctx);
+
+static inline struct coda_q_data *get_q_data(struct coda_ctx *ctx,
+                                            enum v4l2_buf_type type)
+{
+       switch (type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               return &(ctx->q_data[V4L2_M2M_SRC]);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               return &(ctx->q_data[V4L2_M2M_DST]);
+       default:
+               return NULL;
+       }
+}
+
+const char *coda_product_name(int product);
+
+int coda_check_firmware(struct coda_dev *dev);
+
+static inline int coda_get_bitstream_payload(struct coda_ctx *ctx)
+{
+       return kfifo_len(&ctx->bitstream_fifo);
+}
+
+void coda_bit_stream_end_flag(struct coda_ctx *ctx);
+
+int coda_h264_padding(int size, char *p);
+
+extern const struct coda_context_ops coda_bit_encode_ops;
+extern const struct coda_context_ops coda_bit_decode_ops;
+
+irqreturn_t coda_irq_handler(int irq, void *data);
index afb3aec1320e8262351a31cf1efe6f7fcbe695be..d9e1ddb586b190e99da9cd0b97f60163549e655c 100644 (file)
@@ -1,6 +1,8 @@
 config VIDEO_DAVINCI_VPIF_DISPLAY
        tristate "TI DaVinci VPIF V4L2-Display driver"
-       depends on VIDEO_DEV && ARCH_DAVINCI
+       depends on VIDEO_DEV
+       depends on ARCH_DAVINCI || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT
        select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT
@@ -14,7 +16,9 @@ config VIDEO_DAVINCI_VPIF_DISPLAY
 
 config VIDEO_DAVINCI_VPIF_CAPTURE
        tristate "TI DaVinci VPIF video capture driver"
-       depends on VIDEO_DEV && ARCH_DAVINCI
+       depends on VIDEO_DEV
+       depends on ARCH_DAVINCI || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        help
          Enables Davinci VPIF module used for capture devices.
@@ -26,7 +30,9 @@ config VIDEO_DAVINCI_VPIF_CAPTURE
 
 config VIDEO_DM6446_CCDC
        tristate "TI DM6446 CCDC video capture driver"
-       depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3)
+       depends on VIDEO_V4L2
+       depends on ARCH_DAVINCI || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF_DMA_CONTIG
        help
           Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces
@@ -40,7 +46,9 @@ config VIDEO_DM6446_CCDC
 
 config VIDEO_DM355_CCDC
        tristate "TI DM355 CCDC video capture driver"
-       depends on VIDEO_V4L2 && ARCH_DAVINCI
+       depends on VIDEO_V4L2
+       depends on ARCH_DAVINCI || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF_DMA_CONTIG
        help
           Enables DM355 CCD hw module. DM355 CCDC hw interfaces
@@ -55,6 +63,7 @@ config VIDEO_DM355_CCDC
 config VIDEO_DM365_ISIF
        tristate "TI DM365 ISIF video capture driver"
        depends on VIDEO_V4L2 && ARCH_DAVINCI
+       depends on HAS_DMA
        select VIDEOBUF_DMA_CONTIG
        help
           Enables ISIF hw module. This is the hardware module for
@@ -67,6 +76,7 @@ config VIDEO_DM365_ISIF
 config VIDEO_DAVINCI_VPBE_DISPLAY
        tristate "TI DaVinci VPBE V4L2-Display driver"
        depends on ARCH_DAVINCI
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        help
            Enables Davinci VPBE module used for display devices.
index 05f8fb7f7b70a335c89eade3925fca309a79ab5d..3f44deb5b7a76182ad7d637340f989e618ee5a30 100644 (file)
@@ -460,7 +460,7 @@ static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
  * ccdc_write_dfc_entry()
  * write an entry in the dfc table.
  */
-int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
+static int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
 {
 /* TODO This is to be re-visited and adjusted */
 #define DFC_WRITE_WAIT_COUNT   1000
index 07e98df3d86797710dda4b1555dedf84e461f9e2..62a0ebb01056cf867d1400132379dfe0c275ec4f 100644 (file)
@@ -130,9 +130,9 @@ static void ccdc_enable_vport(int flag)
  * This function will configure the window size
  * to be capture in CCDC reg
  */
-void ccdc_setwin(struct v4l2_rect *image_win,
-               enum ccdc_frmfmt frm_fmt,
-               int ppc)
+static void ccdc_setwin(struct v4l2_rect *image_win,
+                       enum ccdc_frmfmt frm_fmt,
+                       int ppc)
 {
        int horz_start, horz_nr_pixels;
        int vert_start, vert_nr_lines;
@@ -291,7 +291,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
                dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed");
                return -EFAULT;
        }
-       config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr;
+       config_params->fault_pxl.fpc_table_addr = (unsigned long)fpc_physaddr;
        return 0;
 }
 
@@ -370,7 +370,7 @@ static int ccdc_set_params(void __user *params)
  * ccdc_config_ycbcr()
  * This function will configure CCDC for YCbCr video capture
  */
-void ccdc_config_ycbcr(void)
+static void ccdc_config_ycbcr(void)
 {
        struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
        u32 syn_mode;
@@ -506,7 +506,7 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
 
        /* Configure Fault pixel if needed */
        regw(fpc->fpc_table_addr, CCDC_FPC_ADDR);
-       dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC_ADDR...\n",
+       dev_dbg(ccdc_cfg.dev, "\nWriting 0x%lx to FPC_ADDR...\n",
                       (fpc->fpc_table_addr));
        /* Write the FPC params with FPC disable */
        val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK;
@@ -523,7 +523,7 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
  * ccdc_config_raw()
  * This function will configure CCDC for Raw capture mode
  */
-void ccdc_config_raw(void)
+static void ccdc_config_raw(void)
 {
        struct ccdc_params_raw *params = &ccdc_cfg.bayer;
        struct ccdc_config_params_raw *config_params =
index ea7661a27479f4ef7cc7580b4e9fd11db7aceb9e..de55f47a77dba1b3bcd90196ed86392fc3fcdf11 100644 (file)
@@ -125,7 +125,7 @@ static DEFINE_MUTEX(ccdc_lock);
 /* ccdc configuration */
 static struct ccdc_config *ccdc_cfg;
 
-const struct vpfe_standard vpfe_standards[] = {
+static const struct vpfe_standard vpfe_standards[] = {
        {V4L2_STD_525_60, 720, 480, {11, 10}, 1},
        {V4L2_STD_625_50, 720, 576, {54, 59}, 1},
 };
@@ -442,11 +442,10 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev,
                return ret;
 
        /* Update the values of sizeimage and bytesperline */
-       if (!ret) {
-               pix->bytesperline = ccdc_dev->hw_ops.get_line_length();
-               pix->sizeimage = pix->bytesperline * pix->height;
-       }
-       return ret;
+       pix->bytesperline = ccdc_dev->hw_ops.get_line_length();
+       pix->sizeimage = pix->bytesperline * pix->height;
+
+       return 0;
 }
 
 static int vpfe_initialize_device(struct vpfe_device *vpfe_dev)
@@ -943,12 +942,11 @@ static int vpfe_g_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *fmt)
 {
        struct vpfe_device *vpfe_dev = video_drvdata(file);
-       int ret = 0;
 
        v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n");
        /* Fill in the information about format */
        *fmt = vpfe_dev->fmt;
-       return ret;
+       return 0;
 }
 
 static int vpfe_enum_fmt_vid_cap(struct file *file, void  *priv,
@@ -1914,7 +1912,7 @@ static int vpfe_probe(struct platform_device *pdev)
        v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
                "trying to register vpfe device.\n");
        v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
-               "video_dev=%x\n", (int)&vpfe_dev->video_dev);
+               "video_dev=%p\n", &vpfe_dev->video_dev);
        vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = video_register_device(vpfe_dev->video_dev,
                                    VFL_TYPE_GRABBER, -1);
index cd08e5248387038f333eee4726775b139988973b..3dad5bd7fe0a8aaad40bdf17b2ac9f6c776d9b44 100644 (file)
@@ -38,6 +38,7 @@ MODULE_LICENSE("GPL");
 #define VPIF_CH3_MAX_MODES     2
 
 spinlock_t vpif_lock;
+EXPORT_SYMBOL_GPL(vpif_lock);
 
 void __iomem *vpif_base;
 EXPORT_SYMBOL_GPL(vpif_base);
index b054b7eec53dc5a98dd43ba02dd8e343bbc19005..3ccb26ff43c8dfa31935c3f8f1eed6dee126c3c5 100644 (file)
@@ -213,8 +213,6 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
        /* Remove buffer from the buffer queue */
        list_del(&common->cur_frm->list);
        spin_unlock_irqrestore(&common->irqlock, flags);
-       /* Mark state of the current frame to active */
-       common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
 
        addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
 
@@ -350,7 +348,6 @@ static void vpif_schedule_next_buffer(struct common_obj *common)
        /* Remove that buffer from the buffer queue */
        list_del(&common->next_frm->list);
        spin_unlock(&common->irqlock);
-       common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
        addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
 
        /* Set top and bottom field addresses in VPIF registers */
@@ -373,7 +370,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
        struct vpif_device *dev = &vpif_obj;
        struct common_obj *common;
        struct channel_obj *ch;
-       enum v4l2_field field;
        int channel_id = 0;
        int fid = -1, i;
 
@@ -383,8 +379,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
 
        ch = dev->dev[channel_id];
 
-       field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
-
        for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) {
                common = &ch->common[i];
                /* skip If streaming is not started in this channel */
@@ -533,7 +527,7 @@ static int vpif_update_std_info(struct channel_obj *ch)
  */
 static void vpif_calculate_offsets(struct channel_obj *ch)
 {
-       unsigned int hpitch, vpitch, sizeimage;
+       unsigned int hpitch, sizeimage;
        struct video_obj *vid_ch = &(ch->video);
        struct vpif_params *vpifparams = &ch->vpifparams;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
@@ -552,7 +546,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
        sizeimage = common->fmt.fmt.pix.sizeimage;
 
        hpitch = common->fmt.fmt.pix.bytesperline;
-       vpitch = sizeimage / (hpitch * 2);
 
        if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
            (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
@@ -1603,7 +1596,7 @@ static int vpif_suspend(struct device *dev)
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
 
-               if (!vb2_is_streaming(&common->buffer_queue))
+               if (!vb2_start_streaming_called(&common->buffer_queue))
                        continue;
 
                mutex_lock(&common->lock);
@@ -1637,7 +1630,7 @@ static int vpif_resume(struct device *dev)
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
 
-               if (!vb2_is_streaming(&common->buffer_queue))
+               if (!vb2_start_streaming_called(&common->buffer_queue))
                        continue;
 
                mutex_lock(&common->lock);
index a03ec7381cfef734da18537f822e34ed959af55f..8d6ced56253c1f9fba99230fdd871f63d5cb0832 100644 (file)
@@ -196,8 +196,6 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
 
        list_del(&common->cur_frm->list);
        spin_unlock_irqrestore(&common->irqlock, flags);
-       /* Mark state of the current frame to active */
-       common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
 
        addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
        common->set_addr((addr + common->ytop_off),
@@ -306,8 +304,6 @@ static void process_progressive_mode(struct common_obj *common)
        /* Remove that buffer from the buffer queue */
        list_del(&common->next_frm->list);
        spin_unlock(&common->irqlock);
-       /* Mark status of the buffer as active */
-       common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
 
        /* Set top and bottom field addrs in VPIF registers */
        addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
@@ -360,7 +356,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
        struct vpif_device *dev = &vpif_obj;
        struct channel_obj *ch;
        struct common_obj *common;
-       enum v4l2_field field;
        int fid = -1, i;
        int channel_id = 0;
 
@@ -369,7 +364,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
                return IRQ_NONE;
 
        ch = dev->dev[channel_id];
-       field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
        for (i = 0; i < VPIF_NUMOBJECTS; i++) {
                common = &ch->common[i];
                /* If streaming is started in this channel */
@@ -502,7 +496,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
        struct vpif_params *vpifparams = &ch->vpifparams;
        enum v4l2_field field = common->fmt.fmt.pix.field;
        struct video_obj *vid_ch = &ch->video;
-       unsigned int hpitch, vpitch, sizeimage;
+       unsigned int hpitch, sizeimage;
 
        if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) {
                if (ch->vpifparams.std_info.frm_fmt)
@@ -516,7 +510,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
        sizeimage = common->fmt.fmt.pix.sizeimage;
 
        hpitch = common->fmt.fmt.pix.bytesperline;
-       vpitch = sizeimage / (hpitch * 2);
        if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
            (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
                common->ytop_off = 0;
@@ -813,17 +806,14 @@ static int vpif_set_output(struct vpif_display_config *vpif_cfg,
 {
        struct vpif_display_chan_config *chan_cfg =
                &vpif_cfg->chan_config[ch->channel_id];
-       struct vpif_subdev_info *subdev_info = NULL;
        struct v4l2_subdev *sd = NULL;
        u32 input = 0, output = 0;
        int sd_index;
        int ret;
 
        sd_index = vpif_output_to_subdev(vpif_cfg, chan_cfg, index);
-       if (sd_index >= 0) {
+       if (sd_index >= 0)
                sd = vpif_obj.sd[sd_index];
-               subdev_info = &vpif_cfg->subdevinfo[sd_index];
-       }
 
        if (sd) {
                input = chan_cfg->outputs[index].input_route;
@@ -1210,8 +1200,8 @@ static int vpif_probe_complete(void)
                INIT_LIST_HEAD(&common->dma_queue);
 
                /* register video device */
-               vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
-                        (int)ch, (int)&ch->video_dev);
+               vpif_dbg(1, debug, "channel=%p,channel->video_dev=%p\n",
+                        ch, &ch->video_dev);
 
                /* Initialize the video_device structure */
                vdev = ch->video_dev;
@@ -1410,7 +1400,7 @@ static int vpif_suspend(struct device *dev)
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
 
-               if (!vb2_is_streaming(&common->buffer_queue))
+               if (!vb2_start_streaming_called(&common->buffer_queue))
                        continue;
 
                mutex_lock(&common->lock);
@@ -1442,7 +1432,7 @@ static int vpif_resume(struct device *dev)
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
 
-               if (!vb2_is_streaming(&common->buffer_queue))
+               if (!vb2_start_streaming_called(&common->buffer_queue))
                        continue;
 
                mutex_lock(&common->lock);
index 9d0cc04d7ab7ee1ffc2e41db4bb14436f9c15e80..b4c9f1d089684f316215803018541a47d0f4270b 100644 (file)
@@ -852,8 +852,8 @@ int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
                (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M))
                swap(addr->cb, addr->cr);
 
-       pr_debug("ADDR: y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
-               addr->y, addr->cb, addr->cr, ret);
+       pr_debug("ADDR: y= %pad  cb= %pad cr= %pad ret= %d",
+               &addr->y, &addr->cb, &addr->cr, ret);
 
        return ret;
 }
@@ -1086,7 +1086,7 @@ static int gsc_probe(struct platform_device *pdev)
        else
                gsc->id = pdev->id;
 
-       if (gsc->id < 0 || gsc->id >= drv_data->num_entities) {
+       if (gsc->id >= drv_data->num_entities) {
                dev_err(dev, "Invalid platform device id: %d\n", gsc->id);
                return -EINVAL;
        }
index e434f1f03d7b3a4458e0543923d9189b2992b8a3..74e1de637e8f65fdb0f8b784e8e24e63374fd43a 100644 (file)
@@ -362,7 +362,6 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh,
 {
        struct gsc_ctx *ctx = fh_to_ctx(fh);
        struct gsc_dev *gsc = ctx->gsc_dev;
-       struct gsc_frame *frame;
        u32 max_cnt;
 
        max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
@@ -376,8 +375,6 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh,
                        gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
        }
 
-       frame = ctx_get_frame(ctx, reqbufs->type);
-
        return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
 }
 
index e22d147a6940fee9969fe3ebcabf3ed6b701900c..ce12a11005115e1196244de742b41740d9040200 100644 (file)
@@ -90,8 +90,8 @@ void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift,
 void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
                                int index)
 {
-       pr_debug("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index,
-                       addr->y, addr->cb, addr->cr);
+       pr_debug("src_buf[%d]: %pad, cb: %pad, cr: %pad", index,
+                       &addr->y, &addr->cb, &addr->cr);
        writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index));
        writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index));
        writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index));
@@ -101,8 +101,8 @@ void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
 void gsc_hw_set_output_addr(struct gsc_dev *dev,
                             struct gsc_addr *addr, int index)
 {
-       pr_debug("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
-                       index, addr->y, addr->cb, addr->cr);
+       pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad",
+                       index, &addr->y, &addr->cb, &addr->cr);
        writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index));
        writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index));
        writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index));
index 5dcaa0a805403c5e23372ee12bf3af06e49d35b7..77c95123774409ac491d45d8db465fdb9c72360b 100644 (file)
@@ -2,7 +2,7 @@
 config VIDEO_SAMSUNG_EXYNOS4_IS
        bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver"
        depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
-       depends on (PLAT_S5P || ARCH_EXYNOS)
+       depends on (PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST)
        depends on OF && COMMON_CLK
        help
          Say Y here to enable camera host interface devices for
@@ -16,6 +16,7 @@ config VIDEO_EXYNOS4_IS_COMMON
 config VIDEO_S5P_FIMC
        tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver"
        depends on I2C
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
        select MFD_SYSCON
@@ -43,6 +44,7 @@ if SOC_EXYNOS4212 || SOC_EXYNOS4412 || SOC_EXYNOS5250
 config VIDEO_EXYNOS_FIMC_LITE
        tristate "EXYNOS FIMC-LITE camera interface driver"
        depends on I2C
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select VIDEO_EXYNOS4_IS_COMMON
        help
@@ -55,6 +57,7 @@ endif
 
 config VIDEO_EXYNOS4_FIMC_IS
        tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        depends on OF
        select FW_LOADER
index e8519e151c1ac0bca0391c51dafde78bbcebcd87..e050e63fe358683c61e1c0c0e00e21e5e25f41d5 100644 (file)
@@ -15,7 +15,7 @@
 
 #include "fimc-is-errno.h"
 
-const char * const fimc_is_param_strerr(unsigned int error)
+const char *fimc_is_param_strerr(unsigned int error)
 {
        switch (error) {
        case ERROR_COMMON_CMD:
@@ -146,7 +146,7 @@ const char * const fimc_is_param_strerr(unsigned int error)
        }
 }
 
-const char * const fimc_is_strerr(unsigned int error)
+const char *fimc_is_strerr(unsigned int error)
 {
        error &= ~IS_ERROR_TIME_OUT_FLAG;
 
index 3de6f6da6f87058e9c0bc8e12a6658b8764d96b7..ef981e74513a9f5f60df574e1045627915b3f0be 100644 (file)
@@ -242,7 +242,7 @@ enum fimc_is_error {
        ERROR_SCALER_FLIP                               = 521,
 };
 
-const char * const fimc_is_strerr(unsigned int error);
-const char * const fimc_is_param_strerr(unsigned int error);
+const char *fimc_is_strerr(unsigned int error);
+const char *fimc_is_param_strerr(unsigned int error);
 
 #endif /* FIMC_IS_ERR_H_ */
index bf1465d1bf6d709b266862bef57b61b257f22bfc..72b9b436c5c0a149601f9caa9d53d95e53084a74 100644 (file)
@@ -667,7 +667,6 @@ void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val)
 void fimc_is_set_initial_params(struct fimc_is *is)
 {
        struct global_param *global;
-       struct sensor_param *sensor;
        struct isp_param *isp;
        struct drc_param *drc;
        struct fd_param *fd;
@@ -676,7 +675,6 @@ void fimc_is_set_initial_params(struct fimc_is *is)
 
        index = is->config_index;
        global = &is->config[index].global;
-       sensor = &is->config[index].sensor;
        isp = &is->config[index].isp;
        drc = &is->config[index].drc;
        fd = &is->config[index].fd;
index 5476dce3ad293716fc940b130d1cf9f4aaa219c5..22162b2567dae446947dce4e7193cff9cfbb0940 100644 (file)
@@ -388,7 +388,7 @@ static void fimc_is_load_firmware(const struct firmware *fw, void *context)
        mutex_lock(&is->lock);
 
        if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) {
-               dev_err(dev, "wrong firmware size: %d\n", fw->size);
+               dev_err(dev, "wrong firmware size: %zu\n", fw->size);
                goto done;
        }
 
@@ -416,7 +416,7 @@ static void fimc_is_load_firmware(const struct firmware *fw, void *context)
 
        dev_info(dev, "loaded firmware: %s, rev. %s\n",
                 is->fw.info, is->fw.version);
-       dev_dbg(dev, "FW size: %d, paddr: %#x\n", fw->size, is->memory.paddr);
+       dev_dbg(dev, "FW size: %zu, paddr: %pad\n", fw->size, &is->memory.paddr);
 
        is->is_shared_region->chip_id = 0xe4412;
        is->is_shared_region->chip_rev_no = 1;
@@ -693,9 +693,9 @@ int fimc_is_hw_initialize(struct fimc_is *is)
                return -EIO;
        }
 
-       pr_debug("shared region: %#x, parameter region: %#x\n",
-                is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET,
-                is->is_dma_p_region);
+       pr_debug("shared region: %pad, parameter region: %pad\n",
+                &is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET,
+                &is->is_dma_p_region);
 
        is->setfile.sub_index = 0;
 
index 93f9cf2ebcd67293d5565c9900b37f949e86c4be..76b6b4d146169824593170c08f79a89a2b18c06f 100644 (file)
@@ -219,9 +219,9 @@ static void isp_video_capture_buffer_queue(struct vb2_buffer *vb)
                                                        ivb->dma_addr[i];
 
                        isp_dbg(2, &video->ve.vdev,
-                               "dma_buf %d (%d/%d/%d) addr: %#x\n",
-                               buf_index, ivb->index, i, vb->v4l2_buf.index,
-                               ivb->dma_addr[i]);
+                               "dma_buf %pad (%d/%d/%d) addr: %pad\n",
+                               &buf_index, ivb->index, i, vb->v4l2_buf.index,
+                               &ivb->dma_addr[i]);
                }
 
                if (++video->buf_count < video->reqbufs_count)
@@ -313,7 +313,6 @@ static int isp_video_release(struct file *file)
        struct fimc_is_video *ivc = &isp->video_capture;
        struct media_entity *entity = &ivc->ve.vdev.entity;
        struct media_device *mdev = entity->parent;
-       int ret = 0;
 
        mutex_lock(&isp->video_lock);
 
@@ -335,7 +334,7 @@ static int isp_video_release(struct file *file)
        pm_runtime_put(&isp->pdev->dev);
        mutex_unlock(&isp->video_lock);
 
-       return ret;
+       return 0;
 }
 
 static const struct v4l2_file_operations isp_video_fops = {
index 344718df5c62885866d8c6ec0953163c7d99c60a..54c49d5e769021fa4036b2efe5dd02b9805ada5c 100644 (file)
@@ -1098,8 +1098,10 @@ static int fimc_md_link_notify(struct media_link *link, unsigned int flags,
        if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
                if (!(flags & MEDIA_LNK_FL_ENABLED))
                        ret = __fimc_md_modify_pipelines(sink, false);
+#if 0
                else
-                       ; /* TODO: Link state change validation */
+                       /* TODO: Link state change validation */
+#endif
        /* After link activation */
        } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
                   (link->flags & MEDIA_LNK_FL_ENABLED)) {
index ae54ef5f535d9c105f643d76e3f78c72ee341095..db6fd14d19366b6a43f89bc363a7586783dd55f7 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
+#include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/videodev2.h>
@@ -752,7 +753,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
        v4l2_of_parse_endpoint(node, &endpoint);
 
        state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;
-       if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES)
+       if (state->index >= CSIS_MAX_ENTITIES)
                return -ENXIO;
 
        /* Get MIPI CSI-2 bus configration from the endpoint node. */
index bf739e3b33984c0ef36d262fa5b70426c74f41ce..6265d36adcebcb893ca9345765fac5337d8835e3 100644 (file)
@@ -1,6 +1,7 @@
 config VIDEO_CAFE_CCIC
        tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
        depends on PCI && I2C && VIDEO_V4L2
+       depends on HAS_DMA
        select VIDEO_OV7670
        select VIDEOBUF2_VMALLOC
        select VIDEOBUF2_DMA_CONTIG
@@ -12,6 +13,7 @@ config VIDEO_CAFE_CCIC
 config VIDEO_MMP_CAMERA
        tristate "Marvell Armada 610 integrated camera controller support"
        depends on ARCH_MMP && I2C && VIDEO_V4L2
+       depends on HAS_DMA
        select VIDEO_OV7670
        select I2C_GPIO
        select VIDEOBUF2_DMA_SG
index be4b51212106e41d1420b6c27b1fb68c041782cc..7a86c77bffa08257d35713361deaadb2e91bc1d8 100644 (file)
@@ -67,7 +67,7 @@ MODULE_PARM_DESC(dma_buf_size,
                "parameters require larger buffers, an attempt to reallocate "
                "will be made.");
 #else /* MCAM_MODE_VMALLOC */
-static const bool alloc_bufs_at_read = 0;
+static const bool alloc_bufs_at_read;
 static const int n_dma_bufs = 3;  /* Used by S/G_PARM */
 #endif /* MCAM_MODE_VMALLOC */
 
index fa8f7cabe364d4d36b20009d462eab02ff95f78f..4971ff21f82b6b46a5a2c43e25a40adfd6611ab8 100644 (file)
@@ -27,7 +27,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <media/videobuf2-dma-contig.h>
-#include <asm/sizes.h>
+#include <linux/sizes.h>
 
 #define EMMAPRP_MODULE_NAME "mem2mem-emmaprp"
 
index 37ad446b35b309acc1f40099f9e71503785e5640..05de442d24e42ca6a2273b55271ea8425efb6a73 100644 (file)
@@ -3,7 +3,7 @@ config VIDEO_OMAP2_VOUT_VRFB
 
 config VIDEO_OMAP2_VOUT
        tristate "OMAP2/OMAP3 V4L2-Display driver"
-       depends on ARCH_OMAP2 || ARCH_OMAP3
+       depends on ARCH_OMAP2 || ARCH_OMAP3 || (COMPILE_TEST && HAS_MMU)
        select VIDEOBUF_GEN
        select VIDEOBUF_DMA_CONTIG
        select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS
index 2d177fa5847168f65d58b33b664c4e40ee3dd638..64ab6fb06b9c6ed478661a93f7fdf8424422237a 100644 (file)
@@ -369,7 +369,7 @@ static int omapvid_setup_overlay(struct omap_vout_device *vout,
 {
        int ret = 0;
        struct omap_overlay_info info;
-       int cropheight, cropwidth, pixheight, pixwidth;
+       int cropheight, cropwidth, pixwidth;
 
        if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 &&
                        (outw != vout->pix.width || outh != vout->pix.height)) {
@@ -389,12 +389,10 @@ static int omapvid_setup_overlay(struct omap_vout_device *vout,
        if (is_rotation_90_or_270(vout)) {
                cropheight = vout->crop.width;
                cropwidth = vout->crop.height;
-               pixheight = vout->pix.width;
                pixwidth = vout->pix.height;
        } else {
                cropheight = vout->crop.height;
                cropwidth = vout->crop.width;
-               pixheight = vout->pix.height;
                pixwidth = vout->pix.width;
        }
 
@@ -991,7 +989,7 @@ static int omap_vout_release(struct file *file)
                mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN |
                        DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2;
                omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
-               vout->streaming = 0;
+               vout->streaming = false;
 
                videobuf_streamoff(q);
                videobuf_queue_cancel(q);
@@ -1451,12 +1449,10 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
        }
        case V4L2_CID_VFLIP:
        {
-               struct omap_overlay *ovl;
                struct omapvideo_info *ovid;
                unsigned int  mirror = a->value;
 
                ovid = &vout->vid_info;
-               ovl = ovid->overlays[0];
 
                mutex_lock(&vout->lock);
                if (mirror && ovid->rotation_type == VOUT_ROT_NONE) {
@@ -1489,7 +1485,7 @@ static int vidioc_reqbufs(struct file *file, void *fh,
        struct omap_vout_device *vout = fh;
        struct videobuf_queue *q = &vout->vbq;
 
-       if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0))
+       if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
                return -EINVAL;
        /* if memory is not mmp or userptr
           return error */
@@ -1648,7 +1644,7 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
        vout->field_id = 0;
 
        /* set flag here. Next QBUF will start DMA */
-       vout->streaming = 1;
+       vout->streaming = true;
 
        vout->first_int = 1;
 
@@ -1708,7 +1704,7 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
        if (!vout->streaming)
                return -EINVAL;
 
-       vout->streaming = 0;
+       vout->streaming = false;
        mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
                | DISPC_IRQ_VSYNC2;
 
@@ -1916,7 +1912,7 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout)
        control[0].id = V4L2_CID_ROTATE;
        control[0].value = 0;
        vout->rotation = 0;
-       vout->mirror = 0;
+       vout->mirror = false;
        vout->control[2].id = V4L2_CID_HFLIP;
        vout->control[2].value = 0;
        if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
index 62e7e5783ce809c52f8905d482bb7aa232a3c287..aa39306afc73ac84439655314460acfa3202b43d 100644 (file)
@@ -148,7 +148,7 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
                        ret =  -ENOMEM;
                        goto release_vrfb_ctx;
                }
-               vout->vrfb_static_allocation = 1;
+               vout->vrfb_static_allocation = true;
        }
        return 0;
 
@@ -336,7 +336,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
                offset = vout->vrfb_context[0].yoffset *
                        vout->vrfb_context[0].bytespp;
                temp_ps = ps / vr_ps;
-               if (mirroring == 0) {
+               if (!mirroring) {
                        *cropped_offset = offset + line_length *
                                temp_ps * cleft + crop->top * temp_ps;
                } else {
@@ -350,7 +350,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
                        vout->vrfb_context[0].bytespp) +
                        (vout->vrfb_context[0].xoffset *
                        vout->vrfb_context[0].bytespp));
-               if (mirroring == 0) {
+               if (!mirroring) {
                        *cropped_offset = offset + (line_length * ps * ctop) +
                                (cleft / vr_ps) * ps;
 
@@ -364,7 +364,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
                offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset *
                        vout->vrfb_context[0].bytespp;
                temp_ps = ps / vr_ps;
-               if (mirroring == 0) {
+               if (!mirroring) {
                        *cropped_offset = offset + line_length *
                            temp_ps * crop->left + ctop * ps;
                } else {
@@ -375,7 +375,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
                }
                break;
        case dss_rotation_0_degree:
-               if (mirroring == 0) {
+               if (!mirroring) {
                        *cropped_offset = (line_length * ps) *
                                crop->top + (crop->left / vr_ps) * ps;
                } else {
index ffde741e0590813c754dda94dc4ee0c9277484bc..4c2314839b487768a9257d61ac3e9a30a46da2c6 100644 (file)
@@ -23,18 +23,18 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
                        struct videobuf_buffer *vb);
 void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout);
 #else
-void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { }
-int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+static inline void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { };
+static inline int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
                        u32 static_vrfb_allocation)
-               { return 0; }
-void omap_vout_release_vrfb(struct omap_vout_device *vout) { }
-int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+               { return 0; };
+static inline void omap_vout_release_vrfb(struct omap_vout_device *vout) { };
+static inline int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
                        unsigned int *count, unsigned int startindex)
-               { return 0; }
-int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+               { return 0; };
+static inline int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
                        struct videobuf_buffer *vb)
-               { return 0; }
-void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) { }
+               { return 0; };
+static inline void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) { };
 #endif
 
 #endif
index c84df0706f3e098542d822e8fefc0755b010d836..e75b0eb2519b77f260e49acb84451ca99edd124c 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 { 244, 0, 247,   0,  12,  27,  36, 247, 250,   0,  27,   0,   4, 250,  12, 244,
index 78deebf7d9650df80a2011c8a175db7124ce4fa5..3b507078016d1e8b9f74f7ee6cd8633a1514ac3e 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
   0,   0,   1,   2,   3,   3,   4,   5,   6,   8,  10,  12,  14,  16,  18,  20,
index 2c7aa67205693f36287756296aaa6e8debf0bc1c..72265e58ca6090add9643759a8ad92a79e4f03bc 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <asm/cacheflush.h>
@@ -999,16 +989,14 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
                                         video, s_stream, 0);
                }
 
-               v4l2_subdev_call(subdev, video, s_stream, 0);
+               ret = v4l2_subdev_call(subdev, video, s_stream, 0);
 
                if (subdev == &isp->isp_res.subdev)
-                       ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
+                       ret |= isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
                else if (subdev == &isp->isp_prev.subdev)
-                       ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview);
+                       ret |= isp_pipeline_wait(isp, isp_pipeline_wait_preview);
                else if (subdev == &isp->isp_ccdc.subdev)
-                       ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc);
-               else
-                       ret = 0;
+                       ret |= isp_pipeline_wait(isp, isp_pipeline_wait_ccdc);
 
                /* Handle stop failures. An entity that fails to stop can
                 * usually just be restarted. Flag the stop failure nonetheless
index 2c314eea125275942db5076c34ac4613d4d02537..cfdfc8714b6b8f7fb7d633a75b48b82971b0e482 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_CORE_H
index 9f727d20f06d98298dcf36a4e0a9e8390ad2d9da..81a9dc053d5883e13844b78d99339cedc5cd7482 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <linux/module.h>
@@ -491,14 +481,13 @@ done:
 static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc)
 {
        unsigned long flags;
+       int ret;
 
        spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
-       if (ccdc->lsc.active) {
-               spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
-               return 1;
-       }
+       ret = ccdc->lsc.active != NULL;
        spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
-       return 0;
+
+       return ret;
 }
 
 static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc)
@@ -818,29 +807,48 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc)
        struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
        struct isp_device *isp = to_isp_device(ccdc);
        const struct isp_format_info *info;
+       struct v4l2_mbus_framefmt *format;
        unsigned long l3_ick = pipe->l3_ick;
        unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8;
        unsigned int div = 0;
-       u32 fmtcfg_vp;
+       u32 fmtcfg = ISPCCDC_FMTCFG_VPEN;
+
+       format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
+
+       if (!format->code) {
+               /* Disable the video port when the input format isn't supported.
+                * This is indicated by a pixel code set to 0.
+                */
+               isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
+               return;
+       }
+
+       isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) |
+                      (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT),
+                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ);
+       isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
+                      ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
+                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
 
-       fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG)
-                 & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK);
+       isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) |
+                      (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
+                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
 
        info = omap3isp_video_format_info(ccdc->formats[CCDC_PAD_SINK].code);
 
        switch (info->width) {
        case 8:
        case 10:
-               fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0;
+               fmtcfg |= ISPCCDC_FMTCFG_VPIN_9_0;
                break;
        case 11:
-               fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1;
+               fmtcfg |= ISPCCDC_FMTCFG_VPIN_10_1;
                break;
        case 12:
-               fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2;
+               fmtcfg |= ISPCCDC_FMTCFG_VPIN_11_2;
                break;
        case 13:
-               fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3;
+               fmtcfg |= ISPCCDC_FMTCFG_VPIN_12_3;
                break;
        }
 
@@ -850,75 +858,59 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc)
                div = l3_ick / pipe->external_rate;
 
        div = clamp(div, 2U, max_div);
-       fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT;
+       fmtcfg |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT;
 
-       isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
-}
-
-/*
- * ccdc_enable_vp - Enable Video Port.
- * @ccdc: Pointer to ISP CCDC device.
- * @enable: 0 Disables VP, 1 Enables VP
- *
- * This is needed for outputting image to Preview, H3A and HIST ISP submodules.
- */
-static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable)
-{
-       struct isp_device *isp = to_isp_device(ccdc);
-
-       isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG,
-                       ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0);
+       isp_reg_writel(isp, fmtcfg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
 }
 
 /*
  * ccdc_config_outlineoffset - Configure memory saving output line offset
  * @ccdc: Pointer to ISP CCDC device.
- * @offset: Address offset to start a new line. Must be twice the
- *          Output width and aligned on 32 byte boundary
- * @oddeven: Specifies the odd/even line pattern to be chosen to store the
- *           output.
- * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines.
+ * @bpl: Number of bytes per line when stored in memory.
+ * @field: Field order when storing interlaced formats in memory.
+ *
+ * Configure the offsets for the line output control:
+ *
+ * - The horizontal line offset is defined as the number of bytes between the
+ *   start of two consecutive lines in memory. Set it to the given bytes per
+ *   line value.
+ *
+ * - The field offset value is defined as the number of lines to offset the
+ *   start of the field identified by FID = 1. Set it to one.
  *
- * - Configures the output line offset when stored in memory
- * - Sets the odd/even line pattern to store the output
- *    (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4))
- * - Configures the number of even and odd line fields in case of rearranging
- * the lines.
+ * - The line offset values are defined as the number of lines (as defined by
+ *   the horizontal line offset) between the start of two consecutive lines for
+ *   all combinations of odd/even lines in odd/even fields. When interleaving
+ *   fields set them all to two lines, and to one line otherwise.
  */
 static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc,
-                                       u32 offset, u8 oddeven, u8 numlines)
+                                     unsigned int bpl,
+                                     enum v4l2_field field)
 {
        struct isp_device *isp = to_isp_device(ccdc);
+       u32 sdofst = 0;
 
-       isp_reg_writel(isp, offset & 0xffff,
-                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF);
+       isp_reg_writel(isp, bpl & 0xffff, OMAP3_ISP_IOMEM_CCDC,
+                      ISPCCDC_HSIZE_OFF);
 
-       isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
-                   ISPCCDC_SDOFST_FINV);
-
-       isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
-                   ISPCCDC_SDOFST_FOFST_4L);
-
-       switch (oddeven) {
-       case EVENEVEN:
-               isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
-                           (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT);
-               break;
-       case ODDEVEN:
-               isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
-                           (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT);
-               break;
-       case EVENODD:
-               isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
-                           (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT);
-               break;
-       case ODDODD:
-               isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
-                           (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT);
+       switch (field) {
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_INTERLACED_BT:
+               /* When interleaving fields in memory offset field one by one
+                * line and set the line offset to two lines.
+                */
+               sdofst |= (1 << ISPCCDC_SDOFST_LOFST0_SHIFT)
+                      |  (1 << ISPCCDC_SDOFST_LOFST1_SHIFT)
+                      |  (1 << ISPCCDC_SDOFST_LOFST2_SHIFT)
+                      |  (1 << ISPCCDC_SDOFST_LOFST3_SHIFT);
                break;
+
        default:
+               /* In all other cases set the line offsets to one line. */
                break;
        }
+
+       isp_reg_writel(isp, sdofst, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST);
 }
 
 /*
@@ -981,10 +973,16 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
 
        if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
            format->code == V4L2_MBUS_FMT_UYVY8_2X8) {
-               /* The bridge is enabled for YUV8 formats. Configure the input
-                * mode accordingly.
+               /* According to the OMAP3 TRM the input mode only affects SYNC
+                * mode, enabling BT.656 mode should take precedence. However,
+                * in practice setting the input mode to YCbCr data on 8 bits
+                * seems to be required in BT.656 mode. In SYNC mode set it to
+                * YCbCr on 16 bits as the bridge is enabled in that case.
                 */
-               syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16;
+               if (ccdc->bt656)
+                       syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR8;
+               else
+                       syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16;
        }
 
        switch (data_size) {
@@ -1008,9 +1006,15 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
        if (pdata && pdata->hs_pol)
                syn_mode |= ISPCCDC_SYN_MODE_HDPOL;
 
-       if (pdata && pdata->vs_pol)
+       /* The polarity of the vertical sync signal output by the BT.656
+        * decoder is not documented and seems to be active low.
+        */
+       if ((pdata && pdata->vs_pol) || ccdc->bt656)
                syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
 
+       if (pdata && pdata->fld_pol)
+               syn_mode |= ISPCCDC_SYN_MODE_FLDPOL;
+
        isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
 
        /* The CCDC_CFG.Y8POS bit is used in YCbCr8 input mode only. The
@@ -1023,8 +1027,16 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
                isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
                            ISPCCDC_CFG_Y8POS);
 
-       isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
-                   ISPCCDC_REC656IF_R656ON);
+       /* Enable or disable BT.656 mode, including error correction for the
+        * synchronization codes.
+        */
+       if (ccdc->bt656)
+               isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
+                           ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH);
+       else
+               isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
+                           ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH);
+
 }
 
 /* CCDC formats descriptions */
@@ -1115,17 +1127,33 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
        unsigned long flags;
        unsigned int bridge;
        unsigned int shift;
+       unsigned int nph;
+       unsigned int sph;
        u32 syn_mode;
        u32 ccdc_pattern;
 
+       ccdc->bt656 = false;
+       ccdc->fields = 0;
+
        pad = media_entity_remote_pad(&ccdc->pads[CCDC_PAD_SINK]);
        sensor = media_entity_to_v4l2_subdev(pad->entity);
-       if (ccdc->input == CCDC_INPUT_PARALLEL)
+       if (ccdc->input == CCDC_INPUT_PARALLEL) {
+               struct v4l2_mbus_config cfg;
+               int ret;
+
+               ret = v4l2_subdev_call(sensor, video, g_mbus_config, &cfg);
+               if (!ret)
+                       ccdc->bt656 = cfg.type == V4L2_MBUS_BT656;
+
                pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
                        ->bus.parallel;
+       }
+
+       /* CCDC_PAD_SINK */
+       format = &ccdc->formats[CCDC_PAD_SINK];
 
        /* Compute the lane shifter shift value and enable the bridge when the
-        * input format is YUV.
+        * input format is a non-BT.656 YUV variant.
         */
        fmt_src.pad = pad->index;
        fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
@@ -1134,12 +1162,13 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
                depth_in = fmt_info->width;
        }
 
-       fmt_info = omap3isp_video_format_info
-               (isp->isp_ccdc.formats[CCDC_PAD_SINK].code);
+       fmt_info = omap3isp_video_format_info(format->code);
        depth_out = fmt_info->width;
        shift = depth_in - depth_out;
 
-       if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8)
+       if (ccdc->bt656)
+               bridge = ISPCTRL_PAR_BRIDGE_DISABLE;
+       else if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8)
                bridge = ISPCTRL_PAR_BRIDGE_LENDIAN;
        else if (fmt_info->code == V4L2_MBUS_FMT_UYVY8_2X8)
                bridge = ISPCTRL_PAR_BRIDGE_BENDIAN;
@@ -1148,6 +1177,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
 
        omap3isp_configure_bridge(isp, ccdc->input, pdata, shift, bridge);
 
+       /* Configure the sync interface. */
        ccdc_config_sync_if(ccdc, pdata, depth_out);
 
        syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
@@ -1167,9 +1197,6 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
        else
                syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ;
 
-       /* CCDC_PAD_SINK */
-       format = &ccdc->formats[CCDC_PAD_SINK];
-
        /* Mosaic filter */
        switch (format->code) {
        case V4L2_MBUS_FMT_SRGGB10_1X10:
@@ -1202,16 +1229,40 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
        format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
        crop = &ccdc->crop;
 
-       isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
-                      ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
+       /* The horizontal coordinates are expressed in pixel clock cycles. We
+        * need two cycles per pixel in BT.656 mode, and one cycle per pixel in
+        * SYNC mode regardless of the format as the bridge is enabled for YUV
+        * formats in that case.
+        */
+       if (ccdc->bt656) {
+               sph = crop->left * 2;
+               nph = crop->width * 2 - 1;
+       } else {
+               sph = crop->left;
+               nph = crop->width - 1;
+       }
+
+       isp_reg_writel(isp, (sph << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
+                      (nph << ISPCCDC_HORZ_INFO_NPH_SHIFT),
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO);
-       isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT,
+       isp_reg_writel(isp, (crop->top << ISPCCDC_VERT_START_SLV0_SHIFT) |
+                      (crop->top << ISPCCDC_VERT_START_SLV1_SHIFT),
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
        isp_reg_writel(isp, (crop->height - 1)
                        << ISPCCDC_VERT_LINES_NLV_SHIFT,
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
 
-       ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0);
+       ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value,
+                                 format->field);
+
+       /* When interleaving fields enable processing of the field input signal.
+        * This will cause the line output control module to apply the field
+        * offset to field 1.
+        */
+       if (ccdc->formats[CCDC_PAD_SINK].field == V4L2_FIELD_ALTERNATE &&
+           (format->field == V4L2_FIELD_INTERLACED_TB ||
+            format->field == V4L2_FIELD_INTERLACED_BT))
+               syn_mode |= ISPCCDC_SYN_MODE_FLDMODE;
 
        /* The CCDC outputs data in UYVY order by default. Swap bytes to get
         * YUYV.
@@ -1223,8 +1274,11 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
                isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
                            ISPCCDC_CFG_BSWD);
 
-       /* Use PACK8 mode for 1byte per pixel formats. */
-       if (omap3isp_video_format_info(format->code)->width <= 8)
+       /* Use PACK8 mode for 1byte per pixel formats. Check for BT.656 mode
+        * explicitly as the driver reports 1X16 instead of 2X8 at the OF pad
+        * for simplicity.
+        */
+       if (omap3isp_video_format_info(format->code)->width <= 8 || ccdc->bt656)
                syn_mode |= ISPCCDC_SYN_MODE_PACK8;
        else
                syn_mode &= ~ISPCCDC_SYN_MODE_PACK8;
@@ -1232,18 +1286,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
        isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
 
        /* CCDC_PAD_SOURCE_VP */
-       format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
-
-       isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) |
-                      (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT),
-                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ);
-       isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
-                      ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
-                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
-
-       isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) |
-                      (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
-                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
+       ccdc_config_vp(ccdc);
 
        /* Lens shading correction. */
        spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
@@ -1277,6 +1320,8 @@ static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable)
 
        isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR,
                        ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0);
+
+       ccdc->running = enable;
 }
 
 static int ccdc_disable(struct isp_ccdc_device *ccdc)
@@ -1287,6 +1332,8 @@ static int ccdc_disable(struct isp_ccdc_device *ccdc)
        spin_lock_irqsave(&ccdc->lock, flags);
        if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS)
                ccdc->stopping = CCDC_STOP_REQUEST;
+       if (!ccdc->running)
+               ccdc->stopping = CCDC_STOP_FINISHED;
        spin_unlock_irqrestore(&ccdc->lock, flags);
 
        ret = wait_event_timeout(ccdc->wait,
@@ -1369,14 +1416,14 @@ static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc,
        return -EBUSY;
 }
 
-/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence
+/* ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence
  * @ccdc: Pointer to ISP CCDC device.
  * @event: Pointing which event trigger handler
  *
  * Return 1 when the event and stopping request combination is satisfied,
  * zero otherwise.
  */
-static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
+static int ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
 {
        int rval = 0;
 
@@ -1458,7 +1505,7 @@ static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events)
        if (ccdc->lsc.state == LSC_STATE_STOPPING)
                ccdc->lsc.state = LSC_STATE_STOPPED;
 
-       if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE))
+       if (ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE))
                goto done;
 
        if (ccdc->lsc.state != LSC_STATE_RECONFIG)
@@ -1486,12 +1533,59 @@ done:
        spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
 }
 
+/*
+ * Check whether the CCDC has captured all fields necessary to complete the
+ * buffer.
+ */
+static bool ccdc_has_all_fields(struct isp_ccdc_device *ccdc)
+{
+       struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+       struct isp_device *isp = to_isp_device(ccdc);
+       enum v4l2_field of_field = ccdc->formats[CCDC_PAD_SOURCE_OF].field;
+       enum v4l2_field field;
+
+       /* When the input is progressive fields don't matter. */
+       if (of_field == V4L2_FIELD_NONE)
+               return true;
+
+       /* Read the current field identifier. */
+       field = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE)
+             & ISPCCDC_SYN_MODE_FLDSTAT
+             ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
+
+       /* When capturing fields in alternate order just store the current field
+        * identifier in the pipeline.
+        */
+       if (of_field == V4L2_FIELD_ALTERNATE) {
+               pipe->field = field;
+               return true;
+       }
+
+       /* The format is interlaced. Make sure we've captured both fields. */
+       ccdc->fields |= field == V4L2_FIELD_BOTTOM
+                     ? CCDC_FIELD_BOTTOM : CCDC_FIELD_TOP;
+
+       if (ccdc->fields != CCDC_FIELD_BOTH)
+               return false;
+
+       /* Verify that the field just captured corresponds to the last field
+        * needed based on the desired field order.
+        */
+       if ((of_field == V4L2_FIELD_INTERLACED_TB && field == V4L2_FIELD_TOP) ||
+           (of_field == V4L2_FIELD_INTERLACED_BT && field == V4L2_FIELD_BOTTOM))
+               return false;
+
+       /* The buffer can be completed, reset the fields for the next buffer. */
+       ccdc->fields = 0;
+
+       return true;
+}
+
 static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
 {
        struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
        struct isp_device *isp = to_isp_device(ccdc);
        struct isp_buffer *buffer;
-       int restart = 0;
 
        /* The CCDC generates VD0 interrupts even when disabled (the datasheet
         * doesn't explicitly state if that's supposed to happen or not, so it
@@ -1500,30 +1594,31 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
         * would thus not be enough, we need to handle the situation explicitly.
         */
        if (list_empty(&ccdc->video_out.dmaqueue))
-               goto done;
+               return 0;
 
        /* We're in continuous mode, and memory writes were disabled due to a
         * buffer underrun. Reenable them now that we have a buffer. The buffer
         * address has been set in ccdc_video_queue.
         */
        if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) {
-               restart = 1;
                ccdc->underrun = 0;
-               goto done;
+               return 1;
        }
 
+       /* Wait for the CCDC to become idle. */
        if (ccdc_sbl_wait_idle(ccdc, 1000)) {
                dev_info(isp->dev, "CCDC won't become idle!\n");
                isp->crashed |= 1U << ccdc->subdev.entity.id;
                omap3isp_pipeline_cancel_stream(pipe);
-               goto done;
+               return 0;
        }
 
+       if (!ccdc_has_all_fields(ccdc))
+               return 1;
+
        buffer = omap3isp_video_buffer_next(&ccdc->video_out);
-       if (buffer != NULL) {
+       if (buffer != NULL)
                ccdc_set_outaddr(ccdc, buffer->dma);
-               restart = 1;
-       }
 
        pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
 
@@ -1532,8 +1627,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
                omap3isp_pipeline_set_stream(pipe,
                                        ISP_PIPELINE_STREAM_SINGLESHOT);
 
-done:
-       return restart;
+       return buffer != NULL;
 }
 
 /*
@@ -1547,11 +1641,38 @@ static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc)
        unsigned long flags;
        int restart = 0;
 
+       /* In BT.656 mode the CCDC doesn't generate an HS/VS interrupt. We thus
+        * need to increment the frame counter here.
+        */
+       if (ccdc->bt656) {
+               struct isp_pipeline *pipe =
+                       to_isp_pipeline(&ccdc->subdev.entity);
+
+               atomic_inc(&pipe->frame_number);
+       }
+
+       /* Emulate a VD1 interrupt for BT.656 mode, as we can't stop the CCDC in
+        * the VD1 interrupt handler in that mode without risking a CCDC stall
+        * if a short frame is received.
+        */
+       if (ccdc->bt656) {
+               spin_lock_irqsave(&ccdc->lock, flags);
+               if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
+                   ccdc->output & CCDC_OUTPUT_MEMORY) {
+                       if (ccdc->lsc.state != LSC_STATE_STOPPED)
+                               __ccdc_lsc_enable(ccdc, 0);
+                       __ccdc_enable(ccdc, 0);
+               }
+               ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1);
+               spin_unlock_irqrestore(&ccdc->lock, flags);
+       }
+
        if (ccdc->output & CCDC_OUTPUT_MEMORY)
                restart = ccdc_isr_buffer(ccdc);
 
        spin_lock_irqsave(&ccdc->lock, flags);
-       if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
+
+       if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
                spin_unlock_irqrestore(&ccdc->lock, flags);
                return;
        }
@@ -1572,6 +1693,18 @@ static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
 {
        unsigned long flags;
 
+       /* In BT.656 mode the synchronization signals are generated by the CCDC
+        * from the embedded sync codes. The VD0 and VD1 interrupts are thus
+        * only triggered when the CCDC is enabled, unlike external sync mode
+        * where the line counter runs even when the CCDC is stopped. We can't
+        * disable the CCDC at VD1 time, as no VD0 interrupt would be generated
+        * for a short frame, which would result in the CCDC being stopped and
+        * no VD interrupt generated anymore. The CCDC is stopped from the VD0
+        * interrupt handler instead for BT.656.
+        */
+       if (ccdc->bt656)
+               return;
+
        spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
 
        /*
@@ -1601,7 +1734,7 @@ static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
                break;
        }
 
-       if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
+       if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
                goto done;
 
        if (ccdc->lsc.request == NULL)
@@ -1656,6 +1789,8 @@ int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events)
 static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
 {
        struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc;
+       unsigned long flags;
+       bool restart = false;
 
        if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
                return -ENODEV;
@@ -1664,9 +1799,20 @@ static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
 
        /* We now have a buffer queued on the output, restart the pipeline
         * on the next CCDC interrupt if running in continuous mode (or when
-        * starting the stream).
+        * starting the stream) in external sync mode, or immediately in BT.656
+        * sync mode as no CCDC interrupt is generated when the CCDC is stopped
+        * in that case.
         */
-       ccdc->underrun = 1;
+       spin_lock_irqsave(&ccdc->lock, flags);
+       if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && !ccdc->running &&
+           ccdc->bt656)
+               restart = true;
+       else
+               ccdc->underrun = 1;
+       spin_unlock_irqrestore(&ccdc->lock, flags);
+
+       if (restart)
+               ccdc_enable(ccdc);
 
        return 0;
 }
@@ -1753,11 +1899,6 @@ static int ccdc_set_stream(struct v4l2_subdev *sd, int enable)
 
                ccdc_configure(ccdc);
 
-               /* TODO: Don't configure the video port if all of its output
-                * links are inactive.
-                */
-               ccdc_config_vp(ccdc);
-               ccdc_enable_vp(ccdc, 1);
                ccdc_print_status(ccdc);
        }
 
@@ -1830,6 +1971,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
        unsigned int width = fmt->width;
        unsigned int height = fmt->height;
        struct v4l2_rect *crop;
+       enum v4l2_field field;
        unsigned int i;
 
        switch (pad) {
@@ -1846,14 +1988,24 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
                /* Clamp the input size. */
                fmt->width = clamp_t(u32, width, 32, 4096);
                fmt->height = clamp_t(u32, height, 32, 4096);
+
+               /* Default to progressive field order. */
+               if (fmt->field == V4L2_FIELD_ANY)
+                       fmt->field = V4L2_FIELD_NONE;
+
                break;
 
        case CCDC_PAD_SOURCE_OF:
                pixelcode = fmt->code;
+               field = fmt->field;
                *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
 
-               /* YUV formats are converted from 2X8 to 1X16 by the bridge and
-                * can be byte-swapped.
+               /* In SYNC mode the bridge converts YUV formats from 2X8 to
+                * 1X16. In BT.656 no such conversion occurs. As we don't know
+                * at this point whether the source will use SYNC or BT.656 mode
+                * let's pretend the conversion always occurs. The CCDC will be
+                * configured to pack bytes in BT.656, hiding the inaccuracy.
+                * In all cases bytes can be swapped.
                 */
                if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
                    fmt->code == V4L2_MBUS_FMT_UYVY8_2X8) {
@@ -1874,6 +2026,17 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
                crop = __ccdc_get_crop(ccdc, fh, which);
                fmt->width = crop->width;
                fmt->height = crop->height;
+
+               /* When input format is interlaced with alternating fields the
+                * CCDC can interleave the fields.
+                */
+               if (fmt->field == V4L2_FIELD_ALTERNATE &&
+                   (field == V4L2_FIELD_INTERLACED_TB ||
+                    field == V4L2_FIELD_INTERLACED_BT)) {
+                       fmt->field = field;
+                       fmt->height *= 2;
+               }
+
                break;
 
        case CCDC_PAD_SOURCE_VP:
@@ -1901,7 +2064,6 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
         * stored on 2 bytes.
         */
        fmt->colorspace = V4L2_COLORSPACE_SRGB;
-       fmt->field = V4L2_FIELD_NONE;
 }
 
 /*
index f65061602c71e585695503d30c4d9f340af1a27a..3440a709794001a68513ba9300e677808b43e401 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_CCDC_H
@@ -103,6 +93,10 @@ struct ispccdc_lsc {
 #define CCDC_PAD_SOURCE_VP             2
 #define CCDC_PADS_NUM                  3
 
+#define CCDC_FIELD_TOP                 1
+#define CCDC_FIELD_BOTTOM              2
+#define CCDC_FIELD_BOTH                        3
+
 /*
  * struct isp_ccdc_device - Structure for the CCDC module to store its own
  *                         information
@@ -123,11 +117,14 @@ struct ispccdc_lsc {
  * @lsc: Lens shading compensation configuration
  * @update: Bitmask of controls to update during the next interrupt
  * @shadow_update: Controls update in progress by userspace
+ * @bt656: Whether the input interface uses BT.656 synchronization
+ * @fields: The fields (CCDC_FIELD_*) stored in the current buffer
  * @underrun: A buffer underrun occurred and a new buffer has been queued
  * @state: Streaming state
  * @lock: Serializes shadow_update with interrupt handler
  * @wait: Wait queue used to stop the module
  * @stopping: Stopping state
+ * @running: Is the CCDC hardware running
  * @ioctl_lock: Serializes ioctl calls and LSC requests freeing
  */
 struct isp_ccdc_device {
@@ -151,11 +148,15 @@ struct isp_ccdc_device {
        unsigned int update;
        unsigned int shadow_update;
 
+       bool bt656;
+       unsigned int fields;
+
        unsigned int underrun:1;
        enum isp_pipeline_stream_state state;
        spinlock_t lock;
        wait_queue_head_t wait;
        unsigned int stopping;
+       bool running;
        struct mutex ioctl_lock;
 };
 
index f3801db9095ca301bf53e564e9d9f4eb33a13327..9cb49b3c04bd2d596093bf6486f2b46c9cd99b3e 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <linux/delay.h>
index 76d65f4576ef017d6ca2dad854a0697e4799e35c..4662bffa79e31a0044e09efe8f09c9042f97869d 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_CCP2_H
index 5a2e47e58b846543f651d38acf5fba05012e2c3e..6530b255f1038cdb7f7cf46aa87a81d2ecbde586 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 #include <linux/delay.h>
 #include <media/v4l2-common.h>
index c57729b7e86e44c0ad74fe3bdb73a2645cff4fd4..453ed62fe3946c7a3dc7fb7cdc2399804fcfc7e1 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_CSI2_H
index c09de32f986a5d797b01ab4ac5f679078e39eb85..e033f2237a72f4383236556d984b6bd773b3832d 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <linux/delay.h>
index 14551fd7769780d3f319c6d5d146ed4742b291ac..e17c88beab92630722fdd6cc48d9a084d0bcfd8e 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_CSI_PHY_H
index fb09fd4ca7559f744ed49a4554a00e38f2d88933..e5b28d0f3b0f49a9069a9c50855e7064bfbb564d 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_H3A_H
index d6811ce263eb113102cceb7611a42e4c5f21f11d..b208c5417146e081facc8675f345f221e346f567 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <linux/slab.h>
index 6fc960cd30f57accab300b749aa7049dcf5efd28..8a83e195f3e30443dc914a4d10dc12edc90f02a9 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 /* Linux specific include files */
index 06a5f8164eaa0a4e5aa9b3e8b64674d83a5f3042..ce822c34c843a7d9c92c020ccd9d19526ac32af1 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <linux/delay.h>
index 0b2a38ec94c4af59bb9c30750d7574d24c4b1dc5..3b5415517dcda6992914b90d3532e80586d87bb9 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_HIST_H
index 720809b07e75f4779f20da64aa1b0af498bad012..605f57ef0a493a82439d3055c233c68015dfaba3 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <linux/device.h>
index f66923407f8c5a90f2a4280201cb21460ad2fab6..16fdc03a3d43bfb114d3ee64e6a5e04521cb5357 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_PREVIEW_H
index b7d90e6fb01dc043c240a20bd27ae444dd96fc79..b5ea8da0b904cbb0498ab7001e1e1e98acb4707a 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_REG_H
 
 #define ISPCCDC_HSIZE_OFF_SHIFT                        0
 
-#define ISPCCDC_SDOFST_FINV                    (1 << 14)
-#define ISPCCDC_SDOFST_FOFST_1L                        0
-#define ISPCCDC_SDOFST_FOFST_4L                        (3 << 12)
+#define ISPCCDC_SDOFST_FIINV                   (1 << 14)
+#define ISPCCDC_SDOFST_FOFST_SHIFT             12
+#define ISPCCDC_SDOFST_FOFST_MASK              (3 << 12)
 #define ISPCCDC_SDOFST_LOFST3_SHIFT            0
 #define ISPCCDC_SDOFST_LOFST2_SHIFT            3
 #define ISPCCDC_SDOFST_LOFST1_SHIFT            6
 #define ISPCCDC_SDOFST_LOFST0_SHIFT            9
-#define EVENEVEN                               1
-#define ODDEVEN                                        2
-#define EVENODD                                        3
-#define ODDODD                                 4
 
 #define ISPCCDC_CLAMP_OBGAIN_SHIFT             0
 #define ISPCCDC_CLAMP_OBST_SHIFT               10
index 6f077c2377db6a7b785f1f8a4b688c2996c0d2b1..05d1ace57451a67536e0d7b7821f953ef5515e7f 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <linux/device.h>
@@ -239,7 +229,7 @@ static void resizer_set_phase(struct isp_res_device *res, u32 h_phase,
                              u32 v_phase)
 {
        struct isp_device *isp = to_isp_device(res);
-       u32 rgval = 0;
+       u32 rgval;
 
        rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
              ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK);
@@ -275,7 +265,7 @@ static void resizer_set_luma(struct isp_res_device *res,
                             struct resizer_luma_yenh *luma)
 {
        struct isp_device *isp = to_isp_device(res);
-       u32 rgval = 0;
+       u32 rgval;
 
        rgval  = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT)
                  & ISPRSZ_YENH_ALGO_MASK;
@@ -322,7 +312,7 @@ static void resizer_set_ratio(struct isp_res_device *res,
 {
        struct isp_device *isp = to_isp_device(res);
        const u16 *h_filter, *v_filter;
-       u32 rgval = 0;
+       u32 rgval;
 
        rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
                              ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK);
@@ -365,9 +355,8 @@ static void resizer_set_output_size(struct isp_res_device *res,
                                    u32 width, u32 height)
 {
        struct isp_device *isp = to_isp_device(res);
-       u32 rgval = 0;
+       u32 rgval;
 
-       dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height);
        rgval  = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT)
                 & ISPRSZ_OUT_SIZE_HORZ_MASK;
        rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT)
@@ -409,7 +398,7 @@ static void resizer_set_output_offset(struct isp_res_device *res, u32 offset)
 static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top)
 {
        struct isp_device *isp = to_isp_device(res);
-       u32 rgval = 0;
+       u32 rgval;
 
        rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT)
                & ISPRSZ_IN_START_HORZ_ST_MASK;
@@ -429,9 +418,7 @@ static void resizer_set_input_size(struct isp_res_device *res,
                                   u32 width, u32 height)
 {
        struct isp_device *isp = to_isp_device(res);
-       u32 rgval = 0;
-
-       dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height);
+       u32 rgval;
 
        rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT)
                & ISPRSZ_IN_SIZE_HORZ_MASK;
@@ -1075,10 +1062,13 @@ static void resizer_isr_buffer(struct isp_res_device *res)
 void omap3isp_resizer_isr(struct isp_res_device *res)
 {
        struct v4l2_mbus_framefmt *informat, *outformat;
+       unsigned long flags;
 
        if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping))
                return;
 
+       spin_lock_irqsave(&res->lock, flags);
+
        if (res->applycrop) {
                outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE,
                                              V4L2_SUBDEV_FORMAT_ACTIVE);
@@ -1088,6 +1078,8 @@ void omap3isp_resizer_isr(struct isp_res_device *res)
                res->applycrop = 0;
        }
 
+       spin_unlock_irqrestore(&res->lock, flags);
+
        resizer_isr_buffer(res);
 }
 
@@ -1290,8 +1282,10 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
 {
        struct isp_res_device *res = v4l2_get_subdevdata(sd);
        struct isp_device *isp = to_isp_device(res);
-       struct v4l2_mbus_framefmt *format_sink, *format_source;
+       const struct v4l2_mbus_framefmt *format_sink;
+       struct v4l2_mbus_framefmt format_source;
        struct resizer_ratio ratio;
+       unsigned long flags;
 
        if (sel->target != V4L2_SEL_TGT_CROP ||
            sel->pad != RESZ_PAD_SINK)
@@ -1299,16 +1293,14 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
 
        format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
                                           sel->which);
-       format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
-                                            sel->which);
-
-       dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__,
-               sel->r.left, sel->r.top, sel->r.width, sel->r.height,
-               sel->which);
+       format_source = *__resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+                                             sel->which);
 
-       dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__,
+       dev_dbg(isp->dev, "%s(%s): req %ux%u -> (%d,%d)/%ux%u -> %ux%u\n",
+               __func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act",
                format_sink->width, format_sink->height,
-               format_source->width, format_source->height);
+               sel->r.left, sel->r.top, sel->r.width, sel->r.height,
+               format_source.width, format_source.height);
 
        /* Clamp the crop rectangle to the bounds, and then mangle it further to
         * fulfill the TRM equations. Store the clamped but otherwise unmangled
@@ -1318,23 +1310,39 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
         * smaller input crop rectangle every time the output size is set if we
         * stored the mangled rectangle.
         */
-       resizer_try_crop(format_sink, format_source, &sel->r);
+       resizer_try_crop(format_sink, &format_source, &sel->r);
        *__resizer_get_crop(res, fh, sel->which) = sel->r;
-       resizer_calc_ratios(res, &sel->r, format_source, &ratio);
+       resizer_calc_ratios(res, &sel->r, &format_source, &ratio);
 
-       if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
+       dev_dbg(isp->dev, "%s(%s): got %ux%u -> (%d,%d)/%ux%u -> %ux%u\n",
+               __func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act",
+               format_sink->width, format_sink->height,
+               sel->r.left, sel->r.top, sel->r.width, sel->r.height,
+               format_source.width, format_source.height);
+
+       if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+               *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) =
+                       format_source;
                return 0;
+       }
+
+       /* Update the source format, resizing ratios and crop rectangle. If
+        * streaming is on the IRQ handler will reprogram the resizer after the
+        * current frame. We thus we need to protect against race conditions.
+        */
+       spin_lock_irqsave(&res->lock, flags);
+
+       *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) =
+               format_source;
 
        res->ratio = ratio;
        res->crop.active = sel->r;
 
-       /*
-        * set_selection can be called while streaming is on. In this case the
-        * crop values will be set in the next IRQ.
-        */
        if (res->state != ISP_PIPELINE_STREAM_STOPPED)
                res->applycrop = 1;
 
+       spin_unlock_irqrestore(&res->lock, flags);
+
        return 0;
 }
 
@@ -1781,6 +1789,8 @@ int omap3isp_resizer_init(struct isp_device *isp)
 
        init_waitqueue_head(&res->wait);
        atomic_set(&res->stopping, 0);
+       spin_lock_init(&res->lock);
+
        return resizer_init_entities(res);
 }
 
index 9b01e9047c15d8ce98d177760ff82a3f588c3071..5414542912e2723b39f0ec034293af10ab7c279c 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_RESIZER_H
 #define OMAP3_ISP_RESIZER_H
 
+#include <linux/spinlock.h>
 #include <linux/types.h>
 
 /*
@@ -96,6 +87,7 @@ enum resizer_input_entity {
 
 /*
  * struct isp_res_device - OMAP3 ISP resizer module
+ * @lock: Protects formats and crop rectangles between set_selection and IRQ
  * @crop.request: Crop rectangle requested by the user
  * @crop.active: Active crop rectangle (based on hardware requirements)
  */
@@ -116,6 +108,7 @@ struct isp_res_device {
        enum isp_pipeline_stream_state state;
        wait_queue_head_t wait;
        atomic_t stopping;
+       spinlock_t lock;
 
        struct {
                struct v4l2_rect request;
index e6cbc1eaf4cab03fa5c29ab1ffba1de8d7bb6cec..a94e8340508f703efea86f51232578e30912f496 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <linux/dma-mapping.h>
index 58d6ac7cb6648ce18f2e8284cc85fe9d96199242..b32b29677e2c48f3fc3bd4dbe9d9208452ee1cf5 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_STAT_H
index e36bac26476c0fd7ac382f5e85effc07a7a0b202..bc38c88c7bd9cc179de2afd0b120aa00dc139833 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #include <asm/cacheflush.h>
@@ -319,10 +309,11 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
            vfh->format.fmt.pix.height != format.fmt.pix.height ||
            vfh->format.fmt.pix.width != format.fmt.pix.width ||
            vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
-           vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
+           vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage ||
+           vfh->format.fmt.pix.field != format.fmt.pix.field)
                return -EINVAL;
 
-       return ret;
+       return 0;
 }
 
 /* -----------------------------------------------------------------------------
@@ -491,6 +482,11 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
        else
                buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
 
+       if (pipe->field != V4L2_FIELD_NONE)
+               buf->vb.v4l2_buf.sequence /= 2;
+
+       buf->vb.v4l2_buf.field = pipe->field;
+
        /* Report pipeline errors to userspace on the capture device side. */
        if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
                state = VB2_BUF_STATE_ERROR;
@@ -641,7 +637,40 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
        if (format->type != video->type)
                return -EINVAL;
 
-       mutex_lock(&video->mutex);
+       /* Replace unsupported field orders with sane defaults. */
+       switch (format->fmt.pix.field) {
+       case V4L2_FIELD_NONE:
+               /* Progressive is supported everywhere. */
+               break;
+       case V4L2_FIELD_ALTERNATE:
+               /* ALTERNATE is not supported on output nodes. */
+               if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+                       format->fmt.pix.field = V4L2_FIELD_NONE;
+               break;
+       case V4L2_FIELD_INTERLACED:
+               /* The ISP has no concept of video standard, select the
+                * top-bottom order when the unqualified interlaced order is
+                * requested.
+                */
+               format->fmt.pix.field = V4L2_FIELD_INTERLACED_TB;
+               /* Fall-through */
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_INTERLACED_BT:
+               /* Interlaced orders are only supported at the CCDC output. */
+               if (video != &video->isp->isp_ccdc.video_out)
+                       format->fmt.pix.field = V4L2_FIELD_NONE;
+               break;
+       case V4L2_FIELD_TOP:
+       case V4L2_FIELD_BOTTOM:
+       case V4L2_FIELD_SEQ_TB:
+       case V4L2_FIELD_SEQ_BT:
+       default:
+               /* All other field orders are currently unsupported, default to
+                * progressive.
+                */
+               format->fmt.pix.field = V4L2_FIELD_NONE;
+               break;
+       }
 
        /* Fill the bytesperline and sizeimage fields by converting to media bus
         * format and back to pixel format.
@@ -649,9 +678,10 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
        isp_video_pix_to_mbus(&format->fmt.pix, &fmt);
        isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
 
+       mutex_lock(&video->mutex);
        vfh->format = *format;
-
        mutex_unlock(&video->mutex);
+
        return 0;
 }
 
@@ -1039,6 +1069,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
        video->queue = &vfh->queue;
        INIT_LIST_HEAD(&video->dmaqueue);
        atomic_set(&pipe->frame_number, -1);
+       pipe->field = vfh->format.fmt.pix.field;
 
        mutex_lock(&video->queue_lock);
        ret = vb2_streamon(&vfh->queue, type);
index 7d2e82122ecda431d417ae10ba8193b9fb9154ec..0b7efedc3da990945bddc7e32389b55693df1ab3 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 #ifndef OMAP3_ISP_VIDEO_H
@@ -88,6 +78,7 @@ enum isp_pipeline_state {
 
 /*
  * struct isp_pipeline - An ISP hardware pipeline
+ * @field: The field being processed by the pipeline
  * @error: A hardware error occurred during capture
  * @entities: Bitmask of entities in the pipeline (indexed by entity ID)
  */
@@ -101,6 +92,7 @@ struct isp_pipeline {
        u32 entities;
        unsigned long l3_ick;
        unsigned int max_rate;
+       enum v4l2_field field;
        atomic_t frame_number;
        bool do_propagation; /* of frame number */
        bool error;
index 098b45e2280f064fc3a304555e3925c1b61ee841..81c5b1566469f16587c319907bf30d6d860243f7 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552,
index d50451a4a2424616915e6c40b18ecb0b8f2454f2..5073f98479379cf3dde0c8ddb47fac14c49ac6fb 100644 (file)
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
  */
 
 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
index f33641384e15adf817542a689bb5df57082fe812..4f81b4c9d113f1a61aa8d10751d2f898dd70228d 100644 (file)
@@ -280,8 +280,8 @@ static int camif_prepare_addr(struct camif_vp *vp, struct vb2_buffer *vb,
                return -EINVAL;
        }
 
-       pr_debug("DMA address: y: %#x  cb: %#x cr: %#x\n",
-                paddr->y, paddr->cb, paddr->cr);
+       pr_debug("DMA address: y: %pad  cb: %pad cr: %pad\n",
+                &paddr->y, &paddr->cb, &paddr->cr);
 
        return 0;
 }
index ebf5b184cce427a1b4274328f7683bd5408af03a..6e0c9988a1919df0ea9b89fb0349f77d7db66e55 100644 (file)
@@ -214,8 +214,8 @@ void camif_hw_set_output_addr(struct camif_vp *vp,
                                                                paddr->cr);
        }
 
-       pr_debug("dst_buf[%d]: %#X, cb: %#X, cr: %#X\n",
-                i, paddr->y, paddr->cb, paddr->cr);
+       pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad\n",
+                i, &paddr->y, &paddr->cb, &paddr->cr);
 }
 
 static void camif_hw_set_out_dma_size(struct camif_vp *vp)
index 357af1ebaeda2791667e8f5c32b12fe7f45bf18a..d79e214ce8ce3e7526027aea3141c16f9a609a39 100644 (file)
@@ -490,14 +490,13 @@ static void job_abort(void *prv)
 {
        struct g2d_ctx *ctx = prv;
        struct g2d_dev *dev = ctx->dev;
-       int ret;
 
        if (dev->curr == NULL) /* No job currently running */
                return;
 
-       ret = wait_event_timeout(dev->irq_queue,
-               dev->curr == NULL,
-               msecs_to_jiffies(G2D_TIMEOUT));
+       wait_event_timeout(dev->irq_queue,
+                          dev->curr == NULL,
+                          msecs_to_jiffies(G2D_TIMEOUT));
 }
 
 static void device_run(void *prv)
index e66acbc2a82d7dcca7dbe106de82b19a68633a51..e525a7c8d885770c7f0e85e3219a387f432a13c2 100644 (file)
@@ -729,7 +729,7 @@ static inline void exynos4_jpeg_set_qtbl_chr(void __iomem *regs, int quality)
                             ARRAY_SIZE(qtbl_chrominance[quality]));
 }
 
-void exynos4_jpeg_set_huff_tbl(void __iomem *base)
+static void exynos4_jpeg_set_huff_tbl(void __iomem *base)
 {
        exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCLL,
                                                        ARRAY_SIZE(hdctbl0));
index d26e1f8465536614201a2a3a66035f3061f95d93..e8c2cad9396272ed9fa6f43c0fbab2699ba0073e 100644 (file)
@@ -233,6 +233,7 @@ void exynos3250_jpeg_set_x(void __iomem *regs, unsigned int x)
        writel(reg, regs + EXYNOS3250_JPGX);
 }
 
+#if 0  /* Currently unused */
 unsigned int exynos3250_jpeg_get_y(void __iomem *regs)
 {
        return readl(regs + EXYNOS3250_JPGY);
@@ -242,6 +243,7 @@ unsigned int exynos3250_jpeg_get_x(void __iomem *regs)
 {
        return readl(regs + EXYNOS3250_JPGX);
 }
+#endif
 
 void exynos3250_jpeg_interrupts_enable(void __iomem *regs)
 {
index da8d6a1a984f84be834c49e0fe21c0a5710ebd89..ab6d6f43c96f1a81361a98b7acc6faa160e368d1 100644 (file)
@@ -23,7 +23,7 @@ void exynos4_jpeg_sw_reset(void __iomem *base)
        reg = readl(base + EXYNOS4_JPEG_CNTL_REG);
        writel(reg & ~EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG);
 
-       ndelay(100000);
+       udelay(100);
 
        writel(reg | EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG);
 }
@@ -151,9 +151,6 @@ void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt)
 
 void exynos4_jpeg_set_interrupt(void __iomem *base)
 {
-       unsigned int reg;
-
-       reg = readl(base + EXYNOS4_INT_EN_REG) & ~EXYNOS4_INT_EN_MASK;
        writel(EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG);
 }
 
@@ -185,7 +182,7 @@ void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value)
                writel(reg | EXYNOS4_HUF_TBL_EN,
                                        base + EXYNOS4_JPEG_CNTL_REG);
        else
-               writel(reg | ~EXYNOS4_HUF_TBL_EN,
+               writel(reg & ~EXYNOS4_HUF_TBL_EN,
                                        base + EXYNOS4_JPEG_CNTL_REG);
 }
 
@@ -196,9 +193,9 @@ void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value)
        reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~(EXYNOS4_SYS_INT_EN);
 
        if (value == 1)
-               writel(EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
+               writel(reg | EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
        else
-               writel(~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
+               writel(reg & ~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
 }
 
 void exynos4_jpeg_set_stream_buf_address(void __iomem *base,
index 52407d7907267e520b81bcc540420f1f2ff34544..e3b8e67e005f12fa21a37045844fce373aef134d 100644 (file)
@@ -324,11 +324,9 @@ int s5p_jpeg_stream_stat_ok(void __iomem *regs)
 
 void s5p_jpeg_clear_int(void __iomem *regs)
 {
-       unsigned long reg;
-
-       reg = readl(regs + S5P_JPGINTST);
+       readl(regs + S5P_JPGINTST);
        writel(S5P_INT_RELEASE, regs + S5P_JPGCOM);
-       reg = readl(regs + S5P_JPGOPR);
+       readl(regs + S5P_JPGOPR);
 }
 
 unsigned int s5p_jpeg_compressed_size(void __iomem *regs)
index d35b0418ab371ec132760182a3f35fe71662e279..165bc86c596267f701cf6997422f953ec412227a 100644 (file)
@@ -37,8 +37,8 @@
 #define S5P_MFC_DEC_NAME       "s5p-mfc-dec"
 #define S5P_MFC_ENC_NAME       "s5p-mfc-enc"
 
-int debug;
-module_param(debug, int, S_IRUGO | S_IWUSR);
+int mfc_debug_level;
+module_param_named(debug, mfc_debug_level, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
 
 /* Helper functions for interrupt processing */
@@ -150,10 +150,10 @@ static void s5p_mfc_watchdog_worker(struct work_struct *work)
                if (!ctx)
                        continue;
                ctx->state = MFCINST_ERROR;
-               s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue,
-                               &ctx->vq_dst);
-               s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
-                               &ctx->vq_src);
+               s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+                                               &ctx->dst_queue, &ctx->vq_dst);
+               s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+                                               &ctx->src_queue, &ctx->vq_src);
                clear_work_bit(ctx);
                wake_up_ctx(ctx, S5P_MFC_R2H_CMD_ERR_RET, 0);
        }
@@ -264,7 +264,12 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
        unsigned int frame_type;
 
        dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev);
-       frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_disp_frame_type, ctx);
+       if (IS_MFCV6_PLUS(dev))
+               frame_type = s5p_mfc_hw_call(dev->mfc_ops,
+                       get_disp_frame_type, ctx);
+       else
+               frame_type = s5p_mfc_hw_call(dev->mfc_ops,
+                       get_dec_frame_type, dev);
 
        /* If frame is same as previous then skip and do not dequeue */
        if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
@@ -327,12 +332,12 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
        if (res_change == S5P_FIMV_RES_INCREASE ||
                res_change == S5P_FIMV_RES_DECREASE) {
                ctx->state = MFCINST_RES_CHANGE_INIT;
-               s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+               s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
                wake_up_ctx(ctx, reason, err);
                if (test_and_clear_bit(0, &dev->hw_lock) == 0)
                        BUG();
                s5p_mfc_clock_off();
-               s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+               s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
                return;
        }
        if (ctx->dpb_flush_flag)
@@ -400,7 +405,7 @@ leave_handle_frame:
        if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
                                    || ctx->dst_queue_cnt < ctx->pb_count)
                clear_work_bit(ctx);
-       s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
        wake_up_ctx(ctx, reason, err);
        if (test_and_clear_bit(0, &dev->hw_lock) == 0)
                BUG();
@@ -409,7 +414,7 @@ leave_handle_frame:
        if (test_bit(0, &dev->enter_suspend))
                wake_up_dev(dev, reason, err);
        else
-               s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+               s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
 }
 
 /* Error handling for interrupt */
@@ -435,10 +440,10 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
                        ctx->state = MFCINST_ERROR;
                        /* Mark all dst buffers as having an error */
                        spin_lock_irqsave(&dev->irqlock, flags);
-                       s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue,
+                       s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
                                                &ctx->dst_queue, &ctx->vq_dst);
                        /* Mark all src buffers as having an error */
-                       s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue,
+                       s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
                                                &ctx->src_queue, &ctx->vq_src);
                        spin_unlock_irqrestore(&dev->irqlock, flags);
                        wake_up_ctx(ctx, reason, err);
@@ -452,7 +457,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
        }
        if (test_and_clear_bit(0, &dev->hw_lock) == 0)
                BUG();
-       s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
        s5p_mfc_clock_off();
        wake_up_dev(dev, reason, err);
        return;
@@ -476,7 +481,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
                ctx->img_height = s5p_mfc_hw_call(dev->mfc_ops, get_img_height,
                                dev);
 
-               s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx);
+               s5p_mfc_hw_call_void(dev->mfc_ops, dec_calc_dpb_size, ctx);
 
                ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count,
                                dev);
@@ -503,12 +508,12 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
                        ctx->head_processed = 1;
                }
        }
-       s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
        clear_work_bit(ctx);
        if (test_and_clear_bit(0, &dev->hw_lock) == 0)
                BUG();
        s5p_mfc_clock_off();
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
        wake_up_ctx(ctx, reason, err);
 }
 
@@ -523,7 +528,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
        if (ctx == NULL)
                return;
        dev = ctx->dev;
-       s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
        ctx->int_type = reason;
        ctx->int_err = err;
        ctx->int_cond = 1;
@@ -550,7 +555,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
                s5p_mfc_clock_off();
 
                wake_up(&ctx->queue);
-               s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+               s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
        } else {
                if (test_and_clear_bit(0, &dev->hw_lock) == 0)
                        BUG();
@@ -591,7 +596,7 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx,
 
        s5p_mfc_clock_off();
        wake_up(&ctx->queue);
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
 }
 
 /* Interrupt processing */
@@ -628,12 +633,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
                if (ctx->c_ops->post_frame_start) {
                        if (ctx->c_ops->post_frame_start(ctx))
                                mfc_err("post_frame_start() failed\n");
-                       s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+                       s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
                        wake_up_ctx(ctx, reason, err);
                        if (test_and_clear_bit(0, &dev->hw_lock) == 0)
                                BUG();
                        s5p_mfc_clock_off();
-                       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+                       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
                } else {
                        s5p_mfc_handle_frame(ctx, reason, err);
                }
@@ -663,7 +668,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
        case S5P_MFC_R2H_CMD_WAKEUP_RET:
                if (ctx)
                        clear_work_bit(ctx);
-               s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+               s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
                wake_up_dev(dev, reason, err);
                clear_bit(0, &dev->hw_lock);
                clear_bit(0, &dev->enter_suspend);
@@ -685,12 +690,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
 
        default:
                mfc_debug(2, "Unknown int reason\n");
-               s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+               s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
        }
        mfc_debug_leave();
        return IRQ_HANDLED;
 irq_cleanup_hw:
-       s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
        ctx->int_type = reason;
        ctx->int_err = err;
        ctx->int_cond = 1;
@@ -699,7 +704,7 @@ irq_cleanup_hw:
 
        s5p_mfc_clock_off();
 
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
        mfc_debug(2, "Exit via irq_cleanup_hw\n");
        return IRQ_HANDLED;
 }
@@ -1311,11 +1316,9 @@ static int s5p_mfc_runtime_resume(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
-       int pre_power;
 
        if (!m_dev->alloc_ctx)
                return 0;
-       pre_power = atomic_read(&m_dev->pm.power);
        atomic_set(&m_dev->pm.power, 1);
        return 0;
 }
@@ -1328,20 +1331,20 @@ static const struct dev_pm_ops s5p_mfc_pm_ops = {
                           NULL)
 };
 
-struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = {
+static struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = {
        .h264_ctx       = MFC_H264_CTX_BUF_SIZE,
        .non_h264_ctx   = MFC_CTX_BUF_SIZE,
        .dsc            = DESC_BUF_SIZE,
        .shm            = SHARED_BUF_SIZE,
 };
 
-struct s5p_mfc_buf_size buf_size_v5 = {
+static struct s5p_mfc_buf_size buf_size_v5 = {
        .fw     = MAX_FW_SIZE,
        .cpb    = MAX_CPB_SIZE,
        .priv   = &mfc_buf_size_v5,
 };
 
-struct s5p_mfc_buf_align mfc_buf_align_v5 = {
+static struct s5p_mfc_buf_align mfc_buf_align_v5 = {
        .base = MFC_BASE_ALIGN_ORDER,
 };
 
@@ -1354,7 +1357,7 @@ static struct s5p_mfc_variant mfc_drvdata_v5 = {
        .fw_name[0]     = "s5p-mfc.fw",
 };
 
-struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
+static struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
        .dev_ctx        = MFC_CTX_BUF_SIZE_V6,
        .h264_dec_ctx   = MFC_H264_DEC_CTX_BUF_SIZE_V6,
        .other_dec_ctx  = MFC_OTHER_DEC_CTX_BUF_SIZE_V6,
@@ -1362,13 +1365,13 @@ struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
        .other_enc_ctx  = MFC_OTHER_ENC_CTX_BUF_SIZE_V6,
 };
 
-struct s5p_mfc_buf_size buf_size_v6 = {
+static struct s5p_mfc_buf_size buf_size_v6 = {
        .fw     = MAX_FW_SIZE_V6,
        .cpb    = MAX_CPB_SIZE_V6,
        .priv   = &mfc_buf_size_v6,
 };
 
-struct s5p_mfc_buf_align mfc_buf_align_v6 = {
+static struct s5p_mfc_buf_align mfc_buf_align_v6 = {
        .base = 0,
 };
 
@@ -1386,7 +1389,7 @@ static struct s5p_mfc_variant mfc_drvdata_v6 = {
        .fw_name[1]     = "s5p-mfc-v6-v2.fw",
 };
 
-struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
+static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
        .dev_ctx        = MFC_CTX_BUF_SIZE_V7,
        .h264_dec_ctx   = MFC_H264_DEC_CTX_BUF_SIZE_V7,
        .other_dec_ctx  = MFC_OTHER_DEC_CTX_BUF_SIZE_V7,
@@ -1394,13 +1397,13 @@ struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
        .other_enc_ctx  = MFC_OTHER_ENC_CTX_BUF_SIZE_V7,
 };
 
-struct s5p_mfc_buf_size buf_size_v7 = {
+static struct s5p_mfc_buf_size buf_size_v7 = {
        .fw     = MAX_FW_SIZE_V7,
        .cpb    = MAX_CPB_SIZE_V7,
        .priv   = &mfc_buf_size_v7,
 };
 
-struct s5p_mfc_buf_align mfc_buf_align_v7 = {
+static struct s5p_mfc_buf_align mfc_buf_align_v7 = {
        .base = 0,
 };
 
@@ -1413,7 +1416,7 @@ static struct s5p_mfc_variant mfc_drvdata_v7 = {
        .fw_name[0]     = "s5p-mfc-v7.fw",
 };
 
-struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
+static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
        .dev_ctx        = MFC_CTX_BUF_SIZE_V8,
        .h264_dec_ctx   = MFC_H264_DEC_CTX_BUF_SIZE_V8,
        .other_dec_ctx  = MFC_OTHER_DEC_CTX_BUF_SIZE_V8,
@@ -1421,13 +1424,13 @@ struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
        .other_enc_ctx  = MFC_OTHER_ENC_CTX_BUF_SIZE_V8,
 };
 
-struct s5p_mfc_buf_size buf_size_v8 = {
+static struct s5p_mfc_buf_size buf_size_v8 = {
        .fw     = MAX_FW_SIZE_V8,
        .cpb    = MAX_CPB_SIZE_V8,
        .priv   = &mfc_buf_size_v8,
 };
 
-struct s5p_mfc_buf_align mfc_buf_align_v8 = {
+static struct s5p_mfc_buf_align mfc_buf_align_v8 = {
        .base = 0,
 };
 
index 9a6efd6c13292dec6ab9ba3ba3c052ea9772f24c..8c4739ca16d67d55b0556c1f8880092759232b56 100644 (file)
@@ -14,6 +14,7 @@
 #include "s5p_mfc_cmd.h"
 #include "s5p_mfc_common.h"
 #include "s5p_mfc_debug.h"
+#include "s5p_mfc_cmd_v5.h"
 
 /* This function is used to send a command to the MFC */
 static int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd,
index ec1a5947ed7d00649dcdaf3a7e8b151695fd6902..f17609669b96a275d06001a3053f43f97fa74406 100644 (file)
@@ -16,6 +16,7 @@
 #include "s5p_mfc_debug.h"
 #include "s5p_mfc_intr.h"
 #include "s5p_mfc_opr.h"
+#include "s5p_mfc_cmd_v6.h"
 
 static int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd,
                                struct s5p_mfc_cmd_args *args)
index 01816ffb384b8834fe816e899f9d38d3ea61d472..3e41ca1293ed364088f75c830f0a4ee5d7c502d0 100644 (file)
@@ -698,6 +698,12 @@ struct mfc_control {
 #define s5p_mfc_hw_call(f, op, args...) \
        ((f && f->op) ? f->op(args) : -ENODEV)
 
+#define s5p_mfc_hw_call_void(f, op, args...) \
+do { \
+       if (f && f->op) \
+               f->op(args); \
+} while (0)
+
 #define fh_to_ctx(__fh) container_of(__fh, struct s5p_mfc_ctx, fh)
 #define ctrl_to_ctx(__ctrl) \
        container_of((__ctrl)->handler, struct s5p_mfc_ctx, ctrl_handler)
index ca9f789228329c9d2f0de9b6fadc34d957f2cadb..0c885a8a0e9fcd4341103991f5da3e788defbc89 100644 (file)
@@ -21,6 +21,7 @@
 #include "s5p_mfc_intr.h"
 #include "s5p_mfc_opr.h"
 #include "s5p_mfc_pm.h"
+#include "s5p_mfc_ctrl.h"
 
 /* Allocate memory for firmware */
 int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
@@ -188,12 +189,12 @@ static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
 {
        if (IS_MFCV6_PLUS(dev)) {
                mfc_write(dev, dev->bank1, S5P_FIMV_RISC_BASE_ADDRESS_V6);
-               mfc_debug(2, "Base Address : %08x\n", dev->bank1);
+               mfc_debug(2, "Base Address : %pad\n", &dev->bank1);
        } else {
                mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A);
                mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B);
-               mfc_debug(2, "Bank1: %08x, Bank2: %08x\n",
-                               dev->bank1, dev->bank2);
+               mfc_debug(2, "Bank1: %pad, Bank2: %pad\n",
+                               &dev->bank1, &dev->bank2);
        }
 }
 
@@ -257,9 +258,9 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
                s5p_mfc_clock_off();
                return ret;
        }
-       mfc_debug(2, "Ok, now will write a command to init the system\n");
+       mfc_debug(2, "Ok, now will wait for completion of hardware init\n");
        if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_SYS_INIT_RET)) {
-               mfc_err("Failed to load firmware\n");
+               mfc_err("Failed to init hardware\n");
                s5p_mfc_reset(dev);
                s5p_mfc_clock_off();
                return -EIO;
@@ -293,7 +294,7 @@ void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)
        s5p_mfc_clock_on();
 
        s5p_mfc_reset(dev);
-       s5p_mfc_hw_call(dev->mfc_ops, release_dev_context_buffer, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, release_dev_context_buffer, dev);
 
        s5p_mfc_clock_off();
 }
@@ -396,7 +397,7 @@ int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
 
        set_work_bit_irqsave(ctx);
        s5p_mfc_clean_ctx_int_flags(ctx);
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
        if (s5p_mfc_wait_for_done_ctx(ctx,
                S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
                /* Error or timeout */
@@ -410,9 +411,9 @@ int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
 
 err_free_desc_buf:
        if (ctx->type == MFCINST_DECODER)
-               s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
+               s5p_mfc_hw_call_void(dev->mfc_ops, release_dec_desc_buffer, ctx);
 err_free_inst_buf:
-       s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
+       s5p_mfc_hw_call_void(dev->mfc_ops, release_instance_buffer, ctx);
 err:
        return ret;
 }
@@ -422,17 +423,17 @@ void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
        ctx->state = MFCINST_RETURN_INST;
        set_work_bit_irqsave(ctx);
        s5p_mfc_clean_ctx_int_flags(ctx);
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
        /* Wait until instance is returned or timeout occurred */
        if (s5p_mfc_wait_for_done_ctx(ctx,
                                S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0))
                mfc_err("Err returning instance\n");
 
        /* Free resources */
-       s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
-       s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
+       s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers, ctx);
+       s5p_mfc_hw_call_void(dev->mfc_ops, release_instance_buffer, ctx);
        if (ctx->type == MFCINST_DECODER)
-               s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
+               s5p_mfc_hw_call_void(dev->mfc_ops, release_dec_desc_buffer, ctx);
 
        ctx->inst_no = MFC_NO_INSTANCE_SET;
        ctx->state = MFCINST_FREE;
index 8e608f5aa0d7c866009a06f7b203c51d136040f1..5936923c631c9f9e1c2d6173cd13ce88adf2a9a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/media/platform/samsung/mfc5/s5p_mfc_debug.h
+ * drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
  *
  * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
  * This file contains debug macros
 #define DEBUG
 
 #ifdef DEBUG
-extern int debug;
+extern int mfc_debug_level;
 
 #define mfc_debug(level, fmt, args...)                         \
        do {                                                    \
-               if (debug >= level)                             \
+               if (mfc_debug_level >= level)                   \
                        printk(KERN_DEBUG "%s:%d: " fmt,        \
                                __func__, __LINE__, ##args);    \
        } while (0)
index 9103258b7df386b8434d7b89e2e92240be1feb5f..a98fe023deaf79e5879d3d9ae5e2317bd44204e8 100644 (file)
@@ -283,17 +283,13 @@ static int vidioc_querycap(struct file *file, void *priv,
 
 /* Enumerate format */
 static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
-                                                       bool mplane, bool out)
+                                                       bool out)
 {
        struct s5p_mfc_dev *dev = video_drvdata(file);
        struct s5p_mfc_fmt *fmt;
        int i, j = 0;
 
        for (i = 0; i < ARRAY_SIZE(formats); ++i) {
-               if (mplane && formats[i].num_planes == 1)
-                       continue;
-               else if (!mplane && formats[i].num_planes > 1)
-                       continue;
                if (out && formats[i].type != MFC_FMT_DEC)
                        continue;
                else if (!out && formats[i].type != MFC_FMT_RAW)
@@ -313,28 +309,16 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
        return 0;
 }
 
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
-                                                       struct v4l2_fmtdesc *f)
-{
-       return vidioc_enum_fmt(file, f, false, false);
-}
-
 static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
                                                        struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(file, f, true, false);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
-                                                       struct v4l2_fmtdesc *f)
-{
-       return vidioc_enum_fmt(file, f, false, true);
+       return vidioc_enum_fmt(file, f, false);
 }
 
 static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,
                                                        struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(file, f, true, true);
+       return vidioc_enum_fmt(file, f, true);
 }
 
 /* Get format */
@@ -543,7 +527,7 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
                ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
                if (ret)
                        goto out;
-               s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
+               s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers, ctx);
                ctx->dst_bufs_cnt = 0;
        } else if (ctx->capture_state == QUEUE_FREE) {
                WARN_ON(ctx->dst_bufs_cnt != 0);
@@ -571,7 +555,7 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
 
                if (s5p_mfc_ctx_ready(ctx))
                        set_work_bit_irqsave(ctx);
-               s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+               s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
                s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_INIT_BUFFERS_RET,
                                          0);
        } else {
@@ -823,8 +807,8 @@ static int vidioc_g_crop(struct file *file, void *priv,
        return 0;
 }
 
-int vidioc_decoder_cmd(struct file *file, void *priv,
-                                               struct v4l2_decoder_cmd *cmd)
+static int vidioc_decoder_cmd(struct file *file, void *priv,
+                             struct v4l2_decoder_cmd *cmd)
 {
        struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
        struct s5p_mfc_dev *dev = ctx->dev;
@@ -846,7 +830,7 @@ int vidioc_decoder_cmd(struct file *file, void *priv,
                        if (s5p_mfc_ctx_ready(ctx))
                                set_work_bit_irqsave(ctx);
                        spin_unlock_irqrestore(&dev->irqlock, flags);
-                       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+                       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
                } else {
                        mfc_err("EOS: marking last buffer of stream");
                        buf = list_entry(ctx->src_queue.prev,
@@ -881,9 +865,7 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh,
 /* v4l2_ioctl_ops */
 static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
        .vidioc_querycap = vidioc_querycap,
-       .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
        .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
-       .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
        .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
        .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
        .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
@@ -990,7 +972,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
        if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
                if (ctx->capture_state == QUEUE_BUFS_MMAPED)
                        return 0;
-               for (i = 0; i <= ctx->src_fmt->num_planes ; i++) {
+               for (i = 0; i < ctx->dst_fmt->num_planes; i++) {
                        if (IS_ERR_OR_NULL(ERR_PTR(
                                        vb2_dma_contig_plane_dma_addr(vb, i)))) {
                                mfc_err("Plane mem not allocated\n");
@@ -1044,7 +1026,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
        /* If context is ready then dev = work->data;schedule it to run */
        if (s5p_mfc_ctx_ready(ctx))
                set_work_bit_irqsave(ctx);
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
        return 0;
 }
 
@@ -1065,8 +1047,8 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
        }
        if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
                spin_lock_irqsave(&dev->irqlock, flags);
-               s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue,
-                               &ctx->vq_dst);
+               s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+                                               &ctx->dst_queue, &ctx->vq_dst);
                INIT_LIST_HEAD(&ctx->dst_queue);
                ctx->dst_queue_cnt = 0;
                ctx->dpb_flush_flag = 1;
@@ -1076,7 +1058,7 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
                        ctx->state = MFCINST_FLUSH;
                        set_work_bit_irqsave(ctx);
                        s5p_mfc_clean_ctx_int_flags(ctx);
-                       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+                       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
                        if (s5p_mfc_wait_for_done_ctx(ctx,
                                S5P_MFC_R2H_CMD_DPB_FLUSH_RET, 0))
                                mfc_err("Err flushing buffers\n");
@@ -1084,8 +1066,8 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
        }
        if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
                spin_lock_irqsave(&dev->irqlock, flags);
-               s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
-                               &ctx->vq_src);
+               s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+                                               &ctx->src_queue, &ctx->vq_src);
                INIT_LIST_HEAD(&ctx->src_queue);
                ctx->src_queue_cnt = 0;
                spin_unlock_irqrestore(&dev->irqlock, flags);
@@ -1124,7 +1106,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
        }
        if (s5p_mfc_ctx_ready(ctx))
                set_work_bit_irqsave(ctx);
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
 }
 
 static struct vb2_ops s5p_mfc_dec_qops = {
@@ -1220,7 +1202,7 @@ void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx)
        else
                f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;
        ctx->dst_fmt = find_format(&f, MFC_FMT_RAW);
-       mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n",
-                       (unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt);
+       mfc_debug(2, "Default src_fmt is %p, dest_fmt is %p\n",
+                       ctx->src_fmt, ctx->dst_fmt);
 }
 
index d26b2484ca10b04576e56437c47e4b38473b4cd8..a904a1c7bb21e70bf8b29719a0c537b87bc0e465 100644 (file)
@@ -739,14 +739,11 @@ static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
 static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_buf *mb_entry;
-       unsigned long mb_y_addr, mb_c_addr;
 
        /* move buffers in ref queue to src queue */
        while (!list_empty(&ctx->ref_queue)) {
                mb_entry = list_entry((&ctx->ref_queue)->next,
                                                struct s5p_mfc_buf, list);
-               mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
-               mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
                list_del(&mb_entry->list);
                ctx->ref_queue_cnt--;
                list_add_tail(&mb_entry->list, &ctx->src_queue);
@@ -770,7 +767,7 @@ static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx)
        dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
        dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
        dst_size = vb2_plane_size(dst_mb->b, 0);
-       s5p_mfc_hw_call(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
+       s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
                        dst_size);
        spin_unlock_irqrestore(&dev->irqlock, flags);
        return 0;
@@ -803,7 +800,7 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
                ctx->state = MFCINST_RUNNING;
                if (s5p_mfc_ctx_ready(ctx))
                        set_work_bit_irqsave(ctx);
-               s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+               s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
        } else {
                enc_pb_count = s5p_mfc_hw_call(dev->mfc_ops,
                                get_enc_dpb_count, dev);
@@ -828,15 +825,15 @@ static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx)
        src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
        src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
        src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
-       s5p_mfc_hw_call(dev->mfc_ops, set_enc_frame_buffer, ctx, src_y_addr,
-                       src_c_addr);
+       s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_frame_buffer, ctx,
+                                                       src_y_addr, src_c_addr);
        spin_unlock_irqrestore(&dev->irqlock, flags);
 
        spin_lock_irqsave(&dev->irqlock, flags);
        dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
        dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
        dst_size = vb2_plane_size(dst_mb->b, 0);
-       s5p_mfc_hw_call(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
+       s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
                        dst_size);
        spin_unlock_irqrestore(&dev->irqlock, flags);
 
@@ -861,7 +858,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
                  mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT));
        spin_lock_irqsave(&dev->irqlock, flags);
        if (slice_type >= 0) {
-               s5p_mfc_hw_call(dev->mfc_ops, get_enc_frame_buffer, ctx,
+               s5p_mfc_hw_call_void(dev->mfc_ops, get_enc_frame_buffer, ctx,
                                &enc_y_addr, &enc_c_addr);
                list_for_each_entry(mb_entry, &ctx->src_queue, list) {
                        mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
@@ -954,17 +951,13 @@ static int vidioc_querycap(struct file *file, void *priv,
 }
 
 static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
-                                                       bool mplane, bool out)
+                                                       bool out)
 {
        struct s5p_mfc_dev *dev = video_drvdata(file);
        struct s5p_mfc_fmt *fmt;
        int i, j = 0;
 
        for (i = 0; i < ARRAY_SIZE(formats); ++i) {
-               if (mplane && formats[i].num_planes == 1)
-                       continue;
-               else if (!mplane && formats[i].num_planes > 1)
-                       continue;
                if (out && formats[i].type != MFC_FMT_RAW)
                        continue;
                else if (!out && formats[i].type != MFC_FMT_ENC)
@@ -984,28 +977,16 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
        return -EINVAL;
 }
 
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
-                                  struct v4l2_fmtdesc *f)
-{
-       return vidioc_enum_fmt(file, f, false, false);
-}
-
 static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
                                          struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(file, f, true, false);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
-                                  struct v4l2_fmtdesc *f)
-{
-       return vidioc_enum_fmt(file, f, false, true);
+       return vidioc_enum_fmt(file, f, false);
 }
 
 static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
                                          struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(file, f, true, true);
+       return vidioc_enum_fmt(file, f, true);
 }
 
 static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
@@ -1127,7 +1108,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
                        pix_fmt_mp->width, pix_fmt_mp->height,
                        ctx->img_width, ctx->img_height);
 
-               s5p_mfc_hw_call(dev->mfc_ops, enc_calc_src_size, ctx);
+               s5p_mfc_hw_call_void(dev->mfc_ops, enc_calc_src_size, ctx);
                pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
                pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
                pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
@@ -1681,8 +1662,8 @@ static int vidioc_g_parm(struct file *file, void *priv,
        return 0;
 }
 
-int vidioc_encoder_cmd(struct file *file, void *priv,
-                                               struct v4l2_encoder_cmd *cmd)
+static int vidioc_encoder_cmd(struct file *file, void *priv,
+                             struct v4l2_encoder_cmd *cmd)
 {
        struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
        struct s5p_mfc_dev *dev = ctx->dev;
@@ -1704,7 +1685,7 @@ int vidioc_encoder_cmd(struct file *file, void *priv,
                        if (s5p_mfc_ctx_ready(ctx))
                                set_work_bit_irqsave(ctx);
                        spin_unlock_irqrestore(&dev->irqlock, flags);
-                       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+                       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
                } else {
                        mfc_debug(2, "EOS: marking last buffer of stream\n");
                        buf = list_entry(ctx->src_queue.prev,
@@ -1736,9 +1717,7 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh,
 
 static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = {
        .vidioc_querycap = vidioc_querycap,
-       .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
        .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
-       .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
        .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
        .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
        .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
@@ -1771,13 +1750,13 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
                return -EINVAL;
        }
        for (i = 0; i < fmt->num_planes; i++) {
-               if (!vb2_dma_contig_plane_dma_addr(vb, i)) {
+               dma_addr_t dma = vb2_dma_contig_plane_dma_addr(vb, i);
+               if (!dma) {
                        mfc_err("failed to get plane cookie\n");
                        return -EINVAL;
                }
-               mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx\n",
-                         vb->v4l2_buf.index, i,
-                         vb2_dma_contig_plane_dma_addr(vb, i));
+               mfc_debug(2, "index: %d, plane[%d] cookie: %pad\n",
+                         vb->v4l2_buf.index, i, &dma);
        }
        return 0;
 }
@@ -1897,7 +1876,7 @@ static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
                ret = check_vb_with_fmt(ctx->dst_fmt, vb);
                if (ret < 0)
                        return ret;
-               mfc_debug(2, "plane size: %ld, dst size: %d\n",
+               mfc_debug(2, "plane size: %ld, dst size: %zu\n",
                        vb2_plane_size(vb, 0), ctx->enc_dst_buf_size);
                if (vb2_plane_size(vb, 0) < ctx->enc_dst_buf_size) {
                        mfc_err("plane size is too small for capture\n");
@@ -1948,7 +1927,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
        /* If context is ready then dev = work->data;schedule it to run */
        if (s5p_mfc_ctx_ready(ctx))
                set_work_bit_irqsave(ctx);
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
 
        return 0;
 }
@@ -1969,14 +1948,14 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
        ctx->state = MFCINST_FINISHED;
        spin_lock_irqsave(&dev->irqlock, flags);
        if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
-               s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue,
-                               &ctx->vq_dst);
+               s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
+                                               &ctx->dst_queue, &ctx->vq_dst);
                INIT_LIST_HEAD(&ctx->dst_queue);
                ctx->dst_queue_cnt = 0;
        }
        if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
                cleanup_ref_queue(ctx);
-               s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
+               s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
                                &ctx->vq_src);
                INIT_LIST_HEAD(&ctx->src_queue);
                ctx->src_queue_cnt = 0;
@@ -2017,7 +1996,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
        }
        if (s5p_mfc_ctx_ready(ctx))
                set_work_bit_irqsave(ctx);
-       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
 }
 
 static struct vb2_ops s5p_mfc_enc_qops = {
index c9a227428e6ad38c99d7fa2e88c533cf8a39dadc..00a1d8b2a8c236195259885bc0aaaca7a549fd37 100644 (file)
@@ -41,7 +41,7 @@ int s5p_mfc_alloc_priv_buf(struct device *dev,
                                        struct s5p_mfc_priv_buf *b)
 {
 
-       mfc_debug(3, "Allocating priv: %d\n", b->size);
+       mfc_debug(3, "Allocating priv: %zu\n", b->size);
 
        b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL);
 
@@ -50,7 +50,7 @@ int s5p_mfc_alloc_priv_buf(struct device *dev,
                return -ENOMEM;
        }
 
-       mfc_debug(3, "Allocated addr %p %08x\n", b->virt, b->dma);
+       mfc_debug(3, "Allocated addr %p %pad\n", b->virt, &b->dma);
        return 0;
 }
 
index 7a7ad32ee60883e81544cf5a036df33c08a5039d..de2b8c69daa535dd6ea906dcd476638e3185c72f 100644 (file)
 struct s5p_mfc_regs {
 
        /* codec common registers */
-       void *risc_on;
-       void *risc2host_int;
-       void *host2risc_int;
-       void *risc_base_address;
-       void *mfc_reset;
-       void *host2risc_command;
-       void *risc2host_command;
-       void *mfc_bus_reset_ctrl;
-       void *firmware_version;
-       void *instance_id;
-       void *codec_type;
-       void *context_mem_addr;
-       void *context_mem_size;
-       void *pixel_format;
-       void *metadata_enable;
-       void *mfc_version;
-       void *dbg_info_enable;
-       void *dbg_buffer_addr;
-       void *dbg_buffer_size;
-       void *hed_control;
-       void *mfc_timeout_value;
-       void *hed_shared_mem_addr;
-       void *dis_shared_mem_addr;/* only v7 */
-       void *ret_instance_id;
-       void *error_code;
-       void *dbg_buffer_output_size;
-       void *metadata_status;
-       void *metadata_addr_mb_info;
-       void *metadata_size_mb_info;
-       void *dbg_info_stage_counter;
+       volatile void __iomem *risc_on;
+       volatile void __iomem *risc2host_int;
+       volatile void __iomem *host2risc_int;
+       volatile void __iomem *risc_base_address;
+       volatile void __iomem *mfc_reset;
+       volatile void __iomem *host2risc_command;
+       volatile void __iomem *risc2host_command;
+       volatile void __iomem *mfc_bus_reset_ctrl;
+       volatile void __iomem *firmware_version;
+       volatile void __iomem *instance_id;
+       volatile void __iomem *codec_type;
+       volatile void __iomem *context_mem_addr;
+       volatile void __iomem *context_mem_size;
+       volatile void __iomem *pixel_format;
+       volatile void __iomem *metadata_enable;
+       volatile void __iomem *mfc_version;
+       volatile void __iomem *dbg_info_enable;
+       volatile void __iomem *dbg_buffer_addr;
+       volatile void __iomem *dbg_buffer_size;
+       volatile void __iomem *hed_control;
+       volatile void __iomem *mfc_timeout_value;
+       volatile void __iomem *hed_shared_mem_addr;
+       volatile void __iomem *dis_shared_mem_addr;/* only v7 */
+       volatile void __iomem *ret_instance_id;
+       volatile void __iomem *error_code;
+       volatile void __iomem *dbg_buffer_output_size;
+       volatile void __iomem *metadata_status;
+       volatile void __iomem *metadata_addr_mb_info;
+       volatile void __iomem *metadata_size_mb_info;
+       volatile void __iomem *dbg_info_stage_counter;
 
        /* decoder registers */
-       void *d_crc_ctrl;
-       void *d_dec_options;
-       void *d_display_delay;
-       void *d_set_frame_width;
-       void *d_set_frame_height;
-       void *d_sei_enable;
-       void *d_min_num_dpb;
-       void *d_min_first_plane_dpb_size;
-       void *d_min_second_plane_dpb_size;
-       void *d_min_third_plane_dpb_size;/* only v8 */
-       void *d_min_num_mv;
-       void *d_mvc_num_views;
-       void *d_min_num_dis;/* only v7 */
-       void *d_min_first_dis_size;/* only v7 */
-       void *d_min_second_dis_size;/* only v7 */
-       void *d_min_third_dis_size;/* only v7 */
-       void *d_post_filter_luma_dpb0;/*  v7 and v8 */
-       void *d_post_filter_luma_dpb1;/* v7 and v8 */
-       void *d_post_filter_luma_dpb2;/* only v7 */
-       void *d_post_filter_chroma_dpb0;/* v7 and v8 */
-       void *d_post_filter_chroma_dpb1;/* v7 and v8 */
-       void *d_post_filter_chroma_dpb2;/* only v7 */
-       void *d_num_dpb;
-       void *d_num_mv;
-       void *d_init_buffer_options;
-       void *d_first_plane_dpb_stride_size;/* only v8 */
-       void *d_second_plane_dpb_stride_size;/* only v8 */
-       void *d_third_plane_dpb_stride_size;/* only v8 */
-       void *d_first_plane_dpb_size;
-       void *d_second_plane_dpb_size;
-       void *d_third_plane_dpb_size;/* only v8 */
-       void *d_mv_buffer_size;
-       void *d_first_plane_dpb;
-       void *d_second_plane_dpb;
-       void *d_third_plane_dpb;
-       void *d_mv_buffer;
-       void *d_scratch_buffer_addr;
-       void *d_scratch_buffer_size;
-       void *d_metadata_buffer_addr;
-       void *d_metadata_buffer_size;
-       void *d_nal_start_options;/* v7 and v8 */
-       void *d_cpb_buffer_addr;
-       void *d_cpb_buffer_size;
-       void *d_available_dpb_flag_upper;
-       void *d_available_dpb_flag_lower;
-       void *d_cpb_buffer_offset;
-       void *d_slice_if_enable;
-       void *d_picture_tag;
-       void *d_stream_data_size;
-       void *d_dynamic_dpb_flag_upper;/* v7 and v8 */
-       void *d_dynamic_dpb_flag_lower;/* v7 and v8 */
-       void *d_display_frame_width;
-       void *d_display_frame_height;
-       void *d_display_status;
-       void *d_display_first_plane_addr;
-       void *d_display_second_plane_addr;
-       void *d_display_third_plane_addr;/* only v8 */
-       void *d_display_frame_type;
-       void *d_display_crop_info1;
-       void *d_display_crop_info2;
-       void *d_display_picture_profile;
-       void *d_display_luma_crc;/* v7 and v8 */
-       void *d_display_chroma0_crc;/* v7 and v8 */
-       void *d_display_chroma1_crc;/* only v8 */
-       void *d_display_luma_crc_top;/* only v6 */
-       void *d_display_chroma_crc_top;/* only v6 */
-       void *d_display_luma_crc_bot;/* only v6 */
-       void *d_display_chroma_crc_bot;/* only v6 */
-       void *d_display_aspect_ratio;
-       void *d_display_extended_ar;
-       void *d_decoded_frame_width;
-       void *d_decoded_frame_height;
-       void *d_decoded_status;
-       void *d_decoded_first_plane_addr;
-       void *d_decoded_second_plane_addr;
-       void *d_decoded_third_plane_addr;/* only v8 */
-       void *d_decoded_frame_type;
-       void *d_decoded_crop_info1;
-       void *d_decoded_crop_info2;
-       void *d_decoded_picture_profile;
-       void *d_decoded_nal_size;
-       void *d_decoded_luma_crc;
-       void *d_decoded_chroma0_crc;
-       void *d_decoded_chroma1_crc;/* only v8 */
-       void *d_ret_picture_tag_top;
-       void *d_ret_picture_tag_bot;
-       void *d_ret_picture_time_top;
-       void *d_ret_picture_time_bot;
-       void *d_chroma_format;
-       void *d_vc1_info;/* v7 and v8 */
-       void *d_mpeg4_info;
-       void *d_h264_info;
-       void *d_metadata_addr_concealed_mb;
-       void *d_metadata_size_concealed_mb;
-       void *d_metadata_addr_vc1_param;
-       void *d_metadata_size_vc1_param;
-       void *d_metadata_addr_sei_nal;
-       void *d_metadata_size_sei_nal;
-       void *d_metadata_addr_vui;
-       void *d_metadata_size_vui;
-       void *d_metadata_addr_mvcvui;/* v7 and v8 */
-       void *d_metadata_size_mvcvui;/* v7 and v8 */
-       void *d_mvc_view_id;
-       void *d_frame_pack_sei_avail;
-       void *d_frame_pack_arrgment_id;
-       void *d_frame_pack_sei_info;
-       void *d_frame_pack_grid_pos;
-       void *d_display_recovery_sei_info;/* v7 and v8 */
-       void *d_decoded_recovery_sei_info;/* v7 and v8 */
-       void *d_display_first_addr;/* only v7 */
-       void *d_display_second_addr;/* only v7 */
-       void *d_display_third_addr;/* only v7 */
-       void *d_decoded_first_addr;/* only v7 */
-       void *d_decoded_second_addr;/* only v7 */
-       void *d_decoded_third_addr;/* only v7 */
-       void *d_used_dpb_flag_upper;/* v7 and v8 */
-       void *d_used_dpb_flag_lower;/* v7 and v8 */
+       volatile void __iomem *d_crc_ctrl;
+       volatile void __iomem *d_dec_options;
+       volatile void __iomem *d_display_delay;
+       volatile void __iomem *d_set_frame_width;
+       volatile void __iomem *d_set_frame_height;
+       volatile void __iomem *d_sei_enable;
+       volatile void __iomem *d_min_num_dpb;
+       volatile void __iomem *d_min_first_plane_dpb_size;
+       volatile void __iomem *d_min_second_plane_dpb_size;
+       volatile void __iomem *d_min_third_plane_dpb_size;/* only v8 */
+       volatile void __iomem *d_min_num_mv;
+       volatile void __iomem *d_mvc_num_views;
+       volatile void __iomem *d_min_num_dis;/* only v7 */
+       volatile void __iomem *d_min_first_dis_size;/* only v7 */
+       volatile void __iomem *d_min_second_dis_size;/* only v7 */
+       volatile void __iomem *d_min_third_dis_size;/* only v7 */
+       volatile void __iomem *d_post_filter_luma_dpb0;/*  v7 and v8 */
+       volatile void __iomem *d_post_filter_luma_dpb1;/* v7 and v8 */
+       volatile void __iomem *d_post_filter_luma_dpb2;/* only v7 */
+       volatile void __iomem *d_post_filter_chroma_dpb0;/* v7 and v8 */
+       volatile void __iomem *d_post_filter_chroma_dpb1;/* v7 and v8 */
+       volatile void __iomem *d_post_filter_chroma_dpb2;/* only v7 */
+       volatile void __iomem *d_num_dpb;
+       volatile void __iomem *d_num_mv;
+       volatile void __iomem *d_init_buffer_options;
+       volatile void __iomem *d_first_plane_dpb_stride_size;/* only v8 */
+       volatile void __iomem *d_second_plane_dpb_stride_size;/* only v8 */
+       volatile void __iomem *d_third_plane_dpb_stride_size;/* only v8 */
+       volatile void __iomem *d_first_plane_dpb_size;
+       volatile void __iomem *d_second_plane_dpb_size;
+       volatile void __iomem *d_third_plane_dpb_size;/* only v8 */
+       volatile void __iomem *d_mv_buffer_size;
+       volatile void __iomem *d_first_plane_dpb;
+       volatile void __iomem *d_second_plane_dpb;
+       volatile void __iomem *d_third_plane_dpb;
+       volatile void __iomem *d_mv_buffer;
+       volatile void __iomem *d_scratch_buffer_addr;
+       volatile void __iomem *d_scratch_buffer_size;
+       volatile void __iomem *d_metadata_buffer_addr;
+       volatile void __iomem *d_metadata_buffer_size;
+       volatile void __iomem *d_nal_start_options;/* v7 and v8 */
+       volatile void __iomem *d_cpb_buffer_addr;
+       volatile void __iomem *d_cpb_buffer_size;
+       volatile void __iomem *d_available_dpb_flag_upper;
+       volatile void __iomem *d_available_dpb_flag_lower;
+       volatile void __iomem *d_cpb_buffer_offset;
+       volatile void __iomem *d_slice_if_enable;
+       volatile void __iomem *d_picture_tag;
+       volatile void __iomem *d_stream_data_size;
+       volatile void __iomem *d_dynamic_dpb_flag_upper;/* v7 and v8 */
+       volatile void __iomem *d_dynamic_dpb_flag_lower;/* v7 and v8 */
+       volatile void __iomem *d_display_frame_width;
+       volatile void __iomem *d_display_frame_height;
+       volatile void __iomem *d_display_status;
+       volatile void __iomem *d_display_first_plane_addr;
+       volatile void __iomem *d_display_second_plane_addr;
+       volatile void __iomem *d_display_third_plane_addr;/* only v8 */
+       volatile void __iomem *d_display_frame_type;
+       volatile void __iomem *d_display_crop_info1;
+       volatile void __iomem *d_display_crop_info2;
+       volatile void __iomem *d_display_picture_profile;
+       volatile void __iomem *d_display_luma_crc;/* v7 and v8 */
+       volatile void __iomem *d_display_chroma0_crc;/* v7 and v8 */
+       volatile void __iomem *d_display_chroma1_crc;/* only v8 */
+       volatile void __iomem *d_display_luma_crc_top;/* only v6 */
+       volatile void __iomem *d_display_chroma_crc_top;/* only v6 */
+       volatile void __iomem *d_display_luma_crc_bot;/* only v6 */
+       volatile void __iomem *d_display_chroma_crc_bot;/* only v6 */
+       volatile void __iomem *d_display_aspect_ratio;
+       volatile void __iomem *d_display_extended_ar;
+       volatile void __iomem *d_decoded_frame_width;
+       volatile void __iomem *d_decoded_frame_height;
+       volatile void __iomem *d_decoded_status;
+       volatile void __iomem *d_decoded_first_plane_addr;
+       volatile void __iomem *d_decoded_second_plane_addr;
+       volatile void __iomem *d_decoded_third_plane_addr;/* only v8 */
+       volatile void __iomem *d_decoded_frame_type;
+       volatile void __iomem *d_decoded_crop_info1;
+       volatile void __iomem *d_decoded_crop_info2;
+       volatile void __iomem *d_decoded_picture_profile;
+       volatile void __iomem *d_decoded_nal_size;
+       volatile void __iomem *d_decoded_luma_crc;
+       volatile void __iomem *d_decoded_chroma0_crc;
+       volatile void __iomem *d_decoded_chroma1_crc;/* only v8 */
+       volatile void __iomem *d_ret_picture_tag_top;
+       volatile void __iomem *d_ret_picture_tag_bot;
+       volatile void __iomem *d_ret_picture_time_top;
+       volatile void __iomem *d_ret_picture_time_bot;
+       volatile void __iomem *d_chroma_format;
+       volatile void __iomem *d_vc1_info;/* v7 and v8 */
+       volatile void __iomem *d_mpeg4_info;
+       volatile void __iomem *d_h264_info;
+       volatile void __iomem *d_metadata_addr_concealed_mb;
+       volatile void __iomem *d_metadata_size_concealed_mb;
+       volatile void __iomem *d_metadata_addr_vc1_param;
+       volatile void __iomem *d_metadata_size_vc1_param;
+       volatile void __iomem *d_metadata_addr_sei_nal;
+       volatile void __iomem *d_metadata_size_sei_nal;
+       volatile void __iomem *d_metadata_addr_vui;
+       volatile void __iomem *d_metadata_size_vui;
+       volatile void __iomem *d_metadata_addr_mvcvui;/* v7 and v8 */
+       volatile void __iomem *d_metadata_size_mvcvui;/* v7 and v8 */
+       volatile void __iomem *d_mvc_view_id;
+       volatile void __iomem *d_frame_pack_sei_avail;
+       volatile void __iomem *d_frame_pack_arrgment_id;
+       volatile void __iomem *d_frame_pack_sei_info;
+       volatile void __iomem *d_frame_pack_grid_pos;
+       volatile void __iomem *d_display_recovery_sei_info;/* v7 and v8 */
+       volatile void __iomem *d_decoded_recovery_sei_info;/* v7 and v8 */
+       volatile void __iomem *d_display_first_addr;/* only v7 */
+       volatile void __iomem *d_display_second_addr;/* only v7 */
+       volatile void __iomem *d_display_third_addr;/* only v7 */
+       volatile void __iomem *d_decoded_first_addr;/* only v7 */
+       volatile void __iomem *d_decoded_second_addr;/* only v7 */
+       volatile void __iomem *d_decoded_third_addr;/* only v7 */
+       volatile void __iomem *d_used_dpb_flag_upper;/* v7 and v8 */
+       volatile void __iomem *d_used_dpb_flag_lower;/* v7 and v8 */
 
        /* encoder registers */
-       void *e_frame_width;
-       void *e_frame_height;
-       void *e_cropped_frame_width;
-       void *e_cropped_frame_height;
-       void *e_frame_crop_offset;
-       void *e_enc_options;
-       void *e_picture_profile;
-       void *e_vbv_buffer_size;
-       void *e_vbv_init_delay;
-       void *e_fixed_picture_qp;
-       void *e_rc_config;
-       void *e_rc_qp_bound;
-       void *e_rc_qp_bound_pb;/* v7 and v8 */
-       void *e_rc_mode;
-       void *e_mb_rc_config;
-       void *e_padding_ctrl;
-       void *e_air_threshold;
-       void *e_mv_hor_range;
-       void *e_mv_ver_range;
-       void *e_num_dpb;
-       void *e_luma_dpb;
-       void *e_chroma_dpb;
-       void *e_me_buffer;
-       void *e_scratch_buffer_addr;
-       void *e_scratch_buffer_size;
-       void *e_tmv_buffer0;
-       void *e_tmv_buffer1;
-       void *e_ir_buffer_addr;/* v7 and v8 */
-       void *e_source_first_plane_addr;
-       void *e_source_second_plane_addr;
-       void *e_source_third_plane_addr;/* v7 and v8 */
-       void *e_source_first_plane_stride;/* v7 and v8 */
-       void *e_source_second_plane_stride;/* v7 and v8 */
-       void *e_source_third_plane_stride;/* v7 and v8 */
-       void *e_stream_buffer_addr;
-       void *e_stream_buffer_size;
-       void *e_roi_buffer_addr;
-       void *e_param_change;
-       void *e_ir_size;
-       void *e_gop_config;
-       void *e_mslice_mode;
-       void *e_mslice_size_mb;
-       void *e_mslice_size_bits;
-       void *e_frame_insertion;
-       void *e_rc_frame_rate;
-       void *e_rc_bit_rate;
-       void *e_rc_roi_ctrl;
-       void *e_picture_tag;
-       void *e_bit_count_enable;
-       void *e_max_bit_count;
-       void *e_min_bit_count;
-       void *e_metadata_buffer_addr;
-       void *e_metadata_buffer_size;
-       void *e_encoded_source_first_plane_addr;
-       void *e_encoded_source_second_plane_addr;
-       void *e_encoded_source_third_plane_addr;/* v7 and v8 */
-       void *e_stream_size;
-       void *e_slice_type;
-       void *e_picture_count;
-       void *e_ret_picture_tag;
-       void *e_stream_buffer_write_pointer; /*  only v6 */
-       void *e_recon_luma_dpb_addr;
-       void *e_recon_chroma_dpb_addr;
-       void *e_metadata_addr_enc_slice;
-       void *e_metadata_size_enc_slice;
-       void *e_mpeg4_options;
-       void *e_mpeg4_hec_period;
-       void *e_aspect_ratio;
-       void *e_extended_sar;
-       void *e_h264_options;
-       void *e_h264_options_2;/* v7 and v8 */
-       void *e_h264_lf_alpha_offset;
-       void *e_h264_lf_beta_offset;
-       void *e_h264_i_period;
-       void *e_h264_fmo_slice_grp_map_type;
-       void *e_h264_fmo_num_slice_grp_minus1;
-       void *e_h264_fmo_slice_grp_change_dir;
-       void *e_h264_fmo_slice_grp_change_rate_minus1;
-       void *e_h264_fmo_run_length_minus1_0;
-       void *e_h264_aso_slice_order_0;
-       void *e_h264_chroma_qp_offset;
-       void *e_h264_num_t_layer;
-       void *e_h264_hierarchical_qp_layer0;
-       void *e_h264_frame_packing_sei_info;
-       void *e_h264_nal_control;/* v7 and v8 */
-       void *e_mvc_frame_qp_view1;
-       void *e_mvc_rc_bit_rate_view1;
-       void *e_mvc_rc_qbound_view1;
-       void *e_mvc_rc_mode_view1;
-       void *e_mvc_inter_view_prediction_on;
-       void *e_vp8_options;/* v7 and v8 */
-       void *e_vp8_filter_options;/* v7 and v8 */
-       void *e_vp8_golden_frame_option;/* v7 and v8 */
-       void *e_vp8_num_t_layer;/* v7 and v8 */
-       void *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */
-       void *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */
-       void *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */
+       volatile void __iomem *e_frame_width;
+       volatile void __iomem *e_frame_height;
+       volatile void __iomem *e_cropped_frame_width;
+       volatile void __iomem *e_cropped_frame_height;
+       volatile void __iomem *e_frame_crop_offset;
+       volatile void __iomem *e_enc_options;
+       volatile void __iomem *e_picture_profile;
+       volatile void __iomem *e_vbv_buffer_size;
+       volatile void __iomem *e_vbv_init_delay;
+       volatile void __iomem *e_fixed_picture_qp;
+       volatile void __iomem *e_rc_config;
+       volatile void __iomem *e_rc_qp_bound;
+       volatile void __iomem *e_rc_qp_bound_pb;/* v7 and v8 */
+       volatile void __iomem *e_rc_mode;
+       volatile void __iomem *e_mb_rc_config;
+       volatile void __iomem *e_padding_ctrl;
+       volatile void __iomem *e_air_threshold;
+       volatile void __iomem *e_mv_hor_range;
+       volatile void __iomem *e_mv_ver_range;
+       volatile void __iomem *e_num_dpb;
+       volatile void __iomem *e_luma_dpb;
+       volatile void __iomem *e_chroma_dpb;
+       volatile void __iomem *e_me_buffer;
+       volatile void __iomem *e_scratch_buffer_addr;
+       volatile void __iomem *e_scratch_buffer_size;
+       volatile void __iomem *e_tmv_buffer0;
+       volatile void __iomem *e_tmv_buffer1;
+       volatile void __iomem *e_ir_buffer_addr;/* v7 and v8 */
+       volatile void __iomem *e_source_first_plane_addr;
+       volatile void __iomem *e_source_second_plane_addr;
+       volatile void __iomem *e_source_third_plane_addr;/* v7 and v8 */
+       volatile void __iomem *e_source_first_plane_stride;/* v7 and v8 */
+       volatile void __iomem *e_source_second_plane_stride;/* v7 and v8 */
+       volatile void __iomem *e_source_third_plane_stride;/* v7 and v8 */
+       volatile void __iomem *e_stream_buffer_addr;
+       volatile void __iomem *e_stream_buffer_size;
+       volatile void __iomem *e_roi_buffer_addr;
+       volatile void __iomem *e_param_change;
+       volatile void __iomem *e_ir_size;
+       volatile void __iomem *e_gop_config;
+       volatile void __iomem *e_mslice_mode;
+       volatile void __iomem *e_mslice_size_mb;
+       volatile void __iomem *e_mslice_size_bits;
+       volatile void __iomem *e_frame_insertion;
+       volatile void __iomem *e_rc_frame_rate;
+       volatile void __iomem *e_rc_bit_rate;
+       volatile void __iomem *e_rc_roi_ctrl;
+       volatile void __iomem *e_picture_tag;
+       volatile void __iomem *e_bit_count_enable;
+       volatile void __iomem *e_max_bit_count;
+       volatile void __iomem *e_min_bit_count;
+       volatile void __iomem *e_metadata_buffer_addr;
+       volatile void __iomem *e_metadata_buffer_size;
+       volatile void __iomem *e_encoded_source_first_plane_addr;
+       volatile void __iomem *e_encoded_source_second_plane_addr;
+       volatile void __iomem *e_encoded_source_third_plane_addr;/* v7 and v8 */
+       volatile void __iomem *e_stream_size;
+       volatile void __iomem *e_slice_type;
+       volatile void __iomem *e_picture_count;
+       volatile void __iomem *e_ret_picture_tag;
+       volatile void __iomem *e_stream_buffer_write_pointer; /*  only v6 */
+       volatile void __iomem *e_recon_luma_dpb_addr;
+       volatile void __iomem *e_recon_chroma_dpb_addr;
+       volatile void __iomem *e_metadata_addr_enc_slice;
+       volatile void __iomem *e_metadata_size_enc_slice;
+       volatile void __iomem *e_mpeg4_options;
+       volatile void __iomem *e_mpeg4_hec_period;
+       volatile void __iomem *e_aspect_ratio;
+       volatile void __iomem *e_extended_sar;
+       volatile void __iomem *e_h264_options;
+       volatile void __iomem *e_h264_options_2;/* v7 and v8 */
+       volatile void __iomem *e_h264_lf_alpha_offset;
+       volatile void __iomem *e_h264_lf_beta_offset;
+       volatile void __iomem *e_h264_i_period;
+       volatile void __iomem *e_h264_fmo_slice_grp_map_type;
+       volatile void __iomem *e_h264_fmo_num_slice_grp_minus1;
+       volatile void __iomem *e_h264_fmo_slice_grp_change_dir;
+       volatile void __iomem *e_h264_fmo_slice_grp_change_rate_minus1;
+       volatile void __iomem *e_h264_fmo_run_length_minus1_0;
+       volatile void __iomem *e_h264_aso_slice_order_0;
+       volatile void __iomem *e_h264_chroma_qp_offset;
+       volatile void __iomem *e_h264_num_t_layer;
+       volatile void __iomem *e_h264_hierarchical_qp_layer0;
+       volatile void __iomem *e_h264_frame_packing_sei_info;
+       volatile void __iomem *e_h264_nal_control;/* v7 and v8 */
+       volatile void __iomem *e_mvc_frame_qp_view1;
+       volatile void __iomem *e_mvc_rc_bit_rate_view1;
+       volatile void __iomem *e_mvc_rc_qbound_view1;
+       volatile void __iomem *e_mvc_rc_mode_view1;
+       volatile void __iomem *e_mvc_inter_view_prediction_on;
+       volatile void __iomem *e_vp8_options;/* v7 and v8 */
+       volatile void __iomem *e_vp8_filter_options;/* v7 and v8 */
+       volatile void __iomem *e_vp8_golden_frame_option;/* v7 and v8 */
+       volatile void __iomem *e_vp8_num_t_layer;/* v7 and v8 */
+       volatile void __iomem *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */
+       volatile void __iomem *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */
+       volatile void __iomem *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */
 };
 
 struct s5p_mfc_hw_ops {
index 58ec7bb26ebc715f11b145f72149b9a4535a9297..7cf07963187dd33e254cc3d89db849f2ae877faf 100644 (file)
@@ -228,6 +228,7 @@ static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx)
        ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm);
        if (ret) {
                mfc_err("Failed to allocate shared memory buffer\n");
+               s5p_mfc_release_priv_buf(dev->mem_dev_l, &ctx->ctx);
                return ret;
        }
 
@@ -262,7 +263,7 @@ static void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev)
 static void s5p_mfc_write_info_v5(struct s5p_mfc_ctx *ctx, unsigned int data,
                        unsigned int ofs)
 {
-       writel(data, (ctx->shm.virt + ofs));
+       writel(data, (volatile void __iomem *)(ctx->shm.virt + ofs));
        wmb();
 }
 
@@ -270,7 +271,7 @@ static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx,
                                unsigned int ofs)
 {
        rmb();
-       return readl(ctx->shm.virt + ofs);
+       return readl((volatile void __iomem *)(ctx->shm.virt + ofs));
 }
 
 static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx)
@@ -377,7 +378,7 @@ static int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx,
 /* Set decoding frame buffer */
 static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
 {
-       unsigned int frame_size, i;
+       unsigned int frame_size_lu, i;
        unsigned int frame_size_ch, frame_size_mv;
        struct s5p_mfc_dev *dev = ctx->dev;
        unsigned int dpb;
@@ -465,23 +466,23 @@ static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
                        ctx->codec_mode);
                return -EINVAL;
        }
-       frame_size = ctx->luma_size;
+       frame_size_lu = ctx->luma_size;
        frame_size_ch = ctx->chroma_size;
        frame_size_mv = ctx->mv_size;
-       mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size, frame_size_ch,
+       mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size_lu, frame_size_ch,
                                                                frame_size_mv);
        for (i = 0; i < ctx->total_dpb_count; i++) {
                /* Bank2 */
-               mfc_debug(2, "Luma %d: %x\n", i,
+               mfc_debug(2, "Luma %d: %zx\n", i,
                                        ctx->dst_bufs[i].cookie.raw.luma);
                mfc_write(dev, OFFSETB(ctx->dst_bufs[i].cookie.raw.luma),
                                                S5P_FIMV_DEC_LUMA_ADR + i * 4);
-               mfc_debug(2, "\tChroma %d: %x\n", i,
+               mfc_debug(2, "\tChroma %d: %zx\n", i,
                                        ctx->dst_bufs[i].cookie.raw.chroma);
                mfc_write(dev, OFFSETA(ctx->dst_bufs[i].cookie.raw.chroma),
                                               S5P_FIMV_DEC_CHROMA_ADR + i * 4);
                if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC) {
-                       mfc_debug(2, "\tBuf2: %x, size: %d\n",
+                       mfc_debug(2, "\tBuf2: %zx, size: %d\n",
                                                        buf_addr2, buf_size2);
                        mfc_write(dev, OFFSETB(buf_addr2),
                                                S5P_FIMV_H264_MV_ADR + i * 4);
@@ -489,14 +490,14 @@ static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
                        buf_size2 -= frame_size_mv;
                }
        }
-       mfc_debug(2, "Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
+       mfc_debug(2, "Buf1: %zu, buf_size1: %d\n", buf_addr1, buf_size1);
        mfc_debug(2, "Buf 1/2 size after: %d/%d (frames %d)\n",
                        buf_size1,  buf_size2, ctx->total_dpb_count);
        if (buf_size1 < 0 || buf_size2 < 0) {
                mfc_debug(2, "Not enough memory has been allocated\n");
                return -ENOMEM;
        }
-       s5p_mfc_write_info_v5(ctx, frame_size, ALLOC_LUMA_DPB_SIZE);
+       s5p_mfc_write_info_v5(ctx, frame_size_lu, ALLOC_LUMA_DPB_SIZE);
        s5p_mfc_write_info_v5(ctx, frame_size_ch, ALLOC_CHROMA_DPB_SIZE);
        if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC)
                s5p_mfc_write_info_v5(ctx, frame_size_mv, ALLOC_MV_SIZE);
@@ -566,7 +567,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
                enc_ref_c_size = ALIGN(guard_width * guard_height,
                                       S5P_FIMV_NV12MT_SALIGN);
        }
-       mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", buf_size1, buf_size2);
+       mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n", buf_size1, buf_size2);
        switch (ctx->codec_mode) {
        case S5P_MFC_CODEC_H264_ENC:
                for (i = 0; i < 2; i++) {
@@ -605,7 +606,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
                                        S5P_FIMV_H264_NBOR_INFO_ADR);
                buf_addr1 += S5P_FIMV_ENC_NBORINFO_SIZE;
                buf_size1 -= S5P_FIMV_ENC_NBORINFO_SIZE;
-               mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+               mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n",
                        buf_size1, buf_size2);
                break;
        case S5P_MFC_CODEC_MPEG4_ENC:
@@ -636,7 +637,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
                                                S5P_FIMV_MPEG4_ACDC_COEF_ADR);
                buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
                buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
-               mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+               mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n",
                        buf_size1, buf_size2);
                break;
        case S5P_MFC_CODEC_H263_ENC:
@@ -662,7 +663,7 @@ static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
                mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_ACDC_COEF_ADR);
                buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
                buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
-               mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+               mfc_debug(2, "buf_size1: %zu, buf_size2: %zu\n",
                        buf_size1, buf_size2);
                break;
        default:
@@ -1186,7 +1187,6 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
        struct s5p_mfc_dev *dev = ctx->dev;
        struct s5p_mfc_buf *temp_vb;
        unsigned long flags;
-       unsigned int index;
 
        if (ctx->state == MFCINST_FINISHING) {
                last_frame = MFC_DEC_LAST_FRAME;
@@ -1211,7 +1211,6 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
                vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
                ctx->consumed_stream, temp_vb->b->v4l2_planes[0].bytesused);
        spin_unlock_irqrestore(&dev->irqlock, flags);
-       index = temp_vb->b->v4l2_buf.index;
        dev->curr_ctx = ctx->num;
        s5p_mfc_clean_ctx_int_flags(ctx);
        if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
index c1c12f8d8f68055352ccc3ef271524ec4d171beb..8798b14bacce8f1e85a658b578728a9976a549f0 100644 (file)
        } while (0)
 #endif /* S5P_MFC_DEBUG_REGWRITE */
 
-#define READL(reg) \
-       (WARN_ON_ONCE(!(reg)) ? 0 : readl(reg))
-#define WRITEL(data, reg) \
-       (WARN_ON_ONCE(!(reg)) ? 0 : writel((data), (reg)))
-
 #define IS_MFCV6_V2(dev) (!IS_MFCV7_PLUS(dev) && dev->fw_ver == MFC_FW_V2)
 
 /* Allocate temporary buffers for decoding */
@@ -105,7 +100,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
                                                mb_width, mb_height),
                                                S5P_FIMV_ME_BUFFER_ALIGN_V6);
 
-               mfc_debug(2, "recon luma size: %d chroma size: %d\n",
+               mfc_debug(2, "recon luma size: %zu chroma size: %zu\n",
                          ctx->luma_dpb_size, ctx->chroma_dpb_size);
        } else {
                return -EINVAL;
@@ -416,10 +411,10 @@ static int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx,
        mfc_debug(2, "inst_no: %d, buf_addr: 0x%08x,\n"
                "buf_size: 0x%08x (%d)\n",
                ctx->inst_no, buf_addr, strm_size, strm_size);
-       WRITEL(strm_size, mfc_regs->d_stream_data_size);
-       WRITEL(buf_addr, mfc_regs->d_cpb_buffer_addr);
-       WRITEL(buf_size->cpb, mfc_regs->d_cpb_buffer_size);
-       WRITEL(start_num_byte, mfc_regs->d_cpb_buffer_offset);
+       writel(strm_size, mfc_regs->d_stream_data_size);
+       writel(buf_addr, mfc_regs->d_cpb_buffer_addr);
+       writel(buf_size->cpb, mfc_regs->d_cpb_buffer_size);
+       writel(start_num_byte, mfc_regs->d_cpb_buffer_offset);
 
        mfc_debug_leave();
        return 0;
@@ -443,17 +438,17 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
        mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count);
        mfc_debug(2, "Setting display delay to %d\n", ctx->display_delay);
 
-       WRITEL(ctx->total_dpb_count, mfc_regs->d_num_dpb);
-       WRITEL(ctx->luma_size, mfc_regs->d_first_plane_dpb_size);
-       WRITEL(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size);
+       writel(ctx->total_dpb_count, mfc_regs->d_num_dpb);
+       writel(ctx->luma_size, mfc_regs->d_first_plane_dpb_size);
+       writel(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size);
 
-       WRITEL(buf_addr1, mfc_regs->d_scratch_buffer_addr);
-       WRITEL(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size);
+       writel(buf_addr1, mfc_regs->d_scratch_buffer_addr);
+       writel(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size);
 
        if (IS_MFCV8(dev)) {
-               WRITEL(ctx->img_width,
+               writel(ctx->img_width,
                        mfc_regs->d_first_plane_dpb_stride_size);
-               WRITEL(ctx->img_width,
+               writel(ctx->img_width,
                        mfc_regs->d_second_plane_dpb_stride_size);
        }
 
@@ -462,8 +457,8 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
 
        if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
                        ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC){
-               WRITEL(ctx->mv_size, mfc_regs->d_mv_buffer_size);
-               WRITEL(ctx->mv_count, mfc_regs->d_num_mv);
+               writel(ctx->mv_size, mfc_regs->d_mv_buffer_size);
+               writel(ctx->mv_count, mfc_regs->d_num_mv);
        }
 
        frame_size = ctx->luma_size;
@@ -474,13 +469,13 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
 
        for (i = 0; i < ctx->total_dpb_count; i++) {
                /* Bank2 */
-               mfc_debug(2, "Luma %d: %x\n", i,
+               mfc_debug(2, "Luma %d: %zx\n", i,
                                        ctx->dst_bufs[i].cookie.raw.luma);
-               WRITEL(ctx->dst_bufs[i].cookie.raw.luma,
+               writel(ctx->dst_bufs[i].cookie.raw.luma,
                                mfc_regs->d_first_plane_dpb + i * 4);
-               mfc_debug(2, "\tChroma %d: %x\n", i,
+               mfc_debug(2, "\tChroma %d: %zx\n", i,
                                        ctx->dst_bufs[i].cookie.raw.chroma);
-               WRITEL(ctx->dst_bufs[i].cookie.raw.chroma,
+               writel(ctx->dst_bufs[i].cookie.raw.chroma,
                                mfc_regs->d_second_plane_dpb + i * 4);
        }
        if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
@@ -492,23 +487,23 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
                        align_gap = buf_addr1 - align_gap;
                        buf_size1 -= align_gap;
 
-                       mfc_debug(2, "\tBuf1: %x, size: %d\n",
+                       mfc_debug(2, "\tBuf1: %zx, size: %d\n",
                                        buf_addr1, buf_size1);
-                       WRITEL(buf_addr1, mfc_regs->d_mv_buffer + i * 4);
+                       writel(buf_addr1, mfc_regs->d_mv_buffer + i * 4);
                        buf_addr1 += frame_size_mv;
                        buf_size1 -= frame_size_mv;
                }
        }
 
-       mfc_debug(2, "Buf1: %u, buf_size1: %d (frames %d)\n",
+       mfc_debug(2, "Buf1: %zu, buf_size1: %d (frames %d)\n",
                        buf_addr1, buf_size1, ctx->total_dpb_count);
        if (buf_size1 < 0) {
                mfc_debug(2, "Not enough memory has been allocated.\n");
                return -ENOMEM;
        }
 
-       WRITEL(ctx->inst_no, mfc_regs->instance_id);
-       s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+       writel(ctx->inst_no, mfc_regs->instance_id);
+       s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_INIT_BUFS_V6, NULL);
 
        mfc_debug(2, "After setting buffers.\n");
@@ -522,8 +517,8 @@ static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx,
        struct s5p_mfc_dev *dev = ctx->dev;
        const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
-       WRITEL(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */
-       WRITEL(size, mfc_regs->e_stream_buffer_size);
+       writel(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */
+       writel(size, mfc_regs->e_stream_buffer_size);
 
        mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%d\n",
                  addr, size);
@@ -537,8 +532,8 @@ static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
        struct s5p_mfc_dev *dev = ctx->dev;
        const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
-       WRITEL(y_addr, mfc_regs->e_source_first_plane_addr);
-       WRITEL(c_addr, mfc_regs->e_source_second_plane_addr);
+       writel(y_addr, mfc_regs->e_source_first_plane_addr);
+       writel(c_addr, mfc_regs->e_source_second_plane_addr);
 
        mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr);
        mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr);
@@ -551,11 +546,11 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
        const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        unsigned long enc_recon_y_addr, enc_recon_c_addr;
 
-       *y_addr = READL(mfc_regs->e_encoded_source_first_plane_addr);
-       *c_addr = READL(mfc_regs->e_encoded_source_second_plane_addr);
+       *y_addr = readl(mfc_regs->e_encoded_source_first_plane_addr);
+       *c_addr = readl(mfc_regs->e_encoded_source_second_plane_addr);
 
-       enc_recon_y_addr = READL(mfc_regs->e_recon_luma_dpb_addr);
-       enc_recon_c_addr = READL(mfc_regs->e_recon_chroma_dpb_addr);
+       enc_recon_y_addr = readl(mfc_regs->e_recon_luma_dpb_addr);
+       enc_recon_c_addr = readl(mfc_regs->e_recon_chroma_dpb_addr);
 
        mfc_debug(2, "recon y addr: 0x%08lx\n", enc_recon_y_addr);
        mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr);
@@ -577,36 +572,36 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
        mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1);
 
        for (i = 0; i < ctx->pb_count; i++) {
-               WRITEL(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));
+               writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));
                buf_addr1 += ctx->luma_dpb_size;
-               WRITEL(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));
+               writel(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));
                buf_addr1 += ctx->chroma_dpb_size;
-               WRITEL(buf_addr1, mfc_regs->e_me_buffer + (4 * i));
+               writel(buf_addr1, mfc_regs->e_me_buffer + (4 * i));
                buf_addr1 += ctx->me_buffer_size;
                buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size +
                        ctx->me_buffer_size);
        }
 
-       WRITEL(buf_addr1, mfc_regs->e_scratch_buffer_addr);
-       WRITEL(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size);
+       writel(buf_addr1, mfc_regs->e_scratch_buffer_addr);
+       writel(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size);
        buf_addr1 += ctx->scratch_buf_size;
        buf_size1 -= ctx->scratch_buf_size;
 
-       WRITEL(buf_addr1, mfc_regs->e_tmv_buffer0);
+       writel(buf_addr1, mfc_regs->e_tmv_buffer0);
        buf_addr1 += ctx->tmv_buffer_size >> 1;
-       WRITEL(buf_addr1, mfc_regs->e_tmv_buffer1);
+       writel(buf_addr1, mfc_regs->e_tmv_buffer1);
        buf_addr1 += ctx->tmv_buffer_size >> 1;
        buf_size1 -= ctx->tmv_buffer_size;
 
-       mfc_debug(2, "Buf1: %u, buf_size1: %d (ref frames %d)\n",
+       mfc_debug(2, "Buf1: %zu, buf_size1: %d (ref frames %d)\n",
                        buf_addr1, buf_size1, ctx->pb_count);
        if (buf_size1 < 0) {
                mfc_debug(2, "Not enough memory has been allocated.\n");
                return -ENOMEM;
        }
 
-       WRITEL(ctx->inst_no, mfc_regs->instance_id);
-       s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+       writel(ctx->inst_no, mfc_regs->instance_id);
+       s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_INIT_BUFS_V6, NULL);
 
        mfc_debug_leave();
@@ -621,15 +616,15 @@ static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx)
 
        /* multi-slice control */
        /* multi-slice MB number or bit size */
-       WRITEL(ctx->slice_mode, mfc_regs->e_mslice_mode);
+       writel(ctx->slice_mode, mfc_regs->e_mslice_mode);
        if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
-               WRITEL(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb);
+               writel(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb);
        } else if (ctx->slice_mode ==
                        V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
-               WRITEL(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits);
+               writel(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits);
        } else {
-               WRITEL(0x0, mfc_regs->e_mslice_size_mb);
-               WRITEL(0x0, mfc_regs->e_mslice_size_bits);
+               writel(0x0, mfc_regs->e_mslice_size_mb);
+               writel(0x0, mfc_regs->e_mslice_size_bits);
        }
 
        return 0;
@@ -645,21 +640,21 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
        mfc_debug_enter();
 
        /* width */
-       WRITEL(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */
+       writel(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */
        /* height */
-       WRITEL(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */
+       writel(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */
 
        /* cropped width */
-       WRITEL(ctx->img_width, mfc_regs->e_cropped_frame_width);
+       writel(ctx->img_width, mfc_regs->e_cropped_frame_width);
        /* cropped height */
-       WRITEL(ctx->img_height, mfc_regs->e_cropped_frame_height);
+       writel(ctx->img_height, mfc_regs->e_cropped_frame_height);
        /* cropped offset */
-       WRITEL(0x0, mfc_regs->e_frame_crop_offset);
+       writel(0x0, mfc_regs->e_frame_crop_offset);
 
        /* pictype : IDR period */
        reg = 0;
        reg |= p->gop_size & 0xFFFF;
-       WRITEL(reg, mfc_regs->e_gop_config);
+       writel(reg, mfc_regs->e_gop_config);
 
        /* multi-slice control */
        /* multi-slice MB number or bit size */
@@ -667,65 +662,65 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
        reg = 0;
        if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
                reg |= (0x1 << 3);
-               WRITEL(reg, mfc_regs->e_enc_options);
+               writel(reg, mfc_regs->e_enc_options);
                ctx->slice_size.mb = p->slice_mb;
        } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
                reg |= (0x1 << 3);
-               WRITEL(reg, mfc_regs->e_enc_options);
+               writel(reg, mfc_regs->e_enc_options);
                ctx->slice_size.bits = p->slice_bit;
        } else {
                reg &= ~(0x1 << 3);
-               WRITEL(reg, mfc_regs->e_enc_options);
+               writel(reg, mfc_regs->e_enc_options);
        }
 
        s5p_mfc_set_slice_mode(ctx);
 
        /* cyclic intra refresh */
-       WRITEL(p->intra_refresh_mb, mfc_regs->e_ir_size);
-       reg = READL(mfc_regs->e_enc_options);
+       writel(p->intra_refresh_mb, mfc_regs->e_ir_size);
+       reg = readl(mfc_regs->e_enc_options);
        if (p->intra_refresh_mb == 0)
                reg &= ~(0x1 << 4);
        else
                reg |= (0x1 << 4);
-       WRITEL(reg, mfc_regs->e_enc_options);
+       writel(reg, mfc_regs->e_enc_options);
 
        /* 'NON_REFERENCE_STORE_ENABLE' for debugging */
-       reg = READL(mfc_regs->e_enc_options);
+       reg = readl(mfc_regs->e_enc_options);
        reg &= ~(0x1 << 9);
-       WRITEL(reg, mfc_regs->e_enc_options);
+       writel(reg, mfc_regs->e_enc_options);
 
        /* memory structure cur. frame */
        if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
                /* 0: Linear, 1: 2D tiled*/
-               reg = READL(mfc_regs->e_enc_options);
+               reg = readl(mfc_regs->e_enc_options);
                reg &= ~(0x1 << 7);
-               WRITEL(reg, mfc_regs->e_enc_options);
+               writel(reg, mfc_regs->e_enc_options);
                /* 0: NV12(CbCr), 1: NV21(CrCb) */
-               WRITEL(0x0, mfc_regs->pixel_format);
+               writel(0x0, mfc_regs->pixel_format);
        } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV21M) {
                /* 0: Linear, 1: 2D tiled*/
-               reg = READL(mfc_regs->e_enc_options);
+               reg = readl(mfc_regs->e_enc_options);
                reg &= ~(0x1 << 7);
-               WRITEL(reg, mfc_regs->e_enc_options);
+               writel(reg, mfc_regs->e_enc_options);
                /* 0: NV12(CbCr), 1: NV21(CrCb) */
-               WRITEL(0x1, mfc_regs->pixel_format);
+               writel(0x1, mfc_regs->pixel_format);
        } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {
                /* 0: Linear, 1: 2D tiled*/
-               reg = READL(mfc_regs->e_enc_options);
+               reg = readl(mfc_regs->e_enc_options);
                reg |= (0x1 << 7);
-               WRITEL(reg, mfc_regs->e_enc_options);
+               writel(reg, mfc_regs->e_enc_options);
                /* 0: NV12(CbCr), 1: NV21(CrCb) */
-               WRITEL(0x0, mfc_regs->pixel_format);
+               writel(0x0, mfc_regs->pixel_format);
        }
 
        /* memory structure recon. frame */
        /* 0: Linear, 1: 2D tiled */
-       reg = READL(mfc_regs->e_enc_options);
+       reg = readl(mfc_regs->e_enc_options);
        reg |= (0x1 << 8);
-       WRITEL(reg, mfc_regs->e_enc_options);
+       writel(reg, mfc_regs->e_enc_options);
 
        /* padding control & value */
-       WRITEL(0x0, mfc_regs->e_padding_ctrl);
+       writel(0x0, mfc_regs->e_padding_ctrl);
        if (p->pad) {
                reg = 0;
                /** enable */
@@ -736,64 +731,64 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
                reg |= ((p->pad_cb & 0xFF) << 8);
                /** y value */
                reg |= p->pad_luma & 0xFF;
-               WRITEL(reg, mfc_regs->e_padding_ctrl);
+               writel(reg, mfc_regs->e_padding_ctrl);
        }
 
        /* rate control config. */
        reg = 0;
        /* frame-level rate control */
        reg |= ((p->rc_frame & 0x1) << 9);
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /* bit rate */
        if (p->rc_frame)
-               WRITEL(p->rc_bitrate,
+               writel(p->rc_bitrate,
                        mfc_regs->e_rc_bit_rate);
        else
-               WRITEL(1, mfc_regs->e_rc_bit_rate);
+               writel(1, mfc_regs->e_rc_bit_rate);
 
        /* reaction coefficient */
        if (p->rc_frame) {
                if (p->rc_reaction_coeff < TIGHT_CBR_MAX) /* tight CBR */
-                       WRITEL(1, mfc_regs->e_rc_mode);
+                       writel(1, mfc_regs->e_rc_mode);
                else                                      /* loose CBR */
-                       WRITEL(2, mfc_regs->e_rc_mode);
+                       writel(2, mfc_regs->e_rc_mode);
        }
 
        /* seq header ctrl */
-       reg = READL(mfc_regs->e_enc_options);
+       reg = readl(mfc_regs->e_enc_options);
        reg &= ~(0x1 << 2);
        reg |= ((p->seq_hdr_mode & 0x1) << 2);
 
        /* frame skip mode */
        reg &= ~(0x3);
        reg |= (p->frame_skip_mode & 0x3);
-       WRITEL(reg, mfc_regs->e_enc_options);
+       writel(reg, mfc_regs->e_enc_options);
 
        /* 'DROP_CONTROL_ENABLE', disable */
-       reg = READL(mfc_regs->e_rc_config);
+       reg = readl(mfc_regs->e_rc_config);
        reg &= ~(0x1 << 10);
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /* setting for MV range [16, 256] */
        reg = (p->mv_h_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
-       WRITEL(reg, mfc_regs->e_mv_hor_range);
+       writel(reg, mfc_regs->e_mv_hor_range);
 
        reg = (p->mv_v_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
-       WRITEL(reg, mfc_regs->e_mv_ver_range);
+       writel(reg, mfc_regs->e_mv_ver_range);
 
-       WRITEL(0x0, mfc_regs->e_frame_insertion);
-       WRITEL(0x0, mfc_regs->e_roi_buffer_addr);
-       WRITEL(0x0, mfc_regs->e_param_change);
-       WRITEL(0x0, mfc_regs->e_rc_roi_ctrl);
-       WRITEL(0x0, mfc_regs->e_picture_tag);
+       writel(0x0, mfc_regs->e_frame_insertion);
+       writel(0x0, mfc_regs->e_roi_buffer_addr);
+       writel(0x0, mfc_regs->e_param_change);
+       writel(0x0, mfc_regs->e_rc_roi_ctrl);
+       writel(0x0, mfc_regs->e_picture_tag);
 
-       WRITEL(0x0, mfc_regs->e_bit_count_enable);
-       WRITEL(0x0, mfc_regs->e_max_bit_count);
-       WRITEL(0x0, mfc_regs->e_min_bit_count);
+       writel(0x0, mfc_regs->e_bit_count_enable);
+       writel(0x0, mfc_regs->e_max_bit_count);
+       writel(0x0, mfc_regs->e_min_bit_count);
 
-       WRITEL(0x0, mfc_regs->e_metadata_buffer_addr);
-       WRITEL(0x0, mfc_regs->e_metadata_buffer_size);
+       writel(0x0, mfc_regs->e_metadata_buffer_addr);
+       writel(0x0, mfc_regs->e_metadata_buffer_size);
 
        mfc_debug_leave();
 
@@ -814,10 +809,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
        s5p_mfc_set_enc_params(ctx);
 
        /* pictype : number of B */
-       reg = READL(mfc_regs->e_gop_config);
+       reg = readl(mfc_regs->e_gop_config);
        reg &= ~(0x3 << 16);
        reg |= ((p->num_b_frame & 0x3) << 16);
-       WRITEL(reg, mfc_regs->e_gop_config);
+       writel(reg, mfc_regs->e_gop_config);
 
        /* profile & level */
        reg = 0;
@@ -825,19 +820,19 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
        reg |= ((p_h264->level & 0xFF) << 8);
        /** profile - 0 ~ 3 */
        reg |= p_h264->profile & 0x3F;
-       WRITEL(reg, mfc_regs->e_picture_profile);
+       writel(reg, mfc_regs->e_picture_profile);
 
        /* rate control config. */
-       reg = READL(mfc_regs->e_rc_config);
+       reg = readl(mfc_regs->e_rc_config);
        /** macroblock level rate control */
        reg &= ~(0x1 << 8);
        reg |= ((p->rc_mb & 0x1) << 8);
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /** frame QP */
        reg &= ~(0x3F);
        reg |= p_h264->rc_frame_qp & 0x3F;
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /* max & min value of QP */
        reg = 0;
@@ -845,16 +840,16 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
        reg |= ((p_h264->rc_max_qp & 0x3F) << 8);
        /** min QP */
        reg |= p_h264->rc_min_qp & 0x3F;
-       WRITEL(reg, mfc_regs->e_rc_qp_bound);
+       writel(reg, mfc_regs->e_rc_qp_bound);
 
        /* other QPs */
-       WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
+       writel(0x0, mfc_regs->e_fixed_picture_qp);
        if (!p->rc_frame && !p->rc_mb) {
                reg = 0;
                reg |= ((p_h264->rc_b_frame_qp & 0x3F) << 16);
                reg |= ((p_h264->rc_p_frame_qp & 0x3F) << 8);
                reg |= p_h264->rc_frame_qp & 0x3F;
-               WRITEL(reg, mfc_regs->e_fixed_picture_qp);
+               writel(reg, mfc_regs->e_fixed_picture_qp);
        }
 
        /* frame rate */
@@ -862,38 +857,38 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                reg = 0;
                reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
                reg |= p->rc_framerate_denom & 0xFFFF;
-               WRITEL(reg, mfc_regs->e_rc_frame_rate);
+               writel(reg, mfc_regs->e_rc_frame_rate);
        }
 
        /* vbv buffer size */
        if (p->frame_skip_mode ==
                        V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
-               WRITEL(p_h264->cpb_size & 0xFFFF,
+               writel(p_h264->cpb_size & 0xFFFF,
                                mfc_regs->e_vbv_buffer_size);
 
                if (p->rc_frame)
-                       WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
+                       writel(p->vbv_delay, mfc_regs->e_vbv_init_delay);
        }
 
        /* interlace */
        reg = 0;
        reg |= ((p_h264->interlace & 0x1) << 3);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
        /* height */
        if (p_h264->interlace) {
-               WRITEL(ctx->img_height >> 1,
+               writel(ctx->img_height >> 1,
                                mfc_regs->e_frame_height); /* 32 align */
                /* cropped height */
-               WRITEL(ctx->img_height >> 1,
+               writel(ctx->img_height >> 1,
                                mfc_regs->e_cropped_frame_height);
        }
 
        /* loop filter ctrl */
-       reg = READL(mfc_regs->e_h264_options);
+       reg = readl(mfc_regs->e_h264_options);
        reg &= ~(0x3 << 1);
        reg |= ((p_h264->loop_filter_mode & 0x3) << 1);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
        /* loopfilter alpha offset */
        if (p_h264->loop_filter_alpha < 0) {
@@ -903,7 +898,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                reg = 0x00;
                reg |= (p_h264->loop_filter_alpha & 0xF);
        }
-       WRITEL(reg, mfc_regs->e_h264_lf_alpha_offset);
+       writel(reg, mfc_regs->e_h264_lf_alpha_offset);
 
        /* loopfilter beta offset */
        if (p_h264->loop_filter_beta < 0) {
@@ -913,28 +908,28 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                reg = 0x00;
                reg |= (p_h264->loop_filter_beta & 0xF);
        }
-       WRITEL(reg, mfc_regs->e_h264_lf_beta_offset);
+       writel(reg, mfc_regs->e_h264_lf_beta_offset);
 
        /* entropy coding mode */
-       reg = READL(mfc_regs->e_h264_options);
+       reg = readl(mfc_regs->e_h264_options);
        reg &= ~(0x1);
        reg |= p_h264->entropy_mode & 0x1;
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
        /* number of ref. picture */
-       reg = READL(mfc_regs->e_h264_options);
+       reg = readl(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 7);
        reg |= (((p_h264->num_ref_pic_4p - 1) & 0x1) << 7);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
        /* 8x8 transform enable */
-       reg = READL(mfc_regs->e_h264_options);
+       reg = readl(mfc_regs->e_h264_options);
        reg &= ~(0x3 << 12);
        reg |= ((p_h264->_8x8_transform & 0x3) << 12);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
        /* macroblock adaptive scaling features */
-       WRITEL(0x0, mfc_regs->e_mb_rc_config);
+       writel(0x0, mfc_regs->e_mb_rc_config);
        if (p->rc_mb) {
                reg = 0;
                /** dark region */
@@ -945,95 +940,95 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                reg |= ((p_h264->rc_mb_static & 0x1) << 1);
                /** high activity region */
                reg |= p_h264->rc_mb_activity & 0x1;
-               WRITEL(reg, mfc_regs->e_mb_rc_config);
+               writel(reg, mfc_regs->e_mb_rc_config);
        }
 
        /* aspect ratio VUI */
-       READL(mfc_regs->e_h264_options);
+       readl(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 5);
        reg |= ((p_h264->vui_sar & 0x1) << 5);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
-       WRITEL(0x0, mfc_regs->e_aspect_ratio);
-       WRITEL(0x0, mfc_regs->e_extended_sar);
+       writel(0x0, mfc_regs->e_aspect_ratio);
+       writel(0x0, mfc_regs->e_extended_sar);
        if (p_h264->vui_sar) {
                /* aspect ration IDC */
                reg = 0;
                reg |= p_h264->vui_sar_idc & 0xFF;
-               WRITEL(reg, mfc_regs->e_aspect_ratio);
+               writel(reg, mfc_regs->e_aspect_ratio);
                if (p_h264->vui_sar_idc == 0xFF) {
                        /* extended SAR */
                        reg = 0;
                        reg |= (p_h264->vui_ext_sar_width & 0xFFFF) << 16;
                        reg |= p_h264->vui_ext_sar_height & 0xFFFF;
-                       WRITEL(reg, mfc_regs->e_extended_sar);
+                       writel(reg, mfc_regs->e_extended_sar);
                }
        }
 
        /* intra picture period for H.264 open GOP */
        /* control */
-       READL(mfc_regs->e_h264_options);
+       readl(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 4);
        reg |= ((p_h264->open_gop & 0x1) << 4);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
        /* value */
-       WRITEL(0x0, mfc_regs->e_h264_i_period);
+       writel(0x0, mfc_regs->e_h264_i_period);
        if (p_h264->open_gop) {
                reg = 0;
                reg |= p_h264->open_gop_size & 0xFFFF;
-               WRITEL(reg, mfc_regs->e_h264_i_period);
+               writel(reg, mfc_regs->e_h264_i_period);
        }
 
        /* 'WEIGHTED_BI_PREDICTION' for B is disable */
-       READL(mfc_regs->e_h264_options);
+       readl(mfc_regs->e_h264_options);
        reg &= ~(0x3 << 9);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
        /* 'CONSTRAINED_INTRA_PRED_ENABLE' is disable */
-       READL(mfc_regs->e_h264_options);
+       readl(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 14);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
        /* ASO */
-       READL(mfc_regs->e_h264_options);
+       readl(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 6);
        reg |= ((p_h264->aso & 0x1) << 6);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
 
        /* hier qp enable */
-       READL(mfc_regs->e_h264_options);
+       readl(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 8);
        reg |= ((p_h264->open_gop & 0x1) << 8);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
        reg = 0;
        if (p_h264->hier_qp && p_h264->hier_qp_layer) {
                reg |= (p_h264->hier_qp_type & 0x1) << 0x3;
                reg |= p_h264->hier_qp_layer & 0x7;
-               WRITEL(reg, mfc_regs->e_h264_num_t_layer);
+               writel(reg, mfc_regs->e_h264_num_t_layer);
                /* QP value for each layer */
                for (i = 0; i < p_h264->hier_qp_layer &&
                                i < ARRAY_SIZE(p_h264->hier_qp_layer_qp); i++) {
-                       WRITEL(p_h264->hier_qp_layer_qp[i],
+                       writel(p_h264->hier_qp_layer_qp[i],
                                mfc_regs->e_h264_hierarchical_qp_layer0
                                + i * 4);
                }
        }
        /* number of coding layer should be zero when hierarchical is disable */
-       WRITEL(reg, mfc_regs->e_h264_num_t_layer);
+       writel(reg, mfc_regs->e_h264_num_t_layer);
 
        /* frame packing SEI generation */
-       READL(mfc_regs->e_h264_options);
+       readl(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 25);
        reg |= ((p_h264->sei_frame_packing & 0x1) << 25);
-       WRITEL(reg, mfc_regs->e_h264_options);
+       writel(reg, mfc_regs->e_h264_options);
        if (p_h264->sei_frame_packing) {
                reg = 0;
                /** current frame0 flag */
                reg |= ((p_h264->sei_fp_curr_frame_0 & 0x1) << 2);
                /** arrangement type */
                reg |= p_h264->sei_fp_arrangement_type & 0x3;
-               WRITEL(reg, mfc_regs->e_h264_frame_packing_sei_info);
+               writel(reg, mfc_regs->e_h264_frame_packing_sei_info);
        }
 
        if (p_h264->fmo) {
@@ -1042,7 +1037,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                        if (p_h264->fmo_slice_grp > 4)
                                p_h264->fmo_slice_grp = 4;
                        for (i = 0; i < (p_h264->fmo_slice_grp & 0xF); i++)
-                               WRITEL(p_h264->fmo_run_len[i] - 1,
+                               writel(p_h264->fmo_run_len[i] - 1,
                                        mfc_regs->e_h264_fmo_run_length_minus1_0
                                        + i * 4);
                        break;
@@ -1054,10 +1049,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN:
                        if (p_h264->fmo_slice_grp > 2)
                                p_h264->fmo_slice_grp = 2;
-                       WRITEL(p_h264->fmo_chg_dir & 0x1,
+                       writel(p_h264->fmo_chg_dir & 0x1,
                                mfc_regs->e_h264_fmo_slice_grp_change_dir);
                        /* the valid range is 0 ~ number of macroblocks -1 */
-                       WRITEL(p_h264->fmo_chg_rate,
+                       writel(p_h264->fmo_chg_rate,
                        mfc_regs->e_h264_fmo_slice_grp_change_rate_minus1);
                        break;
                default:
@@ -1068,12 +1063,12 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                        break;
                }
 
-               WRITEL(p_h264->fmo_map_type,
+               writel(p_h264->fmo_map_type,
                                mfc_regs->e_h264_fmo_slice_grp_map_type);
-               WRITEL(p_h264->fmo_slice_grp - 1,
+               writel(p_h264->fmo_slice_grp - 1,
                                mfc_regs->e_h264_fmo_num_slice_grp_minus1);
        } else {
-               WRITEL(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1);
+               writel(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1);
        }
 
        mfc_debug_leave();
@@ -1094,10 +1089,10 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
        s5p_mfc_set_enc_params(ctx);
 
        /* pictype : number of B */
-       reg = READL(mfc_regs->e_gop_config);
+       reg = readl(mfc_regs->e_gop_config);
        reg &= ~(0x3 << 16);
        reg |= ((p->num_b_frame & 0x3) << 16);
-       WRITEL(reg, mfc_regs->e_gop_config);
+       writel(reg, mfc_regs->e_gop_config);
 
        /* profile & level */
        reg = 0;
@@ -1105,19 +1100,19 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
        reg |= ((p_mpeg4->level & 0xFF) << 8);
        /** profile - 0 ~ 1 */
        reg |= p_mpeg4->profile & 0x3F;
-       WRITEL(reg, mfc_regs->e_picture_profile);
+       writel(reg, mfc_regs->e_picture_profile);
 
        /* rate control config. */
-       reg = READL(mfc_regs->e_rc_config);
+       reg = readl(mfc_regs->e_rc_config);
        /** macroblock level rate control */
        reg &= ~(0x1 << 8);
        reg |= ((p->rc_mb & 0x1) << 8);
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /** frame QP */
        reg &= ~(0x3F);
        reg |= p_mpeg4->rc_frame_qp & 0x3F;
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /* max & min value of QP */
        reg = 0;
@@ -1125,16 +1120,16 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
        reg |= ((p_mpeg4->rc_max_qp & 0x3F) << 8);
        /** min QP */
        reg |= p_mpeg4->rc_min_qp & 0x3F;
-       WRITEL(reg, mfc_regs->e_rc_qp_bound);
+       writel(reg, mfc_regs->e_rc_qp_bound);
 
        /* other QPs */
-       WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
+       writel(0x0, mfc_regs->e_fixed_picture_qp);
        if (!p->rc_frame && !p->rc_mb) {
                reg = 0;
                reg |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 16);
                reg |= ((p_mpeg4->rc_p_frame_qp & 0x3F) << 8);
                reg |= p_mpeg4->rc_frame_qp & 0x3F;
-               WRITEL(reg, mfc_regs->e_fixed_picture_qp);
+               writel(reg, mfc_regs->e_fixed_picture_qp);
        }
 
        /* frame rate */
@@ -1142,21 +1137,21 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
                reg = 0;
                reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
                reg |= p->rc_framerate_denom & 0xFFFF;
-               WRITEL(reg, mfc_regs->e_rc_frame_rate);
+               writel(reg, mfc_regs->e_rc_frame_rate);
        }
 
        /* vbv buffer size */
        if (p->frame_skip_mode ==
                        V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
-               WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
+               writel(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
 
                if (p->rc_frame)
-                       WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
+                       writel(p->vbv_delay, mfc_regs->e_vbv_init_delay);
        }
 
        /* Disable HEC */
-       WRITEL(0x0, mfc_regs->e_mpeg4_options);
-       WRITEL(0x0, mfc_regs->e_mpeg4_hec_period);
+       writel(0x0, mfc_regs->e_mpeg4_options);
+       writel(0x0, mfc_regs->e_mpeg4_hec_period);
 
        mfc_debug_leave();
 
@@ -1179,19 +1174,19 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
        reg = 0;
        /** profile */
        reg |= (0x1 << 4);
-       WRITEL(reg, mfc_regs->e_picture_profile);
+       writel(reg, mfc_regs->e_picture_profile);
 
        /* rate control config. */
-       reg = READL(mfc_regs->e_rc_config);
+       reg = readl(mfc_regs->e_rc_config);
        /** macroblock level rate control */
        reg &= ~(0x1 << 8);
        reg |= ((p->rc_mb & 0x1) << 8);
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /** frame QP */
        reg &= ~(0x3F);
        reg |= p_h263->rc_frame_qp & 0x3F;
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /* max & min value of QP */
        reg = 0;
@@ -1199,16 +1194,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
        reg |= ((p_h263->rc_max_qp & 0x3F) << 8);
        /** min QP */
        reg |= p_h263->rc_min_qp & 0x3F;
-       WRITEL(reg, mfc_regs->e_rc_qp_bound);
+       writel(reg, mfc_regs->e_rc_qp_bound);
 
        /* other QPs */
-       WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
+       writel(0x0, mfc_regs->e_fixed_picture_qp);
        if (!p->rc_frame && !p->rc_mb) {
                reg = 0;
                reg |= ((p_h263->rc_b_frame_qp & 0x3F) << 16);
                reg |= ((p_h263->rc_p_frame_qp & 0x3F) << 8);
                reg |= p_h263->rc_frame_qp & 0x3F;
-               WRITEL(reg, mfc_regs->e_fixed_picture_qp);
+               writel(reg, mfc_regs->e_fixed_picture_qp);
        }
 
        /* frame rate */
@@ -1216,16 +1211,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
                reg = 0;
                reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
                reg |= p->rc_framerate_denom & 0xFFFF;
-               WRITEL(reg, mfc_regs->e_rc_frame_rate);
+               writel(reg, mfc_regs->e_rc_frame_rate);
        }
 
        /* vbv buffer size */
        if (p->frame_skip_mode ==
                        V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
-               WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
+               writel(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
 
                if (p->rc_frame)
-                       WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
+                       writel(p->vbv_delay, mfc_regs->e_vbv_init_delay);
        }
 
        mfc_debug_leave();
@@ -1247,57 +1242,57 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
        s5p_mfc_set_enc_params(ctx);
 
        /* pictype : number of B */
-       reg = READL(mfc_regs->e_gop_config);
+       reg = readl(mfc_regs->e_gop_config);
        reg &= ~(0x3 << 16);
        reg |= ((p->num_b_frame & 0x3) << 16);
-       WRITEL(reg, mfc_regs->e_gop_config);
+       writel(reg, mfc_regs->e_gop_config);
 
        /* profile - 0 ~ 3 */
        reg = p_vp8->profile & 0x3;
-       WRITEL(reg, mfc_regs->e_picture_profile);
+       writel(reg, mfc_regs->e_picture_profile);
 
        /* rate control config. */
-       reg = READL(mfc_regs->e_rc_config);
+       reg = readl(mfc_regs->e_rc_config);
        /** macroblock level rate control */
        reg &= ~(0x1 << 8);
        reg |= ((p->rc_mb & 0x1) << 8);
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /* frame rate */
        if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) {
                reg = 0;
                reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
                reg |= p->rc_framerate_denom & 0xFFFF;
-               WRITEL(reg, mfc_regs->e_rc_frame_rate);
+               writel(reg, mfc_regs->e_rc_frame_rate);
        }
 
        /* frame QP */
        reg &= ~(0x7F);
        reg |= p_vp8->rc_frame_qp & 0x7F;
-       WRITEL(reg, mfc_regs->e_rc_config);
+       writel(reg, mfc_regs->e_rc_config);
 
        /* other QPs */
-       WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
+       writel(0x0, mfc_regs->e_fixed_picture_qp);
        if (!p->rc_frame && !p->rc_mb) {
                reg = 0;
                reg |= ((p_vp8->rc_p_frame_qp & 0x7F) << 8);
                reg |= p_vp8->rc_frame_qp & 0x7F;
-               WRITEL(reg, mfc_regs->e_fixed_picture_qp);
+               writel(reg, mfc_regs->e_fixed_picture_qp);
        }
 
        /* max QP */
        reg = ((p_vp8->rc_max_qp & 0x7F) << 8);
        /* min QP */
        reg |= p_vp8->rc_min_qp & 0x7F;
-       WRITEL(reg, mfc_regs->e_rc_qp_bound);
+       writel(reg, mfc_regs->e_rc_qp_bound);
 
        /* vbv buffer size */
        if (p->frame_skip_mode ==
                        V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
-               WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
+               writel(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
 
                if (p->rc_frame)
-                       WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
+                       writel(p->vbv_delay, mfc_regs->e_vbv_init_delay);
        }
 
        /* VP8 specific params */
@@ -1319,7 +1314,7 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
        }
        reg |= (val & 0xF) << 3;
        reg |= (p_vp8->num_ref & 0x2);
-       WRITEL(reg, mfc_regs->e_vp8_options);
+       writel(reg, mfc_regs->e_vp8_options);
 
        mfc_debug_leave();
 
@@ -1338,9 +1333,9 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
        mfc_debug(2, "InstNo: %d/%d\n", ctx->inst_no,
                        S5P_FIMV_CH_SEQ_HEADER_V6);
        mfc_debug(2, "BUFs: %08x %08x %08x\n",
-                 READL(mfc_regs->d_cpb_buffer_addr),
-                 READL(mfc_regs->d_cpb_buffer_addr),
-                 READL(mfc_regs->d_cpb_buffer_addr));
+                 readl(mfc_regs->d_cpb_buffer_addr),
+                 readl(mfc_regs->d_cpb_buffer_addr),
+                 readl(mfc_regs->d_cpb_buffer_addr));
 
        /* FMO_ASO_CTRL - 0: Enable, 1: Disable */
        reg |= (fmo_aso_ctrl << S5P_FIMV_D_OPT_FMO_ASO_CTRL_MASK_V6);
@@ -1351,11 +1346,11 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
         * set to negative value. */
        if (ctx->display_delay >= 0) {
                reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6);
-               WRITEL(ctx->display_delay, mfc_regs->d_display_delay);
+               writel(ctx->display_delay, mfc_regs->d_display_delay);
        }
 
        if (IS_MFCV7_PLUS(dev) || IS_MFCV6_V2(dev)) {
-               WRITEL(reg, mfc_regs->d_dec_options);
+               writel(reg, mfc_regs->d_dec_options);
                reg = 0;
        }
 
@@ -1370,22 +1365,22 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
                reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6);
 
        if (IS_MFCV7_PLUS(dev) || IS_MFCV6_V2(dev))
-               WRITEL(reg, mfc_regs->d_init_buffer_options);
+               writel(reg, mfc_regs->d_init_buffer_options);
        else
-               WRITEL(reg, mfc_regs->d_dec_options);
+               writel(reg, mfc_regs->d_dec_options);
 
        /* 0: NV12(CbCr), 1: NV21(CrCb) */
        if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M)
-               WRITEL(0x1, mfc_regs->pixel_format);
+               writel(0x1, mfc_regs->pixel_format);
        else
-               WRITEL(0x0, mfc_regs->pixel_format);
+               writel(0x0, mfc_regs->pixel_format);
 
 
        /* sei parse */
-       WRITEL(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable);
+       writel(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable);
 
-       WRITEL(ctx->inst_no, mfc_regs->instance_id);
-       s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+       writel(ctx->inst_no, mfc_regs->instance_id);
+       s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
 
        mfc_debug_leave();
@@ -1400,8 +1395,8 @@ static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
        if (flush) {
                dev->curr_ctx = ctx->num;
                s5p_mfc_clean_ctx_int_flags(ctx);
-               WRITEL(ctx->inst_no, mfc_regs->instance_id);
-               s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+               writel(ctx->inst_no, mfc_regs->instance_id);
+               s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
                                S5P_FIMV_H2R_CMD_FLUSH_V6, NULL);
        }
 }
@@ -1413,19 +1408,19 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,
        struct s5p_mfc_dev *dev = ctx->dev;
        const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
-       WRITEL(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower);
-       WRITEL(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable);
+       writel(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower);
+       writel(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable);
 
-       WRITEL(ctx->inst_no, mfc_regs->instance_id);
+       writel(ctx->inst_no, mfc_regs->instance_id);
        /* Issue different commands to instance basing on whether it
         * is the last frame or not. */
        switch (last_frame) {
        case 0:
-               s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+               s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
                                S5P_FIMV_CH_FRAME_START_V6, NULL);
                break;
        case 1:
-               s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+               s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
                                S5P_FIMV_CH_LAST_FRAME_V6, NULL);
                break;
        default:
@@ -1458,12 +1453,12 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
 
        /* Set stride lengths for v7 & above */
        if (IS_MFCV7_PLUS(dev)) {
-               WRITEL(ctx->img_width, mfc_regs->e_source_first_plane_stride);
-               WRITEL(ctx->img_width, mfc_regs->e_source_second_plane_stride);
+               writel(ctx->img_width, mfc_regs->e_source_first_plane_stride);
+               writel(ctx->img_width, mfc_regs->e_source_second_plane_stride);
        }
 
-       WRITEL(ctx->inst_no, mfc_regs->instance_id);
-       s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+       writel(ctx->inst_no, mfc_regs->instance_id);
+       s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
 
        return 0;
@@ -1479,7 +1474,7 @@ static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx)
 
        if (p_h264->aso) {
                for (i = 0; i < ARRAY_SIZE(p_h264->aso_slice_order); i++) {
-                       WRITEL(p_h264->aso_slice_order[i],
+                       writel(p_h264->aso_slice_order[i],
                                mfc_regs->e_h264_aso_slice_order_0 + i * 4);
                }
        }
@@ -1501,8 +1496,8 @@ static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)
 
        s5p_mfc_set_slice_mode(ctx);
 
-       WRITEL(ctx->inst_no, mfc_regs->instance_id);
-       s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+       writel(ctx->inst_no, mfc_regs->instance_id);
+       s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_FRAME_START_V6, NULL);
 
        mfc_debug(2, "--\n");
@@ -1877,15 +1872,15 @@ static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq)
 static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev)
 {
        const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
-       WRITEL(0, mfc_regs->risc2host_command);
-       WRITEL(0, mfc_regs->risc2host_int);
+       writel(0, mfc_regs->risc2host_command);
+       writel(0, mfc_regs->risc2host_int);
 }
 
 static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data,
                unsigned int ofs)
 {
        s5p_mfc_clock_on();
-       WRITEL(data, (void *)ofs);
+       writel(data, (volatile void __iomem *)((unsigned long)ofs));
        s5p_mfc_clock_off();
 }
 
@@ -1895,7 +1890,7 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)
        int ret;
 
        s5p_mfc_clock_on();
-       ret = READL((void *)ofs);
+       ret = readl((volatile void __iomem *)((unsigned long)ofs));
        s5p_mfc_clock_off();
 
        return ret;
@@ -1903,51 +1898,51 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)
 
 static int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_display_first_plane_addr);
+       return readl(dev->mfc_regs->d_display_first_plane_addr);
 }
 
 static int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_decoded_first_plane_addr);
+       return readl(dev->mfc_regs->d_decoded_first_plane_addr);
 }
 
 static int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_display_status);
+       return readl(dev->mfc_regs->d_display_status);
 }
 
 static int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_decoded_status);
+       return readl(dev->mfc_regs->d_decoded_status);
 }
 
 static int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_decoded_frame_type) &
+       return readl(dev->mfc_regs->d_decoded_frame_type) &
                S5P_FIMV_DECODE_FRAME_MASK_V6;
 }
 
 static int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
-       return READL(dev->mfc_regs->d_display_frame_type) &
+       return readl(dev->mfc_regs->d_display_frame_type) &
                S5P_FIMV_DECODE_FRAME_MASK_V6;
 }
 
 static int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_decoded_nal_size);
+       return readl(dev->mfc_regs->d_decoded_nal_size);
 }
 
 static int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->risc2host_command) &
+       return readl(dev->mfc_regs->risc2host_command) &
                S5P_FIMV_RISC2HOST_CMD_MASK;
 }
 
 static int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->error_code);
+       return readl(dev->mfc_regs->error_code);
 }
 
 static int s5p_mfc_err_dec_v6(unsigned int err)
@@ -1962,87 +1957,87 @@ static int s5p_mfc_err_dspl_v6(unsigned int err)
 
 static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_display_frame_width);
+       return readl(dev->mfc_regs->d_display_frame_width);
 }
 
 static int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_display_frame_height);
+       return readl(dev->mfc_regs->d_display_frame_height);
 }
 
 static int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_min_num_dpb);
+       return readl(dev->mfc_regs->d_min_num_dpb);
 }
 
 static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_min_num_mv);
+       return readl(dev->mfc_regs->d_min_num_mv);
 }
 
 static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->ret_instance_id);
+       return readl(dev->mfc_regs->ret_instance_id);
 }
 
 static int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->e_num_dpb);
+       return readl(dev->mfc_regs->e_num_dpb);
 }
 
 static int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->e_stream_size);
+       return readl(dev->mfc_regs->e_stream_size);
 }
 
 static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->e_slice_type);
+       return readl(dev->mfc_regs->e_slice_type);
 }
 
 static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->e_picture_count);
+       return readl(dev->mfc_regs->e_picture_count);
 }
 
 static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
-       return READL(dev->mfc_regs->d_frame_pack_sei_avail);
+       return readl(dev->mfc_regs->d_frame_pack_sei_avail);
 }
 
 static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_mvc_num_views);
+       return readl(dev->mfc_regs->d_mvc_num_views);
 }
 
 static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev)
 {
-       return READL(dev->mfc_regs->d_mvc_view_id);
+       return readl(dev->mfc_regs->d_mvc_view_id);
 }
 
 static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx)
 {
        return s5p_mfc_read_info_v6(ctx,
-               (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_top);
+               (__force unsigned long) ctx->dev->mfc_regs->d_ret_picture_tag_top);
 }
 
 static unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx)
 {
        return s5p_mfc_read_info_v6(ctx,
-               (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_bot);
+               (__force unsigned long) ctx->dev->mfc_regs->d_ret_picture_tag_bot);
 }
 
 static unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx)
 {
        return s5p_mfc_read_info_v6(ctx,
-               (unsigned int) ctx->dev->mfc_regs->d_display_crop_info1);
+               (__force unsigned long) ctx->dev->mfc_regs->d_display_crop_info1);
 }
 
 static unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx)
 {
        return s5p_mfc_read_info_v6(ctx,
-               (unsigned int) ctx->dev->mfc_regs->d_display_crop_info2);
+               (__force unsigned long) ctx->dev->mfc_regs->d_display_crop_info2);
 }
 
 static struct s5p_mfc_regs mfc_regs;
index b6a8be97a96c57715d2454518052351d43864340..826c48945bf50b188357c31d8108c627697bbac6 100644 (file)
@@ -21,7 +21,7 @@
 #include "s5p_mfc_pm.h"
 
 #define MFC_GATE_CLK_NAME      "mfc"
-#define MFC_SCLK_NAME          "sclk-mfc"
+#define MFC_SCLK_NAME          "sclk_mfc"
 #define MFC_SCLK_RATE          (200 * 1000000)
 
 #define CLK_DEBUG
index 369a4c191e18a8e80e63edcb57cb790df28394a3..a9d56f8936b4e576d2e4552b1d86323d15801da5 100644 (file)
@@ -8,7 +8,8 @@
 
 config VIDEO_SAMSUNG_S5P_TV
        bool "Samsung TV driver for S5P platform"
-       depends on (PLAT_S5P || ARCH_EXYNOS) && PM_RUNTIME
+       depends on PM_RUNTIME
+       depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
        default n
        ---help---
          Say Y here to enable selecting the TV output devices for
@@ -70,6 +71,7 @@ config VIDEO_SAMSUNG_S5P_MIXER
        tristate "Samsung Mixer and Video Processor Driver"
        depends on VIDEO_DEV && VIDEO_V4L2
        depends on VIDEO_SAMSUNG_S5P_TV
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        help
          Say Y here if you want support for the Mixer in Samsung S5P SoCs.
index 754740f4b671db0a0749d3b21ee7afdf5c1fc8b1..37c8bd694c5f250c0e7d64f8a601cb14d5c93bcd 100644 (file)
@@ -615,7 +615,7 @@ static int hdmi_s_power(struct v4l2_subdev *sd, int on)
        else
                ret = pm_runtime_put_sync(hdev->dev);
        /* only values < 0 indicate errors */
-       return IS_ERR_VALUE(ret) ? ret : 0;
+       return ret < 0 ? ret : 0;
 }
 
 static int hdmi_s_dv_timings(struct v4l2_subdev *sd,
index 5a7c3796f22e349de4edf608bd5eb8ffee62d0dd..72cf892dd0089af58d9d2eac6bf533b881b1d6bd 100644 (file)
@@ -190,7 +190,7 @@ static int sdo_s_power(struct v4l2_subdev *sd, int on)
                ret = pm_runtime_put_sync(dev);
 
        /* only values < 0 indicate errors */
-       return IS_ERR_VALUE(ret) ? ret : 0;
+       return ret < 0 ? ret : 0;
 }
 
 static int sdo_streamon(struct sdo_device *sdev)
index 3dd762e5b67e8e53ff05d2fd57351c71e9471797..db8c17bb4aaaf357b6aa1c613e5aee570b953d5d 100644 (file)
@@ -289,7 +289,7 @@ static int sii9234_s_power(struct v4l2_subdev *sd, int on)
        else
                ret = pm_runtime_put(&ctx->client->dev);
        /* only values < 0 indicate errors */
-       return IS_ERR_VALUE(ret) ? ret : 0;
+       return ret < 0 ? ret : 0;
 }
 
 static int sii9234_s_stream(struct v4l2_subdev *sd, int enable)
index 8dc279d4d5610fac3219cb253aa9d383108ab3d4..be3b3bc71a0fcdd2f9e931a06127a71aa33555a9 100644 (file)
@@ -26,6 +26,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-mem2mem.h>
+#include <media/v4l2-image-sizes.h>
 #include <media/videobuf2-dma-contig.h>
 
 #define VEU_STR 0x00 /* start register */
@@ -135,9 +136,6 @@ enum sh_veu_fmt_idx {
        SH_VEU_FMT_RGB24,
 };
 
-#define VGA_WIDTH      640
-#define VGA_HEIGHT     480
-
 #define DEFAULT_IN_WIDTH       VGA_WIDTH
 #define DEFAULT_IN_HEIGHT      VGA_HEIGHT
 #define DEFAULT_IN_FMTIDX      SH_VEU_FMT_NV12
index 6540847f4e1d3345cb90a73ef6ad8531c03f2150..f2776cd415ca81625df0972330adbb636db9d6d8 100644 (file)
@@ -20,6 +20,8 @@ config SOC_CAMERA_PLATFORM
 config VIDEO_MX3
        tristate "i.MX3x Camera Sensor Interface driver"
        depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
+       depends on MX3_IPU || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        ---help---
          This is a v4l2 driver for the i.MX3x Camera Sensor Interface
@@ -35,6 +37,7 @@ config VIDEO_RCAR_VIN
        tristate "R-Car Video Input (VIN) support"
        depends on VIDEO_DEV && SOC_CAMERA
        depends on ARCH_SHMOBILE || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select SOC_CAMERA_SCALE_CROP
        ---help---
@@ -51,6 +54,7 @@ config VIDEO_SH_MOBILE_CEU
        tristate "SuperH Mobile CEU Interface driver"
        depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
        depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        select SOC_CAMERA_SCALE_CROP
        ---help---
@@ -58,7 +62,9 @@ config VIDEO_SH_MOBILE_CEU
 
 config VIDEO_OMAP1
        tristate "OMAP1 Camera Interface driver"
-       depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA
+       depends on VIDEO_DEV && SOC_CAMERA
+       depends on ARCH_OMAP1
+       depends on HAS_DMA
        select VIDEOBUF_DMA_CONTIG
        select VIDEOBUF_DMA_SG
        ---help---
@@ -66,14 +72,18 @@ config VIDEO_OMAP1
 
 config VIDEO_MX2
        tristate "i.MX27 Camera Sensor Interface driver"
-       depends on VIDEO_DEV && SOC_CAMERA && SOC_IMX27
+       depends on VIDEO_DEV && SOC_CAMERA
+       depends on SOC_IMX27 || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        ---help---
          This is a v4l2 driver for the i.MX27 Camera Sensor Interface
 
 config VIDEO_ATMEL_ISI
        tristate "ATMEL Image Sensor Interface (ISI) support"
-       depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
+       depends on VIDEO_DEV && SOC_CAMERA
+       depends on ARCH_AT91 || COMPILE_TEST
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        ---help---
          This module makes the ATMEL Image Sensor Interface available
index 3408b045b3f1cf2697715c4e552c0289284f3443..c5291b00105758b8452b0f9ef383780c794fbac7 100644 (file)
@@ -54,7 +54,7 @@ static void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl)
 struct isi_dma_desc {
        struct list_head list;
        struct fbd *p_fbd;
-       u32 fbd_phys;
+       dma_addr_t fbd_phys;
 };
 
 /* Frame buffer data */
@@ -75,7 +75,7 @@ struct atmel_isi {
 
        /* Allocate descriptors for dma buffer use */
        struct fbd                      *p_fb_descriptors;
-       u32                             fb_descriptors_phys;
+       dma_addr_t                      fb_descriptors_phys;
        struct                          list_head dma_desc_head;
        struct isi_dma_desc             dma_desc[MAX_BUFFER_NUM];
 
@@ -169,7 +169,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi)
                isi->active = list_entry(isi->video_buffer_list.next,
                                        struct frame_buffer, list);
                isi_writel(isi, ISI_DMA_C_DSCR,
-                       isi->active->p_dma_desc->fbd_phys);
+                       (u32)isi->active->p_dma_desc->fbd_phys);
                isi_writel(isi, ISI_DMA_C_CTRL,
                        ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
                isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
@@ -346,7 +346,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
                return;
        }
 
-       isi_writel(isi, ISI_DMA_C_DSCR, buffer->p_dma_desc->fbd_phys);
+       isi_writel(isi, ISI_DMA_C_DSCR, (u32)buffer->p_dma_desc->fbd_phys);
        isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
        isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
 
@@ -384,7 +384,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
        struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
        struct atmel_isi *isi = ici->priv;
-       u32 sr = 0;
        int ret;
 
        /* Reset ISI */
@@ -394,11 +393,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
                return ret;
        }
        /* Disable all interrupts */
-       isi_writel(isi, ISI_INTDIS, ~0UL);
+       isi_writel(isi, ISI_INTDIS, (u32)~0UL);
 
        spin_lock_irq(&isi->lock);
        /* Clear any pending interrupt */
-       sr = isi_readl(isi, ISI_STATUS);
+       isi_readl(isi, ISI_STATUS);
 
        if (count)
                start_dma(isi, isi->active);
index b40bc2e5ba47d6cbcf8ec942adc53a0797970ac7..2347612a4cc167a08af0bf831020d7b7dc932ebd 100644 (file)
@@ -809,10 +809,9 @@ static int mx2_camera_init_videobuf(struct vb2_queue *q,
 
 static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
 {
-       u32 cntl;
        int count = 0;
 
-       cntl = readl(pcdev->base_emma + PRP_CNTL);
+       readl(pcdev->base_emma + PRP_CNTL);
        writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
        while (count++ < 100) {
                if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
@@ -1003,7 +1002,7 @@ static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
                              struct v4l2_mbus_framefmt *mf_in,
                              struct v4l2_pix_format *pix_out, bool apply)
 {
-       int num, den;
+       unsigned int num, den;
        unsigned long m;
        int i, dir;
 
index 64dc80ccd6f932ded137ab36dd562bda860d2366..66178fc9f9ebf90b720430016321668ab5dbcfd8 100644 (file)
@@ -1694,7 +1694,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev,
                break;
        default:
                break;
-       };
+       }
 
        if (ep.bus.parallel.flags & V4L2_MBUS_MASTER)
                pcdev->platform_flags |= PXA_CAMERA_MASTER;
index 85d579f65f5206d0558ed38e4ed6ccba5aa7f0e5..20defcb8b31b005fca81c2f4c0f4ad3b51ffd817 100644 (file)
@@ -981,7 +981,7 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
 
                if (shift == 3) {
                        dev_err(dev,
-                               "Failed to configure the client below %ux%x\n",
+                               "Failed to configure the client below %ux%u\n",
                                mf.width, mf.height);
                        return -EIO;
                }
@@ -1502,7 +1502,7 @@ static int rcar_vin_probe(struct platform_device *pdev)
        } else {
                priv->ici.nr = of_alias_get_id(pdev->dev.of_node, "vin");
                priv->chip = (enum chip_id)match->data;
-       };
+       }
 
        spin_lock_init(&priv->lock);
        INIT_LIST_HEAD(&priv->capture);
index f4308fed54318625d6e802aac288b96bd8cecd68..8e61b976da19ed17fe09866eb3efd7d6544a02bd 100644 (file)
@@ -437,6 +437,22 @@ static int soc_camera_prepare_buf(struct file *file, void *priv,
                return vb2_prepare_buf(&icd->vb2_vidq, b);
 }
 
+static int soc_camera_expbuf(struct file *file, void *priv,
+                            struct v4l2_exportbuffer *p)
+{
+       struct soc_camera_device *icd = file->private_data;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+       if (icd->streamer != file)
+               return -EBUSY;
+
+       /* videobuf2 only */
+       if (ici->ops->init_videobuf)
+               return -EINVAL;
+       else
+               return vb2_expbuf(&icd->vb2_vidq, p);
+}
+
 /* Always entered with .host_lock held */
 static int soc_camera_init_user_formats(struct soc_camera_device *icd)
 {
@@ -1347,13 +1363,11 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,
                return -ENODEV;
        }
 
-       ssdd = kzalloc(sizeof(*ssdd), GFP_KERNEL);
+       ssdd = kmemdup(&sdesc->subdev_desc, sizeof(*ssdd), GFP_KERNEL);
        if (!ssdd) {
                ret = -ENOMEM;
                goto ealloc;
        }
-
-       memcpy(ssdd, &sdesc->subdev_desc, sizeof(*ssdd));
        /*
         * In synchronous case we request regulators ourselves in
         * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try
@@ -2085,6 +2099,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
        .vidioc_dqbuf            = soc_camera_dqbuf,
        .vidioc_create_bufs      = soc_camera_create_bufs,
        .vidioc_prepare_buf      = soc_camera_prepare_buf,
+       .vidioc_expbuf           = soc_camera_expbuf,
        .vidioc_streamon         = soc_camera_streamon,
        .vidioc_streamoff        = soc_camera_streamoff,
        .vidioc_cropcap          = soc_camera_cropcap,
index a51a0135980534e6952db9f492b091128938eb1e..3e2e3a33e6ed850e4ceccd223f90f1c4eb956875 100644 (file)
@@ -329,7 +329,7 @@ int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size)
        if (!buf->addr)
                return -ENOMEM;
 
-       WARN_ON((u32) buf->addr & VPDMA_DESC_ALIGN);
+       WARN_ON(((unsigned long)buf->addr & VPDMA_DESC_ALIGN) != 0);
 
        return 0;
 }
@@ -584,7 +584,7 @@ static void dump_dtd(struct vpdma_dtd *dtd)
                pr_debug("word1: line_length = %d, xfer_height = %d\n",
                        dtd_get_line_length(dtd), dtd_get_xfer_height(dtd));
 
-       pr_debug("word2: start_addr = 0x%08x\n", dtd->start_addr);
+       pr_debug("word2: start_addr = %pad\n", &dtd->start_addr);
 
        pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, "
                "pri = %d, next_chan = %d\n", dtd_get_pkt_type(dtd),
index 972f43f69206b8156dc492d6915f1ca97e8ba536..9a081c2911598502fb33e0d500d876a0361fcbc0 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #include <linux/log2.h>
+#include <linux/sizes.h>
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-ctrls.h>
@@ -138,12 +139,12 @@ struct vpe_dei_regs {
  * default expert DEI register values, unlikely to be modified.
  */
 static const struct vpe_dei_regs dei_regs = {
-       0x020C0804u,
-       0x0118100Fu,
-       0x08040200u,
-       0x1010100Cu,
-       0x10101010u,
-       0x10101010u,
+       .mdt_spacial_freq_thr_reg = 0x020C0804u,
+       .edi_config_reg = 0x0118100Fu,
+       .edi_lut_reg0 = 0x08040200u,
+       .edi_lut_reg1 = 0x1010100Cu,
+       .edi_lut_reg2 = 0x10101010u,
+       .edi_lut_reg3 = 0x10101010u,
 };
 
 /*
@@ -834,10 +835,10 @@ static int set_srcdst_params(struct vpe_ctx *ctx)
                                        VPDMA_STRIDE_ALIGN);
                mv_buf_size = bytes_per_line * s_q_data->height;
 
-               ctx->deinterlacing = 1;
+               ctx->deinterlacing = true;
                src_h <<= 1;
        } else {
-               ctx->deinterlacing = 0;
+               ctx->deinterlacing = false;
                mv_buf_size = 0;
        }
 
@@ -2343,8 +2344,7 @@ v4l2_dev_unreg:
 
 static int vpe_remove(struct platform_device *pdev)
 {
-       struct vpe_dev *dev =
-               (struct vpe_dev *) platform_get_drvdata(pdev);
+       struct vpe_dev *dev = platform_get_drvdata(pdev);
 
        v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME);
 
index b4f9d03636e3e8067e7adefc50070318915e37d1..ae6870cb8339dbff2959c956e8a8229d96b598a3 100644 (file)
@@ -18,6 +18,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-ctrls.h>
+#include <media/v4l2-image-sizes.h>
 #include <media/ov7670.h>
 #include <media/videobuf-dma-sg.h>
 #include <linux/delay.h>
@@ -48,14 +49,6 @@ MODULE_PARM_DESC(override_serial,
                "the XO 1.5 serial port is enabled.  Set this option "
                "to force-enable the camera.");
 
-/*
- * Basic window sizes.
- */
-#define VGA_WIDTH      640
-#define VGA_HEIGHT     480
-#define QCIF_WIDTH     176
-#define        QCIF_HEIGHT     144
-
 /*
  * The structure describing our camera.
  */
@@ -89,7 +82,7 @@ struct via_camera {
         * live in frame buffer memory, so we don't call them "DMA".
         */
        unsigned int cb_offsets[3];     /* offsets into fb mem */
-       u8 *cb_addrs[3];                /* Kernel-space addresses */
+       u8 __iomem *cb_addrs[3];                /* Kernel-space addresses */
        int n_cap_bufs;                 /* How many are we using? */
        int next_buf;
        struct videobuf_queue vb_queue;
@@ -1283,7 +1276,7 @@ static bool viacam_serial_is_enabled(void)
                        VIACAM_SERIAL_CREG, &cbyte);
        if ((cbyte & VIACAM_SERIAL_BIT) == 0)
                return false; /* Not enabled */
-       if (override_serial == 0) {
+       if (!override_serial) {
                printk(KERN_NOTICE "Via camera: serial port is enabled, " \
                                "refusing to load.\n");
                printk(KERN_NOTICE "Specify override_serial=1 to force " \
diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c
deleted file mode 100644 (file)
index 8033371..0000000
+++ /dev/null
@@ -1,1542 +0,0 @@
-/*
- * Virtual Video driver - This code emulates a real video device with v4l2 api
- *
- * Copyright (c) 2006 by:
- *      Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
- *      Ted Walther <ted--a.t--enumera.com>
- *      John Sokol <sokol--a.t--videotechnology.com>
- *      http://v4l.videotechnology.com/
- *
- *      Conversion to videobuf2 by Pawel Osciak & Marek Szyprowski
- *      Copyright (c) 2010 Samsung Electronics
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD Licence, GNU General Public License
- * as published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version
- */
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/font.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <media/videobuf2-vmalloc.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-common.h>
-
-#define VIVI_MODULE_NAME "vivi"
-
-/* Maximum allowed frame rate
- *
- * Vivi will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range.
- *
- * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that
- * might hit application errors when they manipulate these values.
- *
- * Besides, for tpf < 1ms image-generation logic should be changed, to avoid
- * producing frames with equal content.
- */
-#define FPS_MAX 1000
-
-#define MAX_WIDTH 1920
-#define MAX_HEIGHT 1200
-
-#define VIVI_VERSION "0.8.1"
-
-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_VERSION(VIVI_VERSION);
-
-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");
-
-/* Global font descriptor */
-static const u8 *font8x16;
-
-/* timeperframe: min/max and default */
-static const struct v4l2_fract
-       tpf_min     = {.numerator = 1,          .denominator = FPS_MAX},
-       tpf_max     = {.numerator = FPS_MAX,    .denominator = 1},
-       tpf_default = {.numerator = 1001,       .denominator = 30000};  /* NTSC */
-
-#define dprintk(dev, level, fmt, arg...) \
-       v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
-
-/* ------------------------------------------------------------------
-       Basic structures
-   ------------------------------------------------------------------*/
-
-struct vivi_fmt {
-       const char *name;
-       u32   fourcc;          /* v4l2 format id */
-       u8    depth;
-       bool  is_yuv;
-};
-
-static const struct vivi_fmt formats[] = {
-       {
-               .name     = "4:2:2, packed, YUYV",
-               .fourcc   = V4L2_PIX_FMT_YUYV,
-               .depth    = 16,
-               .is_yuv   = true,
-       },
-       {
-               .name     = "4:2:2, packed, UYVY",
-               .fourcc   = V4L2_PIX_FMT_UYVY,
-               .depth    = 16,
-               .is_yuv   = true,
-       },
-       {
-               .name     = "4:2:2, packed, YVYU",
-               .fourcc   = V4L2_PIX_FMT_YVYU,
-               .depth    = 16,
-               .is_yuv   = true,
-       },
-       {
-               .name     = "4:2:2, packed, VYUY",
-               .fourcc   = V4L2_PIX_FMT_VYUY,
-               .depth    = 16,
-               .is_yuv   = true,
-       },
-       {
-               .name     = "RGB565 (LE)",
-               .fourcc   = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
-               .depth    = 16,
-       },
-       {
-               .name     = "RGB565 (BE)",
-               .fourcc   = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
-               .depth    = 16,
-       },
-       {
-               .name     = "RGB555 (LE)",
-               .fourcc   = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
-               .depth    = 16,
-       },
-       {
-               .name     = "RGB555 (BE)",
-               .fourcc   = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
-               .depth    = 16,
-       },
-       {
-               .name     = "RGB24 (LE)",
-               .fourcc   = V4L2_PIX_FMT_RGB24, /* rgb */
-               .depth    = 24,
-       },
-       {
-               .name     = "RGB24 (BE)",
-               .fourcc   = V4L2_PIX_FMT_BGR24, /* bgr */
-               .depth    = 24,
-       },
-       {
-               .name     = "RGB32 (LE)",
-               .fourcc   = V4L2_PIX_FMT_RGB32, /* argb */
-               .depth    = 32,
-       },
-       {
-               .name     = "RGB32 (BE)",
-               .fourcc   = V4L2_PIX_FMT_BGR32, /* bgra */
-               .depth    = 32,
-       },
-};
-
-static const struct vivi_fmt *__get_format(u32 pixelformat)
-{
-       const struct vivi_fmt *fmt;
-       unsigned int k;
-
-       for (k = 0; k < ARRAY_SIZE(formats); k++) {
-               fmt = &formats[k];
-               if (fmt->fourcc == pixelformat)
-                       break;
-       }
-
-       if (k == ARRAY_SIZE(formats))
-               return NULL;
-
-       return &formats[k];
-}
-
-static const struct vivi_fmt *get_format(struct v4l2_format *f)
-{
-       return __get_format(f->fmt.pix.pixelformat);
-}
-
-/* buffer for one video frame */
-struct vivi_buffer {
-       /* common v4l buffer stuff -- must be first */
-       struct vb2_buffer       vb;
-       struct list_head        list;
-};
-
-struct vivi_dmaqueue {
-       struct list_head       active;
-
-       /* thread for generating video stream*/
-       struct task_struct         *kthread;
-       wait_queue_head_t          wq;
-       /* Counters to control fps rate */
-       int                        frame;
-       int                        ini_jiffies;
-};
-
-static LIST_HEAD(vivi_devlist);
-
-struct vivi_dev {
-       struct list_head           vivi_devlist;
-       struct v4l2_device         v4l2_dev;
-       struct v4l2_ctrl_handler   ctrl_handler;
-       struct video_device        vdev;
-
-       /* controls */
-       struct v4l2_ctrl           *brightness;
-       struct v4l2_ctrl           *contrast;
-       struct v4l2_ctrl           *saturation;
-       struct v4l2_ctrl           *hue;
-       struct {
-               /* autogain/gain cluster */
-               struct v4l2_ctrl           *autogain;
-               struct v4l2_ctrl           *gain;
-       };
-       struct v4l2_ctrl           *volume;
-       struct v4l2_ctrl           *alpha;
-       struct v4l2_ctrl           *button;
-       struct v4l2_ctrl           *boolean;
-       struct v4l2_ctrl           *int32;
-       struct v4l2_ctrl           *int64;
-       struct v4l2_ctrl           *menu;
-       struct v4l2_ctrl           *string;
-       struct v4l2_ctrl           *bitmask;
-       struct v4l2_ctrl           *int_menu;
-
-       spinlock_t                 slock;
-       struct mutex               mutex;
-
-       struct vivi_dmaqueue       vidq;
-
-       /* Several counters */
-       unsigned                   ms;
-       unsigned long              jiffies;
-       unsigned                   button_pressed;
-
-       int                        mv_count;    /* Controls bars movement */
-
-       /* Input Number */
-       int                        input;
-
-       /* video capture */
-       const struct vivi_fmt      *fmt;
-       struct v4l2_fract          timeperframe;
-       unsigned int               width, height;
-       struct vb2_queue           vb_vidq;
-       unsigned int               seq_count;
-
-       u8                         bars[9][3];
-       u8                         line[MAX_WIDTH * 8] __attribute__((__aligned__(4)));
-       unsigned int               pixelsize;
-       u8                         alpha_component;
-       u32                        textfg, textbg;
-};
-
-/* ------------------------------------------------------------------
-       DMA and thread functions
-   ------------------------------------------------------------------*/
-
-/* Bars and Colors should match positions */
-
-enum colors {
-       WHITE,
-       AMBER,
-       CYAN,
-       GREEN,
-       MAGENTA,
-       RED,
-       BLUE,
-       BLACK,
-       TEXT_BLACK,
-};
-
-/* R   G   B */
-#define COLOR_WHITE    {204, 204, 204}
-#define COLOR_AMBER    {208, 208,   0}
-#define COLOR_CYAN     {  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[9][3];
-};
-
-/* Maximum number of bars are 10 - otherwise, the input print code
-   should be modified */
-static const struct bar_std bars[] = {
-       {       /* Standard ITU-R color bar sequence */
-               { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN,
-                 COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK }
-       }, {
-               { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE,
-                 COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK }
-       }, {
-               { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE,
-                 COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK }
-       }, {
-               { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE,
-                 COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK }
-       },
-};
-
-#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 */
-#define TO_V(r, g, b) \
-       (((28784 * r - 24103 * g - 4681 * b  + 32768) >> 16) + 128)
-/* RGB to  U(Cb) Color transform */
-#define TO_U(r, g, b) \
-       (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
-
-/* precalculate color bar values to speed up rendering */
-static void precalculate_bars(struct vivi_dev *dev)
-{
-       u8 r, g, b;
-       int k, is_yuv;
-
-       for (k = 0; k < 9; k++) {
-               r = bars[dev->input].bar[k][0];
-               g = bars[dev->input].bar[k][1];
-               b = bars[dev->input].bar[k][2];
-               is_yuv = dev->fmt->is_yuv;
-
-               switch (dev->fmt->fourcc) {
-               case V4L2_PIX_FMT_RGB565:
-               case V4L2_PIX_FMT_RGB565X:
-                       r >>= 3;
-                       g >>= 2;
-                       b >>= 3;
-                       break;
-               case V4L2_PIX_FMT_RGB555:
-               case V4L2_PIX_FMT_RGB555X:
-                       r >>= 3;
-                       g >>= 3;
-                       b >>= 3;
-                       break;
-               case V4L2_PIX_FMT_YUYV:
-               case V4L2_PIX_FMT_UYVY:
-               case V4L2_PIX_FMT_YVYU:
-               case V4L2_PIX_FMT_VYUY:
-               case V4L2_PIX_FMT_RGB24:
-               case V4L2_PIX_FMT_BGR24:
-               case V4L2_PIX_FMT_RGB32:
-               case V4L2_PIX_FMT_BGR32:
-                       break;
-               }
-
-               if (is_yuv) {
-                       dev->bars[k][0] = TO_Y(r, g, b);        /* Luma */
-                       dev->bars[k][1] = TO_U(r, g, b);        /* Cb */
-                       dev->bars[k][2] = TO_V(r, g, b);        /* Cr */
-               } else {
-                       dev->bars[k][0] = r;
-                       dev->bars[k][1] = g;
-                       dev->bars[k][2] = b;
-               }
-       }
-}
-
-/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
-static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd)
-{
-       u8 r_y, g_u, b_v;
-       u8 alpha = dev->alpha_component;
-       int color;
-       u8 *p;
-
-       r_y = dev->bars[colorpos][0]; /* R or precalculated Y */
-       g_u = dev->bars[colorpos][1]; /* G or precalculated U */
-       b_v = dev->bars[colorpos][2]; /* B or precalculated V */
-
-       for (color = 0; color < dev->pixelsize; color++) {
-               p = buf + color;
-
-               switch (dev->fmt->fourcc) {
-               case V4L2_PIX_FMT_YUYV:
-                       switch (color) {
-                       case 0:
-                               *p = r_y;
-                               break;
-                       case 1:
-                               *p = odd ? b_v : g_u;
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_UYVY:
-                       switch (color) {
-                       case 0:
-                               *p = odd ? b_v : g_u;
-                               break;
-                       case 1:
-                               *p = r_y;
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_YVYU:
-                       switch (color) {
-                       case 0:
-                               *p = r_y;
-                               break;
-                       case 1:
-                               *p = odd ? g_u : b_v;
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_VYUY:
-                       switch (color) {
-                       case 0:
-                               *p = odd ? g_u : b_v;
-                               break;
-                       case 1:
-                               *p = r_y;
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_RGB565:
-                       switch (color) {
-                       case 0:
-                               *p = (g_u << 5) | b_v;
-                               break;
-                       case 1:
-                               *p = (r_y << 3) | (g_u >> 3);
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_RGB565X:
-                       switch (color) {
-                       case 0:
-                               *p = (r_y << 3) | (g_u >> 3);
-                               break;
-                       case 1:
-                               *p = (g_u << 5) | b_v;
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_RGB555:
-                       switch (color) {
-                       case 0:
-                               *p = (g_u << 5) | b_v;
-                               break;
-                       case 1:
-                               *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_RGB555X:
-                       switch (color) {
-                       case 0:
-                               *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
-                               break;
-                       case 1:
-                               *p = (g_u << 5) | b_v;
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_RGB24:
-                       switch (color) {
-                       case 0:
-                               *p = r_y;
-                               break;
-                       case 1:
-                               *p = g_u;
-                               break;
-                       case 2:
-                               *p = b_v;
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_BGR24:
-                       switch (color) {
-                       case 0:
-                               *p = b_v;
-                               break;
-                       case 1:
-                               *p = g_u;
-                               break;
-                       case 2:
-                               *p = r_y;
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_RGB32:
-                       switch (color) {
-                       case 0:
-                               *p = alpha;
-                               break;
-                       case 1:
-                               *p = r_y;
-                               break;
-                       case 2:
-                               *p = g_u;
-                               break;
-                       case 3:
-                               *p = b_v;
-                               break;
-                       }
-                       break;
-               case V4L2_PIX_FMT_BGR32:
-                       switch (color) {
-                       case 0:
-                               *p = b_v;
-                               break;
-                       case 1:
-                               *p = g_u;
-                               break;
-                       case 2:
-                               *p = r_y;
-                               break;
-                       case 3:
-                               *p = alpha;
-                               break;
-                       }
-                       break;
-               }
-       }
-}
-
-static void precalculate_line(struct vivi_dev *dev)
-{
-       unsigned pixsize  = dev->pixelsize;
-       unsigned pixsize2 = 2*pixsize;
-       int colorpos;
-       u8 *pos;
-
-       for (colorpos = 0; colorpos < 16; ++colorpos) {
-               u8 pix[8];
-               int wstart =  colorpos    * dev->width / 8;
-               int wend   = (colorpos+1) * dev->width / 8;
-               int w;
-
-               gen_twopix(dev, &pix[0],        colorpos % 8, 0);
-               gen_twopix(dev, &pix[pixsize],  colorpos % 8, 1);
-
-               for (w = wstart/2*2, pos = dev->line + w*pixsize; w < wend; w += 2, pos += pixsize2)
-                       memcpy(pos, pix, pixsize2);
-       }
-}
-
-/* need this to do rgb24 rendering */
-typedef struct { u16 __; u8 _; } __attribute__((packed)) x24;
-
-static void gen_text(struct vivi_dev *dev, char *basep,
-                                       int y, int x, char *text)
-{
-       int line;
-       unsigned int width = dev->width;
-
-       /* Checks if it is possible to show string */
-       if (y + 16 >= dev->height || x + strlen(text) * 8 >= width)
-               return;
-
-       /* Print stream time */
-#define PRINTSTR(PIXTYPE) do { \
-       PIXTYPE fg;     \
-       PIXTYPE bg;     \
-       memcpy(&fg, &dev->textfg, sizeof(PIXTYPE));     \
-       memcpy(&bg, &dev->textbg, sizeof(PIXTYPE));     \
-       \
-       for (line = 0; line < 16; line++) {     \
-               PIXTYPE *pos = (PIXTYPE *)( basep + ((y + line) * width + x) * sizeof(PIXTYPE) );       \
-               u8 *s;  \
-       \
-               for (s = text; *s; s++) {       \
-                       u8 chr = font8x16[*s * 16 + line];      \
-       \
-                       pos[0] = (chr & (0x01 << 7) ? fg : bg); \
-                       pos[1] = (chr & (0x01 << 6) ? fg : bg); \
-                       pos[2] = (chr & (0x01 << 5) ? fg : bg); \
-                       pos[3] = (chr & (0x01 << 4) ? fg : bg); \
-                       pos[4] = (chr & (0x01 << 3) ? fg : bg); \
-                       pos[5] = (chr & (0x01 << 2) ? fg : bg); \
-                       pos[6] = (chr & (0x01 << 1) ? fg : bg); \
-                       pos[7] = (chr & (0x01 << 0) ? fg : bg); \
-       \
-                       pos += 8;       \
-               }       \
-       }       \
-} while (0)
-
-       switch (dev->pixelsize) {
-       case 2:
-               PRINTSTR(u16); break;
-       case 4:
-               PRINTSTR(u32); break;
-       case 3:
-               PRINTSTR(x24); break;
-       }
-}
-
-static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
-{
-       int stride = dev->width * dev->pixelsize;
-       int hmax = dev->height;
-       void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
-       unsigned ms;
-       char str[100];
-       int h, line = 1;
-       u8 *linestart;
-       s32 gain;
-
-       if (!vbuf)
-               return;
-
-       linestart = dev->line + (dev->mv_count % dev->width) * dev->pixelsize;
-
-       for (h = 0; h < hmax; h++)
-               memcpy(vbuf + h * stride, linestart, stride);
-
-       /* Updates stream time */
-
-       gen_twopix(dev, (u8 *)&dev->textbg, TEXT_BLACK, /*odd=*/ 0);
-       gen_twopix(dev, (u8 *)&dev->textfg, WHITE, /*odd=*/ 0);
-
-       dev->ms += jiffies_to_msecs(jiffies - dev->jiffies);
-       dev->jiffies = jiffies;
-       ms = dev->ms;
-       snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ",
-                       (ms / (60 * 60 * 1000)) % 24,
-                       (ms / (60 * 1000)) % 60,
-                       (ms / 1000) % 60,
-                       ms % 1000);
-       gen_text(dev, vbuf, line++ * 16, 16, str);
-       snprintf(str, sizeof(str), " %dx%d, input %d ",
-                       dev->width, dev->height, dev->input);
-       gen_text(dev, vbuf, line++ * 16, 16, str);
-
-       gain = v4l2_ctrl_g_ctrl(dev->gain);
-       mutex_lock(dev->ctrl_handler.lock);
-       snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
-                       dev->brightness->cur.val,
-                       dev->contrast->cur.val,
-                       dev->saturation->cur.val,
-                       dev->hue->cur.val);
-       gen_text(dev, vbuf, line++ * 16, 16, str);
-       snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d, alpha 0x%02x ",
-                       dev->autogain->cur.val, gain, dev->volume->cur.val,
-                       dev->alpha->cur.val);
-       gen_text(dev, vbuf, line++ * 16, 16, str);
-       snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
-                       dev->int32->cur.val,
-                       *dev->int64->p_cur.p_s64,
-                       dev->bitmask->cur.val);
-       gen_text(dev, vbuf, line++ * 16, 16, str);
-       snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
-                       dev->boolean->cur.val,
-                       dev->menu->qmenu[dev->menu->cur.val],
-                       dev->string->p_cur.p_char);
-       gen_text(dev, vbuf, line++ * 16, 16, str);
-       snprintf(str, sizeof(str), " integer_menu %lld, value %d ",
-                       dev->int_menu->qmenu_int[dev->int_menu->cur.val],
-                       dev->int_menu->cur.val);
-       gen_text(dev, vbuf, line++ * 16, 16, str);
-       mutex_unlock(dev->ctrl_handler.lock);
-       if (dev->button_pressed) {
-               dev->button_pressed--;
-               snprintf(str, sizeof(str), " button pressed!");
-               gen_text(dev, vbuf, line++ * 16, 16, str);
-       }
-
-       dev->mv_count += 2;
-
-       buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
-       buf->vb.v4l2_buf.sequence = dev->seq_count++;
-       v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
-}
-
-static void vivi_thread_tick(struct vivi_dev *dev)
-{
-       struct vivi_dmaqueue *dma_q = &dev->vidq;
-       struct vivi_buffer *buf;
-       unsigned long flags = 0;
-
-       dprintk(dev, 1, "Thread tick\n");
-
-       spin_lock_irqsave(&dev->slock, flags);
-       if (list_empty(&dma_q->active)) {
-               dprintk(dev, 1, "No active queue to serve\n");
-               spin_unlock_irqrestore(&dev->slock, flags);
-               return;
-       }
-
-       buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
-       list_del(&buf->list);
-       spin_unlock_irqrestore(&dev->slock, flags);
-
-       v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
-
-       /* Fill buffer */
-       vivi_fillbuff(dev, buf);
-       dprintk(dev, 1, "filled buffer %p\n", buf);
-
-       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
-       dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
-}
-
-#define frames_to_ms(dev, frames)                              \
-       ((frames * dev->timeperframe.numerator * 1000) / dev->timeperframe.denominator)
-
-static void vivi_sleep(struct vivi_dev *dev)
-{
-       struct vivi_dmaqueue *dma_q = &dev->vidq;
-       int timeout;
-       DECLARE_WAITQUEUE(wait, current);
-
-       dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__,
-               (unsigned long)dma_q);
-
-       add_wait_queue(&dma_q->wq, &wait);
-       if (kthread_should_stop())
-               goto stop_task;
-
-       /* Calculate time to wake up */
-       timeout = msecs_to_jiffies(frames_to_ms(dev, 1));
-
-       vivi_thread_tick(dev);
-
-       schedule_timeout_interruptible(timeout);
-
-stop_task:
-       remove_wait_queue(&dma_q->wq, &wait);
-       try_to_freeze();
-}
-
-static int vivi_thread(void *data)
-{
-       struct vivi_dev *dev = data;
-
-       dprintk(dev, 1, "thread started\n");
-
-       set_freezable();
-
-       for (;;) {
-               vivi_sleep(dev);
-
-               if (kthread_should_stop())
-                       break;
-       }
-       dprintk(dev, 1, "thread: exit\n");
-       return 0;
-}
-
-static int vivi_start_generating(struct vivi_dev *dev)
-{
-       struct vivi_dmaqueue *dma_q = &dev->vidq;
-
-       dprintk(dev, 1, "%s\n", __func__);
-
-       /* Resets frame counters */
-       dev->ms = 0;
-       dev->mv_count = 0;
-       dev->jiffies = jiffies;
-
-       dma_q->frame = 0;
-       dma_q->ini_jiffies = jiffies;
-       dma_q->kthread = kthread_run(vivi_thread, dev, "%s",
-                                    dev->v4l2_dev.name);
-
-       if (IS_ERR(dma_q->kthread)) {
-               v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
-               return PTR_ERR(dma_q->kthread);
-       }
-       /* Wakes thread */
-       wake_up_interruptible(&dma_q->wq);
-
-       dprintk(dev, 1, "returning from %s\n", __func__);
-       return 0;
-}
-
-static void vivi_stop_generating(struct vivi_dev *dev)
-{
-       struct vivi_dmaqueue *dma_q = &dev->vidq;
-
-       dprintk(dev, 1, "%s\n", __func__);
-
-       /* shutdown control thread */
-       if (dma_q->kthread) {
-               kthread_stop(dma_q->kthread);
-               dma_q->kthread = NULL;
-       }
-
-       /*
-        * Typical driver might need to wait here until dma engine stops.
-        * In this case we can abort imiedetly, so it's just a noop.
-        */
-
-       /* Release all active buffers */
-       while (!list_empty(&dma_q->active)) {
-               struct vivi_buffer *buf;
-               buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
-               list_del(&buf->list);
-               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
-               dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
-       }
-}
-/* ------------------------------------------------------------------
-       Videobuf operations
-   ------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
-                               unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vq);
-       unsigned long size;
-
-       size = dev->width * dev->height * dev->pixelsize;
-       if (fmt) {
-               if (fmt->fmt.pix.sizeimage < size)
-                       return -EINVAL;
-               size = fmt->fmt.pix.sizeimage;
-               /* check against insane over 8K resolution buffers */
-               if (size > 7680 * 4320 * dev->pixelsize)
-                       return -EINVAL;
-       }
-
-       *nplanes = 1;
-
-       sizes[0] = size;
-
-       /*
-        * videobuf2-vmalloc allocator is context-less so no need to set
-        * alloc_ctxs array.
-        */
-
-       dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
-               *nbuffers, size);
-
-       return 0;
-}
-
-static int buffer_prepare(struct vb2_buffer *vb)
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-       struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
-       unsigned long size;
-
-       dprintk(dev, 1, "%s, field=%d\n", __func__, vb->v4l2_buf.field);
-
-       BUG_ON(NULL == dev->fmt);
-
-       /*
-        * Theses properties only change when queue is idle, see s_fmt.
-        * The below checks should not be performed here, on each
-        * buffer_prepare (i.e. on each qbuf). Most of the code in this function
-        * should thus be moved to buffer_init and s_fmt.
-        */
-       if (dev->width  < 48 || dev->width  > MAX_WIDTH ||
-           dev->height < 32 || dev->height > MAX_HEIGHT)
-               return -EINVAL;
-
-       size = dev->width * dev->height * dev->pixelsize;
-       if (vb2_plane_size(vb, 0) < size) {
-               dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
-                               __func__, vb2_plane_size(vb, 0), size);
-               return -EINVAL;
-       }
-
-       vb2_set_plane_payload(&buf->vb, 0, size);
-
-       precalculate_bars(dev);
-       precalculate_line(dev);
-
-       return 0;
-}
-
-static void buffer_queue(struct vb2_buffer *vb)
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-       struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
-       struct vivi_dmaqueue *vidq = &dev->vidq;
-       unsigned long flags = 0;
-
-       dprintk(dev, 1, "%s\n", __func__);
-
-       spin_lock_irqsave(&dev->slock, flags);
-       list_add_tail(&buf->list, &vidq->active);
-       spin_unlock_irqrestore(&dev->slock, flags);
-}
-
-static int start_streaming(struct vb2_queue *vq, unsigned int count)
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vq);
-       int err;
-
-       dprintk(dev, 1, "%s\n", __func__);
-       dev->seq_count = 0;
-       err = vivi_start_generating(dev);
-       if (err) {
-               struct vivi_buffer *buf, *tmp;
-
-               list_for_each_entry_safe(buf, tmp, &dev->vidq.active, list) {
-                       list_del(&buf->list);
-                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
-               }
-       }
-       return err;
-}
-
-/* abort streaming and wait for last buffer */
-static void stop_streaming(struct vb2_queue *vq)
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vq);
-       dprintk(dev, 1, "%s\n", __func__);
-       vivi_stop_generating(dev);
-}
-
-static void vivi_lock(struct vb2_queue *vq)
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vq);
-       mutex_lock(&dev->mutex);
-}
-
-static void vivi_unlock(struct vb2_queue *vq)
-{
-       struct vivi_dev *dev = vb2_get_drv_priv(vq);
-       mutex_unlock(&dev->mutex);
-}
-
-
-static const struct vb2_ops vivi_video_qops = {
-       .queue_setup            = queue_setup,
-       .buf_prepare            = buffer_prepare,
-       .buf_queue              = buffer_queue,
-       .start_streaming        = start_streaming,
-       .stop_streaming         = stop_streaming,
-       .wait_prepare           = vivi_unlock,
-       .wait_finish            = vivi_lock,
-};
-
-/* ------------------------------------------------------------------
-       IOCTL vidioc handling
-   ------------------------------------------------------------------*/
-static int vidioc_querycap(struct file *file, void  *priv,
-                                       struct v4l2_capability *cap)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-
-       strcpy(cap->driver, "vivi");
-       strcpy(cap->card, "vivi");
-       snprintf(cap->bus_info, sizeof(cap->bus_info),
-                       "platform:%s", dev->v4l2_dev.name);
-       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
-                           V4L2_CAP_READWRITE;
-       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-       return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
-                                       struct v4l2_fmtdesc *f)
-{
-       const struct vivi_fmt *fmt;
-
-       if (f->index >= ARRAY_SIZE(formats))
-               return -EINVAL;
-
-       fmt = &formats[f->index];
-
-       strlcpy(f->description, fmt->name, sizeof(f->description));
-       f->pixelformat = fmt->fourcc;
-       return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
-                                       struct v4l2_format *f)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-
-       f->fmt.pix.width        = dev->width;
-       f->fmt.pix.height       = dev->height;
-       f->fmt.pix.field        = V4L2_FIELD_INTERLACED;
-       f->fmt.pix.pixelformat  = dev->fmt->fourcc;
-       f->fmt.pix.bytesperline =
-               (f->fmt.pix.width * dev->fmt->depth) >> 3;
-       f->fmt.pix.sizeimage =
-               f->fmt.pix.height * f->fmt.pix.bytesperline;
-       if (dev->fmt->is_yuv)
-               f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-       else
-               f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
-       return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
-                       struct v4l2_format *f)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       const struct vivi_fmt *fmt;
-
-       fmt = get_format(f);
-       if (!fmt) {
-               dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
-                       f->fmt.pix.pixelformat);
-               f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
-               fmt = get_format(f);
-       }
-
-       f->fmt.pix.field = V4L2_FIELD_INTERLACED;
-       v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
-                             &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
-       f->fmt.pix.bytesperline =
-               (f->fmt.pix.width * fmt->depth) >> 3;
-       f->fmt.pix.sizeimage =
-               f->fmt.pix.height * f->fmt.pix.bytesperline;
-       if (fmt->is_yuv)
-               f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-       else
-               f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
-       return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
-                                       struct v4l2_format *f)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       struct vb2_queue *q = &dev->vb_vidq;
-
-       int ret = vidioc_try_fmt_vid_cap(file, priv, f);
-       if (ret < 0)
-               return ret;
-
-       if (vb2_is_busy(q)) {
-               dprintk(dev, 1, "%s device busy\n", __func__);
-               return -EBUSY;
-       }
-
-       dev->fmt = get_format(f);
-       dev->pixelsize = dev->fmt->depth / 8;
-       dev->width = f->fmt.pix.width;
-       dev->height = f->fmt.pix.height;
-
-       return 0;
-}
-
-static int vidioc_enum_framesizes(struct file *file, void *fh,
-                                        struct v4l2_frmsizeenum *fsize)
-{
-       static const struct v4l2_frmsize_stepwise sizes = {
-               48, MAX_WIDTH, 4,
-               32, MAX_HEIGHT, 1
-       };
-       int i;
-
-       if (fsize->index)
-               return -EINVAL;
-       for (i = 0; i < ARRAY_SIZE(formats); i++)
-               if (formats[i].fourcc == fsize->pixel_format)
-                       break;
-       if (i == ARRAY_SIZE(formats))
-               return -EINVAL;
-       fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
-       fsize->stepwise = sizes;
-       return 0;
-}
-
-/* only one input in this sample driver */
-static int vidioc_enum_input(struct file *file, void *priv,
-                               struct v4l2_input *inp)
-{
-       if (inp->index >= NUM_INPUTS)
-               return -EINVAL;
-
-       inp->type = V4L2_INPUT_TYPE_CAMERA;
-       sprintf(inp->name, "Camera %u", inp->index);
-       return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-
-       *i = dev->input;
-       return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-
-       if (i >= NUM_INPUTS)
-               return -EINVAL;
-
-       if (i == dev->input)
-               return 0;
-
-       dev->input = i;
-       /*
-        * Modify the brightness range depending on the input.
-        * This makes it easy to use vivi to test if applications can
-        * handle control range modifications and is also how this is
-        * typically used in practice as different inputs may be hooked
-        * up to different receivers with different control ranges.
-        */
-       v4l2_ctrl_modify_range(dev->brightness,
-                       128 * i, 255 + 128 * i, 1, 127 + 128 * i);
-       precalculate_bars(dev);
-       precalculate_line(dev);
-       return 0;
-}
-
-/* timeperframe is arbitrary and continuous */
-static int vidioc_enum_frameintervals(struct file *file, void *priv,
-                                            struct v4l2_frmivalenum *fival)
-{
-       const struct vivi_fmt *fmt;
-
-       if (fival->index)
-               return -EINVAL;
-
-       fmt = __get_format(fival->pixel_format);
-       if (!fmt)
-               return -EINVAL;
-
-       /* check for valid width/height */
-       if (fival->width < 48 || fival->width > MAX_WIDTH || (fival->width & 3))
-               return -EINVAL;
-       if (fival->height < 32 || fival->height > MAX_HEIGHT)
-               return -EINVAL;
-
-       fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
-
-       /* fill in stepwise (step=1.0 is required by V4L2 spec) */
-       fival->stepwise.min  = tpf_min;
-       fival->stepwise.max  = tpf_max;
-       fival->stepwise.step = (struct v4l2_fract) {1, 1};
-
-       return 0;
-}
-
-static int vidioc_g_parm(struct file *file, void *priv,
-                         struct v4l2_streamparm *parm)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-
-       if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-
-       parm->parm.capture.capability   = V4L2_CAP_TIMEPERFRAME;
-       parm->parm.capture.timeperframe = dev->timeperframe;
-       parm->parm.capture.readbuffers  = 1;
-       return 0;
-}
-
-#define FRACT_CMP(a, OP, b)    \
-       ((u64)(a).numerator * (b).denominator  OP  (u64)(b).numerator * (a).denominator)
-
-static int vidioc_s_parm(struct file *file, void *priv,
-                         struct v4l2_streamparm *parm)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       struct v4l2_fract tpf;
-
-       if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-
-       tpf = parm->parm.capture.timeperframe;
-
-       /* tpf: {*, 0} resets timing; clip to [min, max]*/
-       tpf = tpf.denominator ? tpf : tpf_default;
-       tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
-       tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
-
-       dev->timeperframe = tpf;
-       parm->parm.capture.timeperframe = tpf;
-       parm->parm.capture.readbuffers  = 1;
-       return 0;
-}
-
-/* --- controls ---------------------------------------------- */
-
-static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
-       struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
-
-       if (ctrl == dev->autogain)
-               dev->gain->val = jiffies & 0xff;
-       return 0;
-}
-
-static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
-{
-       struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
-
-       switch (ctrl->id) {
-       case V4L2_CID_ALPHA_COMPONENT:
-               dev->alpha_component = ctrl->val;
-               break;
-       default:
-               if (ctrl == dev->button)
-                       dev->button_pressed = 30;
-               break;
-       }
-       return 0;
-}
-
-/* ------------------------------------------------------------------
-       File operations for the device
-   ------------------------------------------------------------------*/
-
-static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
-       .g_volatile_ctrl = vivi_g_volatile_ctrl,
-       .s_ctrl = vivi_s_ctrl,
-};
-
-#define VIVI_CID_CUSTOM_BASE   (V4L2_CID_USER_BASE | 0xf000)
-
-static const struct v4l2_ctrl_config vivi_ctrl_button = {
-       .ops = &vivi_ctrl_ops,
-       .id = VIVI_CID_CUSTOM_BASE + 0,
-       .name = "Button",
-       .type = V4L2_CTRL_TYPE_BUTTON,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_boolean = {
-       .ops = &vivi_ctrl_ops,
-       .id = VIVI_CID_CUSTOM_BASE + 1,
-       .name = "Boolean",
-       .type = V4L2_CTRL_TYPE_BOOLEAN,
-       .min = 0,
-       .max = 1,
-       .step = 1,
-       .def = 1,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_int32 = {
-       .ops = &vivi_ctrl_ops,
-       .id = VIVI_CID_CUSTOM_BASE + 2,
-       .name = "Integer 32 Bits",
-       .type = V4L2_CTRL_TYPE_INTEGER,
-       .min = -0x80000000LL,
-       .max = 0x7fffffff,
-       .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_int64 = {
-       .ops = &vivi_ctrl_ops,
-       .id = VIVI_CID_CUSTOM_BASE + 3,
-       .name = "Integer 64 Bits",
-       .type = V4L2_CTRL_TYPE_INTEGER64,
-       .min = LLONG_MIN,
-       .max = LLONG_MAX,
-       .step = 1,
-};
-
-static const char * const vivi_ctrl_menu_strings[] = {
-       "Menu Item 0 (Skipped)",
-       "Menu Item 1",
-       "Menu Item 2 (Skipped)",
-       "Menu Item 3",
-       "Menu Item 4",
-       "Menu Item 5 (Skipped)",
-       NULL,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_menu = {
-       .ops = &vivi_ctrl_ops,
-       .id = VIVI_CID_CUSTOM_BASE + 4,
-       .name = "Menu",
-       .type = V4L2_CTRL_TYPE_MENU,
-       .min = 1,
-       .max = 4,
-       .def = 3,
-       .menu_skip_mask = 0x04,
-       .qmenu = vivi_ctrl_menu_strings,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_string = {
-       .ops = &vivi_ctrl_ops,
-       .id = VIVI_CID_CUSTOM_BASE + 5,
-       .name = "String",
-       .type = V4L2_CTRL_TYPE_STRING,
-       .min = 2,
-       .max = 4,
-       .step = 1,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_bitmask = {
-       .ops = &vivi_ctrl_ops,
-       .id = VIVI_CID_CUSTOM_BASE + 6,
-       .name = "Bitmask",
-       .type = V4L2_CTRL_TYPE_BITMASK,
-       .def = 0x80002000,
-       .min = 0,
-       .max = 0x80402010,
-       .step = 0,
-};
-
-static const s64 vivi_ctrl_int_menu_values[] = {
-       1, 1, 2, 3, 5, 8, 13, 21, 42,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_int_menu = {
-       .ops = &vivi_ctrl_ops,
-       .id = VIVI_CID_CUSTOM_BASE + 7,
-       .name = "Integer menu",
-       .type = V4L2_CTRL_TYPE_INTEGER_MENU,
-       .min = 1,
-       .max = 8,
-       .def = 4,
-       .menu_skip_mask = 0x02,
-       .qmenu_int = vivi_ctrl_int_menu_values,
-};
-
-static const struct v4l2_file_operations vivi_fops = {
-       .owner          = THIS_MODULE,
-       .open           = v4l2_fh_open,
-       .release        = vb2_fop_release,
-       .read           = vb2_fop_read,
-       .poll           = vb2_fop_poll,
-       .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
-       .mmap           = vb2_fop_mmap,
-};
-
-static const struct v4l2_ioctl_ops vivi_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_enum_framesizes   = vidioc_enum_framesizes,
-       .vidioc_reqbufs       = vb2_ioctl_reqbufs,
-       .vidioc_create_bufs   = vb2_ioctl_create_bufs,
-       .vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
-       .vidioc_querybuf      = vb2_ioctl_querybuf,
-       .vidioc_qbuf          = vb2_ioctl_qbuf,
-       .vidioc_dqbuf         = vb2_ioctl_dqbuf,
-       .vidioc_enum_input    = vidioc_enum_input,
-       .vidioc_g_input       = vidioc_g_input,
-       .vidioc_s_input       = vidioc_s_input,
-       .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
-       .vidioc_g_parm        = vidioc_g_parm,
-       .vidioc_s_parm        = vidioc_s_parm,
-       .vidioc_streamon      = vb2_ioctl_streamon,
-       .vidioc_streamoff     = vb2_ioctl_streamoff,
-       .vidioc_log_status    = v4l2_ctrl_log_status,
-       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
-       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static const struct video_device vivi_template = {
-       .name           = "vivi",
-       .fops           = &vivi_fops,
-       .ioctl_ops      = &vivi_ioctl_ops,
-       .release        = video_device_release_empty,
-};
-
-/* -----------------------------------------------------------------
-       Initialization and module stuff
-   ------------------------------------------------------------------*/
-
-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);
-
-               v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
-                       video_device_node_name(&dev->vdev));
-               video_unregister_device(&dev->vdev);
-               v4l2_device_unregister(&dev->v4l2_dev);
-               v4l2_ctrl_handler_free(&dev->ctrl_handler);
-               kfree(dev);
-       }
-
-       return 0;
-}
-
-static int __init vivi_create_instance(int inst)
-{
-       struct vivi_dev *dev;
-       struct video_device *vfd;
-       struct v4l2_ctrl_handler *hdl;
-       struct vb2_queue *q;
-       int ret;
-
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (!dev)
-               return -ENOMEM;
-
-       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;
-
-       dev->fmt = &formats[0];
-       dev->timeperframe = tpf_default;
-       dev->width = 640;
-       dev->height = 480;
-       dev->pixelsize = dev->fmt->depth / 8;
-       hdl = &dev->ctrl_handler;
-       v4l2_ctrl_handler_init(hdl, 11);
-       dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
-                       V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
-       dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
-                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
-       dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
-                       V4L2_CID_CONTRAST, 0, 255, 1, 16);
-       dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
-                       V4L2_CID_SATURATION, 0, 255, 1, 127);
-       dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
-                       V4L2_CID_HUE, -128, 127, 1, 0);
-       dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
-                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
-       dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
-                       V4L2_CID_GAIN, 0, 255, 1, 100);
-       dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
-                       V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
-       dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
-       dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
-       dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
-       dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
-       dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
-       dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
-       dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
-       dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL);
-       if (hdl->error) {
-               ret = hdl->error;
-               goto unreg_dev;
-       }
-       v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
-       dev->v4l2_dev.ctrl_handler = hdl;
-
-       /* initialize locks */
-       spin_lock_init(&dev->slock);
-
-       /* initialize queue */
-       q = &dev->vb_vidq;
-       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
-       q->drv_priv = dev;
-       q->buf_struct_size = sizeof(struct vivi_buffer);
-       q->ops = &vivi_video_qops;
-       q->mem_ops = &vb2_vmalloc_memops;
-       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-
-       ret = vb2_queue_init(q);
-       if (ret)
-               goto unreg_dev;
-
-       mutex_init(&dev->mutex);
-
-       /* init video dma queues */
-       INIT_LIST_HEAD(&dev->vidq.active);
-       init_waitqueue_head(&dev->vidq.wq);
-
-       vfd = &dev->vdev;
-       *vfd = vivi_template;
-       vfd->debug = debug;
-       vfd->v4l2_dev = &dev->v4l2_dev;
-       vfd->queue = q;
-
-       /*
-        * Provide a mutex to v4l2 core. It will be used to protect
-        * all fops and v4l2 ioctls.
-        */
-       vfd->lock = &dev->mutex;
-       video_set_drvdata(vfd, dev);
-
-       ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
-       if (ret < 0)
-               goto unreg_dev;
-
-       /* Now that everything is fine, let's add it to device list */
-       list_add_tail(&dev->vivi_devlist, &vivi_devlist);
-
-       v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
-                 video_device_node_name(vfd));
-       return 0;
-
-unreg_dev:
-       v4l2_ctrl_handler_free(hdl);
-       v4l2_device_unregister(&dev->v4l2_dev);
-free_dev:
-       kfree(dev);
-       return ret;
-}
-
-/* This routine allocates from 1 to n_devs virtual drivers.
-
-   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)
-{
-       const struct font_desc *font = find_font("VGA8x16");
-       int ret = 0, i;
-
-       if (font == NULL) {
-               printk(KERN_ERR "vivi: could not find font\n");
-               return -ENODEV;
-       }
-       font8x16 = font->data;
-
-       if (n_devs <= 0)
-               n_devs = 1;
-
-       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) {
-               printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
-               return ret;
-       }
-
-       printk(KERN_INFO "Video Technology Magazine Virtual Video "
-                       "Capture Board ver %s successfully loaded.\n",
-                       VIVI_VERSION);
-
-       /* n_devs will reflect the actual number of allocated devices */
-       n_devs = i;
-
-       return ret;
-}
-
-static void __exit vivi_exit(void)
-{
-       vivi_release();
-}
-
-module_init(vivi_init);
-module_exit(vivi_exit);
diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig
new file mode 100644 (file)
index 0000000..d71139a
--- /dev/null
@@ -0,0 +1,19 @@
+config VIDEO_VIVID
+       tristate "Virtual Video Test Driver"
+       depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
+       select FONT_SUPPORT
+       select FONT_8x16
+       select VIDEOBUF2_VMALLOC
+       default n
+       ---help---
+         Enables a virtual video driver. This driver emulates a webcam,
+         TV, S-Video and HDMI capture hardware, including VBI support for
+         the SDTV inputs. Also video output, VBI output, radio receivers,
+         transmitters and software defined radio capture is emulated.
+
+         It is highly configurable and is ideal for testing applications.
+         Error injection is supported to test rare errors that are hard
+         to reproduce in real hardware.
+
+         Say Y here if you want to test video apps or debug V4L devices.
+         When in doubt, say N.
diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile
new file mode 100644 (file)
index 0000000..756fc12
--- /dev/null
@@ -0,0 +1,6 @@
+vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \
+               vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \
+               vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \
+               vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \
+               vivid-osd.o vivid-tpg.o vivid-tpg-colors.o
+obj-$(CONFIG_VIDEO_VIVID) += vivid.o
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
new file mode 100644 (file)
index 0000000..2c61a62
--- /dev/null
@@ -0,0 +1,1390 @@
+/*
+ * vivid-core.c - A Virtual Video Test Driver, core initialization
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-vid-cap.h"
+#include "vivid-vid-out.h"
+#include "vivid-radio-common.h"
+#include "vivid-radio-rx.h"
+#include "vivid-radio-tx.h"
+#include "vivid-sdr-cap.h"
+#include "vivid-vbi-cap.h"
+#include "vivid-vbi-out.h"
+#include "vivid-osd.h"
+#include "vivid-ctrls.h"
+
+#define VIVID_MODULE_NAME "vivid"
+
+/* The maximum number of vivid devices */
+#define VIVID_MAX_DEVS 64
+
+MODULE_DESCRIPTION("Virtual Video Test Driver");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static unsigned n_devs = 1;
+module_param(n_devs, uint, 0444);
+MODULE_PARM_DESC(n_devs, " number of driver instances to create");
+
+static int vid_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(vid_cap_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vid_cap_nr, " videoX start number, -1 is autodetect");
+
+static int vid_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(vid_out_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vid_out_nr, " videoX start number, -1 is autodetect");
+
+static int vbi_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(vbi_cap_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vbi_cap_nr, " vbiX start number, -1 is autodetect");
+
+static int vbi_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(vbi_out_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vbi_out_nr, " vbiX start number, -1 is autodetect");
+
+static int sdr_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(sdr_cap_nr, int, NULL, 0444);
+MODULE_PARM_DESC(sdr_cap_nr, " swradioX start number, -1 is autodetect");
+
+static int radio_rx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(radio_rx_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_rx_nr, " radioX start number, -1 is autodetect");
+
+static int radio_tx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(radio_tx_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_tx_nr, " radioX start number, -1 is autodetect");
+
+static int ccs_cap_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(ccs_cap_mode, int, NULL, 0444);
+MODULE_PARM_DESC(ccs_cap_mode, " capture crop/compose/scale mode:\n"
+                          "\t\t    bit 0=crop, 1=compose, 2=scale,\n"
+                          "\t\t    -1=user-controlled (default)");
+
+static int ccs_out_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 };
+module_param_array(ccs_out_mode, int, NULL, 0444);
+MODULE_PARM_DESC(ccs_out_mode, " output crop/compose/scale mode:\n"
+                          "\t\t    bit 0=crop, 1=compose, 2=scale,\n"
+                          "\t\t    -1=user-controlled (default)");
+
+static unsigned multiplanar[VIVID_MAX_DEVS];
+module_param_array(multiplanar, uint, NULL, 0444);
+MODULE_PARM_DESC(multiplanar, " 0 (default) is alternating single and multiplanar devices,\n"
+                             "\t\t    1 is single planar devices,\n"
+                             "\t\t    2 is multiplanar devices");
+
+/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */
+static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d };
+module_param_array(node_types, uint, NULL, 0444);
+MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the following meaning:\n"
+                            "\t\t    bit 0: Video Capture node\n"
+                            "\t\t    bit 2-3: VBI Capture node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n"
+                            "\t\t    bit 4: Radio Receiver node\n"
+                            "\t\t    bit 5: Software Defined Radio Receiver node\n"
+                            "\t\t    bit 8: Video Output node\n"
+                            "\t\t    bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n"
+                            "\t\t    bit 12: Radio Transmitter node\n"
+                            "\t\t    bit 16: Framebuffer for testing overlays");
+
+/* Default: 4 inputs */
+static unsigned num_inputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 4 };
+module_param_array(num_inputs, uint, NULL, 0444);
+MODULE_PARM_DESC(num_inputs, " number of inputs, default is 4");
+
+/* Default: input 0 = WEBCAM, 1 = TV, 2 = SVID, 3 = HDMI */
+static unsigned input_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0xe4 };
+module_param_array(input_types, uint, NULL, 0444);
+MODULE_PARM_DESC(input_types, " input types, default is 0xe4. Two bits per input,\n"
+                             "\t\t    bits 0-1 == input 0, bits 31-30 == input 15.\n"
+                             "\t\t    Type 0 == webcam, 1 == TV, 2 == S-Video, 3 == HDMI");
+
+/* Default: 2 outputs */
+static unsigned num_outputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 };
+module_param_array(num_outputs, uint, NULL, 0444);
+MODULE_PARM_DESC(num_outputs, " number of outputs, default is 2");
+
+/* Default: output 0 = SVID, 1 = HDMI */
+static unsigned output_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 2 };
+module_param_array(output_types, uint, NULL, 0444);
+MODULE_PARM_DESC(output_types, " output types, default is 0x02. One bit per output,\n"
+                             "\t\t    bit 0 == output 0, bit 15 == output 15.\n"
+                             "\t\t    Type 0 == S-Video, 1 == HDMI");
+
+unsigned vivid_debug;
+module_param(vivid_debug, uint, 0644);
+MODULE_PARM_DESC(vivid_debug, " activates debug info");
+
+static bool no_error_inj;
+module_param(no_error_inj, bool, 0444);
+MODULE_PARM_DESC(no_error_inj, " if set disable the error injecting controls");
+
+static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS];
+
+const struct v4l2_rect vivid_min_rect = {
+       0, 0, MIN_WIDTH, MIN_HEIGHT
+};
+
+const struct v4l2_rect vivid_max_rect = {
+       0, 0, MAX_WIDTH * MAX_ZOOM, MAX_HEIGHT * MAX_ZOOM
+};
+
+static const u8 vivid_hdmi_edid[256] = {
+       0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+       0x63, 0x3a, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00,
+       0x0a, 0x18, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
+       0x0e, 0x00, 0xb2, 0xa0, 0x57, 0x49, 0x9b, 0x26,
+       0x10, 0x48, 0x4f, 0x2f, 0xcf, 0x00, 0x31, 0x59,
+       0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40,
+       0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a,
+       0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+       0x46, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1e,
+       0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,
+       0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,
+       0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00,  'v',
+       '4',   'l',  '2',  '-',  'h',  'd',  'm',  'i',
+       0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x10,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0,
+
+       0x02, 0x03, 0x1a, 0xc0, 0x48, 0xa2, 0x10, 0x04,
+       0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07,
+       0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2,
+       0x00, 0x2a, 0x01, 0x1d, 0x00, 0x80, 0x51, 0xd0,
+       0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a,
+       0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 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, 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, 0x00, 0x00, 0x00, 0xd7
+};
+
+void vivid_lock(struct vb2_queue *vq)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+
+       mutex_lock(&dev->mutex);
+}
+
+void vivid_unlock(struct vb2_queue *vq)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+
+       mutex_unlock(&dev->mutex);
+}
+
+static int vidioc_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *cap)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+
+       strcpy(cap->driver, "vivid");
+       strcpy(cap->card, "vivid");
+       snprintf(cap->bus_info, sizeof(cap->bus_info),
+                       "platform:%s", dev->v4l2_dev.name);
+
+       if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_RX)
+               cap->device_caps = dev->vid_cap_caps;
+       if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_TX)
+               cap->device_caps = dev->vid_out_caps;
+       else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_RX)
+               cap->device_caps = dev->vbi_cap_caps;
+       else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_TX)
+               cap->device_caps = dev->vbi_out_caps;
+       else if (vdev->vfl_type == VFL_TYPE_SDR)
+               cap->device_caps = dev->sdr_cap_caps;
+       else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_RX)
+               cap->device_caps = dev->radio_rx_caps;
+       else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_TX)
+               cap->device_caps = dev->radio_tx_caps;
+       cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps |
+               dev->vbi_cap_caps | dev->vbi_out_caps |
+               dev->radio_rx_caps | dev->radio_tx_caps |
+               dev->sdr_cap_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int vidioc_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_type == VFL_TYPE_RADIO)
+               return vivid_radio_rx_s_hw_freq_seek(file, fh, a);
+       return -ENOTTY;
+}
+
+static int vidioc_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_type == VFL_TYPE_RADIO)
+               return vivid_radio_rx_enum_freq_bands(file, fh, band);
+       if (vdev->vfl_type == VFL_TYPE_SDR)
+               return vivid_sdr_enum_freq_bands(file, fh, band);
+       return -ENOTTY;
+}
+
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_type == VFL_TYPE_RADIO)
+               return vivid_radio_rx_g_tuner(file, fh, vt);
+       if (vdev->vfl_type == VFL_TYPE_SDR)
+               return vivid_sdr_g_tuner(file, fh, vt);
+       return vivid_video_g_tuner(file, fh, vt);
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_type == VFL_TYPE_RADIO)
+               return vivid_radio_rx_s_tuner(file, fh, vt);
+       if (vdev->vfl_type == VFL_TYPE_SDR)
+               return vivid_sdr_s_tuner(file, fh, vt);
+       return vivid_video_s_tuner(file, fh, vt);
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_type == VFL_TYPE_RADIO)
+               return vivid_radio_g_frequency(file,
+                       vdev->vfl_dir == VFL_DIR_RX ?
+                       &dev->radio_rx_freq : &dev->radio_tx_freq, vf);
+       if (vdev->vfl_type == VFL_TYPE_SDR)
+               return vivid_sdr_g_frequency(file, fh, vf);
+       return vivid_video_g_frequency(file, fh, vf);
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_type == VFL_TYPE_RADIO)
+               return vivid_radio_s_frequency(file,
+                       vdev->vfl_dir == VFL_DIR_RX ?
+                       &dev->radio_rx_freq : &dev->radio_tx_freq, vf);
+       if (vdev->vfl_type == VFL_TYPE_SDR)
+               return vivid_sdr_s_frequency(file, fh, vf);
+       return vivid_video_s_frequency(file, fh, vf);
+}
+
+static int vidioc_overlay(struct file *file, void *fh, unsigned i)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_overlay(file, fh, i);
+       return vivid_vid_out_overlay(file, fh, i);
+}
+
+static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_g_fbuf(file, fh, a);
+       return vivid_vid_out_g_fbuf(file, fh, a);
+}
+
+static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_s_fbuf(file, fh, a);
+       return vivid_vid_out_s_fbuf(file, fh, a);
+}
+
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_s_std(file, fh, id);
+       return vivid_vid_out_s_std(file, fh, id);
+}
+
+static int vidioc_s_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_s_dv_timings(file, fh, timings);
+       return vivid_vid_out_s_dv_timings(file, fh, timings);
+}
+
+static int vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cc)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_cropcap(file, fh, cc);
+       return vivid_vid_out_cropcap(file, fh, cc);
+}
+
+static int vidioc_g_selection(struct file *file, void *fh,
+                             struct v4l2_selection *sel)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_g_selection(file, fh, sel);
+       return vivid_vid_out_g_selection(file, fh, sel);
+}
+
+static int vidioc_s_selection(struct file *file, void *fh,
+                             struct v4l2_selection *sel)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_s_selection(file, fh, sel);
+       return vivid_vid_out_s_selection(file, fh, sel);
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+                         struct v4l2_streamparm *parm)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_g_parm(file, fh, parm);
+       return vivid_vid_out_g_parm(file, fh, parm);
+}
+
+static int vidioc_s_parm(struct file *file, void *fh,
+                         struct v4l2_streamparm *parm)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_vid_cap_s_parm(file, fh, parm);
+       return vivid_vid_out_g_parm(file, fh, parm);
+}
+
+static ssize_t vivid_radio_read(struct file *file, char __user *buf,
+                        size_t size, loff_t *offset)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_TX)
+               return -EINVAL;
+       return vivid_radio_rx_read(file, buf, size, offset);
+}
+
+static ssize_t vivid_radio_write(struct file *file, const char __user *buf,
+                         size_t size, loff_t *offset)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return -EINVAL;
+       return vivid_radio_tx_write(file, buf, size, offset);
+}
+
+static unsigned int vivid_radio_poll(struct file *file, struct poll_table_struct *wait)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX)
+               return vivid_radio_rx_poll(file, wait);
+       return vivid_radio_tx_poll(file, wait);
+}
+
+static bool vivid_is_in_use(struct video_device *vdev)
+{
+       unsigned long flags;
+       bool res;
+
+       spin_lock_irqsave(&vdev->fh_lock, flags);
+       res = !list_empty(&vdev->fh_list);
+       spin_unlock_irqrestore(&vdev->fh_lock, flags);
+       return res;
+}
+
+static bool vivid_is_last_user(struct vivid_dev *dev)
+{
+       unsigned uses = vivid_is_in_use(&dev->vid_cap_dev) +
+                       vivid_is_in_use(&dev->vid_out_dev) +
+                       vivid_is_in_use(&dev->vbi_cap_dev) +
+                       vivid_is_in_use(&dev->vbi_out_dev) +
+                       vivid_is_in_use(&dev->sdr_cap_dev) +
+                       vivid_is_in_use(&dev->radio_rx_dev) +
+                       vivid_is_in_use(&dev->radio_tx_dev);
+
+       return uses == 1;
+}
+
+static int vivid_fop_release(struct file *file)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+
+       mutex_lock(&dev->mutex);
+       if (!no_error_inj && v4l2_fh_is_singular_file(file) &&
+           !video_is_registered(vdev) && vivid_is_last_user(dev)) {
+               /*
+                * I am the last user of this driver, and a disconnect
+                * was forced (since this video_device is unregistered),
+                * so re-register all video_device's again.
+                */
+               v4l2_info(&dev->v4l2_dev, "reconnect\n");
+               set_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
+               set_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
+               set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
+               set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
+               set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
+               set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
+               set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
+       }
+       mutex_unlock(&dev->mutex);
+       if (file->private_data == dev->overlay_cap_owner)
+               dev->overlay_cap_owner = NULL;
+       if (file->private_data == dev->radio_rx_rds_owner) {
+               dev->radio_rx_rds_last_block = 0;
+               dev->radio_rx_rds_owner = NULL;
+       }
+       if (file->private_data == dev->radio_tx_rds_owner) {
+               dev->radio_tx_rds_last_block = 0;
+               dev->radio_tx_rds_owner = NULL;
+       }
+       if (vdev->queue)
+               return vb2_fop_release(file);
+       return v4l2_fh_release(file);
+}
+
+static const struct v4l2_file_operations vivid_fops = {
+       .owner          = THIS_MODULE,
+       .open           = v4l2_fh_open,
+       .release        = vivid_fop_release,
+       .read           = vb2_fop_read,
+       .write          = vb2_fop_write,
+       .poll           = vb2_fop_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = vb2_fop_mmap,
+};
+
+static const struct v4l2_file_operations vivid_radio_fops = {
+       .owner          = THIS_MODULE,
+       .open           = v4l2_fh_open,
+       .release        = vivid_fop_release,
+       .read           = vivid_radio_read,
+       .write          = vivid_radio_write,
+       .poll           = vivid_radio_poll,
+       .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops vivid_ioctl_ops = {
+       .vidioc_querycap                = vidioc_querycap,
+
+       .vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt_vid,
+       .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_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_mplane,
+       .vidioc_g_fmt_vid_cap_mplane    = vidioc_g_fmt_vid_cap_mplane,
+       .vidioc_try_fmt_vid_cap_mplane  = vidioc_try_fmt_vid_cap_mplane,
+       .vidioc_s_fmt_vid_cap_mplane    = vidioc_s_fmt_vid_cap_mplane,
+
+       .vidioc_enum_fmt_vid_out        = vidioc_enum_fmt_vid,
+       .vidioc_g_fmt_vid_out           = vidioc_g_fmt_vid_out,
+       .vidioc_try_fmt_vid_out         = vidioc_try_fmt_vid_out,
+       .vidioc_s_fmt_vid_out           = vidioc_s_fmt_vid_out,
+       .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_mplane,
+       .vidioc_g_fmt_vid_out_mplane    = vidioc_g_fmt_vid_out_mplane,
+       .vidioc_try_fmt_vid_out_mplane  = vidioc_try_fmt_vid_out_mplane,
+       .vidioc_s_fmt_vid_out_mplane    = vidioc_s_fmt_vid_out_mplane,
+
+       .vidioc_g_selection             = vidioc_g_selection,
+       .vidioc_s_selection             = vidioc_s_selection,
+       .vidioc_cropcap                 = vidioc_cropcap,
+
+       .vidioc_g_fmt_vbi_cap           = vidioc_g_fmt_vbi_cap,
+       .vidioc_try_fmt_vbi_cap         = vidioc_g_fmt_vbi_cap,
+       .vidioc_s_fmt_vbi_cap           = vidioc_s_fmt_vbi_cap,
+
+       .vidioc_g_fmt_sliced_vbi_cap    = vidioc_g_fmt_sliced_vbi_cap,
+       .vidioc_try_fmt_sliced_vbi_cap  = vidioc_try_fmt_sliced_vbi_cap,
+       .vidioc_s_fmt_sliced_vbi_cap    = vidioc_s_fmt_sliced_vbi_cap,
+       .vidioc_g_sliced_vbi_cap        = vidioc_g_sliced_vbi_cap,
+
+       .vidioc_g_fmt_vbi_out           = vidioc_g_fmt_vbi_out,
+       .vidioc_try_fmt_vbi_out         = vidioc_g_fmt_vbi_out,
+       .vidioc_s_fmt_vbi_out           = vidioc_s_fmt_vbi_out,
+
+       .vidioc_g_fmt_sliced_vbi_out    = vidioc_g_fmt_sliced_vbi_out,
+       .vidioc_try_fmt_sliced_vbi_out  = vidioc_try_fmt_sliced_vbi_out,
+       .vidioc_s_fmt_sliced_vbi_out    = vidioc_s_fmt_sliced_vbi_out,
+
+       .vidioc_enum_fmt_sdr_cap        = vidioc_enum_fmt_sdr_cap,
+       .vidioc_g_fmt_sdr_cap           = vidioc_g_fmt_sdr_cap,
+       .vidioc_try_fmt_sdr_cap         = vidioc_g_fmt_sdr_cap,
+       .vidioc_s_fmt_sdr_cap           = vidioc_g_fmt_sdr_cap,
+
+       .vidioc_overlay                 = vidioc_overlay,
+       .vidioc_enum_framesizes         = vidioc_enum_framesizes,
+       .vidioc_enum_frameintervals     = vidioc_enum_frameintervals,
+       .vidioc_g_parm                  = vidioc_g_parm,
+       .vidioc_s_parm                  = vidioc_s_parm,
+
+       .vidioc_enum_fmt_vid_overlay    = vidioc_enum_fmt_vid_overlay,
+       .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_vid_out_overlay   = vidioc_g_fmt_vid_out_overlay,
+       .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_out_overlay,
+       .vidioc_s_fmt_vid_out_overlay   = vidioc_s_fmt_vid_out_overlay,
+       .vidioc_g_fbuf                  = vidioc_g_fbuf,
+       .vidioc_s_fbuf                  = vidioc_s_fbuf,
+
+       .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs             = vb2_ioctl_create_bufs,
+       .vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf                = vb2_ioctl_querybuf,
+       .vidioc_qbuf                    = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
+/* Not yet     .vidioc_expbuf          = vb2_ioctl_expbuf,*/
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
+
+       .vidioc_enum_input              = vidioc_enum_input,
+       .vidioc_g_input                 = vidioc_g_input,
+       .vidioc_s_input                 = vidioc_s_input,
+       .vidioc_s_audio                 = vidioc_s_audio,
+       .vidioc_g_audio                 = vidioc_g_audio,
+       .vidioc_enumaudio               = vidioc_enumaudio,
+       .vidioc_s_frequency             = vidioc_s_frequency,
+       .vidioc_g_frequency             = vidioc_g_frequency,
+       .vidioc_s_tuner                 = vidioc_s_tuner,
+       .vidioc_g_tuner                 = vidioc_g_tuner,
+       .vidioc_s_modulator             = vidioc_s_modulator,
+       .vidioc_g_modulator             = vidioc_g_modulator,
+       .vidioc_s_hw_freq_seek          = vidioc_s_hw_freq_seek,
+       .vidioc_enum_freq_bands         = vidioc_enum_freq_bands,
+
+       .vidioc_enum_output             = vidioc_enum_output,
+       .vidioc_g_output                = vidioc_g_output,
+       .vidioc_s_output                = vidioc_s_output,
+       .vidioc_s_audout                = vidioc_s_audout,
+       .vidioc_g_audout                = vidioc_g_audout,
+       .vidioc_enumaudout              = vidioc_enumaudout,
+
+       .vidioc_querystd                = vidioc_querystd,
+       .vidioc_g_std                   = vidioc_g_std,
+       .vidioc_s_std                   = vidioc_s_std,
+       .vidioc_s_dv_timings            = vidioc_s_dv_timings,
+       .vidioc_g_dv_timings            = vidioc_g_dv_timings,
+       .vidioc_query_dv_timings        = vidioc_query_dv_timings,
+       .vidioc_enum_dv_timings         = vidioc_enum_dv_timings,
+       .vidioc_dv_timings_cap          = vidioc_dv_timings_cap,
+       .vidioc_g_edid                  = vidioc_g_edid,
+       .vidioc_s_edid                  = vidioc_s_edid,
+
+       .vidioc_log_status              = v4l2_ctrl_log_status,
+       .vidioc_subscribe_event         = vidioc_subscribe_event,
+       .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
+};
+
+/* -----------------------------------------------------------------
+       Initialization and module stuff
+   ------------------------------------------------------------------*/
+
+static int __init vivid_create_instance(int inst)
+{
+       static const struct v4l2_dv_timings def_dv_timings =
+                                       V4L2_DV_BT_CEA_1280X720P60;
+       unsigned in_type_counter[4] = { 0, 0, 0, 0 };
+       unsigned out_type_counter[4] = { 0, 0, 0, 0 };
+       int ccs_cap = ccs_cap_mode[inst];
+       int ccs_out = ccs_out_mode[inst];
+       bool has_tuner;
+       bool has_modulator;
+       struct vivid_dev *dev;
+       struct video_device *vfd;
+       struct vb2_queue *q;
+       unsigned node_type = node_types[inst];
+       v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0;
+       int ret;
+       int i;
+
+       /* allocate main vivid state structure */
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       dev->inst = inst;
+
+       /* register v4l2_device */
+       snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
+                       "%s-%03d", VIVID_MODULE_NAME, inst);
+       ret = v4l2_device_register(NULL, &dev->v4l2_dev);
+       if (ret)
+               goto free_dev;
+
+       /* start detecting feature set */
+
+       /* do we use single- or multi-planar? */
+       if (multiplanar[inst] == 0)
+               dev->multiplanar = inst & 1;
+       else
+               dev->multiplanar = multiplanar[inst] > 1;
+       v4l2_info(&dev->v4l2_dev, "using %splanar format API\n",
+                       dev->multiplanar ? "multi" : "single ");
+
+       /* how many inputs do we have and of what type? */
+       dev->num_inputs = num_inputs[inst];
+       if (dev->num_inputs < 1)
+               dev->num_inputs = 1;
+       if (dev->num_inputs >= MAX_INPUTS)
+               dev->num_inputs = MAX_INPUTS;
+       for (i = 0; i < dev->num_inputs; i++) {
+               dev->input_type[i] = (input_types[inst] >> (i * 2)) & 0x3;
+               dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++;
+       }
+       dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID];
+
+       /* how many outputs do we have and of what type? */
+       dev->num_outputs = num_outputs[inst];
+       if (dev->num_outputs < 1)
+               dev->num_outputs = 1;
+       if (dev->num_outputs >= MAX_OUTPUTS)
+               dev->num_outputs = MAX_OUTPUTS;
+       for (i = 0; i < dev->num_outputs; i++) {
+               dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID;
+               dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++;
+       }
+       dev->has_audio_outputs = out_type_counter[SVID];
+
+       /* do we create a video capture device? */
+       dev->has_vid_cap = node_type & 0x0001;
+
+       /* do we create a vbi capture device? */
+       if (in_type_counter[TV] || in_type_counter[SVID]) {
+               dev->has_raw_vbi_cap = node_type & 0x0004;
+               dev->has_sliced_vbi_cap = node_type & 0x0008;
+               dev->has_vbi_cap = dev->has_raw_vbi_cap | dev->has_sliced_vbi_cap;
+       }
+
+       /* do we create a video output device? */
+       dev->has_vid_out = node_type & 0x0100;
+
+       /* do we create a vbi output device? */
+       if (out_type_counter[SVID]) {
+               dev->has_raw_vbi_out = node_type & 0x0400;
+               dev->has_sliced_vbi_out = node_type & 0x0800;
+               dev->has_vbi_out = dev->has_raw_vbi_out | dev->has_sliced_vbi_out;
+       }
+
+       /* do we create a radio receiver device? */
+       dev->has_radio_rx = node_type & 0x0010;
+
+       /* do we create a radio transmitter device? */
+       dev->has_radio_tx = node_type & 0x1000;
+
+       /* do we create a software defined radio capture device? */
+       dev->has_sdr_cap = node_type & 0x0020;
+
+       /* do we have a tuner? */
+       has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) ||
+                   dev->has_radio_rx || dev->has_sdr_cap;
+
+       /* do we have a modulator? */
+       has_modulator = dev->has_radio_tx;
+
+       if (dev->has_vid_cap)
+               /* do we have a framebuffer for overlay testing? */
+               dev->has_fb = node_type & 0x10000;
+
+       /* can we do crop/compose/scaling while capturing? */
+       if (no_error_inj && ccs_cap == -1)
+               ccs_cap = 7;
+
+       /* if ccs_cap == -1, then the use can select it using controls */
+       if (ccs_cap != -1) {
+               dev->has_crop_cap = ccs_cap & 1;
+               dev->has_compose_cap = ccs_cap & 2;
+               dev->has_scaler_cap = ccs_cap & 4;
+               v4l2_info(&dev->v4l2_dev, "Capture Crop: %c Compose: %c Scaler: %c\n",
+                       dev->has_crop_cap ? 'Y' : 'N',
+                       dev->has_compose_cap ? 'Y' : 'N',
+                       dev->has_scaler_cap ? 'Y' : 'N');
+       }
+
+       /* can we do crop/compose/scaling with video output? */
+       if (no_error_inj && ccs_out == -1)
+               ccs_out = 7;
+
+       /* if ccs_out == -1, then the use can select it using controls */
+       if (ccs_out != -1) {
+               dev->has_crop_out = ccs_out & 1;
+               dev->has_compose_out = ccs_out & 2;
+               dev->has_scaler_out = ccs_out & 4;
+               v4l2_info(&dev->v4l2_dev, "Output Crop: %c Compose: %c Scaler: %c\n",
+                       dev->has_crop_out ? 'Y' : 'N',
+                       dev->has_compose_out ? 'Y' : 'N',
+                       dev->has_scaler_out ? 'Y' : 'N');
+       }
+
+       /* end detecting feature set */
+
+       if (dev->has_vid_cap) {
+               /* set up the capabilities of the video capture device */
+               dev->vid_cap_caps = dev->multiplanar ?
+                       V4L2_CAP_VIDEO_CAPTURE_MPLANE :
+                       V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY;
+               dev->vid_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+               if (dev->has_audio_inputs)
+                       dev->vid_cap_caps |= V4L2_CAP_AUDIO;
+               if (in_type_counter[TV])
+                       dev->vid_cap_caps |= V4L2_CAP_TUNER;
+       }
+       if (dev->has_vid_out) {
+               /* set up the capabilities of the video output device */
+               dev->vid_out_caps = dev->multiplanar ?
+                       V4L2_CAP_VIDEO_OUTPUT_MPLANE :
+                       V4L2_CAP_VIDEO_OUTPUT;
+               if (dev->has_fb)
+                       dev->vid_out_caps |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
+               dev->vid_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+               if (dev->has_audio_outputs)
+                       dev->vid_out_caps |= V4L2_CAP_AUDIO;
+       }
+       if (dev->has_vbi_cap) {
+               /* set up the capabilities of the vbi capture device */
+               dev->vbi_cap_caps = (dev->has_raw_vbi_cap ? V4L2_CAP_VBI_CAPTURE : 0) |
+                                   (dev->has_sliced_vbi_cap ? V4L2_CAP_SLICED_VBI_CAPTURE : 0);
+               dev->vbi_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+               if (dev->has_audio_inputs)
+                       dev->vbi_cap_caps |= V4L2_CAP_AUDIO;
+               if (in_type_counter[TV])
+                       dev->vbi_cap_caps |= V4L2_CAP_TUNER;
+       }
+       if (dev->has_vbi_out) {
+               /* set up the capabilities of the vbi output device */
+               dev->vbi_out_caps = (dev->has_raw_vbi_out ? V4L2_CAP_VBI_OUTPUT : 0) |
+                                   (dev->has_sliced_vbi_out ? V4L2_CAP_SLICED_VBI_OUTPUT : 0);
+               dev->vbi_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+               if (dev->has_audio_outputs)
+                       dev->vbi_out_caps |= V4L2_CAP_AUDIO;
+       }
+       if (dev->has_sdr_cap) {
+               /* set up the capabilities of the sdr capture device */
+               dev->sdr_cap_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER;
+               dev->sdr_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+       }
+       /* set up the capabilities of the radio receiver device */
+       if (dev->has_radio_rx)
+               dev->radio_rx_caps = V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE |
+                                    V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
+                                    V4L2_CAP_READWRITE;
+       /* set up the capabilities of the radio transmitter device */
+       if (dev->has_radio_tx)
+               dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR |
+                                    V4L2_CAP_READWRITE;
+
+       /* initialize the test pattern generator */
+       tpg_init(&dev->tpg, 640, 360);
+       if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH))
+               goto free_dev;
+       dev->scaled_line = vzalloc(MAX_ZOOM * MAX_WIDTH);
+       if (!dev->scaled_line)
+               goto free_dev;
+       dev->blended_line = vzalloc(MAX_ZOOM * MAX_WIDTH);
+       if (!dev->blended_line)
+               goto free_dev;
+
+       /* load the edid */
+       dev->edid = vmalloc(256 * 128);
+       if (!dev->edid)
+               goto free_dev;
+
+       /* create a string array containing the names of all the preset timings */
+       while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width)
+               dev->query_dv_timings_size++;
+       dev->query_dv_timings_qmenu = kmalloc(dev->query_dv_timings_size *
+                                          (sizeof(void *) + 32), GFP_KERNEL);
+       if (dev->query_dv_timings_qmenu == NULL)
+               goto free_dev;
+       for (i = 0; i < dev->query_dv_timings_size; i++) {
+               const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt;
+               char *p = (char *)&dev->query_dv_timings_qmenu[dev->query_dv_timings_size];
+               u32 htot, vtot;
+
+               p += i * 32;
+               dev->query_dv_timings_qmenu[i] = p;
+
+               htot = V4L2_DV_BT_FRAME_WIDTH(bt);
+               vtot = V4L2_DV_BT_FRAME_HEIGHT(bt);
+               snprintf(p, 32, "%ux%u%s%u",
+                       bt->width, bt->height, bt->interlaced ? "i" : "p",
+                       (u32)bt->pixelclock / (htot * vtot));
+       }
+
+       /* disable invalid ioctls based on the feature set */
+       if (!dev->has_audio_inputs) {
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_AUDIO);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_AUDIO);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMAUDIO);
+               v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_AUDIO);
+               v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_AUDIO);
+               v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_ENUMAUDIO);
+       }
+       if (!dev->has_audio_outputs) {
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_AUDOUT);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_AUDOUT);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMAUDOUT);
+               v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_AUDOUT);
+               v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_AUDOUT);
+               v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_ENUMAUDOUT);
+       }
+       if (!in_type_counter[TV] && !in_type_counter[SVID]) {
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_STD);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_STD);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMSTD);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERYSTD);
+       }
+       if (!out_type_counter[SVID]) {
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_STD);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_STD);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMSTD);
+       }
+       if (!has_tuner && !has_modulator) {
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_FREQUENCY);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_FREQUENCY);
+               v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_FREQUENCY);
+               v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_FREQUENCY);
+       }
+       if (!has_tuner) {
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_TUNER);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_TUNER);
+               v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_TUNER);
+               v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_TUNER);
+       }
+       if (in_type_counter[HDMI] == 0) {
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_EDID);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_EDID);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_DV_TIMINGS_CAP);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_DV_TIMINGS);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_DV_TIMINGS);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUM_DV_TIMINGS);
+               v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERY_DV_TIMINGS);
+       }
+       if (out_type_counter[HDMI] == 0) {
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_EDID);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_DV_TIMINGS_CAP);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_DV_TIMINGS);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_DV_TIMINGS);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_DV_TIMINGS);
+       }
+       if (!dev->has_fb) {
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FBUF);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FBUF);
+               v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_OVERLAY);
+       }
+       v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
+       v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
+       v4l2_disable_ioctl(&dev->sdr_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
+       v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FREQUENCY);
+       v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FREQUENCY);
+       v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMESIZES);
+       v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMEINTERVALS);
+       v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_FREQUENCY);
+       v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_FREQUENCY);
+
+       /* configure internal data */
+       dev->fmt_cap = &vivid_formats[0];
+       dev->fmt_out = &vivid_formats[0];
+       if (!dev->multiplanar)
+               vivid_formats[0].data_offset[0] = 0;
+       dev->webcam_size_idx = 1;
+       dev->webcam_ival_idx = 3;
+       tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc);
+       dev->std_cap = V4L2_STD_PAL;
+       dev->std_out = V4L2_STD_PAL;
+       if (dev->input_type[0] == TV || dev->input_type[0] == SVID)
+               tvnorms_cap = V4L2_STD_ALL;
+       if (dev->output_type[0] == SVID)
+               tvnorms_out = V4L2_STD_ALL;
+       dev->dv_timings_cap = def_dv_timings;
+       dev->dv_timings_out = def_dv_timings;
+       dev->tv_freq = 2804 /* 175.25 * 16 */;
+       dev->tv_audmode = V4L2_TUNER_MODE_STEREO;
+       dev->tv_field_cap = V4L2_FIELD_INTERLACED;
+       dev->tv_field_out = V4L2_FIELD_INTERLACED;
+       dev->radio_rx_freq = 95000 * 16;
+       dev->radio_rx_audmode = V4L2_TUNER_MODE_STEREO;
+       if (dev->has_radio_tx) {
+               dev->radio_tx_freq = 95500 * 16;
+               dev->radio_rds_loop = false;
+       }
+       dev->radio_tx_subchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS;
+       dev->sdr_adc_freq = 300000;
+       dev->sdr_fm_freq = 50000000;
+       dev->edid_max_blocks = dev->edid_blocks = 2;
+       memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid));
+       ktime_get_ts(&dev->radio_rds_init_ts);
+
+       /* create all controls */
+       ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj,
+                       in_type_counter[TV] || in_type_counter[SVID] ||
+                       out_type_counter[SVID],
+                       in_type_counter[HDMI] || out_type_counter[HDMI]);
+       if (ret)
+               goto unreg_dev;
+
+       /*
+        * update the capture and output formats to do a proper initial
+        * configuration.
+        */
+       vivid_update_format_cap(dev, false);
+       vivid_update_format_out(dev);
+
+       v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_cap);
+       v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_out);
+       v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_cap);
+       v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_out);
+       v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx);
+       v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx);
+       v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap);
+
+       /* initialize overlay */
+       dev->fb_cap.fmt.width = dev->src_rect.width;
+       dev->fb_cap.fmt.height = dev->src_rect.height;
+       dev->fb_cap.fmt.pixelformat = dev->fmt_cap->fourcc;
+       dev->fb_cap.fmt.bytesperline = dev->src_rect.width * tpg_g_twopixelsize(&dev->tpg, 0) / 2;
+       dev->fb_cap.fmt.sizeimage = dev->src_rect.height * dev->fb_cap.fmt.bytesperline;
+
+       /* initialize locks */
+       spin_lock_init(&dev->slock);
+       mutex_init(&dev->mutex);
+
+       /* init dma queues */
+       INIT_LIST_HEAD(&dev->vid_cap_active);
+       INIT_LIST_HEAD(&dev->vid_out_active);
+       INIT_LIST_HEAD(&dev->vbi_cap_active);
+       INIT_LIST_HEAD(&dev->vbi_out_active);
+       INIT_LIST_HEAD(&dev->sdr_cap_active);
+
+       /* start creating the vb2 queues */
+       if (dev->has_vid_cap) {
+               /* initialize vid_cap queue */
+               q = &dev->vb_vid_cap_q;
+               q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
+                       V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+               q->drv_priv = dev;
+               q->buf_struct_size = sizeof(struct vivid_buffer);
+               q->ops = &vivid_vid_cap_qops;
+               q->mem_ops = &vb2_vmalloc_memops;
+               q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+               q->min_buffers_needed = 2;
+
+               ret = vb2_queue_init(q);
+               if (ret)
+                       goto unreg_dev;
+       }
+
+       if (dev->has_vid_out) {
+               /* initialize vid_out queue */
+               q = &dev->vb_vid_out_q;
+               q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
+                       V4L2_BUF_TYPE_VIDEO_OUTPUT;
+               q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE;
+               q->drv_priv = dev;
+               q->buf_struct_size = sizeof(struct vivid_buffer);
+               q->ops = &vivid_vid_out_qops;
+               q->mem_ops = &vb2_vmalloc_memops;
+               q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+               q->min_buffers_needed = 2;
+
+               ret = vb2_queue_init(q);
+               if (ret)
+                       goto unreg_dev;
+       }
+
+       if (dev->has_vbi_cap) {
+               /* initialize vbi_cap queue */
+               q = &dev->vb_vbi_cap_q;
+               q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE :
+                                             V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+               q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+               q->drv_priv = dev;
+               q->buf_struct_size = sizeof(struct vivid_buffer);
+               q->ops = &vivid_vbi_cap_qops;
+               q->mem_ops = &vb2_vmalloc_memops;
+               q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+               q->min_buffers_needed = 2;
+
+               ret = vb2_queue_init(q);
+               if (ret)
+                       goto unreg_dev;
+       }
+
+       if (dev->has_vbi_out) {
+               /* initialize vbi_out queue */
+               q = &dev->vb_vbi_out_q;
+               q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT :
+                                             V4L2_BUF_TYPE_SLICED_VBI_OUTPUT;
+               q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE;
+               q->drv_priv = dev;
+               q->buf_struct_size = sizeof(struct vivid_buffer);
+               q->ops = &vivid_vbi_out_qops;
+               q->mem_ops = &vb2_vmalloc_memops;
+               q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+               q->min_buffers_needed = 2;
+
+               ret = vb2_queue_init(q);
+               if (ret)
+                       goto unreg_dev;
+       }
+
+       if (dev->has_sdr_cap) {
+               /* initialize sdr_cap queue */
+               q = &dev->vb_sdr_cap_q;
+               q->type = V4L2_BUF_TYPE_SDR_CAPTURE;
+               q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+               q->drv_priv = dev;
+               q->buf_struct_size = sizeof(struct vivid_buffer);
+               q->ops = &vivid_sdr_cap_qops;
+               q->mem_ops = &vb2_vmalloc_memops;
+               q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+               q->min_buffers_needed = 8;
+
+               ret = vb2_queue_init(q);
+               if (ret)
+                       goto unreg_dev;
+       }
+
+       if (dev->has_fb) {
+               /* Create framebuffer for testing capture/output overlay */
+               ret = vivid_fb_init(dev);
+               if (ret)
+                       goto unreg_dev;
+               v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n",
+                               dev->fb_info.node);
+       }
+
+       /* finally start creating the device nodes */
+       if (dev->has_vid_cap) {
+               vfd = &dev->vid_cap_dev;
+               strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name));
+               vfd->fops = &vivid_fops;
+               vfd->ioctl_ops = &vivid_ioctl_ops;
+               vfd->release = video_device_release_empty;
+               vfd->v4l2_dev = &dev->v4l2_dev;
+               vfd->queue = &dev->vb_vid_cap_q;
+               vfd->tvnorms = tvnorms_cap;
+
+               /*
+                * Provide a mutex to v4l2 core. It will be used to protect
+                * all fops and v4l2 ioctls.
+                */
+               vfd->lock = &dev->mutex;
+               video_set_drvdata(vfd, dev);
+
+               ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]);
+               if (ret < 0)
+                       goto unreg_dev;
+               v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n",
+                                         video_device_node_name(vfd));
+       }
+
+       if (dev->has_vid_out) {
+               vfd = &dev->vid_out_dev;
+               strlcpy(vfd->name, "vivid-vid-out", sizeof(vfd->name));
+               vfd->vfl_dir = VFL_DIR_TX;
+               vfd->fops = &vivid_fops;
+               vfd->ioctl_ops = &vivid_ioctl_ops;
+               vfd->release = video_device_release_empty;
+               vfd->v4l2_dev = &dev->v4l2_dev;
+               vfd->queue = &dev->vb_vid_out_q;
+               vfd->tvnorms = tvnorms_out;
+
+               /*
+                * Provide a mutex to v4l2 core. It will be used to protect
+                * all fops and v4l2 ioctls.
+                */
+               vfd->lock = &dev->mutex;
+               video_set_drvdata(vfd, dev);
+
+               ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]);
+               if (ret < 0)
+                       goto unreg_dev;
+               v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s\n",
+                                         video_device_node_name(vfd));
+       }
+
+       if (dev->has_vbi_cap) {
+               vfd = &dev->vbi_cap_dev;
+               strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name));
+               vfd->fops = &vivid_fops;
+               vfd->ioctl_ops = &vivid_ioctl_ops;
+               vfd->release = video_device_release_empty;
+               vfd->v4l2_dev = &dev->v4l2_dev;
+               vfd->queue = &dev->vb_vbi_cap_q;
+               vfd->lock = &dev->mutex;
+               vfd->tvnorms = tvnorms_cap;
+               video_set_drvdata(vfd, dev);
+
+               ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_cap_nr[inst]);
+               if (ret < 0)
+                       goto unreg_dev;
+               v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s, supports %s VBI\n",
+                                         video_device_node_name(vfd),
+                                         (dev->has_raw_vbi_cap && dev->has_sliced_vbi_cap) ?
+                                         "raw and sliced" :
+                                         (dev->has_raw_vbi_cap ? "raw" : "sliced"));
+       }
+
+       if (dev->has_vbi_out) {
+               vfd = &dev->vbi_out_dev;
+               strlcpy(vfd->name, "vivid-vbi-out", sizeof(vfd->name));
+               vfd->vfl_dir = VFL_DIR_TX;
+               vfd->fops = &vivid_fops;
+               vfd->ioctl_ops = &vivid_ioctl_ops;
+               vfd->release = video_device_release_empty;
+               vfd->v4l2_dev = &dev->v4l2_dev;
+               vfd->queue = &dev->vb_vbi_out_q;
+               vfd->lock = &dev->mutex;
+               vfd->tvnorms = tvnorms_out;
+               video_set_drvdata(vfd, dev);
+
+               ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_out_nr[inst]);
+               if (ret < 0)
+                       goto unreg_dev;
+               v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s, supports %s VBI\n",
+                                         video_device_node_name(vfd),
+                                         (dev->has_raw_vbi_out && dev->has_sliced_vbi_out) ?
+                                         "raw and sliced" :
+                                         (dev->has_raw_vbi_out ? "raw" : "sliced"));
+       }
+
+       if (dev->has_sdr_cap) {
+               vfd = &dev->sdr_cap_dev;
+               strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name));
+               vfd->fops = &vivid_fops;
+               vfd->ioctl_ops = &vivid_ioctl_ops;
+               vfd->release = video_device_release_empty;
+               vfd->v4l2_dev = &dev->v4l2_dev;
+               vfd->queue = &dev->vb_sdr_cap_q;
+               vfd->lock = &dev->mutex;
+               video_set_drvdata(vfd, dev);
+
+               ret = video_register_device(vfd, VFL_TYPE_SDR, sdr_cap_nr[inst]);
+               if (ret < 0)
+                       goto unreg_dev;
+               v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n",
+                                         video_device_node_name(vfd));
+       }
+
+       if (dev->has_radio_rx) {
+               vfd = &dev->radio_rx_dev;
+               strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name));
+               vfd->fops = &vivid_radio_fops;
+               vfd->ioctl_ops = &vivid_ioctl_ops;
+               vfd->release = video_device_release_empty;
+               vfd->v4l2_dev = &dev->v4l2_dev;
+               vfd->lock = &dev->mutex;
+               video_set_drvdata(vfd, dev);
+
+               ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_rx_nr[inst]);
+               if (ret < 0)
+                       goto unreg_dev;
+               v4l2_info(&dev->v4l2_dev, "V4L2 receiver device registered as %s\n",
+                                         video_device_node_name(vfd));
+       }
+
+       if (dev->has_radio_tx) {
+               vfd = &dev->radio_tx_dev;
+               strlcpy(vfd->name, "vivid-rad-tx", sizeof(vfd->name));
+               vfd->vfl_dir = VFL_DIR_TX;
+               vfd->fops = &vivid_radio_fops;
+               vfd->ioctl_ops = &vivid_ioctl_ops;
+               vfd->release = video_device_release_empty;
+               vfd->v4l2_dev = &dev->v4l2_dev;
+               vfd->lock = &dev->mutex;
+               video_set_drvdata(vfd, dev);
+
+               ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_tx_nr[inst]);
+               if (ret < 0)
+                       goto unreg_dev;
+               v4l2_info(&dev->v4l2_dev, "V4L2 transmitter device registered as %s\n",
+                                         video_device_node_name(vfd));
+       }
+
+       /* Now that everything is fine, let's add it to device list */
+       vivid_devs[inst] = dev;
+
+       return 0;
+
+unreg_dev:
+       video_unregister_device(&dev->radio_tx_dev);
+       video_unregister_device(&dev->radio_rx_dev);
+       video_unregister_device(&dev->sdr_cap_dev);
+       video_unregister_device(&dev->vbi_out_dev);
+       video_unregister_device(&dev->vbi_cap_dev);
+       video_unregister_device(&dev->vid_out_dev);
+       video_unregister_device(&dev->vid_cap_dev);
+       vivid_free_controls(dev);
+       v4l2_device_unregister(&dev->v4l2_dev);
+free_dev:
+       vfree(dev->scaled_line);
+       vfree(dev->blended_line);
+       vfree(dev->edid);
+       tpg_free(&dev->tpg);
+       kfree(dev->query_dv_timings_qmenu);
+       kfree(dev);
+       return ret;
+}
+
+/* This routine allocates from 1 to n_devs virtual drivers.
+
+   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 vivid_init(void)
+{
+       const struct font_desc *font = find_font("VGA8x16");
+       int ret = 0, i;
+
+       if (font == NULL) {
+               pr_err("vivid: could not find font\n");
+               return -ENODEV;
+       }
+
+       tpg_set_font(font->data);
+
+       n_devs = clamp_t(unsigned, n_devs, 1, VIVID_MAX_DEVS);
+
+       for (i = 0; i < n_devs; i++) {
+               ret = vivid_create_instance(i);
+               if (ret) {
+                       /* If some instantiations succeeded, keep driver */
+                       if (i)
+                               ret = 0;
+                       break;
+               }
+       }
+
+       if (ret < 0) {
+               pr_err("vivid: error %d while loading driver\n", ret);
+               return ret;
+       }
+
+       /* n_devs will reflect the actual number of allocated devices */
+       n_devs = i;
+
+       return ret;
+}
+
+static void __exit vivid_exit(void)
+{
+       struct vivid_dev *dev;
+       unsigned i;
+
+       for (i = 0; vivid_devs[i]; i++) {
+               dev = vivid_devs[i];
+
+               if (dev->has_vid_cap) {
+                       v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+                               video_device_node_name(&dev->vid_cap_dev));
+                       video_unregister_device(&dev->vid_cap_dev);
+               }
+               if (dev->has_vid_out) {
+                       v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+                               video_device_node_name(&dev->vid_out_dev));
+                       video_unregister_device(&dev->vid_out_dev);
+               }
+               if (dev->has_vbi_cap) {
+                       v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+                               video_device_node_name(&dev->vbi_cap_dev));
+                       video_unregister_device(&dev->vbi_cap_dev);
+               }
+               if (dev->has_vbi_out) {
+                       v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+                               video_device_node_name(&dev->vbi_out_dev));
+                       video_unregister_device(&dev->vbi_out_dev);
+               }
+               if (dev->has_sdr_cap) {
+                       v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+                               video_device_node_name(&dev->sdr_cap_dev));
+                       video_unregister_device(&dev->sdr_cap_dev);
+               }
+               if (dev->has_radio_rx) {
+                       v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+                               video_device_node_name(&dev->radio_rx_dev));
+                       video_unregister_device(&dev->radio_rx_dev);
+               }
+               if (dev->has_radio_tx) {
+                       v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+                               video_device_node_name(&dev->radio_tx_dev));
+                       video_unregister_device(&dev->radio_tx_dev);
+               }
+               if (dev->has_fb) {
+                       v4l2_info(&dev->v4l2_dev, "unregistering fb%d\n",
+                               dev->fb_info.node);
+                       unregister_framebuffer(&dev->fb_info);
+                       vivid_fb_release_buffers(dev);
+               }
+               v4l2_device_unregister(&dev->v4l2_dev);
+               vivid_free_controls(dev);
+               vfree(dev->scaled_line);
+               vfree(dev->blended_line);
+               vfree(dev->edid);
+               vfree(dev->bitmap_cap);
+               vfree(dev->bitmap_out);
+               tpg_free(&dev->tpg);
+               kfree(dev->query_dv_timings_qmenu);
+               kfree(dev);
+               vivid_devs[i] = NULL;
+       }
+}
+
+module_init(vivid_init);
+module_exit(vivid_exit);
diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
new file mode 100644 (file)
index 0000000..811c286
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * vivid-core.h - core datastructures
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_CORE_H_
+#define _VIVID_CORE_H_
+
+#include <linux/fb.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ctrls.h>
+#include "vivid-tpg.h"
+#include "vivid-rds-gen.h"
+#include "vivid-vbi-gen.h"
+
+#define dprintk(dev, level, fmt, arg...) \
+       v4l2_dbg(level, vivid_debug, &dev->v4l2_dev, fmt, ## arg)
+
+/* Maximum allowed frame rate
+ *
+ * vivid will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range.
+ *
+ * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that
+ * might hit application errors when they manipulate these values.
+ *
+ * Besides, for tpf < 10ms image-generation logic should be changed, to avoid
+ * producing frames with equal content.
+ */
+#define FPS_MAX 100
+
+/* The maximum number of clip rectangles */
+#define MAX_CLIPS  16
+/* The maximum number of inputs */
+#define MAX_INPUTS 16
+/* The maximum number of outputs */
+#define MAX_OUTPUTS 16
+/* The maximum up or down scaling factor is 4 */
+#define MAX_ZOOM  4
+/* The maximum image width/height are set to 4K DMT */
+#define MAX_WIDTH  4096
+#define MAX_HEIGHT 2160
+/* The minimum image width/height */
+#define MIN_WIDTH  16
+#define MIN_HEIGHT 16
+/* The data_offset of plane 0 for the multiplanar formats */
+#define PLANE0_DATA_OFFSET 128
+
+/* The supported TV frequency range in MHz */
+#define MIN_TV_FREQ (44U * 16U)
+#define MAX_TV_FREQ (958U * 16U)
+
+/* The number of samples returned in every SDR buffer */
+#define SDR_CAP_SAMPLES_PER_BUF 0x4000
+
+/* used by the threads to know when to resync internal counters */
+#define JIFFIES_PER_DAY (3600U * 24U * HZ)
+#define JIFFIES_RESYNC (JIFFIES_PER_DAY * (0xf0000000U / JIFFIES_PER_DAY))
+
+extern const struct v4l2_rect vivid_min_rect;
+extern const struct v4l2_rect vivid_max_rect;
+extern unsigned vivid_debug;
+
+struct vivid_fmt {
+       const char *name;
+       u32     fourcc;          /* v4l2 format id */
+       u8      depth;
+       bool    is_yuv;
+       bool    can_do_overlay;
+       u32     alpha_mask;
+       u8      planes;
+       u32     data_offset[2];
+};
+
+extern struct vivid_fmt vivid_formats[];
+
+/* buffer for one video frame */
+struct vivid_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct vb2_buffer       vb;
+       struct list_head        list;
+};
+
+enum vivid_input {
+       WEBCAM,
+       TV,
+       SVID,
+       HDMI,
+};
+
+enum vivid_signal_mode {
+       CURRENT_DV_TIMINGS,
+       CURRENT_STD = CURRENT_DV_TIMINGS,
+       NO_SIGNAL,
+       NO_LOCK,
+       OUT_OF_RANGE,
+       SELECTED_DV_TIMINGS,
+       SELECTED_STD = SELECTED_DV_TIMINGS,
+       CYCLE_DV_TIMINGS,
+       CYCLE_STD = CYCLE_DV_TIMINGS,
+       CUSTOM_DV_TIMINGS,
+};
+
+#define VIVID_INVALID_SIGNAL(mode) \
+       ((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE)
+
+struct vivid_dev {
+       unsigned                        inst;
+       struct v4l2_device              v4l2_dev;
+       struct v4l2_ctrl_handler        ctrl_hdl_user_gen;
+       struct v4l2_ctrl_handler        ctrl_hdl_user_vid;
+       struct v4l2_ctrl_handler        ctrl_hdl_user_aud;
+       struct v4l2_ctrl_handler        ctrl_hdl_streaming;
+       struct v4l2_ctrl_handler        ctrl_hdl_sdtv_cap;
+       struct v4l2_ctrl_handler        ctrl_hdl_loop_out;
+       struct video_device             vid_cap_dev;
+       struct v4l2_ctrl_handler        ctrl_hdl_vid_cap;
+       struct video_device             vid_out_dev;
+       struct v4l2_ctrl_handler        ctrl_hdl_vid_out;
+       struct video_device             vbi_cap_dev;
+       struct v4l2_ctrl_handler        ctrl_hdl_vbi_cap;
+       struct video_device             vbi_out_dev;
+       struct v4l2_ctrl_handler        ctrl_hdl_vbi_out;
+       struct video_device             radio_rx_dev;
+       struct v4l2_ctrl_handler        ctrl_hdl_radio_rx;
+       struct video_device             radio_tx_dev;
+       struct v4l2_ctrl_handler        ctrl_hdl_radio_tx;
+       struct video_device             sdr_cap_dev;
+       struct v4l2_ctrl_handler        ctrl_hdl_sdr_cap;
+       spinlock_t                      slock;
+       struct mutex                    mutex;
+
+       /* capabilities */
+       u32                             vid_cap_caps;
+       u32                             vid_out_caps;
+       u32                             vbi_cap_caps;
+       u32                             vbi_out_caps;
+       u32                             sdr_cap_caps;
+       u32                             radio_rx_caps;
+       u32                             radio_tx_caps;
+
+       /* supported features */
+       bool                            multiplanar;
+       unsigned                        num_inputs;
+       u8                              input_type[MAX_INPUTS];
+       u8                              input_name_counter[MAX_INPUTS];
+       unsigned                        num_outputs;
+       u8                              output_type[MAX_OUTPUTS];
+       u8                              output_name_counter[MAX_OUTPUTS];
+       bool                            has_audio_inputs;
+       bool                            has_audio_outputs;
+       bool                            has_vid_cap;
+       bool                            has_vid_out;
+       bool                            has_vbi_cap;
+       bool                            has_raw_vbi_cap;
+       bool                            has_sliced_vbi_cap;
+       bool                            has_vbi_out;
+       bool                            has_raw_vbi_out;
+       bool                            has_sliced_vbi_out;
+       bool                            has_radio_rx;
+       bool                            has_radio_tx;
+       bool                            has_sdr_cap;
+       bool                            has_fb;
+
+       bool                            can_loop_video;
+
+       /* controls */
+       struct v4l2_ctrl                *brightness;
+       struct v4l2_ctrl                *contrast;
+       struct v4l2_ctrl                *saturation;
+       struct v4l2_ctrl                *hue;
+       struct {
+               /* autogain/gain cluster */
+               struct v4l2_ctrl        *autogain;
+               struct v4l2_ctrl        *gain;
+       };
+       struct v4l2_ctrl                *volume;
+       struct v4l2_ctrl                *mute;
+       struct v4l2_ctrl                *alpha;
+       struct v4l2_ctrl                *button;
+       struct v4l2_ctrl                *boolean;
+       struct v4l2_ctrl                *int32;
+       struct v4l2_ctrl                *int64;
+       struct v4l2_ctrl                *menu;
+       struct v4l2_ctrl                *string;
+       struct v4l2_ctrl                *bitmask;
+       struct v4l2_ctrl                *int_menu;
+       struct v4l2_ctrl                *test_pattern;
+       struct v4l2_ctrl                *colorspace;
+       struct v4l2_ctrl                *rgb_range_cap;
+       struct v4l2_ctrl                *real_rgb_range_cap;
+       struct {
+               /* std_signal_mode/standard cluster */
+               struct v4l2_ctrl        *ctrl_std_signal_mode;
+               struct v4l2_ctrl        *ctrl_standard;
+       };
+       struct {
+               /* dv_timings_signal_mode/timings cluster */
+               struct v4l2_ctrl        *ctrl_dv_timings_signal_mode;
+               struct v4l2_ctrl        *ctrl_dv_timings;
+       };
+       struct v4l2_ctrl                *ctrl_has_crop_cap;
+       struct v4l2_ctrl                *ctrl_has_compose_cap;
+       struct v4l2_ctrl                *ctrl_has_scaler_cap;
+       struct v4l2_ctrl                *ctrl_has_crop_out;
+       struct v4l2_ctrl                *ctrl_has_compose_out;
+       struct v4l2_ctrl                *ctrl_has_scaler_out;
+       struct v4l2_ctrl                *ctrl_tx_mode;
+       struct v4l2_ctrl                *ctrl_tx_rgb_range;
+
+       struct v4l2_ctrl                *radio_tx_rds_pi;
+       struct v4l2_ctrl                *radio_tx_rds_pty;
+       struct v4l2_ctrl                *radio_tx_rds_mono_stereo;
+       struct v4l2_ctrl                *radio_tx_rds_art_head;
+       struct v4l2_ctrl                *radio_tx_rds_compressed;
+       struct v4l2_ctrl                *radio_tx_rds_dyn_pty;
+       struct v4l2_ctrl                *radio_tx_rds_ta;
+       struct v4l2_ctrl                *radio_tx_rds_tp;
+       struct v4l2_ctrl                *radio_tx_rds_ms;
+       struct v4l2_ctrl                *radio_tx_rds_psname;
+       struct v4l2_ctrl                *radio_tx_rds_radiotext;
+
+       struct v4l2_ctrl                *radio_rx_rds_pty;
+       struct v4l2_ctrl                *radio_rx_rds_ta;
+       struct v4l2_ctrl                *radio_rx_rds_tp;
+       struct v4l2_ctrl                *radio_rx_rds_ms;
+       struct v4l2_ctrl                *radio_rx_rds_psname;
+       struct v4l2_ctrl                *radio_rx_rds_radiotext;
+
+       unsigned                        input_brightness[MAX_INPUTS];
+       unsigned                        osd_mode;
+       unsigned                        button_pressed;
+       bool                            sensor_hflip;
+       bool                            sensor_vflip;
+       bool                            hflip;
+       bool                            vflip;
+       bool                            vbi_cap_interlaced;
+       bool                            loop_video;
+
+       /* Framebuffer */
+       unsigned long                   video_pbase;
+       void                            *video_vbase;
+       u32                             video_buffer_size;
+       int                             display_width;
+       int                             display_height;
+       int                             display_byte_stride;
+       int                             bits_per_pixel;
+       int                             bytes_per_pixel;
+       struct fb_info                  fb_info;
+       struct fb_var_screeninfo        fb_defined;
+       struct fb_fix_screeninfo        fb_fix;
+
+       /* Error injection */
+       bool                            queue_setup_error;
+       bool                            buf_prepare_error;
+       bool                            start_streaming_error;
+       bool                            dqbuf_error;
+       bool                            seq_wrap;
+       bool                            time_wrap;
+       __kernel_time_t                 time_wrap_offset;
+       unsigned                        perc_dropped_buffers;
+       enum vivid_signal_mode          std_signal_mode;
+       unsigned                        query_std_last;
+       v4l2_std_id                     query_std;
+       enum tpg_video_aspect           std_aspect_ratio;
+
+       enum vivid_signal_mode          dv_timings_signal_mode;
+       char                            **query_dv_timings_qmenu;
+       unsigned                        query_dv_timings_size;
+       unsigned                        query_dv_timings_last;
+       unsigned                        query_dv_timings;
+       enum tpg_video_aspect           dv_timings_aspect_ratio;
+
+       /* Input */
+       unsigned                        input;
+       v4l2_std_id                     std_cap;
+       struct v4l2_dv_timings          dv_timings_cap;
+       u32                             service_set_cap;
+       struct vivid_vbi_gen_data       vbi_gen;
+       u8                              *edid;
+       unsigned                        edid_blocks;
+       unsigned                        edid_max_blocks;
+       unsigned                        webcam_size_idx;
+       unsigned                        webcam_ival_idx;
+       unsigned                        tv_freq;
+       unsigned                        tv_audmode;
+       unsigned                        tv_field_cap;
+       unsigned                        tv_audio_input;
+
+       /* Capture Overlay */
+       struct v4l2_framebuffer         fb_cap;
+       struct v4l2_fh                  *overlay_cap_owner;
+       void                            *fb_vbase_cap;
+       int                             overlay_cap_top, overlay_cap_left;
+       enum v4l2_field                 overlay_cap_field;
+       void                            *bitmap_cap;
+       struct v4l2_clip                clips_cap[MAX_CLIPS];
+       struct v4l2_clip                try_clips_cap[MAX_CLIPS];
+       unsigned                        clipcount_cap;
+
+       /* Output */
+       unsigned                        output;
+       v4l2_std_id                     std_out;
+       struct v4l2_dv_timings          dv_timings_out;
+       u32                             colorspace_out;
+       u32                             service_set_out;
+       u32                             bytesperline_out[2];
+       unsigned                        tv_field_out;
+       unsigned                        tv_audio_output;
+       bool                            vbi_out_have_wss;
+       u8                              vbi_out_wss[2];
+       bool                            vbi_out_have_cc[2];
+       u8                              vbi_out_cc[2][2];
+       bool                            dvi_d_out;
+       u8                              *scaled_line;
+       u8                              *blended_line;
+       unsigned                        cur_scaled_line;
+
+       /* Output Overlay */
+       void                            *fb_vbase_out;
+       bool                            overlay_out_enabled;
+       int                             overlay_out_top, overlay_out_left;
+       void                            *bitmap_out;
+       struct v4l2_clip                clips_out[MAX_CLIPS];
+       struct v4l2_clip                try_clips_out[MAX_CLIPS];
+       unsigned                        clipcount_out;
+       unsigned                        fbuf_out_flags;
+       u32                             chromakey_out;
+       u8                              global_alpha_out;
+
+       /* video capture */
+       struct tpg_data                 tpg;
+       unsigned                        ms_vid_cap;
+       bool                            must_blank[VIDEO_MAX_FRAME];
+
+       const struct vivid_fmt          *fmt_cap;
+       struct v4l2_fract               timeperframe_vid_cap;
+       enum v4l2_field                 field_cap;
+       struct v4l2_rect                src_rect;
+       struct v4l2_rect                fmt_cap_rect;
+       struct v4l2_rect                crop_cap;
+       struct v4l2_rect                compose_cap;
+       struct v4l2_rect                crop_bounds_cap;
+       struct vb2_queue                vb_vid_cap_q;
+       struct list_head                vid_cap_active;
+       struct vb2_queue                vb_vbi_cap_q;
+       struct list_head                vbi_cap_active;
+
+       /* thread for generating video capture stream */
+       struct task_struct              *kthread_vid_cap;
+       unsigned long                   jiffies_vid_cap;
+       u32                             cap_seq_offset;
+       u32                             cap_seq_count;
+       bool                            cap_seq_resync;
+       u32                             vid_cap_seq_start;
+       u32                             vid_cap_seq_count;
+       bool                            vid_cap_streaming;
+       u32                             vbi_cap_seq_start;
+       u32                             vbi_cap_seq_count;
+       bool                            vbi_cap_streaming;
+       bool                            stream_sliced_vbi_cap;
+
+       /* video output */
+       const struct vivid_fmt          *fmt_out;
+       struct v4l2_fract               timeperframe_vid_out;
+       enum v4l2_field                 field_out;
+       struct v4l2_rect                sink_rect;
+       struct v4l2_rect                fmt_out_rect;
+       struct v4l2_rect                crop_out;
+       struct v4l2_rect                compose_out;
+       struct v4l2_rect                compose_bounds_out;
+       struct vb2_queue                vb_vid_out_q;
+       struct list_head                vid_out_active;
+       struct vb2_queue                vb_vbi_out_q;
+       struct list_head                vbi_out_active;
+
+       /* video loop precalculated rectangles */
+
+       /*
+        * Intersection between what the output side composes and the capture side
+        * crops. I.e., what actually needs to be copied from the output buffer to
+        * the capture buffer.
+        */
+       struct v4l2_rect                loop_vid_copy;
+       /* The part of the output buffer that (after scaling) corresponds to loop_vid_copy. */
+       struct v4l2_rect                loop_vid_out;
+       /* The part of the capture buffer that (after scaling) corresponds to loop_vid_copy. */
+       struct v4l2_rect                loop_vid_cap;
+       /*
+        * The intersection of the framebuffer, the overlay output window and
+        * loop_vid_copy. I.e., the part of the framebuffer that actually should be
+        * blended with the compose_out rectangle. This uses the framebuffer origin.
+        */
+       struct v4l2_rect                loop_fb_copy;
+       /* The same as loop_fb_copy but with compose_out origin. */
+       struct v4l2_rect                loop_vid_overlay;
+       /*
+        * The part of the capture buffer that (after scaling) corresponds
+        * to loop_vid_overlay.
+        */
+       struct v4l2_rect                loop_vid_overlay_cap;
+
+       /* thread for generating video output stream */
+       struct task_struct              *kthread_vid_out;
+       unsigned long                   jiffies_vid_out;
+       u32                             out_seq_offset;
+       u32                             out_seq_count;
+       bool                            out_seq_resync;
+       u32                             vid_out_seq_start;
+       u32                             vid_out_seq_count;
+       bool                            vid_out_streaming;
+       u32                             vbi_out_seq_start;
+       u32                             vbi_out_seq_count;
+       bool                            vbi_out_streaming;
+       bool                            stream_sliced_vbi_out;
+
+       /* SDR capture */
+       struct vb2_queue                vb_sdr_cap_q;
+       struct list_head                sdr_cap_active;
+       unsigned                        sdr_adc_freq;
+       unsigned                        sdr_fm_freq;
+       int                             sdr_fixp_src_phase;
+       int                             sdr_fixp_mod_phase;
+
+       bool                            tstamp_src_is_soe;
+       bool                            has_crop_cap;
+       bool                            has_compose_cap;
+       bool                            has_scaler_cap;
+       bool                            has_crop_out;
+       bool                            has_compose_out;
+       bool                            has_scaler_out;
+
+       /* thread for generating SDR stream */
+       struct task_struct              *kthread_sdr_cap;
+       unsigned long                   jiffies_sdr_cap;
+       u32                             sdr_cap_seq_offset;
+       u32                             sdr_cap_seq_count;
+       bool                            sdr_cap_seq_resync;
+
+       /* RDS generator */
+       struct vivid_rds_gen            rds_gen;
+
+       /* Radio receiver */
+       unsigned                        radio_rx_freq;
+       unsigned                        radio_rx_audmode;
+       int                             radio_rx_sig_qual;
+       unsigned                        radio_rx_hw_seek_mode;
+       bool                            radio_rx_hw_seek_prog_lim;
+       bool                            radio_rx_rds_controls;
+       bool                            radio_rx_rds_enabled;
+       unsigned                        radio_rx_rds_use_alternates;
+       unsigned                        radio_rx_rds_last_block;
+       struct v4l2_fh                  *radio_rx_rds_owner;
+
+       /* Radio transmitter */
+       unsigned                        radio_tx_freq;
+       unsigned                        radio_tx_subchans;
+       bool                            radio_tx_rds_controls;
+       unsigned                        radio_tx_rds_last_block;
+       struct v4l2_fh                  *radio_tx_rds_owner;
+
+       /* Shared between radio receiver and transmitter */
+       bool                            radio_rds_loop;
+       struct timespec                 radio_rds_init_ts;
+};
+
+static inline bool vivid_is_webcam(const struct vivid_dev *dev)
+{
+       return dev->input_type[dev->input] == WEBCAM;
+}
+
+static inline bool vivid_is_tv_cap(const struct vivid_dev *dev)
+{
+       return dev->input_type[dev->input] == TV;
+}
+
+static inline bool vivid_is_svid_cap(const struct vivid_dev *dev)
+{
+       return dev->input_type[dev->input] == SVID;
+}
+
+static inline bool vivid_is_hdmi_cap(const struct vivid_dev *dev)
+{
+       return dev->input_type[dev->input] == HDMI;
+}
+
+static inline bool vivid_is_sdtv_cap(const struct vivid_dev *dev)
+{
+       return vivid_is_tv_cap(dev) || vivid_is_svid_cap(dev);
+}
+
+static inline bool vivid_is_svid_out(const struct vivid_dev *dev)
+{
+       return dev->output_type[dev->output] == SVID;
+}
+
+static inline bool vivid_is_hdmi_out(const struct vivid_dev *dev)
+{
+       return dev->output_type[dev->output] == HDMI;
+}
+
+void vivid_lock(struct vb2_queue *vq);
+void vivid_unlock(struct vb2_queue *vq);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
new file mode 100644 (file)
index 0000000..d5cbf00
--- /dev/null
@@ -0,0 +1,1502 @@
+/*
+ * vivid-ctrls.c - control support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-cap.h"
+#include "vivid-vid-out.h"
+#include "vivid-vid-common.h"
+#include "vivid-radio-common.h"
+#include "vivid-osd.h"
+#include "vivid-ctrls.h"
+
+#define VIVID_CID_CUSTOM_BASE          (V4L2_CID_USER_BASE | 0xf000)
+#define VIVID_CID_BUTTON               (VIVID_CID_CUSTOM_BASE + 0)
+#define VIVID_CID_BOOLEAN              (VIVID_CID_CUSTOM_BASE + 1)
+#define VIVID_CID_INTEGER              (VIVID_CID_CUSTOM_BASE + 2)
+#define VIVID_CID_INTEGER64            (VIVID_CID_CUSTOM_BASE + 3)
+#define VIVID_CID_MENU                 (VIVID_CID_CUSTOM_BASE + 4)
+#define VIVID_CID_STRING               (VIVID_CID_CUSTOM_BASE + 5)
+#define VIVID_CID_BITMASK              (VIVID_CID_CUSTOM_BASE + 6)
+#define VIVID_CID_INTMENU              (VIVID_CID_CUSTOM_BASE + 7)
+
+#define VIVID_CID_VIVID_BASE           (0x00f00000 | 0xf000)
+#define VIVID_CID_VIVID_CLASS          (0x00f00000 | 1)
+#define VIVID_CID_TEST_PATTERN         (VIVID_CID_VIVID_BASE + 0)
+#define VIVID_CID_OSD_TEXT_MODE                (VIVID_CID_VIVID_BASE + 1)
+#define VIVID_CID_HOR_MOVEMENT         (VIVID_CID_VIVID_BASE + 2)
+#define VIVID_CID_VERT_MOVEMENT                (VIVID_CID_VIVID_BASE + 3)
+#define VIVID_CID_SHOW_BORDER          (VIVID_CID_VIVID_BASE + 4)
+#define VIVID_CID_SHOW_SQUARE          (VIVID_CID_VIVID_BASE + 5)
+#define VIVID_CID_INSERT_SAV           (VIVID_CID_VIVID_BASE + 6)
+#define VIVID_CID_INSERT_EAV           (VIVID_CID_VIVID_BASE + 7)
+#define VIVID_CID_VBI_CAP_INTERLACED   (VIVID_CID_VIVID_BASE + 8)
+
+#define VIVID_CID_HFLIP                        (VIVID_CID_VIVID_BASE + 20)
+#define VIVID_CID_VFLIP                        (VIVID_CID_VIVID_BASE + 21)
+#define VIVID_CID_STD_ASPECT_RATIO     (VIVID_CID_VIVID_BASE + 22)
+#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 23)
+#define VIVID_CID_TSTAMP_SRC           (VIVID_CID_VIVID_BASE + 24)
+#define VIVID_CID_COLORSPACE           (VIVID_CID_VIVID_BASE + 25)
+#define VIVID_CID_LIMITED_RGB_RANGE    (VIVID_CID_VIVID_BASE + 26)
+#define VIVID_CID_ALPHA_MODE           (VIVID_CID_VIVID_BASE + 27)
+#define VIVID_CID_HAS_CROP_CAP         (VIVID_CID_VIVID_BASE + 28)
+#define VIVID_CID_HAS_COMPOSE_CAP      (VIVID_CID_VIVID_BASE + 29)
+#define VIVID_CID_HAS_SCALER_CAP       (VIVID_CID_VIVID_BASE + 30)
+#define VIVID_CID_HAS_CROP_OUT         (VIVID_CID_VIVID_BASE + 31)
+#define VIVID_CID_HAS_COMPOSE_OUT      (VIVID_CID_VIVID_BASE + 32)
+#define VIVID_CID_HAS_SCALER_OUT       (VIVID_CID_VIVID_BASE + 33)
+#define VIVID_CID_LOOP_VIDEO           (VIVID_CID_VIVID_BASE + 34)
+#define VIVID_CID_SEQ_WRAP             (VIVID_CID_VIVID_BASE + 35)
+#define VIVID_CID_TIME_WRAP            (VIVID_CID_VIVID_BASE + 36)
+#define VIVID_CID_MAX_EDID_BLOCKS      (VIVID_CID_VIVID_BASE + 37)
+#define VIVID_CID_PERCENTAGE_FILL      (VIVID_CID_VIVID_BASE + 38)
+
+#define VIVID_CID_STD_SIGNAL_MODE      (VIVID_CID_VIVID_BASE + 60)
+#define VIVID_CID_STANDARD             (VIVID_CID_VIVID_BASE + 61)
+#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 62)
+#define VIVID_CID_DV_TIMINGS           (VIVID_CID_VIVID_BASE + 63)
+#define VIVID_CID_PERC_DROPPED         (VIVID_CID_VIVID_BASE + 64)
+#define VIVID_CID_DISCONNECT           (VIVID_CID_VIVID_BASE + 65)
+#define VIVID_CID_DQBUF_ERROR          (VIVID_CID_VIVID_BASE + 66)
+#define VIVID_CID_QUEUE_SETUP_ERROR    (VIVID_CID_VIVID_BASE + 67)
+#define VIVID_CID_BUF_PREPARE_ERROR    (VIVID_CID_VIVID_BASE + 68)
+#define VIVID_CID_START_STR_ERROR      (VIVID_CID_VIVID_BASE + 69)
+#define VIVID_CID_QUEUE_ERROR          (VIVID_CID_VIVID_BASE + 70)
+#define VIVID_CID_CLEAR_FB             (VIVID_CID_VIVID_BASE + 71)
+
+#define VIVID_CID_RADIO_SEEK_MODE      (VIVID_CID_VIVID_BASE + 90)
+#define VIVID_CID_RADIO_SEEK_PROG_LIM  (VIVID_CID_VIVID_BASE + 91)
+#define VIVID_CID_RADIO_RX_RDS_RBDS    (VIVID_CID_VIVID_BASE + 92)
+#define VIVID_CID_RADIO_RX_RDS_BLOCKIO (VIVID_CID_VIVID_BASE + 93)
+
+#define VIVID_CID_RADIO_TX_RDS_BLOCKIO (VIVID_CID_VIVID_BASE + 94)
+
+
+/* General User Controls */
+
+static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_gen);
+
+       switch (ctrl->id) {
+       case VIVID_CID_DISCONNECT:
+               v4l2_info(&dev->v4l2_dev, "disconnect\n");
+               clear_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
+               clear_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
+               clear_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
+               clear_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
+               clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
+               clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
+               clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
+               break;
+       case VIVID_CID_CLEAR_FB:
+               vivid_clear_fb(dev);
+               break;
+       case VIVID_CID_BUTTON:
+               dev->button_pressed = 30;
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_user_gen_ctrl_ops = {
+       .s_ctrl = vivid_user_gen_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_button = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_BUTTON,
+       .name = "Button",
+       .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_boolean = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_BOOLEAN,
+       .name = "Boolean",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .min = 0,
+       .max = 1,
+       .step = 1,
+       .def = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_int32 = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_INTEGER,
+       .name = "Integer 32 Bits",
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .min = 0xffffffff80000000ULL,
+       .max = 0x7fffffff,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_int64 = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_INTEGER64,
+       .name = "Integer 64 Bits",
+       .type = V4L2_CTRL_TYPE_INTEGER64,
+       .min = 0x8000000000000000ULL,
+       .max = 0x7fffffffffffffffLL,
+       .step = 1,
+};
+
+static const char * const vivid_ctrl_menu_strings[] = {
+       "Menu Item 0 (Skipped)",
+       "Menu Item 1",
+       "Menu Item 2 (Skipped)",
+       "Menu Item 3",
+       "Menu Item 4",
+       "Menu Item 5 (Skipped)",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_menu = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_MENU,
+       .name = "Menu",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .min = 1,
+       .max = 4,
+       .def = 3,
+       .menu_skip_mask = 0x04,
+       .qmenu = vivid_ctrl_menu_strings,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_string = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_STRING,
+       .name = "String",
+       .type = V4L2_CTRL_TYPE_STRING,
+       .min = 2,
+       .max = 4,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_bitmask = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_BITMASK,
+       .name = "Bitmask",
+       .type = V4L2_CTRL_TYPE_BITMASK,
+       .def = 0x80002000,
+       .min = 0,
+       .max = 0x80402010,
+       .step = 0,
+};
+
+static const s64 vivid_ctrl_int_menu_values[] = {
+       1, 1, 2, 3, 5, 8, 13, 21, 42,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_int_menu = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_INTMENU,
+       .name = "Integer Menu",
+       .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+       .min = 1,
+       .max = 8,
+       .def = 4,
+       .menu_skip_mask = 0x02,
+       .qmenu_int = vivid_ctrl_int_menu_values,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_disconnect = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_DISCONNECT,
+       .name = "Disconnect",
+       .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_clear_fb = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .id = VIVID_CID_CLEAR_FB,
+       .name = "Clear Framebuffer",
+       .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+
+/* Video User Controls */
+
+static int vivid_user_vid_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_vid);
+
+       switch (ctrl->id) {
+       case V4L2_CID_AUTOGAIN:
+               dev->gain->val = dev->jiffies_vid_cap & 0xff;
+               break;
+       }
+       return 0;
+}
+
+static int vivid_user_vid_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_vid);
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               dev->input_brightness[dev->input] = ctrl->val - dev->input * 128;
+               tpg_s_brightness(&dev->tpg, dev->input_brightness[dev->input]);
+               break;
+       case V4L2_CID_CONTRAST:
+               tpg_s_contrast(&dev->tpg, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               tpg_s_saturation(&dev->tpg, ctrl->val);
+               break;
+       case V4L2_CID_HUE:
+               tpg_s_hue(&dev->tpg, ctrl->val);
+               break;
+       case V4L2_CID_HFLIP:
+               dev->hflip = ctrl->val;
+               tpg_s_hflip(&dev->tpg, dev->sensor_hflip ^ dev->hflip);
+               break;
+       case V4L2_CID_VFLIP:
+               dev->vflip = ctrl->val;
+               tpg_s_vflip(&dev->tpg, dev->sensor_vflip ^ dev->vflip);
+               break;
+       case V4L2_CID_ALPHA_COMPONENT:
+               tpg_s_alpha_component(&dev->tpg, ctrl->val);
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_user_vid_ctrl_ops = {
+       .g_volatile_ctrl = vivid_user_vid_g_volatile_ctrl,
+       .s_ctrl = vivid_user_vid_s_ctrl,
+};
+
+
+/* Video Capture Controls */
+
+static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap);
+       unsigned i;
+
+       switch (ctrl->id) {
+       case VIVID_CID_TEST_PATTERN:
+               vivid_update_quality(dev);
+               tpg_s_pattern(&dev->tpg, ctrl->val);
+               break;
+       case VIVID_CID_COLORSPACE:
+               tpg_s_colorspace(&dev->tpg, ctrl->val);
+               vivid_send_source_change(dev, TV);
+               vivid_send_source_change(dev, SVID);
+               vivid_send_source_change(dev, HDMI);
+               vivid_send_source_change(dev, WEBCAM);
+               break;
+       case V4L2_CID_DV_RX_RGB_RANGE:
+               if (!vivid_is_hdmi_cap(dev))
+                       break;
+               tpg_s_rgb_range(&dev->tpg, ctrl->val);
+               break;
+       case VIVID_CID_LIMITED_RGB_RANGE:
+               tpg_s_real_rgb_range(&dev->tpg, ctrl->val ?
+                               V4L2_DV_RGB_RANGE_LIMITED : V4L2_DV_RGB_RANGE_FULL);
+               break;
+       case VIVID_CID_ALPHA_MODE:
+               tpg_s_alpha_mode(&dev->tpg, ctrl->val);
+               break;
+       case VIVID_CID_HOR_MOVEMENT:
+               tpg_s_mv_hor_mode(&dev->tpg, ctrl->val);
+               break;
+       case VIVID_CID_VERT_MOVEMENT:
+               tpg_s_mv_vert_mode(&dev->tpg, ctrl->val);
+               break;
+       case VIVID_CID_OSD_TEXT_MODE:
+               dev->osd_mode = ctrl->val;
+               break;
+       case VIVID_CID_PERCENTAGE_FILL:
+               tpg_s_perc_fill(&dev->tpg, ctrl->val);
+               for (i = 0; i < VIDEO_MAX_FRAME; i++)
+                       dev->must_blank[i] = ctrl->val < 100;
+               break;
+       case VIVID_CID_INSERT_SAV:
+               tpg_s_insert_sav(&dev->tpg, ctrl->val);
+               break;
+       case VIVID_CID_INSERT_EAV:
+               tpg_s_insert_eav(&dev->tpg, ctrl->val);
+               break;
+       case VIVID_CID_HFLIP:
+               dev->sensor_hflip = ctrl->val;
+               tpg_s_hflip(&dev->tpg, dev->sensor_hflip ^ dev->hflip);
+               break;
+       case VIVID_CID_VFLIP:
+               dev->sensor_vflip = ctrl->val;
+               tpg_s_vflip(&dev->tpg, dev->sensor_vflip ^ dev->vflip);
+               break;
+       case VIVID_CID_HAS_CROP_CAP:
+               dev->has_crop_cap = ctrl->val;
+               vivid_update_format_cap(dev, true);
+               break;
+       case VIVID_CID_HAS_COMPOSE_CAP:
+               dev->has_compose_cap = ctrl->val;
+               vivid_update_format_cap(dev, true);
+               break;
+       case VIVID_CID_HAS_SCALER_CAP:
+               dev->has_scaler_cap = ctrl->val;
+               vivid_update_format_cap(dev, true);
+               break;
+       case VIVID_CID_SHOW_BORDER:
+               tpg_s_show_border(&dev->tpg, ctrl->val);
+               break;
+       case VIVID_CID_SHOW_SQUARE:
+               tpg_s_show_square(&dev->tpg, ctrl->val);
+               break;
+       case VIVID_CID_STD_ASPECT_RATIO:
+               dev->std_aspect_ratio = ctrl->val;
+               tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev));
+               break;
+       case VIVID_CID_DV_TIMINGS_SIGNAL_MODE:
+               dev->dv_timings_signal_mode = dev->ctrl_dv_timings_signal_mode->val;
+               if (dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS)
+                       dev->query_dv_timings = dev->ctrl_dv_timings->val;
+               v4l2_ctrl_activate(dev->ctrl_dv_timings,
+                               dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS);
+               vivid_update_quality(dev);
+               vivid_send_source_change(dev, HDMI);
+               break;
+       case VIVID_CID_DV_TIMINGS_ASPECT_RATIO:
+               dev->dv_timings_aspect_ratio = ctrl->val;
+               tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev));
+               break;
+       case VIVID_CID_TSTAMP_SRC:
+               dev->tstamp_src_is_soe = ctrl->val;
+               dev->vb_vid_cap_q.timestamp_flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+               if (dev->tstamp_src_is_soe)
+                       dev->vb_vid_cap_q.timestamp_flags |= V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
+               break;
+       case VIVID_CID_MAX_EDID_BLOCKS:
+               dev->edid_max_blocks = ctrl->val;
+               if (dev->edid_blocks > dev->edid_max_blocks)
+                       dev->edid_blocks = dev->edid_max_blocks;
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_vid_cap_ctrl_ops = {
+       .s_ctrl = vivid_vid_cap_s_ctrl,
+};
+
+static const char * const vivid_ctrl_hor_movement_strings[] = {
+       "Move Left Fast",
+       "Move Left",
+       "Move Left Slow",
+       "No Movement",
+       "Move Right Slow",
+       "Move Right",
+       "Move Right Fast",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_hor_movement = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_HOR_MOVEMENT,
+       .name = "Horizontal Movement",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .max = TPG_MOVE_POS_FAST,
+       .def = TPG_MOVE_NONE,
+       .qmenu = vivid_ctrl_hor_movement_strings,
+};
+
+static const char * const vivid_ctrl_vert_movement_strings[] = {
+       "Move Up Fast",
+       "Move Up",
+       "Move Up Slow",
+       "No Movement",
+       "Move Down Slow",
+       "Move Down",
+       "Move Down Fast",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_vert_movement = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_VERT_MOVEMENT,
+       .name = "Vertical Movement",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .max = TPG_MOVE_POS_FAST,
+       .def = TPG_MOVE_NONE,
+       .qmenu = vivid_ctrl_vert_movement_strings,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_show_border = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_SHOW_BORDER,
+       .name = "Show Border",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_show_square = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_SHOW_SQUARE,
+       .name = "Show Square",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+static const char * const vivid_ctrl_osd_mode_strings[] = {
+       "All",
+       "Counters Only",
+       "None",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_osd_mode = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_OSD_TEXT_MODE,
+       .name = "OSD Text Mode",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .max = 2,
+       .qmenu = vivid_ctrl_osd_mode_strings,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_perc_fill = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_PERCENTAGE_FILL,
+       .name = "Fill Percentage of Frame",
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .min = 0,
+       .max = 100,
+       .def = 100,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_insert_sav = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_INSERT_SAV,
+       .name = "Insert SAV Code in Image",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_insert_eav = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_INSERT_EAV,
+       .name = "Insert EAV Code in Image",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_hflip = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_HFLIP,
+       .name = "Sensor Flipped Horizontally",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_vflip = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_VFLIP,
+       .name = "Sensor Flipped Vertically",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_has_crop_cap = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_HAS_CROP_CAP,
+       .name = "Enable Capture Cropping",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .def = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_has_compose_cap = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_HAS_COMPOSE_CAP,
+       .name = "Enable Capture Composing",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .def = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_cap = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_HAS_SCALER_CAP,
+       .name = "Enable Capture Scaler",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .def = 1,
+       .step = 1,
+};
+
+static const char * const vivid_ctrl_tstamp_src_strings[] = {
+       "End of Frame",
+       "Start of Exposure",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_tstamp_src = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_TSTAMP_SRC,
+       .name = "Timestamp Source",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .max = 1,
+       .qmenu = vivid_ctrl_tstamp_src_strings,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_std_aspect_ratio = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_STD_ASPECT_RATIO,
+       .name = "Standard Aspect Ratio",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .min = 1,
+       .max = 4,
+       .def = 1,
+       .qmenu = tpg_aspect_strings,
+};
+
+static const char * const vivid_ctrl_dv_timings_signal_mode_strings[] = {
+       "Current DV Timings",
+       "No Signal",
+       "No Lock",
+       "Out of Range",
+       "Selected DV Timings",
+       "Cycle Through All DV Timings",
+       "Custom DV Timings",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_dv_timings_signal_mode = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_DV_TIMINGS_SIGNAL_MODE,
+       .name = "DV Timings Signal Mode",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .max = 5,
+       .qmenu = vivid_ctrl_dv_timings_signal_mode_strings,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_dv_timings_aspect_ratio = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_DV_TIMINGS_ASPECT_RATIO,
+       .name = "DV Timings Aspect Ratio",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .max = 3,
+       .qmenu = tpg_aspect_strings,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_max_edid_blocks = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_MAX_EDID_BLOCKS,
+       .name = "Maximum EDID Blocks",
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .min = 1,
+       .max = 256,
+       .def = 2,
+       .step = 1,
+};
+
+static const char * const vivid_ctrl_colorspace_strings[] = {
+       "",
+       "SMPTE 170M",
+       "SMPTE 240M",
+       "REC 709",
+       "", /* Skip Bt878 entry */
+       "470 System M",
+       "470 System BG",
+       "", /* Skip JPEG entry */
+       "sRGB",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_colorspace = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_COLORSPACE,
+       .name = "Colorspace",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .min = 1,
+       .max = 8,
+       .menu_skip_mask = (1 << 4) | (1 << 7),
+       .def = 8,
+       .qmenu = vivid_ctrl_colorspace_strings,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_alpha_mode = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_ALPHA_MODE,
+       .name = "Apply Alpha To Red Only",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_limited_rgb_range = {
+       .ops = &vivid_vid_cap_ctrl_ops,
+       .id = VIVID_CID_LIMITED_RGB_RANGE,
+       .name = "Limited RGB Range (16-235)",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+
+/* VBI Capture Control */
+
+static int vivid_vbi_cap_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vbi_cap);
+
+       switch (ctrl->id) {
+       case VIVID_CID_VBI_CAP_INTERLACED:
+               dev->vbi_cap_interlaced = ctrl->val;
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_vbi_cap_ctrl_ops = {
+       .s_ctrl = vivid_vbi_cap_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_vbi_cap_interlaced = {
+       .ops = &vivid_vbi_cap_ctrl_ops,
+       .id = VIVID_CID_VBI_CAP_INTERLACED,
+       .name = "Interlaced VBI Format",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+
+/* Video Output Controls */
+
+static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_out);
+       struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
+
+       switch (ctrl->id) {
+       case VIVID_CID_HAS_CROP_OUT:
+               dev->has_crop_out = ctrl->val;
+               vivid_update_format_out(dev);
+               break;
+       case VIVID_CID_HAS_COMPOSE_OUT:
+               dev->has_compose_out = ctrl->val;
+               vivid_update_format_out(dev);
+               break;
+       case VIVID_CID_HAS_SCALER_OUT:
+               dev->has_scaler_out = ctrl->val;
+               vivid_update_format_out(dev);
+               break;
+       case V4L2_CID_DV_TX_MODE:
+               dev->dvi_d_out = ctrl->val == V4L2_DV_TX_MODE_DVI_D;
+               if (!vivid_is_hdmi_out(dev))
+                       break;
+               if (!dev->dvi_d_out && (bt->standards & V4L2_DV_BT_STD_CEA861)) {
+                       if (bt->width == 720 && bt->height <= 576)
+                               dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;
+                       else
+                               dev->colorspace_out = V4L2_COLORSPACE_REC709;
+               } else {
+                       dev->colorspace_out = V4L2_COLORSPACE_SRGB;
+               }
+               if (dev->loop_video)
+                       vivid_send_source_change(dev, HDMI);
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_vid_out_ctrl_ops = {
+       .s_ctrl = vivid_vid_out_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_has_crop_out = {
+       .ops = &vivid_vid_out_ctrl_ops,
+       .id = VIVID_CID_HAS_CROP_OUT,
+       .name = "Enable Output Cropping",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .def = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_has_compose_out = {
+       .ops = &vivid_vid_out_ctrl_ops,
+       .id = VIVID_CID_HAS_COMPOSE_OUT,
+       .name = "Enable Output Composing",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .def = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = {
+       .ops = &vivid_vid_out_ctrl_ops,
+       .id = VIVID_CID_HAS_SCALER_OUT,
+       .name = "Enable Output Scaler",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .def = 1,
+       .step = 1,
+};
+
+
+/* Streaming Controls */
+
+static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_streaming);
+       struct timeval tv;
+
+       switch (ctrl->id) {
+       case VIVID_CID_DQBUF_ERROR:
+               dev->dqbuf_error = true;
+               break;
+       case VIVID_CID_PERC_DROPPED:
+               dev->perc_dropped_buffers = ctrl->val;
+               break;
+       case VIVID_CID_QUEUE_SETUP_ERROR:
+               dev->queue_setup_error = true;
+               break;
+       case VIVID_CID_BUF_PREPARE_ERROR:
+               dev->buf_prepare_error = true;
+               break;
+       case VIVID_CID_START_STR_ERROR:
+               dev->start_streaming_error = true;
+               break;
+       case VIVID_CID_QUEUE_ERROR:
+               if (dev->vb_vid_cap_q.start_streaming_called)
+                       vb2_queue_error(&dev->vb_vid_cap_q);
+               if (dev->vb_vbi_cap_q.start_streaming_called)
+                       vb2_queue_error(&dev->vb_vbi_cap_q);
+               if (dev->vb_vid_out_q.start_streaming_called)
+                       vb2_queue_error(&dev->vb_vid_out_q);
+               if (dev->vb_vbi_out_q.start_streaming_called)
+                       vb2_queue_error(&dev->vb_vbi_out_q);
+               if (dev->vb_sdr_cap_q.start_streaming_called)
+                       vb2_queue_error(&dev->vb_sdr_cap_q);
+               break;
+       case VIVID_CID_SEQ_WRAP:
+               dev->seq_wrap = ctrl->val;
+               break;
+       case VIVID_CID_TIME_WRAP:
+               dev->time_wrap = ctrl->val;
+               if (ctrl->val == 0) {
+                       dev->time_wrap_offset = 0;
+                       break;
+               }
+               v4l2_get_timestamp(&tv);
+               dev->time_wrap_offset = -tv.tv_sec - 16;
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_streaming_ctrl_ops = {
+       .s_ctrl = vivid_streaming_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_dqbuf_error = {
+       .ops = &vivid_streaming_ctrl_ops,
+       .id = VIVID_CID_DQBUF_ERROR,
+       .name = "Inject V4L2_BUF_FLAG_ERROR",
+       .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_perc_dropped = {
+       .ops = &vivid_streaming_ctrl_ops,
+       .id = VIVID_CID_PERC_DROPPED,
+       .name = "Percentage of Dropped Buffers",
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .min = 0,
+       .max = 100,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_queue_setup_error = {
+       .ops = &vivid_streaming_ctrl_ops,
+       .id = VIVID_CID_QUEUE_SETUP_ERROR,
+       .name = "Inject VIDIOC_REQBUFS Error",
+       .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_buf_prepare_error = {
+       .ops = &vivid_streaming_ctrl_ops,
+       .id = VIVID_CID_BUF_PREPARE_ERROR,
+       .name = "Inject VIDIOC_QBUF Error",
+       .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_start_streaming_error = {
+       .ops = &vivid_streaming_ctrl_ops,
+       .id = VIVID_CID_START_STR_ERROR,
+       .name = "Inject VIDIOC_STREAMON Error",
+       .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_queue_error = {
+       .ops = &vivid_streaming_ctrl_ops,
+       .id = VIVID_CID_QUEUE_ERROR,
+       .name = "Inject Fatal Streaming Error",
+       .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_seq_wrap = {
+       .ops = &vivid_streaming_ctrl_ops,
+       .id = VIVID_CID_SEQ_WRAP,
+       .name = "Wrap Sequence Number",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_time_wrap = {
+       .ops = &vivid_streaming_ctrl_ops,
+       .id = VIVID_CID_TIME_WRAP,
+       .name = "Wrap Timestamp",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+
+/* SDTV Capture Controls */
+
+static int vivid_sdtv_cap_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_sdtv_cap);
+
+       switch (ctrl->id) {
+       case VIVID_CID_STD_SIGNAL_MODE:
+               dev->std_signal_mode = dev->ctrl_std_signal_mode->val;
+               if (dev->std_signal_mode == SELECTED_STD)
+                       dev->query_std = vivid_standard[dev->ctrl_standard->val];
+               v4l2_ctrl_activate(dev->ctrl_standard, dev->std_signal_mode == SELECTED_STD);
+               vivid_update_quality(dev);
+               vivid_send_source_change(dev, TV);
+               vivid_send_source_change(dev, SVID);
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_sdtv_cap_ctrl_ops = {
+       .s_ctrl = vivid_sdtv_cap_s_ctrl,
+};
+
+static const char * const vivid_ctrl_std_signal_mode_strings[] = {
+       "Current Standard",
+       "No Signal",
+       "No Lock",
+       "",
+       "Selected Standard",
+       "Cycle Through All Standards",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_std_signal_mode = {
+       .ops = &vivid_sdtv_cap_ctrl_ops,
+       .id = VIVID_CID_STD_SIGNAL_MODE,
+       .name = "Standard Signal Mode",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .max = 5,
+       .menu_skip_mask = 1 << 3,
+       .qmenu = vivid_ctrl_std_signal_mode_strings,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_standard = {
+       .ops = &vivid_sdtv_cap_ctrl_ops,
+       .id = VIVID_CID_STANDARD,
+       .name = "Standard",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .max = 14,
+       .qmenu = vivid_ctrl_standard_strings,
+};
+
+
+
+/* Radio Receiver Controls */
+
+static int vivid_radio_rx_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_radio_rx);
+
+       switch (ctrl->id) {
+       case VIVID_CID_RADIO_SEEK_MODE:
+               dev->radio_rx_hw_seek_mode = ctrl->val;
+               break;
+       case VIVID_CID_RADIO_SEEK_PROG_LIM:
+               dev->radio_rx_hw_seek_prog_lim = ctrl->val;
+               break;
+       case VIVID_CID_RADIO_RX_RDS_RBDS:
+               dev->rds_gen.use_rbds = ctrl->val;
+               break;
+       case VIVID_CID_RADIO_RX_RDS_BLOCKIO:
+               dev->radio_rx_rds_controls = ctrl->val;
+               dev->radio_rx_caps &= ~V4L2_CAP_READWRITE;
+               dev->radio_rx_rds_use_alternates = false;
+               if (!dev->radio_rx_rds_controls) {
+                       dev->radio_rx_caps |= V4L2_CAP_READWRITE;
+                       __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, 0);
+                       __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, 0);
+                       __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, 0);
+                       __v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, 0);
+                       __v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, "");
+                       __v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, "");
+               }
+               v4l2_ctrl_activate(dev->radio_rx_rds_pty, dev->radio_rx_rds_controls);
+               v4l2_ctrl_activate(dev->radio_rx_rds_psname, dev->radio_rx_rds_controls);
+               v4l2_ctrl_activate(dev->radio_rx_rds_radiotext, dev->radio_rx_rds_controls);
+               v4l2_ctrl_activate(dev->radio_rx_rds_ta, dev->radio_rx_rds_controls);
+               v4l2_ctrl_activate(dev->radio_rx_rds_tp, dev->radio_rx_rds_controls);
+               v4l2_ctrl_activate(dev->radio_rx_rds_ms, dev->radio_rx_rds_controls);
+               break;
+       case V4L2_CID_RDS_RECEPTION:
+               dev->radio_rx_rds_enabled = ctrl->val;
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_radio_rx_ctrl_ops = {
+       .s_ctrl = vivid_radio_rx_s_ctrl,
+};
+
+static const char * const vivid_ctrl_radio_rds_mode_strings[] = {
+       "Block I/O",
+       "Controls",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_radio_rx_rds_blockio = {
+       .ops = &vivid_radio_rx_ctrl_ops,
+       .id = VIVID_CID_RADIO_RX_RDS_BLOCKIO,
+       .name = "RDS Rx I/O Mode",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .qmenu = vivid_ctrl_radio_rds_mode_strings,
+       .max = 1,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_radio_rx_rds_rbds = {
+       .ops = &vivid_radio_rx_ctrl_ops,
+       .id = VIVID_CID_RADIO_RX_RDS_RBDS,
+       .name = "Generate RBDS Instead of RDS",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+static const char * const vivid_ctrl_radio_hw_seek_mode_strings[] = {
+       "Bounded",
+       "Wrap Around",
+       "Both",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_radio_hw_seek_mode = {
+       .ops = &vivid_radio_rx_ctrl_ops,
+       .id = VIVID_CID_RADIO_SEEK_MODE,
+       .name = "Radio HW Seek Mode",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .max = 2,
+       .qmenu = vivid_ctrl_radio_hw_seek_mode_strings,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_radio_hw_seek_prog_lim = {
+       .ops = &vivid_radio_rx_ctrl_ops,
+       .id = VIVID_CID_RADIO_SEEK_PROG_LIM,
+       .name = "Radio Programmable HW Seek",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+
+/* Radio Transmitter Controls */
+
+static int vivid_radio_tx_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_radio_tx);
+
+       switch (ctrl->id) {
+       case VIVID_CID_RADIO_TX_RDS_BLOCKIO:
+               dev->radio_tx_rds_controls = ctrl->val;
+               dev->radio_tx_caps &= ~V4L2_CAP_READWRITE;
+               if (!dev->radio_tx_rds_controls)
+                       dev->radio_tx_caps |= V4L2_CAP_READWRITE;
+               break;
+       case V4L2_CID_RDS_TX_PTY:
+               if (dev->radio_rx_rds_controls)
+                       v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, ctrl->val);
+               break;
+       case V4L2_CID_RDS_TX_PS_NAME:
+               if (dev->radio_rx_rds_controls)
+                       v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, ctrl->p_new.p_char);
+               break;
+       case V4L2_CID_RDS_TX_RADIO_TEXT:
+               if (dev->radio_rx_rds_controls)
+                       v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, ctrl->p_new.p_char);
+               break;
+       case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT:
+               if (dev->radio_rx_rds_controls)
+                       v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, ctrl->val);
+               break;
+       case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM:
+               if (dev->radio_rx_rds_controls)
+                       v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, ctrl->val);
+               break;
+       case V4L2_CID_RDS_TX_MUSIC_SPEECH:
+               if (dev->radio_rx_rds_controls)
+                       v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, ctrl->val);
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_radio_tx_ctrl_ops = {
+       .s_ctrl = vivid_radio_tx_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_radio_tx_rds_blockio = {
+       .ops = &vivid_radio_tx_ctrl_ops,
+       .id = VIVID_CID_RADIO_TX_RDS_BLOCKIO,
+       .name = "RDS Tx I/O Mode",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .qmenu = vivid_ctrl_radio_rds_mode_strings,
+       .max = 1,
+       .def = 1,
+};
+
+
+
+/* Video Loop Control */
+
+static int vivid_loop_out_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_out);
+
+       switch (ctrl->id) {
+       case VIVID_CID_LOOP_VIDEO:
+               dev->loop_video = ctrl->val;
+               vivid_update_quality(dev);
+               vivid_send_source_change(dev, SVID);
+               vivid_send_source_change(dev, HDMI);
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_loop_out_ctrl_ops = {
+       .s_ctrl = vivid_loop_out_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_loop_video = {
+       .ops = &vivid_loop_out_ctrl_ops,
+       .id = VIVID_CID_LOOP_VIDEO,
+       .name = "Loop Video",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .max = 1,
+       .step = 1,
+};
+
+
+static const struct v4l2_ctrl_config vivid_ctrl_class = {
+       .ops = &vivid_user_gen_ctrl_ops,
+       .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY,
+       .id = VIVID_CID_VIVID_CLASS,
+       .name = "Vivid Controls",
+       .type = V4L2_CTRL_TYPE_CTRL_CLASS,
+};
+
+int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
+               bool show_ccs_out, bool no_error_inj,
+               bool has_sdtv, bool has_hdmi)
+{
+       struct v4l2_ctrl_handler *hdl_user_gen = &dev->ctrl_hdl_user_gen;
+       struct v4l2_ctrl_handler *hdl_user_vid = &dev->ctrl_hdl_user_vid;
+       struct v4l2_ctrl_handler *hdl_user_aud = &dev->ctrl_hdl_user_aud;
+       struct v4l2_ctrl_handler *hdl_streaming = &dev->ctrl_hdl_streaming;
+       struct v4l2_ctrl_handler *hdl_sdtv_cap = &dev->ctrl_hdl_sdtv_cap;
+       struct v4l2_ctrl_handler *hdl_loop_out = &dev->ctrl_hdl_loop_out;
+       struct v4l2_ctrl_handler *hdl_vid_cap = &dev->ctrl_hdl_vid_cap;
+       struct v4l2_ctrl_handler *hdl_vid_out = &dev->ctrl_hdl_vid_out;
+       struct v4l2_ctrl_handler *hdl_vbi_cap = &dev->ctrl_hdl_vbi_cap;
+       struct v4l2_ctrl_handler *hdl_vbi_out = &dev->ctrl_hdl_vbi_out;
+       struct v4l2_ctrl_handler *hdl_radio_rx = &dev->ctrl_hdl_radio_rx;
+       struct v4l2_ctrl_handler *hdl_radio_tx = &dev->ctrl_hdl_radio_tx;
+       struct v4l2_ctrl_handler *hdl_sdr_cap = &dev->ctrl_hdl_sdr_cap;
+       struct v4l2_ctrl_config vivid_ctrl_dv_timings = {
+               .ops = &vivid_vid_cap_ctrl_ops,
+               .id = VIVID_CID_DV_TIMINGS,
+               .name = "DV Timings",
+               .type = V4L2_CTRL_TYPE_MENU,
+       };
+       int i;
+
+       v4l2_ctrl_handler_init(hdl_user_gen, 10);
+       v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_user_vid, 9);
+       v4l2_ctrl_new_custom(hdl_user_vid, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_user_aud, 2);
+       v4l2_ctrl_new_custom(hdl_user_aud, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_streaming, 8);
+       v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_sdtv_cap, 2);
+       v4l2_ctrl_new_custom(hdl_sdtv_cap, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_loop_out, 1);
+       v4l2_ctrl_new_custom(hdl_loop_out, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_vid_cap, 55);
+       v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_vid_out, 26);
+       v4l2_ctrl_new_custom(hdl_vid_out, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_vbi_cap, 21);
+       v4l2_ctrl_new_custom(hdl_vbi_cap, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_vbi_out, 19);
+       v4l2_ctrl_new_custom(hdl_vbi_out, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_radio_rx, 17);
+       v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_radio_tx, 17);
+       v4l2_ctrl_new_custom(hdl_radio_tx, &vivid_ctrl_class, NULL);
+       v4l2_ctrl_handler_init(hdl_sdr_cap, 18);
+       v4l2_ctrl_new_custom(hdl_sdr_cap, &vivid_ctrl_class, NULL);
+
+       /* User Controls */
+       dev->volume = v4l2_ctrl_new_std(hdl_user_aud, NULL,
+               V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
+       dev->mute = v4l2_ctrl_new_std(hdl_user_aud, NULL,
+               V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+       if (dev->has_vid_cap) {
+               dev->brightness = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+               for (i = 0; i < MAX_INPUTS; i++)
+                       dev->input_brightness[i] = 128;
+               dev->contrast = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 128);
+               dev->saturation = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 255, 1, 128);
+               dev->hue = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+                       V4L2_CID_HUE, -128, 128, 1, 0);
+               v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+                       V4L2_CID_HFLIP, 0, 1, 1, 0);
+               v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+                       V4L2_CID_VFLIP, 0, 1, 1, 0);
+               dev->autogain = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+               dev->gain = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 255, 1, 100);
+               dev->alpha = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
+                       V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
+       }
+       dev->button = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_button, NULL);
+       dev->int32 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int32, NULL);
+       dev->int64 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int64, NULL);
+       dev->boolean = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_boolean, NULL);
+       dev->menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_menu, NULL);
+       dev->string = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_string, NULL);
+       dev->bitmask = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_bitmask, NULL);
+       dev->int_menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int_menu, NULL);
+
+       if (dev->has_vid_cap) {
+               /* Image Processing Controls */
+               struct v4l2_ctrl_config vivid_ctrl_test_pattern = {
+                       .ops = &vivid_vid_cap_ctrl_ops,
+                       .id = VIVID_CID_TEST_PATTERN,
+                       .name = "Test Pattern",
+                       .type = V4L2_CTRL_TYPE_MENU,
+                       .max = TPG_PAT_NOISE,
+                       .qmenu = tpg_pattern_strings,
+               };
+
+               dev->test_pattern = v4l2_ctrl_new_custom(hdl_vid_cap,
+                               &vivid_ctrl_test_pattern, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_perc_fill, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_hor_movement, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_vert_movement, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_osd_mode, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_show_border, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_show_square, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_hflip, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_vflip, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_sav, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_eav, NULL);
+               if (show_ccs_cap) {
+                       dev->ctrl_has_crop_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
+                               &vivid_ctrl_has_crop_cap, NULL);
+                       dev->ctrl_has_compose_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
+                               &vivid_ctrl_has_compose_cap, NULL);
+                       dev->ctrl_has_scaler_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
+                               &vivid_ctrl_has_scaler_cap, NULL);
+               }
+
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_tstamp_src, NULL);
+               dev->colorspace = v4l2_ctrl_new_custom(hdl_vid_cap,
+                       &vivid_ctrl_colorspace, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_alpha_mode, NULL);
+       }
+
+       if (dev->has_vid_out && show_ccs_out) {
+               dev->ctrl_has_crop_out = v4l2_ctrl_new_custom(hdl_vid_out,
+                       &vivid_ctrl_has_crop_out, NULL);
+               dev->ctrl_has_compose_out = v4l2_ctrl_new_custom(hdl_vid_out,
+                       &vivid_ctrl_has_compose_out, NULL);
+               dev->ctrl_has_scaler_out = v4l2_ctrl_new_custom(hdl_vid_out,
+                       &vivid_ctrl_has_scaler_out, NULL);
+       }
+
+       /*
+        * Testing this driver with v4l2-compliance will trigger the error
+        * injection controls, and after that nothing will work as expected.
+        * So we have a module option to drop these error injecting controls
+        * allowing us to run v4l2_compliance again.
+        */
+       if (!no_error_inj) {
+               v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_disconnect, NULL);
+               v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_dqbuf_error, NULL);
+               v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_perc_dropped, NULL);
+               v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_queue_setup_error, NULL);
+               v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_buf_prepare_error, NULL);
+               v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_start_streaming_error, NULL);
+               v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_queue_error, NULL);
+               v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_seq_wrap, NULL);
+               v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_time_wrap, NULL);
+       }
+
+       if (has_sdtv && (dev->has_vid_cap || dev->has_vbi_cap)) {
+               if (dev->has_vid_cap)
+                       v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_std_aspect_ratio, NULL);
+               dev->ctrl_std_signal_mode = v4l2_ctrl_new_custom(hdl_sdtv_cap,
+                       &vivid_ctrl_std_signal_mode, NULL);
+               dev->ctrl_standard = v4l2_ctrl_new_custom(hdl_sdtv_cap,
+                       &vivid_ctrl_standard, NULL);
+               if (dev->ctrl_std_signal_mode)
+                       v4l2_ctrl_cluster(2, &dev->ctrl_std_signal_mode);
+               if (dev->has_raw_vbi_cap)
+                       v4l2_ctrl_new_custom(hdl_vbi_cap, &vivid_ctrl_vbi_cap_interlaced, NULL);
+       }
+
+       if (has_hdmi && dev->has_vid_cap) {
+               dev->ctrl_dv_timings_signal_mode = v4l2_ctrl_new_custom(hdl_vid_cap,
+                                       &vivid_ctrl_dv_timings_signal_mode, NULL);
+
+               vivid_ctrl_dv_timings.max = dev->query_dv_timings_size - 1;
+               vivid_ctrl_dv_timings.qmenu =
+                       (const char * const *)dev->query_dv_timings_qmenu;
+               dev->ctrl_dv_timings = v4l2_ctrl_new_custom(hdl_vid_cap,
+                       &vivid_ctrl_dv_timings, NULL);
+               if (dev->ctrl_dv_timings_signal_mode)
+                       v4l2_ctrl_cluster(2, &dev->ctrl_dv_timings_signal_mode);
+
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_dv_timings_aspect_ratio, NULL);
+               v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_max_edid_blocks, NULL);
+               dev->real_rgb_range_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
+                       &vivid_ctrl_limited_rgb_range, NULL);
+               dev->rgb_range_cap = v4l2_ctrl_new_std_menu(hdl_vid_cap,
+                       &vivid_vid_cap_ctrl_ops,
+                       V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
+                       0, V4L2_DV_RGB_RANGE_AUTO);
+       }
+       if (has_hdmi && dev->has_vid_out) {
+               /*
+                * We aren't doing anything with this at the moment, but
+                * HDMI outputs typically have this controls.
+                */
+               dev->ctrl_tx_rgb_range = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL,
+                       V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
+                       0, V4L2_DV_RGB_RANGE_AUTO);
+               dev->ctrl_tx_mode = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL,
+                       V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI,
+                       0, V4L2_DV_TX_MODE_HDMI);
+       }
+       if ((dev->has_vid_cap && dev->has_vid_out) ||
+           (dev->has_vbi_cap && dev->has_vbi_out))
+               v4l2_ctrl_new_custom(hdl_loop_out, &vivid_ctrl_loop_video, NULL);
+
+       if (dev->has_fb)
+               v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_clear_fb, NULL);
+
+       if (dev->has_radio_rx) {
+               v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_hw_seek_mode, NULL);
+               v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_hw_seek_prog_lim, NULL);
+               v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_rx_rds_blockio, NULL);
+               v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_rx_rds_rbds, NULL);
+               v4l2_ctrl_new_std(hdl_radio_rx, &vivid_radio_rx_ctrl_ops,
+                       V4L2_CID_RDS_RECEPTION, 0, 1, 1, 1);
+               dev->radio_rx_rds_pty = v4l2_ctrl_new_std(hdl_radio_rx,
+                       &vivid_radio_rx_ctrl_ops,
+                       V4L2_CID_RDS_RX_PTY, 0, 31, 1, 0);
+               dev->radio_rx_rds_psname = v4l2_ctrl_new_std(hdl_radio_rx,
+                       &vivid_radio_rx_ctrl_ops,
+                       V4L2_CID_RDS_RX_PS_NAME, 0, 8, 8, 0);
+               dev->radio_rx_rds_radiotext = v4l2_ctrl_new_std(hdl_radio_rx,
+                       &vivid_radio_rx_ctrl_ops,
+                       V4L2_CID_RDS_RX_RADIO_TEXT, 0, 64, 64, 0);
+               dev->radio_rx_rds_ta = v4l2_ctrl_new_std(hdl_radio_rx,
+                       &vivid_radio_rx_ctrl_ops,
+                       V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0);
+               dev->radio_rx_rds_tp = v4l2_ctrl_new_std(hdl_radio_rx,
+                       &vivid_radio_rx_ctrl_ops,
+                       V4L2_CID_RDS_RX_TRAFFIC_PROGRAM, 0, 1, 1, 0);
+               dev->radio_rx_rds_ms = v4l2_ctrl_new_std(hdl_radio_rx,
+                       &vivid_radio_rx_ctrl_ops,
+                       V4L2_CID_RDS_RX_MUSIC_SPEECH, 0, 1, 1, 1);
+       }
+       if (dev->has_radio_tx) {
+               v4l2_ctrl_new_custom(hdl_radio_tx,
+                       &vivid_ctrl_radio_tx_rds_blockio, NULL);
+               dev->radio_tx_rds_pi = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_PI, 0, 0xffff, 1, 0x8088);
+               dev->radio_tx_rds_pty = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_PTY, 0, 31, 1, 3);
+               dev->radio_tx_rds_psname = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_PS_NAME, 0, 8, 8, 0);
+               if (dev->radio_tx_rds_psname)
+                       v4l2_ctrl_s_ctrl_string(dev->radio_tx_rds_psname, "VIVID-TX");
+               dev->radio_tx_rds_radiotext = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_RADIO_TEXT, 0, 64 * 2, 64, 0);
+               if (dev->radio_tx_rds_radiotext)
+                       v4l2_ctrl_s_ctrl_string(dev->radio_tx_rds_radiotext,
+                              "This is a VIVID default Radio Text template text, change at will");
+               dev->radio_tx_rds_mono_stereo = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_MONO_STEREO, 0, 1, 1, 1);
+               dev->radio_tx_rds_art_head = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_ARTIFICIAL_HEAD, 0, 1, 1, 0);
+               dev->radio_tx_rds_compressed = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_COMPRESSED, 0, 1, 1, 0);
+               dev->radio_tx_rds_dyn_pty = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_DYNAMIC_PTY, 0, 1, 1, 0);
+               dev->radio_tx_rds_ta = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0);
+               dev->radio_tx_rds_tp = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_TRAFFIC_PROGRAM, 0, 1, 1, 1);
+               dev->radio_tx_rds_ms = v4l2_ctrl_new_std(hdl_radio_tx,
+                       &vivid_radio_tx_ctrl_ops,
+                       V4L2_CID_RDS_TX_MUSIC_SPEECH, 0, 1, 1, 1);
+       }
+       if (hdl_user_gen->error)
+               return hdl_user_gen->error;
+       if (hdl_user_vid->error)
+               return hdl_user_vid->error;
+       if (hdl_user_aud->error)
+               return hdl_user_aud->error;
+       if (hdl_streaming->error)
+               return hdl_streaming->error;
+       if (hdl_sdr_cap->error)
+               return hdl_sdr_cap->error;
+       if (hdl_loop_out->error)
+               return hdl_loop_out->error;
+
+       if (dev->autogain)
+               v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
+
+       if (dev->has_vid_cap) {
+               v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL);
+               v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
+               v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
+               v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
+               v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
+               if (hdl_vid_cap->error)
+                       return hdl_vid_cap->error;
+               dev->vid_cap_dev.ctrl_handler = hdl_vid_cap;
+       }
+       if (dev->has_vid_out) {
+               v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_gen, NULL);
+               v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL);
+               v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL);
+               v4l2_ctrl_add_handler(hdl_vid_out, hdl_loop_out, NULL);
+               if (hdl_vid_out->error)
+                       return hdl_vid_out->error;
+               dev->vid_out_dev.ctrl_handler = hdl_vid_out;
+       }
+       if (dev->has_vbi_cap) {
+               v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_user_gen, NULL);
+               v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_streaming, NULL);
+               v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_sdtv_cap, NULL);
+               if (hdl_vbi_cap->error)
+                       return hdl_vbi_cap->error;
+               dev->vbi_cap_dev.ctrl_handler = hdl_vbi_cap;
+       }
+       if (dev->has_vbi_out) {
+               v4l2_ctrl_add_handler(hdl_vbi_out, hdl_user_gen, NULL);
+               v4l2_ctrl_add_handler(hdl_vbi_out, hdl_streaming, NULL);
+               v4l2_ctrl_add_handler(hdl_vbi_out, hdl_loop_out, NULL);
+               if (hdl_vbi_out->error)
+                       return hdl_vbi_out->error;
+               dev->vbi_out_dev.ctrl_handler = hdl_vbi_out;
+       }
+       if (dev->has_radio_rx) {
+               v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_gen, NULL);
+               v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_aud, NULL);
+               if (hdl_radio_rx->error)
+                       return hdl_radio_rx->error;
+               dev->radio_rx_dev.ctrl_handler = hdl_radio_rx;
+       }
+       if (dev->has_radio_tx) {
+               v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_gen, NULL);
+               v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_aud, NULL);
+               if (hdl_radio_tx->error)
+                       return hdl_radio_tx->error;
+               dev->radio_tx_dev.ctrl_handler = hdl_radio_tx;
+       }
+       if (dev->has_sdr_cap) {
+               v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_user_gen, NULL);
+               v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_streaming, NULL);
+               if (hdl_sdr_cap->error)
+                       return hdl_sdr_cap->error;
+               dev->sdr_cap_dev.ctrl_handler = hdl_sdr_cap;
+       }
+       return 0;
+}
+
+void vivid_free_controls(struct vivid_dev *dev)
+{
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_vid_cap);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_vid_out);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_vbi_cap);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_vbi_out);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_radio_rx);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_radio_tx);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdr_cap);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_gen);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_vid);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_aud);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_streaming);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdtv_cap);
+       v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_out);
+}
diff --git a/drivers/media/platform/vivid/vivid-ctrls.h b/drivers/media/platform/vivid/vivid-ctrls.h
new file mode 100644 (file)
index 0000000..9bcca9d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * vivid-ctrls.h - control support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_CTRLS_H_
+#define _VIVID_CTRLS_H_
+
+enum vivid_hw_seek_modes {
+       VIVID_HW_SEEK_BOUNDED,
+       VIVID_HW_SEEK_WRAP,
+       VIVID_HW_SEEK_BOTH,
+};
+
+int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
+               bool show_ccs_out, bool no_error_inj,
+               bool has_sdtv, bool has_hdmi);
+void vivid_free_controls(struct vivid_dev *dev);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
new file mode 100644 (file)
index 0000000..39a67cf
--- /dev/null
@@ -0,0 +1,886 @@
+/*
+ * vivid-kthread-cap.h - video/vbi capture thread support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/random.h>
+#include <linux/v4l2-dv-timings.h>
+#include <asm/div64.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-vid-cap.h"
+#include "vivid-vid-out.h"
+#include "vivid-radio-common.h"
+#include "vivid-radio-rx.h"
+#include "vivid-radio-tx.h"
+#include "vivid-sdr-cap.h"
+#include "vivid-vbi-cap.h"
+#include "vivid-vbi-out.h"
+#include "vivid-osd.h"
+#include "vivid-ctrls.h"
+#include "vivid-kthread-cap.h"
+
+static inline v4l2_std_id vivid_get_std_cap(const struct vivid_dev *dev)
+{
+       if (vivid_is_sdtv_cap(dev))
+               return dev->std_cap;
+       return 0;
+}
+
+static void copy_pix(struct vivid_dev *dev, int win_y, int win_x,
+                       u16 *cap, const u16 *osd)
+{
+       u16 out;
+       int left = dev->overlay_out_left;
+       int top = dev->overlay_out_top;
+       int fb_x = win_x + left;
+       int fb_y = win_y + top;
+       int i;
+
+       out = *cap;
+       *cap = *osd;
+       if (dev->bitmap_out) {
+               const u8 *p = dev->bitmap_out;
+               unsigned stride = (dev->compose_out.width + 7) / 8;
+
+               win_x -= dev->compose_out.left;
+               win_y -= dev->compose_out.top;
+               if (!(p[stride * win_y + win_x / 8] & (1 << (win_x & 7))))
+                       return;
+       }
+
+       for (i = 0; i < dev->clipcount_out; i++) {
+               struct v4l2_rect *r = &dev->clips_out[i].c;
+
+               if (fb_y >= r->top && fb_y < r->top + r->height &&
+                   fb_x >= r->left && fb_x < r->left + r->width)
+                       return;
+       }
+       if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_CHROMAKEY) &&
+           *osd != dev->chromakey_out)
+               return;
+       if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) &&
+           out == dev->chromakey_out)
+               return;
+       if (dev->fmt_cap->alpha_mask) {
+               if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) &&
+                   dev->global_alpha_out)
+                       return;
+               if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) &&
+                   *cap & dev->fmt_cap->alpha_mask)
+                       return;
+               if ((dev->fbuf_out_flags & V4L2_FBUF_FLAG_LOCAL_INV_ALPHA) &&
+                   !(*cap & dev->fmt_cap->alpha_mask))
+                       return;
+       }
+       *cap = out;
+}
+
+static void blend_line(struct vivid_dev *dev, unsigned y_offset, unsigned x_offset,
+               u8 *vcapbuf, const u8 *vosdbuf,
+               unsigned width, unsigned pixsize)
+{
+       unsigned x;
+
+       for (x = 0; x < width; x++, vcapbuf += pixsize, vosdbuf += pixsize) {
+               copy_pix(dev, y_offset, x_offset + x,
+                        (u16 *)vcapbuf, (const u16 *)vosdbuf);
+       }
+}
+
+static void scale_line(const u8 *src, u8 *dst, unsigned srcw, unsigned dstw, unsigned twopixsize)
+{
+       /* Coarse scaling with Bresenham */
+       unsigned int_part;
+       unsigned fract_part;
+       unsigned src_x = 0;
+       unsigned error = 0;
+       unsigned x;
+
+       /*
+        * We always combine two pixels to prevent color bleed in the packed
+        * yuv case.
+        */
+       srcw /= 2;
+       dstw /= 2;
+       int_part = srcw / dstw;
+       fract_part = srcw % dstw;
+       for (x = 0; x < dstw; x++, dst += twopixsize) {
+               memcpy(dst, src + src_x * twopixsize, twopixsize);
+               src_x += int_part;
+               error += fract_part;
+               if (error >= dstw) {
+                       error -= dstw;
+                       src_x++;
+               }
+       }
+}
+
+/*
+ * Precalculate the rectangles needed to perform video looping:
+ *
+ * The nominal pipeline is that the video output buffer is cropped by
+ * crop_out, scaled to compose_out, overlaid with the output overlay,
+ * cropped on the capture side by crop_cap and scaled again to the video
+ * capture buffer using compose_cap.
+ *
+ * To keep things efficient we calculate the intersection of compose_out
+ * and crop_cap (since that's the only part of the video that will
+ * actually end up in the capture buffer), determine which part of the
+ * video output buffer that is and which part of the video capture buffer
+ * so we can scale the video straight from the output buffer to the capture
+ * buffer without any intermediate steps.
+ *
+ * If we need to deal with an output overlay, then there is no choice and
+ * that intermediate step still has to be taken. For the output overlay
+ * support we calculate the intersection of the framebuffer and the overlay
+ * window (which may be partially or wholly outside of the framebuffer
+ * itself) and the intersection of that with loop_vid_copy (i.e. the part of
+ * the actual looped video that will be overlaid). The result is calculated
+ * both in framebuffer coordinates (loop_fb_copy) and compose_out coordinates
+ * (loop_vid_overlay). Finally calculate the part of the capture buffer that
+ * will receive that overlaid video.
+ */
+static void vivid_precalc_copy_rects(struct vivid_dev *dev)
+{
+       /* Framebuffer rectangle */
+       struct v4l2_rect r_fb = {
+               0, 0, dev->display_width, dev->display_height
+       };
+       /* Overlay window rectangle in framebuffer coordinates */
+       struct v4l2_rect r_overlay = {
+               dev->overlay_out_left, dev->overlay_out_top,
+               dev->compose_out.width, dev->compose_out.height
+       };
+
+       dev->loop_vid_copy = rect_intersect(&dev->crop_cap, &dev->compose_out);
+
+       dev->loop_vid_out = dev->loop_vid_copy;
+       rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out);
+       dev->loop_vid_out.left += dev->crop_out.left;
+       dev->loop_vid_out.top += dev->crop_out.top;
+
+       dev->loop_vid_cap = dev->loop_vid_copy;
+       rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap);
+
+       dprintk(dev, 1,
+               "loop_vid_copy: %dx%d@%dx%d loop_vid_out: %dx%d@%dx%d loop_vid_cap: %dx%d@%dx%d\n",
+               dev->loop_vid_copy.width, dev->loop_vid_copy.height,
+               dev->loop_vid_copy.left, dev->loop_vid_copy.top,
+               dev->loop_vid_out.width, dev->loop_vid_out.height,
+               dev->loop_vid_out.left, dev->loop_vid_out.top,
+               dev->loop_vid_cap.width, dev->loop_vid_cap.height,
+               dev->loop_vid_cap.left, dev->loop_vid_cap.top);
+
+       r_overlay = rect_intersect(&r_fb, &r_overlay);
+
+       /* shift r_overlay to the same origin as compose_out */
+       r_overlay.left += dev->compose_out.left - dev->overlay_out_left;
+       r_overlay.top += dev->compose_out.top - dev->overlay_out_top;
+
+       dev->loop_vid_overlay = rect_intersect(&r_overlay, &dev->loop_vid_copy);
+       dev->loop_fb_copy = dev->loop_vid_overlay;
+
+       /* shift dev->loop_fb_copy back again to the fb origin */
+       dev->loop_fb_copy.left -= dev->compose_out.left - dev->overlay_out_left;
+       dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top;
+
+       dev->loop_vid_overlay_cap = dev->loop_vid_overlay;
+       rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap);
+
+       dprintk(dev, 1,
+               "loop_fb_copy: %dx%d@%dx%d loop_vid_overlay: %dx%d@%dx%d loop_vid_overlay_cap: %dx%d@%dx%d\n",
+               dev->loop_fb_copy.width, dev->loop_fb_copy.height,
+               dev->loop_fb_copy.left, dev->loop_fb_copy.top,
+               dev->loop_vid_overlay.width, dev->loop_vid_overlay.height,
+               dev->loop_vid_overlay.left, dev->loop_vid_overlay.top,
+               dev->loop_vid_overlay_cap.width, dev->loop_vid_overlay_cap.height,
+               dev->loop_vid_overlay_cap.left, dev->loop_vid_overlay_cap.top);
+}
+
+static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf,
+               struct vivid_buffer *vid_cap_buf)
+{
+       bool blank = dev->must_blank[vid_cap_buf->vb.v4l2_buf.index];
+       struct tpg_data *tpg = &dev->tpg;
+       struct vivid_buffer *vid_out_buf = NULL;
+       unsigned pixsize = tpg_g_twopixelsize(tpg, p) / 2;
+       unsigned img_width = dev->compose_cap.width;
+       unsigned img_height = dev->compose_cap.height;
+       unsigned stride_cap = tpg->bytesperline[p];
+       unsigned stride_out = dev->bytesperline_out[p];
+       unsigned stride_osd = dev->display_byte_stride;
+       unsigned hmax = (img_height * tpg->perc_fill) / 100;
+       u8 *voutbuf;
+       u8 *vosdbuf = NULL;
+       unsigned y;
+       bool blend = dev->bitmap_out || dev->clipcount_out || dev->fbuf_out_flags;
+       /* Coarse scaling with Bresenham */
+       unsigned vid_out_int_part;
+       unsigned vid_out_fract_part;
+       unsigned vid_out_y = 0;
+       unsigned vid_out_error = 0;
+       unsigned vid_overlay_int_part = 0;
+       unsigned vid_overlay_fract_part = 0;
+       unsigned vid_overlay_y = 0;
+       unsigned vid_overlay_error = 0;
+       unsigned vid_cap_right;
+       bool quick;
+
+       vid_out_int_part = dev->loop_vid_out.height / dev->loop_vid_cap.height;
+       vid_out_fract_part = dev->loop_vid_out.height % dev->loop_vid_cap.height;
+
+       if (!list_empty(&dev->vid_out_active))
+               vid_out_buf = list_entry(dev->vid_out_active.next,
+                                        struct vivid_buffer, list);
+       if (vid_out_buf == NULL)
+               return -ENODATA;
+
+       vid_cap_buf->vb.v4l2_buf.field = vid_out_buf->vb.v4l2_buf.field;
+
+       voutbuf = vb2_plane_vaddr(&vid_out_buf->vb, p) +
+                                 vid_out_buf->vb.v4l2_planes[p].data_offset;
+       voutbuf += dev->loop_vid_out.left * pixsize + dev->loop_vid_out.top * stride_out;
+       vcapbuf += dev->compose_cap.left * pixsize + dev->compose_cap.top * stride_cap;
+
+       if (dev->loop_vid_copy.width == 0 || dev->loop_vid_copy.height == 0) {
+               /*
+                * If there is nothing to copy, then just fill the capture window
+                * with black.
+                */
+               for (y = 0; y < hmax; y++, vcapbuf += stride_cap)
+                       memcpy(vcapbuf, tpg->black_line[p], img_width * pixsize);
+               return 0;
+       }
+
+       if (dev->overlay_out_enabled &&
+           dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) {
+               vosdbuf = dev->video_vbase;
+               vosdbuf += dev->loop_fb_copy.left * pixsize +
+                          dev->loop_fb_copy.top * stride_osd;
+               vid_overlay_int_part = dev->loop_vid_overlay.height /
+                                      dev->loop_vid_overlay_cap.height;
+               vid_overlay_fract_part = dev->loop_vid_overlay.height %
+                                        dev->loop_vid_overlay_cap.height;
+       }
+
+       vid_cap_right = dev->loop_vid_cap.left + dev->loop_vid_cap.width;
+       /* quick is true if no video scaling is needed */
+       quick = dev->loop_vid_out.width == dev->loop_vid_cap.width;
+
+       dev->cur_scaled_line = dev->loop_vid_out.height;
+       for (y = 0; y < hmax; y++, vcapbuf += stride_cap) {
+               /* osdline is true if this line requires overlay blending */
+               bool osdline = vosdbuf && y >= dev->loop_vid_overlay_cap.top &&
+                         y < dev->loop_vid_overlay_cap.top + dev->loop_vid_overlay_cap.height;
+
+               /*
+                * If this line of the capture buffer doesn't get any video, then
+                * just fill with black.
+                */
+               if (y < dev->loop_vid_cap.top ||
+                   y >= dev->loop_vid_cap.top + dev->loop_vid_cap.height) {
+                       memcpy(vcapbuf, tpg->black_line[p], img_width * pixsize);
+                       continue;
+               }
+
+               /* fill the left border with black */
+               if (dev->loop_vid_cap.left)
+                       memcpy(vcapbuf, tpg->black_line[p], dev->loop_vid_cap.left * pixsize);
+
+               /* fill the right border with black */
+               if (vid_cap_right < img_width)
+                       memcpy(vcapbuf + vid_cap_right * pixsize,
+                               tpg->black_line[p], (img_width - vid_cap_right) * pixsize);
+
+               if (quick && !osdline) {
+                       memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize,
+                              voutbuf + vid_out_y * stride_out,
+                              dev->loop_vid_cap.width * pixsize);
+                       goto update_vid_out_y;
+               }
+               if (dev->cur_scaled_line == vid_out_y) {
+                       memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize,
+                              dev->scaled_line,
+                              dev->loop_vid_cap.width * pixsize);
+                       goto update_vid_out_y;
+               }
+               if (!osdline) {
+                       scale_line(voutbuf + vid_out_y * stride_out, dev->scaled_line,
+                               dev->loop_vid_out.width, dev->loop_vid_cap.width,
+                               tpg_g_twopixelsize(tpg, p));
+               } else {
+                       /*
+                        * Offset in bytes within loop_vid_copy to the start of the
+                        * loop_vid_overlay rectangle.
+                        */
+                       unsigned offset =
+                               (dev->loop_vid_overlay.left - dev->loop_vid_copy.left) * pixsize;
+                       u8 *osd = vosdbuf + vid_overlay_y * stride_osd;
+
+                       scale_line(voutbuf + vid_out_y * stride_out, dev->blended_line,
+                               dev->loop_vid_out.width, dev->loop_vid_copy.width,
+                               tpg_g_twopixelsize(tpg, p));
+                       if (blend)
+                               blend_line(dev, vid_overlay_y + dev->loop_vid_overlay.top,
+                                          dev->loop_vid_overlay.left,
+                                          dev->blended_line + offset, osd,
+                                          dev->loop_vid_overlay.width, pixsize);
+                       else
+                               memcpy(dev->blended_line + offset,
+                                      osd, dev->loop_vid_overlay.width * pixsize);
+                       scale_line(dev->blended_line, dev->scaled_line,
+                                       dev->loop_vid_copy.width, dev->loop_vid_cap.width,
+                                       tpg_g_twopixelsize(tpg, p));
+               }
+               dev->cur_scaled_line = vid_out_y;
+               memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize,
+                      dev->scaled_line,
+                      dev->loop_vid_cap.width * pixsize);
+
+update_vid_out_y:
+               if (osdline) {
+                       vid_overlay_y += vid_overlay_int_part;
+                       vid_overlay_error += vid_overlay_fract_part;
+                       if (vid_overlay_error >= dev->loop_vid_overlay_cap.height) {
+                               vid_overlay_error -= dev->loop_vid_overlay_cap.height;
+                               vid_overlay_y++;
+                       }
+               }
+               vid_out_y += vid_out_int_part;
+               vid_out_error += vid_out_fract_part;
+               if (vid_out_error >= dev->loop_vid_cap.height) {
+                       vid_out_error -= dev->loop_vid_cap.height;
+                       vid_out_y++;
+               }
+       }
+
+       if (!blank)
+               return 0;
+       for (; y < img_height; y++, vcapbuf += stride_cap)
+               memcpy(vcapbuf, tpg->contrast_line[p], img_width * pixsize);
+       return 0;
+}
+
+static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
+{
+       unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1;
+       unsigned line_height = 16 / factor;
+       bool is_tv = vivid_is_sdtv_cap(dev);
+       bool is_60hz = is_tv && (dev->std_cap & V4L2_STD_525_60);
+       unsigned p;
+       int line = 1;
+       u8 *basep[TPG_MAX_PLANES][2];
+       unsigned ms;
+       char str[100];
+       s32 gain;
+       bool is_loop = false;
+
+       if (dev->loop_video && dev->can_loop_video &&
+           ((vivid_is_svid_cap(dev) && !VIVID_INVALID_SIGNAL(dev->std_signal_mode)) ||
+            (vivid_is_hdmi_cap(dev) && !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode))))
+               is_loop = true;
+
+       buf->vb.v4l2_buf.sequence = dev->vid_cap_seq_count;
+       /*
+        * Take the timestamp now if the timestamp source is set to
+        * "Start of Exposure".
+        */
+       if (dev->tstamp_src_is_soe)
+               v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+       if (dev->field_cap == V4L2_FIELD_ALTERNATE) {
+               /*
+                * 60 Hz standards start with the bottom field, 50 Hz standards
+                * with the top field. So if the 0-based seq_count is even,
+                * then the field is TOP for 50 Hz and BOTTOM for 60 Hz
+                * standards.
+                */
+               buf->vb.v4l2_buf.field = ((dev->vid_cap_seq_count & 1) ^ is_60hz) ?
+                       V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+               /*
+                * The sequence counter counts frames, not fields. So divide
+                * by two.
+                */
+               buf->vb.v4l2_buf.sequence /= 2;
+       } else {
+               buf->vb.v4l2_buf.field = dev->field_cap;
+       }
+       tpg_s_field(&dev->tpg, buf->vb.v4l2_buf.field);
+       tpg_s_perc_fill_blank(&dev->tpg, dev->must_blank[buf->vb.v4l2_buf.index]);
+
+       vivid_precalc_copy_rects(dev);
+
+       for (p = 0; p < tpg_g_planes(&dev->tpg); p++) {
+               void *vbuf = vb2_plane_vaddr(&buf->vb, p);
+
+               /*
+                * The first plane of a multiplanar format has a non-zero
+                * data_offset. This helps testing whether the application
+                * correctly supports non-zero data offsets.
+                */
+               if (dev->fmt_cap->data_offset[p]) {
+                       memset(vbuf, dev->fmt_cap->data_offset[p] & 0xff,
+                              dev->fmt_cap->data_offset[p]);
+                       vbuf += dev->fmt_cap->data_offset[p];
+               }
+               tpg_calc_text_basep(&dev->tpg, basep, p, vbuf);
+               if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf))
+                       tpg_fillbuffer(&dev->tpg, vivid_get_std_cap(dev), p, vbuf);
+       }
+       dev->must_blank[buf->vb.v4l2_buf.index] = false;
+
+       /* Updates stream time, only update at the start of a new frame. */
+       if (dev->field_cap != V4L2_FIELD_ALTERNATE || (buf->vb.v4l2_buf.sequence & 1) == 0)
+               dev->ms_vid_cap = jiffies_to_msecs(jiffies - dev->jiffies_vid_cap);
+
+       ms = dev->ms_vid_cap;
+       if (dev->osd_mode <= 1) {
+               snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d %u%s",
+                               (ms / (60 * 60 * 1000)) % 24,
+                               (ms / (60 * 1000)) % 60,
+                               (ms / 1000) % 60,
+                               ms % 1000,
+                               buf->vb.v4l2_buf.sequence,
+                               (dev->field_cap == V4L2_FIELD_ALTERNATE) ?
+                                       (buf->vb.v4l2_buf.field == V4L2_FIELD_TOP ?
+                                        " top" : " bottom") : "");
+               tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+       }
+       if (dev->osd_mode == 0) {
+               snprintf(str, sizeof(str), " %dx%d, input %d ",
+                               dev->src_rect.width, dev->src_rect.height, dev->input);
+               tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+
+               gain = v4l2_ctrl_g_ctrl(dev->gain);
+               mutex_lock(dev->ctrl_hdl_user_vid.lock);
+               snprintf(str, sizeof(str),
+                       " brightness %3d, contrast %3d, saturation %3d, hue %d ",
+                       dev->brightness->cur.val,
+                       dev->contrast->cur.val,
+                       dev->saturation->cur.val,
+                       dev->hue->cur.val);
+               tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+               snprintf(str, sizeof(str),
+                       " autogain %d, gain %3d, alpha 0x%02x ",
+                       dev->autogain->cur.val, gain, dev->alpha->cur.val);
+               mutex_unlock(dev->ctrl_hdl_user_vid.lock);
+               tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+               mutex_lock(dev->ctrl_hdl_user_aud.lock);
+               snprintf(str, sizeof(str),
+                       " volume %3d, mute %d ",
+                       dev->volume->cur.val, dev->mute->cur.val);
+               mutex_unlock(dev->ctrl_hdl_user_aud.lock);
+               tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+               mutex_lock(dev->ctrl_hdl_user_gen.lock);
+               snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
+                       dev->int32->cur.val,
+                       *dev->int64->p_cur.p_s64,
+                       dev->bitmask->cur.val);
+               tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+               snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
+                       dev->boolean->cur.val,
+                       dev->menu->qmenu[dev->menu->cur.val],
+                       dev->string->p_cur.p_char);
+               tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+               snprintf(str, sizeof(str), " integer_menu %lld, value %d ",
+                       dev->int_menu->qmenu_int[dev->int_menu->cur.val],
+                       dev->int_menu->cur.val);
+               mutex_unlock(dev->ctrl_hdl_user_gen.lock);
+               tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+               if (dev->button_pressed) {
+                       dev->button_pressed--;
+                       snprintf(str, sizeof(str), " button pressed!");
+                       tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+               }
+       }
+
+       /*
+        * If "End of Frame" is specified at the timestamp source, then take
+        * the timestamp now.
+        */
+       if (!dev->tstamp_src_is_soe)
+               v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+       buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+}
+
+/*
+ * Return true if this pixel coordinate is a valid video pixel.
+ */
+static bool valid_pix(struct vivid_dev *dev, int win_y, int win_x, int fb_y, int fb_x)
+{
+       int i;
+
+       if (dev->bitmap_cap) {
+               /*
+                * Only if the corresponding bit in the bitmap is set can
+                * the video pixel be shown. Coordinates are relative to
+                * the overlay window set by VIDIOC_S_FMT.
+                */
+               const u8 *p = dev->bitmap_cap;
+               unsigned stride = (dev->compose_cap.width + 7) / 8;
+
+               if (!(p[stride * win_y + win_x / 8] & (1 << (win_x & 7))))
+                       return false;
+       }
+
+       for (i = 0; i < dev->clipcount_cap; i++) {
+               /*
+                * Only if the framebuffer coordinate is not in any of the
+                * clip rectangles will be video pixel be shown.
+                */
+               struct v4l2_rect *r = &dev->clips_cap[i].c;
+
+               if (fb_y >= r->top && fb_y < r->top + r->height &&
+                   fb_x >= r->left && fb_x < r->left + r->width)
+                       return false;
+       }
+       return true;
+}
+
+/*
+ * Draw the image into the overlay buffer.
+ * Note that the combination of overlay and multiplanar is not supported.
+ */
+static void vivid_overlay(struct vivid_dev *dev, struct vivid_buffer *buf)
+{
+       struct tpg_data *tpg = &dev->tpg;
+       unsigned pixsize = tpg_g_twopixelsize(tpg, 0) / 2;
+       void *vbase = dev->fb_vbase_cap;
+       void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
+       unsigned img_width = dev->compose_cap.width;
+       unsigned img_height = dev->compose_cap.height;
+       unsigned stride = tpg->bytesperline[0];
+       /* if quick is true, then valid_pix() doesn't have to be called */
+       bool quick = dev->bitmap_cap == NULL && dev->clipcount_cap == 0;
+       int x, y, w, out_x = 0;
+
+       if ((dev->overlay_cap_field == V4L2_FIELD_TOP ||
+            dev->overlay_cap_field == V4L2_FIELD_BOTTOM) &&
+           dev->overlay_cap_field != buf->vb.v4l2_buf.field)
+               return;
+
+       vbuf += dev->compose_cap.left * pixsize + dev->compose_cap.top * stride;
+       x = dev->overlay_cap_left;
+       w = img_width;
+       if (x < 0) {
+               out_x = -x;
+               w = w - out_x;
+               x = 0;
+       } else {
+               w = dev->fb_cap.fmt.width - x;
+               if (w > img_width)
+                       w = img_width;
+       }
+       if (w <= 0)
+               return;
+       if (dev->overlay_cap_top >= 0)
+               vbase += dev->overlay_cap_top * dev->fb_cap.fmt.bytesperline;
+       for (y = dev->overlay_cap_top;
+            y < dev->overlay_cap_top + (int)img_height;
+            y++, vbuf += stride) {
+               int px;
+
+               if (y < 0 || y > dev->fb_cap.fmt.height)
+                       continue;
+               if (quick) {
+                       memcpy(vbase + x * pixsize,
+                              vbuf + out_x * pixsize, w * pixsize);
+                       vbase += dev->fb_cap.fmt.bytesperline;
+                       continue;
+               }
+               for (px = 0; px < w; px++) {
+                       if (!valid_pix(dev, y - dev->overlay_cap_top,
+                                      px + out_x, y, px + x))
+                               continue;
+                       memcpy(vbase + (px + x) * pixsize,
+                              vbuf + (px + out_x) * pixsize,
+                              pixsize);
+               }
+               vbase += dev->fb_cap.fmt.bytesperline;
+       }
+}
+
+static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs)
+{
+       struct vivid_buffer *vid_cap_buf = NULL;
+       struct vivid_buffer *vbi_cap_buf = NULL;
+
+       dprintk(dev, 1, "Video Capture Thread Tick\n");
+
+       while (dropped_bufs-- > 1)
+               tpg_update_mv_count(&dev->tpg,
+                               dev->field_cap == V4L2_FIELD_NONE ||
+                               dev->field_cap == V4L2_FIELD_ALTERNATE);
+
+       /* Drop a certain percentage of buffers. */
+       if (dev->perc_dropped_buffers &&
+           prandom_u32_max(100) < dev->perc_dropped_buffers)
+               goto update_mv;
+
+       spin_lock(&dev->slock);
+       if (!list_empty(&dev->vid_cap_active)) {
+               vid_cap_buf = list_entry(dev->vid_cap_active.next, struct vivid_buffer, list);
+               list_del(&vid_cap_buf->list);
+       }
+       if (!list_empty(&dev->vbi_cap_active)) {
+               if (dev->field_cap != V4L2_FIELD_ALTERNATE ||
+                   (dev->vbi_cap_seq_count & 1)) {
+                       vbi_cap_buf = list_entry(dev->vbi_cap_active.next,
+                                                struct vivid_buffer, list);
+                       list_del(&vbi_cap_buf->list);
+               }
+       }
+       spin_unlock(&dev->slock);
+
+       if (!vid_cap_buf && !vbi_cap_buf)
+               goto update_mv;
+
+       if (vid_cap_buf) {
+               /* Fill buffer */
+               vivid_fillbuff(dev, vid_cap_buf);
+               dprintk(dev, 1, "filled buffer %d\n",
+                       vid_cap_buf->vb.v4l2_buf.index);
+
+               /* Handle overlay */
+               if (dev->overlay_cap_owner && dev->fb_cap.base &&
+                               dev->fb_cap.fmt.pixelformat == dev->fmt_cap->fourcc)
+                       vivid_overlay(dev, vid_cap_buf);
+
+               vb2_buffer_done(&vid_cap_buf->vb, dev->dqbuf_error ?
+                               VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+               dprintk(dev, 2, "vid_cap buffer %d done\n",
+                               vid_cap_buf->vb.v4l2_buf.index);
+       }
+
+       if (vbi_cap_buf) {
+               if (dev->stream_sliced_vbi_cap)
+                       vivid_sliced_vbi_cap_process(dev, vbi_cap_buf);
+               else
+                       vivid_raw_vbi_cap_process(dev, vbi_cap_buf);
+               vb2_buffer_done(&vbi_cap_buf->vb, dev->dqbuf_error ?
+                               VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+               dprintk(dev, 2, "vbi_cap %d done\n",
+                               vbi_cap_buf->vb.v4l2_buf.index);
+       }
+       dev->dqbuf_error = false;
+
+update_mv:
+       /* Update the test pattern movement counters */
+       tpg_update_mv_count(&dev->tpg, dev->field_cap == V4L2_FIELD_NONE ||
+                                      dev->field_cap == V4L2_FIELD_ALTERNATE);
+}
+
+static int vivid_thread_vid_cap(void *data)
+{
+       struct vivid_dev *dev = data;
+       u64 numerators_since_start;
+       u64 buffers_since_start;
+       u64 next_jiffies_since_start;
+       unsigned long jiffies_since_start;
+       unsigned long cur_jiffies;
+       unsigned wait_jiffies;
+       unsigned numerator;
+       unsigned denominator;
+       int dropped_bufs;
+
+       dprintk(dev, 1, "Video Capture Thread Start\n");
+
+       set_freezable();
+
+       /* Resets frame counters */
+       dev->cap_seq_offset = 0;
+       dev->cap_seq_count = 0;
+       dev->cap_seq_resync = false;
+       dev->jiffies_vid_cap = jiffies;
+
+       for (;;) {
+               try_to_freeze();
+               if (kthread_should_stop())
+                       break;
+
+               mutex_lock(&dev->mutex);
+               cur_jiffies = jiffies;
+               if (dev->cap_seq_resync) {
+                       dev->jiffies_vid_cap = cur_jiffies;
+                       dev->cap_seq_offset = dev->cap_seq_count + 1;
+                       dev->cap_seq_count = 0;
+                       dev->cap_seq_resync = false;
+               }
+               numerator = dev->timeperframe_vid_cap.numerator;
+               denominator = dev->timeperframe_vid_cap.denominator;
+
+               if (dev->field_cap == V4L2_FIELD_ALTERNATE)
+                       denominator *= 2;
+
+               /* Calculate the number of jiffies since we started streaming */
+               jiffies_since_start = cur_jiffies - dev->jiffies_vid_cap;
+               /* Get the number of buffers streamed since the start */
+               buffers_since_start = (u64)jiffies_since_start * denominator +
+                                     (HZ * numerator) / 2;
+               do_div(buffers_since_start, HZ * numerator);
+
+               /*
+                * After more than 0xf0000000 (rounded down to a multiple of
+                * 'jiffies-per-day' to ease jiffies_to_msecs calculation)
+                * jiffies have passed since we started streaming reset the
+                * counters and keep track of the sequence offset.
+                */
+               if (jiffies_since_start > JIFFIES_RESYNC) {
+                       dev->jiffies_vid_cap = cur_jiffies;
+                       dev->cap_seq_offset = buffers_since_start;
+                       buffers_since_start = 0;
+               }
+               dropped_bufs = buffers_since_start + dev->cap_seq_offset - dev->cap_seq_count;
+               dev->cap_seq_count = buffers_since_start + dev->cap_seq_offset;
+               dev->vid_cap_seq_count = dev->cap_seq_count - dev->vid_cap_seq_start;
+               dev->vbi_cap_seq_count = dev->cap_seq_count - dev->vbi_cap_seq_start;
+
+               vivid_thread_vid_cap_tick(dev, dropped_bufs);
+
+               /*
+                * Calculate the number of 'numerators' streamed since we started,
+                * including the current buffer.
+                */
+               numerators_since_start = ++buffers_since_start * numerator;
+
+               /* And the number of jiffies since we started */
+               jiffies_since_start = jiffies - dev->jiffies_vid_cap;
+
+               mutex_unlock(&dev->mutex);
+
+               /*
+                * Calculate when that next buffer is supposed to start
+                * in jiffies since we started streaming.
+                */
+               next_jiffies_since_start = numerators_since_start * HZ +
+                                          denominator / 2;
+               do_div(next_jiffies_since_start, denominator);
+               /* If it is in the past, then just schedule asap */
+               if (next_jiffies_since_start < jiffies_since_start)
+                       next_jiffies_since_start = jiffies_since_start;
+
+               wait_jiffies = next_jiffies_since_start - jiffies_since_start;
+               schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1);
+       }
+       dprintk(dev, 1, "Video Capture Thread End\n");
+       return 0;
+}
+
+static void vivid_grab_controls(struct vivid_dev *dev, bool grab)
+{
+       v4l2_ctrl_grab(dev->ctrl_has_crop_cap, grab);
+       v4l2_ctrl_grab(dev->ctrl_has_compose_cap, grab);
+       v4l2_ctrl_grab(dev->ctrl_has_scaler_cap, grab);
+}
+
+int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
+{
+       dprintk(dev, 1, "%s\n", __func__);
+
+       if (dev->kthread_vid_cap) {
+               u32 seq_count = dev->cap_seq_count + dev->seq_wrap * 128;
+
+               if (pstreaming == &dev->vid_cap_streaming)
+                       dev->vid_cap_seq_start = seq_count;
+               else
+                       dev->vbi_cap_seq_start = seq_count;
+               *pstreaming = true;
+               return 0;
+       }
+
+       /* Resets frame counters */
+       tpg_init_mv_count(&dev->tpg);
+
+       dev->vid_cap_seq_start = dev->seq_wrap * 128;
+       dev->vbi_cap_seq_start = dev->seq_wrap * 128;
+
+       dev->kthread_vid_cap = kthread_run(vivid_thread_vid_cap, dev,
+                       "%s-vid-cap", dev->v4l2_dev.name);
+
+       if (IS_ERR(dev->kthread_vid_cap)) {
+               v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+               return PTR_ERR(dev->kthread_vid_cap);
+       }
+       *pstreaming = true;
+       vivid_grab_controls(dev, true);
+
+       dprintk(dev, 1, "returning from %s\n", __func__);
+       return 0;
+}
+
+void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
+{
+       dprintk(dev, 1, "%s\n", __func__);
+
+       if (dev->kthread_vid_cap == NULL)
+               return;
+
+       *pstreaming = false;
+       if (pstreaming == &dev->vid_cap_streaming) {
+               /* Release all active buffers */
+               while (!list_empty(&dev->vid_cap_active)) {
+                       struct vivid_buffer *buf;
+
+                       buf = list_entry(dev->vid_cap_active.next,
+                                        struct vivid_buffer, list);
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+                       dprintk(dev, 2, "vid_cap buffer %d done\n",
+                               buf->vb.v4l2_buf.index);
+               }
+       }
+
+       if (pstreaming == &dev->vbi_cap_streaming) {
+               while (!list_empty(&dev->vbi_cap_active)) {
+                       struct vivid_buffer *buf;
+
+                       buf = list_entry(dev->vbi_cap_active.next,
+                                        struct vivid_buffer, list);
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+                       dprintk(dev, 2, "vbi_cap buffer %d done\n",
+                               buf->vb.v4l2_buf.index);
+               }
+       }
+
+       if (dev->vid_cap_streaming || dev->vbi_cap_streaming)
+               return;
+
+       /* shutdown control thread */
+       vivid_grab_controls(dev, false);
+       mutex_unlock(&dev->mutex);
+       kthread_stop(dev->kthread_vid_cap);
+       dev->kthread_vid_cap = NULL;
+       mutex_lock(&dev->mutex);
+}
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.h b/drivers/media/platform/vivid/vivid-kthread-cap.h
new file mode 100644 (file)
index 0000000..5b92fc9
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * vivid-kthread-cap.h - video/vbi capture thread support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_KTHREAD_CAP_H_
+#define _VIVID_KTHREAD_CAP_H_
+
+int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming);
+void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c
new file mode 100644 (file)
index 0000000..d9f36cc
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * vivid-kthread-out.h - video/vbi output thread support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/random.h>
+#include <linux/v4l2-dv-timings.h>
+#include <asm/div64.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-vid-cap.h"
+#include "vivid-vid-out.h"
+#include "vivid-radio-common.h"
+#include "vivid-radio-rx.h"
+#include "vivid-radio-tx.h"
+#include "vivid-sdr-cap.h"
+#include "vivid-vbi-cap.h"
+#include "vivid-vbi-out.h"
+#include "vivid-osd.h"
+#include "vivid-ctrls.h"
+#include "vivid-kthread-out.h"
+
+static void vivid_thread_vid_out_tick(struct vivid_dev *dev)
+{
+       struct vivid_buffer *vid_out_buf = NULL;
+       struct vivid_buffer *vbi_out_buf = NULL;
+
+       dprintk(dev, 1, "Video Output Thread Tick\n");
+
+       /* Drop a certain percentage of buffers. */
+       if (dev->perc_dropped_buffers &&
+           prandom_u32_max(100) < dev->perc_dropped_buffers)
+               return;
+
+       spin_lock(&dev->slock);
+       /*
+        * Only dequeue buffer if there is at least one more pending.
+        * This makes video loopback possible.
+        */
+       if (!list_empty(&dev->vid_out_active) &&
+           !list_is_singular(&dev->vid_out_active)) {
+               vid_out_buf = list_entry(dev->vid_out_active.next,
+                                        struct vivid_buffer, list);
+               list_del(&vid_out_buf->list);
+       }
+       if (!list_empty(&dev->vbi_out_active) &&
+           (dev->field_out != V4L2_FIELD_ALTERNATE ||
+            (dev->vbi_out_seq_count & 1))) {
+               vbi_out_buf = list_entry(dev->vbi_out_active.next,
+                                        struct vivid_buffer, list);
+               list_del(&vbi_out_buf->list);
+       }
+       spin_unlock(&dev->slock);
+
+       if (!vid_out_buf && !vbi_out_buf)
+               return;
+
+       if (vid_out_buf) {
+               vid_out_buf->vb.v4l2_buf.sequence = dev->vid_out_seq_count;
+               if (dev->field_out == V4L2_FIELD_ALTERNATE) {
+                       /*
+                        * The sequence counter counts frames, not fields. So divide
+                        * by two.
+                        */
+                       vid_out_buf->vb.v4l2_buf.sequence /= 2;
+               }
+               v4l2_get_timestamp(&vid_out_buf->vb.v4l2_buf.timestamp);
+               vid_out_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+               vb2_buffer_done(&vid_out_buf->vb, dev->dqbuf_error ?
+                               VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+               dprintk(dev, 2, "vid_out buffer %d done\n",
+                       vid_out_buf->vb.v4l2_buf.index);
+       }
+
+       if (vbi_out_buf) {
+               if (dev->stream_sliced_vbi_out)
+                       vivid_sliced_vbi_out_process(dev, vbi_out_buf);
+
+               vbi_out_buf->vb.v4l2_buf.sequence = dev->vbi_out_seq_count;
+               v4l2_get_timestamp(&vbi_out_buf->vb.v4l2_buf.timestamp);
+               vbi_out_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+               vb2_buffer_done(&vbi_out_buf->vb, dev->dqbuf_error ?
+                               VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+               dprintk(dev, 2, "vbi_out buffer %d done\n",
+                       vbi_out_buf->vb.v4l2_buf.index);
+       }
+       dev->dqbuf_error = false;
+}
+
+static int vivid_thread_vid_out(void *data)
+{
+       struct vivid_dev *dev = data;
+       u64 numerators_since_start;
+       u64 buffers_since_start;
+       u64 next_jiffies_since_start;
+       unsigned long jiffies_since_start;
+       unsigned long cur_jiffies;
+       unsigned wait_jiffies;
+       unsigned numerator;
+       unsigned denominator;
+
+       dprintk(dev, 1, "Video Output Thread Start\n");
+
+       set_freezable();
+
+       /* Resets frame counters */
+       dev->out_seq_offset = 0;
+       if (dev->seq_wrap)
+               dev->out_seq_count = 0xffffff80U;
+       dev->jiffies_vid_out = jiffies;
+       dev->vid_out_seq_start = dev->vbi_out_seq_start = 0;
+       dev->out_seq_resync = false;
+
+       for (;;) {
+               try_to_freeze();
+               if (kthread_should_stop())
+                       break;
+
+               mutex_lock(&dev->mutex);
+               cur_jiffies = jiffies;
+               if (dev->out_seq_resync) {
+                       dev->jiffies_vid_out = cur_jiffies;
+                       dev->out_seq_offset = dev->out_seq_count + 1;
+                       dev->out_seq_count = 0;
+                       dev->out_seq_resync = false;
+               }
+               numerator = dev->timeperframe_vid_out.numerator;
+               denominator = dev->timeperframe_vid_out.denominator;
+
+               if (dev->field_out == V4L2_FIELD_ALTERNATE)
+                       denominator *= 2;
+
+               /* Calculate the number of jiffies since we started streaming */
+               jiffies_since_start = cur_jiffies - dev->jiffies_vid_out;
+               /* Get the number of buffers streamed since the start */
+               buffers_since_start = (u64)jiffies_since_start * denominator +
+                                     (HZ * numerator) / 2;
+               do_div(buffers_since_start, HZ * numerator);
+
+               /*
+                * After more than 0xf0000000 (rounded down to a multiple of
+                * 'jiffies-per-day' to ease jiffies_to_msecs calculation)
+                * jiffies have passed since we started streaming reset the
+                * counters and keep track of the sequence offset.
+                */
+               if (jiffies_since_start > JIFFIES_RESYNC) {
+                       dev->jiffies_vid_out = cur_jiffies;
+                       dev->out_seq_offset = buffers_since_start;
+                       buffers_since_start = 0;
+               }
+               dev->out_seq_count = buffers_since_start + dev->out_seq_offset;
+               dev->vid_out_seq_count = dev->out_seq_count - dev->vid_out_seq_start;
+               dev->vbi_out_seq_count = dev->out_seq_count - dev->vbi_out_seq_start;
+
+               vivid_thread_vid_out_tick(dev);
+               mutex_unlock(&dev->mutex);
+
+               /*
+                * Calculate the number of 'numerators' streamed since we started,
+                * not including the current buffer.
+                */
+               numerators_since_start = buffers_since_start * numerator;
+
+               /* And the number of jiffies since we started */
+               jiffies_since_start = jiffies - dev->jiffies_vid_out;
+
+               /* Increase by the 'numerator' of one buffer */
+               numerators_since_start += numerator;
+               /*
+                * Calculate when that next buffer is supposed to start
+                * in jiffies since we started streaming.
+                */
+               next_jiffies_since_start = numerators_since_start * HZ +
+                                          denominator / 2;
+               do_div(next_jiffies_since_start, denominator);
+               /* If it is in the past, then just schedule asap */
+               if (next_jiffies_since_start < jiffies_since_start)
+                       next_jiffies_since_start = jiffies_since_start;
+
+               wait_jiffies = next_jiffies_since_start - jiffies_since_start;
+               schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1);
+       }
+       dprintk(dev, 1, "Video Output Thread End\n");
+       return 0;
+}
+
+static void vivid_grab_controls(struct vivid_dev *dev, bool grab)
+{
+       v4l2_ctrl_grab(dev->ctrl_has_crop_out, grab);
+       v4l2_ctrl_grab(dev->ctrl_has_compose_out, grab);
+       v4l2_ctrl_grab(dev->ctrl_has_scaler_out, grab);
+       v4l2_ctrl_grab(dev->ctrl_tx_mode, grab);
+       v4l2_ctrl_grab(dev->ctrl_tx_rgb_range, grab);
+}
+
+int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
+{
+       dprintk(dev, 1, "%s\n", __func__);
+
+       if (dev->kthread_vid_out) {
+               u32 seq_count = dev->out_seq_count + dev->seq_wrap * 128;
+
+               if (pstreaming == &dev->vid_out_streaming)
+                       dev->vid_out_seq_start = seq_count;
+               else
+                       dev->vbi_out_seq_start = seq_count;
+               *pstreaming = true;
+               return 0;
+       }
+
+       /* Resets frame counters */
+       dev->jiffies_vid_out = jiffies;
+       dev->vid_out_seq_start = dev->seq_wrap * 128;
+       dev->vbi_out_seq_start = dev->seq_wrap * 128;
+
+       dev->kthread_vid_out = kthread_run(vivid_thread_vid_out, dev,
+                       "%s-vid-out", dev->v4l2_dev.name);
+
+       if (IS_ERR(dev->kthread_vid_out)) {
+               v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+               return PTR_ERR(dev->kthread_vid_out);
+       }
+       *pstreaming = true;
+       vivid_grab_controls(dev, true);
+
+       dprintk(dev, 1, "returning from %s\n", __func__);
+       return 0;
+}
+
+void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
+{
+       dprintk(dev, 1, "%s\n", __func__);
+
+       if (dev->kthread_vid_out == NULL)
+               return;
+
+       *pstreaming = false;
+       if (pstreaming == &dev->vid_out_streaming) {
+               /* Release all active buffers */
+               while (!list_empty(&dev->vid_out_active)) {
+                       struct vivid_buffer *buf;
+
+                       buf = list_entry(dev->vid_out_active.next,
+                                        struct vivid_buffer, list);
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+                       dprintk(dev, 2, "vid_out buffer %d done\n",
+                               buf->vb.v4l2_buf.index);
+               }
+       }
+
+       if (pstreaming == &dev->vbi_out_streaming) {
+               while (!list_empty(&dev->vbi_out_active)) {
+                       struct vivid_buffer *buf;
+
+                       buf = list_entry(dev->vbi_out_active.next,
+                                        struct vivid_buffer, list);
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+                       dprintk(dev, 2, "vbi_out buffer %d done\n",
+                               buf->vb.v4l2_buf.index);
+               }
+       }
+
+       if (dev->vid_out_streaming || dev->vbi_out_streaming)
+               return;
+
+       /* shutdown control thread */
+       vivid_grab_controls(dev, false);
+       mutex_unlock(&dev->mutex);
+       kthread_stop(dev->kthread_vid_out);
+       dev->kthread_vid_out = NULL;
+       mutex_lock(&dev->mutex);
+}
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.h b/drivers/media/platform/vivid/vivid-kthread-out.h
new file mode 100644 (file)
index 0000000..2bf04a1
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * vivid-kthread-out.h - video/vbi output thread support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_KTHREAD_OUT_H_
+#define _VIVID_KTHREAD_OUT_H_
+
+int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming);
+void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c
new file mode 100644 (file)
index 0000000..084d346
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * vivid-osd.c - osd support for testing overlays.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/fb.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
+
+#include "vivid-core.h"
+#include "vivid-osd.h"
+
+#define MAX_OSD_WIDTH  720
+#define MAX_OSD_HEIGHT 576
+
+/*
+ * Order: white, yellow, cyan, green, magenta, red, blue, black,
+ * and same again with the alpha bit set (if any)
+ */
+static const u16 rgb555[16] = {
+       0x7fff, 0x7fe0, 0x03ff, 0x03e0, 0x7c1f, 0x7c00, 0x001f, 0x0000,
+       0xffff, 0xffe0, 0x83ff, 0x83e0, 0xfc1f, 0xfc00, 0x801f, 0x8000
+};
+
+static const u16 rgb565[16] = {
+       0xffff, 0xffe0, 0x07ff, 0x07e0, 0xf81f, 0xf800, 0x001f, 0x0000,
+       0xffff, 0xffe0, 0x07ff, 0x07e0, 0xf81f, 0xf800, 0x001f, 0x0000
+};
+
+void vivid_clear_fb(struct vivid_dev *dev)
+{
+       void *p = dev->video_vbase;
+       const u16 *rgb = rgb555;
+       unsigned x, y;
+
+       if (dev->fb_defined.green.length == 6)
+               rgb = rgb565;
+
+       for (y = 0; y < dev->display_height; y++) {
+               u16 *d = p;
+
+               for (x = 0; x < dev->display_width; x++)
+                       d[x] = rgb[(y / 16 + x / 16) % 16];
+               p += dev->display_byte_stride;
+       }
+}
+
+/* --------------------------------------------------------------------- */
+
+static int vivid_fb_ioctl(struct fb_info *info, unsigned cmd, unsigned long arg)
+{
+       struct vivid_dev *dev = (struct vivid_dev *)info->par;
+
+       switch (cmd) {
+       case FBIOGET_VBLANK: {
+               struct fb_vblank vblank;
+
+               vblank.flags = FB_VBLANK_HAVE_COUNT | FB_VBLANK_HAVE_VCOUNT |
+                       FB_VBLANK_HAVE_VSYNC;
+               vblank.count = 0;
+               vblank.vcount = 0;
+               vblank.hcount = 0;
+               if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
+                       return -EFAULT;
+               return 0;
+       }
+
+       default:
+               dprintk(dev, 1, "Unknown ioctl %08x\n", cmd);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/* Framebuffer device handling */
+
+static int vivid_fb_set_var(struct vivid_dev *dev, struct fb_var_screeninfo *var)
+{
+       dprintk(dev, 1, "vivid_fb_set_var\n");
+
+       if (var->bits_per_pixel != 16) {
+               dprintk(dev, 1, "vivid_fb_set_var - Invalid bpp\n");
+               return -EINVAL;
+       }
+       dev->display_byte_stride = var->xres * dev->bytes_per_pixel;
+
+       return 0;
+}
+
+static int vivid_fb_get_fix(struct vivid_dev *dev, struct fb_fix_screeninfo *fix)
+{
+       dprintk(dev, 1, "vivid_fb_get_fix\n");
+       memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+       strlcpy(fix->id, "vioverlay fb", sizeof(fix->id));
+       fix->smem_start = dev->video_pbase;
+       fix->smem_len = dev->video_buffer_size;
+       fix->type = FB_TYPE_PACKED_PIXELS;
+       fix->visual = FB_VISUAL_TRUECOLOR;
+       fix->xpanstep = 1;
+       fix->ypanstep = 1;
+       fix->ywrapstep = 0;
+       fix->line_length = dev->display_byte_stride;
+       fix->accel = FB_ACCEL_NONE;
+       return 0;
+}
+
+/* Check the requested display mode, returning -EINVAL if we can't
+   handle it. */
+
+static int _vivid_fb_check_var(struct fb_var_screeninfo *var, struct vivid_dev *dev)
+{
+       dprintk(dev, 1, "vivid_fb_check_var\n");
+
+       var->bits_per_pixel = 16;
+       if (var->green.length == 5) {
+               var->red.offset = 10;
+               var->red.length = 5;
+               var->green.offset = 5;
+               var->green.length = 5;
+               var->blue.offset = 0;
+               var->blue.length = 5;
+               var->transp.offset = 15;
+               var->transp.length = 1;
+       } else {
+               var->red.offset = 11;
+               var->red.length = 5;
+               var->green.offset = 5;
+               var->green.length = 6;
+               var->blue.offset = 0;
+               var->blue.length = 5;
+               var->transp.offset = 0;
+               var->transp.length = 0;
+       }
+       var->xoffset = var->yoffset = 0;
+       var->left_margin = var->upper_margin = 0;
+       var->nonstd = 0;
+
+       var->vmode &= ~FB_VMODE_MASK;
+       var->vmode = FB_VMODE_NONINTERLACED;
+
+       /* Dummy values */
+       var->hsync_len = 24;
+       var->vsync_len = 2;
+       var->pixclock = 84316;
+       var->right_margin = 776;
+       var->lower_margin = 591;
+       return 0;
+}
+
+static int vivid_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct vivid_dev *dev = (struct vivid_dev *) info->par;
+
+       dprintk(dev, 1, "vivid_fb_check_var\n");
+       return _vivid_fb_check_var(var, dev);
+}
+
+static int vivid_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       return 0;
+}
+
+static int vivid_fb_set_par(struct fb_info *info)
+{
+       int rc = 0;
+       struct vivid_dev *dev = (struct vivid_dev *) info->par;
+
+       dprintk(dev, 1, "vivid_fb_set_par\n");
+
+       rc = vivid_fb_set_var(dev, &info->var);
+       vivid_fb_get_fix(dev, &info->fix);
+       return rc;
+}
+
+static int vivid_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                               unsigned blue, unsigned transp,
+                               struct fb_info *info)
+{
+       u32 color, *palette;
+
+       if (regno >= info->cmap.len)
+               return -EINVAL;
+
+       color = ((transp & 0xFF00) << 16) | ((red & 0xFF00) << 8) |
+                (green & 0xFF00) | ((blue & 0xFF00) >> 8);
+       if (regno >= 16)
+               return -EINVAL;
+
+       palette = info->pseudo_palette;
+       if (info->var.bits_per_pixel == 16) {
+               switch (info->var.green.length) {
+               case 6:
+                       color = (red & 0xf800) |
+                               ((green & 0xfc00) >> 5) |
+                               ((blue & 0xf800) >> 11);
+                       break;
+               case 5:
+                       color = ((red & 0xf800) >> 1) |
+                               ((green & 0xf800) >> 6) |
+                               ((blue & 0xf800) >> 11) |
+                               (transp ? 0x8000 : 0);
+                       break;
+               }
+       }
+       palette[regno] = color;
+       return 0;
+}
+
+/* We don't really support blanking. All this does is enable or
+   disable the OSD. */
+static int vivid_fb_blank(int blank_mode, struct fb_info *info)
+{
+       struct vivid_dev *dev = (struct vivid_dev *)info->par;
+
+       dprintk(dev, 1, "Set blanking mode : %d\n", blank_mode);
+       switch (blank_mode) {
+       case FB_BLANK_UNBLANK:
+               break;
+       case FB_BLANK_NORMAL:
+       case FB_BLANK_HSYNC_SUSPEND:
+       case FB_BLANK_VSYNC_SUSPEND:
+       case FB_BLANK_POWERDOWN:
+               break;
+       }
+       return 0;
+}
+
+static struct fb_ops vivid_fb_ops = {
+       .owner = THIS_MODULE,
+       .fb_check_var   = vivid_fb_check_var,
+       .fb_set_par     = vivid_fb_set_par,
+       .fb_setcolreg   = vivid_fb_setcolreg,
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+       .fb_cursor      = NULL,
+       .fb_ioctl       = vivid_fb_ioctl,
+       .fb_pan_display = vivid_fb_pan_display,
+       .fb_blank       = vivid_fb_blank,
+};
+
+/* Initialization */
+
+
+/* Setup our initial video mode */
+static int vivid_fb_init_vidmode(struct vivid_dev *dev)
+{
+       struct v4l2_rect start_window;
+
+       /* Color mode */
+
+       dev->bits_per_pixel = 16;
+       dev->bytes_per_pixel = dev->bits_per_pixel / 8;
+
+       start_window.width = MAX_OSD_WIDTH;
+       start_window.left = 0;
+
+       dev->display_byte_stride = start_window.width * dev->bytes_per_pixel;
+
+       /* Vertical size & position */
+
+       start_window.height = MAX_OSD_HEIGHT;
+       start_window.top = 0;
+
+       dev->display_width = start_window.width;
+       dev->display_height = start_window.height;
+
+       /* Generate a valid fb_var_screeninfo */
+
+       dev->fb_defined.xres = dev->display_width;
+       dev->fb_defined.yres = dev->display_height;
+       dev->fb_defined.xres_virtual = dev->display_width;
+       dev->fb_defined.yres_virtual = dev->display_height;
+       dev->fb_defined.bits_per_pixel = dev->bits_per_pixel;
+       dev->fb_defined.vmode = FB_VMODE_NONINTERLACED;
+       dev->fb_defined.left_margin = start_window.left + 1;
+       dev->fb_defined.upper_margin = start_window.top + 1;
+       dev->fb_defined.accel_flags = FB_ACCEL_NONE;
+       dev->fb_defined.nonstd = 0;
+       /* set default to 1:5:5:5 */
+       dev->fb_defined.green.length = 5;
+
+       /* We've filled in the most data, let the usual mode check
+          routine fill in the rest. */
+       _vivid_fb_check_var(&dev->fb_defined, dev);
+
+       /* Generate valid fb_fix_screeninfo */
+
+       vivid_fb_get_fix(dev, &dev->fb_fix);
+
+       /* Generate valid fb_info */
+
+       dev->fb_info.node = -1;
+       dev->fb_info.flags = FBINFO_FLAG_DEFAULT;
+       dev->fb_info.fbops = &vivid_fb_ops;
+       dev->fb_info.par = dev;
+       dev->fb_info.var = dev->fb_defined;
+       dev->fb_info.fix = dev->fb_fix;
+       dev->fb_info.screen_base = (u8 __iomem *)dev->video_vbase;
+       dev->fb_info.fbops = &vivid_fb_ops;
+
+       /* Supply some monitor specs. Bogus values will do for now */
+       dev->fb_info.monspecs.hfmin = 8000;
+       dev->fb_info.monspecs.hfmax = 70000;
+       dev->fb_info.monspecs.vfmin = 10;
+       dev->fb_info.monspecs.vfmax = 100;
+
+       /* Allocate color map */
+       if (fb_alloc_cmap(&dev->fb_info.cmap, 256, 1)) {
+               pr_err("abort, unable to alloc cmap\n");
+               return -ENOMEM;
+       }
+
+       /* Allocate the pseudo palette */
+       dev->fb_info.pseudo_palette = kmalloc_array(16, sizeof(u32), GFP_KERNEL);
+
+       return dev->fb_info.pseudo_palette ? 0 : -ENOMEM;
+}
+
+/* Release any memory we've grabbed */
+void vivid_fb_release_buffers(struct vivid_dev *dev)
+{
+       if (dev->video_vbase == NULL)
+               return;
+
+       /* Release cmap */
+       if (dev->fb_info.cmap.len)
+               fb_dealloc_cmap(&dev->fb_info.cmap);
+
+       /* Release pseudo palette */
+       kfree(dev->fb_info.pseudo_palette);
+       kfree((void *)dev->video_vbase);
+}
+
+/* Initialize the specified card */
+
+int vivid_fb_init(struct vivid_dev *dev)
+{
+       int ret;
+
+       dev->video_buffer_size = MAX_OSD_HEIGHT * MAX_OSD_WIDTH * 2;
+       dev->video_vbase = kzalloc(dev->video_buffer_size, GFP_KERNEL | GFP_DMA32);
+       if (dev->video_vbase == NULL)
+               return -ENOMEM;
+       dev->video_pbase = virt_to_phys(dev->video_vbase);
+
+       pr_info("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
+                       dev->video_pbase, dev->video_vbase,
+                       dev->video_buffer_size / 1024);
+
+       /* Set the startup video mode information */
+       ret = vivid_fb_init_vidmode(dev);
+       if (ret) {
+               vivid_fb_release_buffers(dev);
+               return ret;
+       }
+
+       vivid_clear_fb(dev);
+
+       /* Register the framebuffer */
+       if (register_framebuffer(&dev->fb_info) < 0) {
+               vivid_fb_release_buffers(dev);
+               return -EINVAL;
+       }
+
+       /* Set the card to the requested mode */
+       vivid_fb_set_par(&dev->fb_info);
+       return 0;
+
+}
diff --git a/drivers/media/platform/vivid/vivid-osd.h b/drivers/media/platform/vivid/vivid-osd.h
new file mode 100644 (file)
index 0000000..57c9daa
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * vivid-osd.h - output overlay support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_OSD_H_
+#define _VIVID_OSD_H_
+
+int vivid_fb_init(struct vivid_dev *dev);
+void vivid_fb_release_buffers(struct vivid_dev *dev);
+void vivid_clear_fb(struct vivid_dev *dev);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-radio-common.c b/drivers/media/platform/vivid/vivid-radio-common.c
new file mode 100644 (file)
index 0000000..78c1e92
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * vivid-radio-common.c - common radio rx/tx support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+
+#include "vivid-core.h"
+#include "vivid-ctrls.h"
+#include "vivid-radio-common.h"
+#include "vivid-rds-gen.h"
+
+/*
+ * These functions are shared between the vivid receiver and transmitter
+ * since both use the same frequency bands.
+ */
+
+const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS] = {
+       /* Band FM */
+       {
+               .type = V4L2_TUNER_RADIO,
+               .index = 0,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                             V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow   = FM_FREQ_RANGE_LOW,
+               .rangehigh  = FM_FREQ_RANGE_HIGH,
+               .modulation = V4L2_BAND_MODULATION_FM,
+       },
+       /* Band AM */
+       {
+               .type = V4L2_TUNER_RADIO,
+               .index = 1,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow   = AM_FREQ_RANGE_LOW,
+               .rangehigh  = AM_FREQ_RANGE_HIGH,
+               .modulation = V4L2_BAND_MODULATION_AM,
+       },
+       /* Band SW */
+       {
+               .type = V4L2_TUNER_RADIO,
+               .index = 2,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow   = SW_FREQ_RANGE_LOW,
+               .rangehigh  = SW_FREQ_RANGE_HIGH,
+               .modulation = V4L2_BAND_MODULATION_AM,
+       },
+};
+
+/*
+ * Initialize the RDS generator. If we can loop, then the RDS generator
+ * is set up with the values from the RDS TX controls, otherwise it
+ * will fill in standard values using one of two alternates.
+ */
+void vivid_radio_rds_init(struct vivid_dev *dev)
+{
+       struct vivid_rds_gen *rds = &dev->rds_gen;
+       bool alt = dev->radio_rx_rds_use_alternates;
+
+       /* Do nothing, blocks will be filled by the transmitter */
+       if (dev->radio_rds_loop && !dev->radio_tx_rds_controls)
+               return;
+
+       if (dev->radio_rds_loop) {
+               v4l2_ctrl_lock(dev->radio_tx_rds_pi);
+               rds->picode = dev->radio_tx_rds_pi->cur.val;
+               rds->pty = dev->radio_tx_rds_pty->cur.val;
+               rds->mono_stereo = dev->radio_tx_rds_mono_stereo->cur.val;
+               rds->art_head = dev->radio_tx_rds_art_head->cur.val;
+               rds->compressed = dev->radio_tx_rds_compressed->cur.val;
+               rds->dyn_pty = dev->radio_tx_rds_dyn_pty->cur.val;
+               rds->ta = dev->radio_tx_rds_ta->cur.val;
+               rds->tp = dev->radio_tx_rds_tp->cur.val;
+               rds->ms = dev->radio_tx_rds_ms->cur.val;
+               strlcpy(rds->psname,
+                       dev->radio_tx_rds_psname->p_cur.p_char,
+                       sizeof(rds->psname));
+               strlcpy(rds->radiotext,
+                       dev->radio_tx_rds_radiotext->p_cur.p_char + alt * 64,
+                       sizeof(rds->radiotext));
+               v4l2_ctrl_unlock(dev->radio_tx_rds_pi);
+       } else {
+               vivid_rds_gen_fill(rds, dev->radio_rx_freq, alt);
+       }
+       if (dev->radio_rx_rds_controls) {
+               v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, rds->pty);
+               v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, rds->ta);
+               v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, rds->tp);
+               v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, rds->ms);
+               v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, rds->psname);
+               v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, rds->radiotext);
+               if (!dev->radio_rds_loop)
+                       dev->radio_rx_rds_use_alternates = !dev->radio_rx_rds_use_alternates;
+       }
+       vivid_rds_generate(rds);
+}
+
+/*
+ * Calculate the emulated signal quality taking into account the frequency
+ * the transmitter is using.
+ */
+static void vivid_radio_calc_sig_qual(struct vivid_dev *dev)
+{
+       int mod = 16000;
+       int delta = 800;
+       int sig_qual, sig_qual_tx = mod;
+
+       /*
+        * For SW and FM there is a channel every 1000 kHz, for AM there is one
+        * every 100 kHz.
+        */
+       if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH) {
+               mod /= 10;
+               delta /= 10;
+       }
+       sig_qual = (dev->radio_rx_freq + delta) % mod - delta;
+       if (dev->has_radio_tx)
+               sig_qual_tx = dev->radio_rx_freq - dev->radio_tx_freq;
+       if (abs(sig_qual_tx) <= abs(sig_qual)) {
+               sig_qual = sig_qual_tx;
+               /*
+                * Zero the internal rds buffer if we are going to loop
+                * rds blocks.
+                */
+               if (!dev->radio_rds_loop && !dev->radio_tx_rds_controls)
+                       memset(dev->rds_gen.data, 0,
+                              sizeof(dev->rds_gen.data));
+               dev->radio_rds_loop = dev->radio_rx_freq >= FM_FREQ_RANGE_LOW;
+       } else {
+               dev->radio_rds_loop = false;
+       }
+       if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH)
+               sig_qual *= 10;
+       dev->radio_rx_sig_qual = sig_qual;
+}
+
+int vivid_radio_g_frequency(struct file *file, const unsigned *pfreq, struct v4l2_frequency *vf)
+{
+       if (vf->tuner != 0)
+               return -EINVAL;
+       vf->frequency = *pfreq;
+       return 0;
+}
+
+int vivid_radio_s_frequency(struct file *file, unsigned *pfreq, const struct v4l2_frequency *vf)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       unsigned freq;
+       unsigned band;
+
+       if (vf->tuner != 0)
+               return -EINVAL;
+
+       if (vf->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) / 2)
+               band = BAND_FM;
+       else if (vf->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) / 2)
+               band = BAND_AM;
+       else
+               band = BAND_SW;
+
+       freq = clamp_t(u32, vf->frequency, vivid_radio_bands[band].rangelow,
+                                          vivid_radio_bands[band].rangehigh);
+       *pfreq = freq;
+
+       /*
+        * For both receiver and transmitter recalculate the signal quality
+        * (since that depends on both frequencies) and re-init the rds
+        * generator.
+        */
+       vivid_radio_calc_sig_qual(dev);
+       vivid_radio_rds_init(dev);
+       return 0;
+}
diff --git a/drivers/media/platform/vivid/vivid-radio-common.h b/drivers/media/platform/vivid/vivid-radio-common.h
new file mode 100644 (file)
index 0000000..92fe589
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * vivid-radio-common.h - common radio rx/tx support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_RADIO_COMMON_H_
+#define _VIVID_RADIO_COMMON_H_
+
+/* The supported radio frequency ranges in kHz */
+#define FM_FREQ_RANGE_LOW       (64000U * 16U)
+#define FM_FREQ_RANGE_HIGH      (108000U * 16U)
+#define AM_FREQ_RANGE_LOW       (520U * 16U)
+#define AM_FREQ_RANGE_HIGH      (1710U * 16U)
+#define SW_FREQ_RANGE_LOW       (2300U * 16U)
+#define SW_FREQ_RANGE_HIGH      (26100U * 16U)
+
+enum { BAND_FM, BAND_AM, BAND_SW, TOT_BANDS };
+
+extern const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS];
+
+int vivid_radio_g_frequency(struct file *file, const unsigned *freq, struct v4l2_frequency *vf);
+int vivid_radio_s_frequency(struct file *file, unsigned *freq, const struct v4l2_frequency *vf);
+
+void vivid_radio_rds_init(struct vivid_dev *dev);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-radio-rx.c b/drivers/media/platform/vivid/vivid-radio-rx.c
new file mode 100644 (file)
index 0000000..c7651a5
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * vivid-radio-rx.c - radio receiver support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-ctrls.h"
+#include "vivid-radio-common.h"
+#include "vivid-rds-gen.h"
+#include "vivid-radio-rx.h"
+
+ssize_t vivid_radio_rx_read(struct file *file, char __user *buf,
+                        size_t size, loff_t *offset)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct timespec ts;
+       struct v4l2_rds_data *data = dev->rds_gen.data;
+       bool use_alternates;
+       unsigned blk;
+       int perc;
+       int i;
+
+       if (dev->radio_rx_rds_controls)
+               return -EINVAL;
+       if (size < sizeof(*data))
+               return 0;
+       size = sizeof(*data) * (size / sizeof(*data));
+
+       if (mutex_lock_interruptible(&dev->mutex))
+               return -ERESTARTSYS;
+       if (dev->radio_rx_rds_owner &&
+           file->private_data != dev->radio_rx_rds_owner) {
+               mutex_unlock(&dev->mutex);
+               return -EBUSY;
+       }
+       if (dev->radio_rx_rds_owner == NULL) {
+               vivid_radio_rds_init(dev);
+               dev->radio_rx_rds_owner = file->private_data;
+       }
+
+retry:
+       ktime_get_ts(&ts);
+       use_alternates = ts.tv_sec % 10 >= 5;
+       if (dev->radio_rx_rds_last_block == 0 ||
+           dev->radio_rx_rds_use_alternates != use_alternates) {
+               dev->radio_rx_rds_use_alternates = use_alternates;
+               /* Re-init the RDS generator */
+               vivid_radio_rds_init(dev);
+       }
+       ts = timespec_sub(ts, dev->radio_rds_init_ts);
+       blk = ts.tv_sec * 100 + ts.tv_nsec / 10000000;
+       blk = (blk * VIVID_RDS_GEN_BLOCKS) / 500;
+       if (blk >= dev->radio_rx_rds_last_block + VIVID_RDS_GEN_BLOCKS)
+               dev->radio_rx_rds_last_block = blk - VIVID_RDS_GEN_BLOCKS + 1;
+
+       /*
+        * No data is available if there hasn't been time to get new data,
+        * or if the RDS receiver has been disabled, or if we use the data
+        * from the RDS transmitter and that RDS transmitter has been disabled,
+        * or if the signal quality is too weak.
+        */
+       if (blk == dev->radio_rx_rds_last_block || !dev->radio_rx_rds_enabled ||
+           (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) ||
+           abs(dev->radio_rx_sig_qual) > 200) {
+               mutex_unlock(&dev->mutex);
+               if (file->f_flags & O_NONBLOCK)
+                       return -EWOULDBLOCK;
+               if (msleep_interruptible(20) && signal_pending(current))
+                       return -EINTR;
+               if (mutex_lock_interruptible(&dev->mutex))
+                       return -ERESTARTSYS;
+               goto retry;
+       }
+
+       /* abs(dev->radio_rx_sig_qual) <= 200, map that to a 0-50% range */
+       perc = abs(dev->radio_rx_sig_qual) / 4;
+
+       for (i = 0; i < size && blk > dev->radio_rx_rds_last_block;
+                       dev->radio_rx_rds_last_block++) {
+               unsigned data_blk = dev->radio_rx_rds_last_block % VIVID_RDS_GEN_BLOCKS;
+               struct v4l2_rds_data rds = data[data_blk];
+
+               if (data_blk == 0 && dev->radio_rds_loop)
+                       vivid_radio_rds_init(dev);
+               if (perc && prandom_u32_max(100) < perc) {
+                       switch (prandom_u32_max(4)) {
+                       case 0:
+                               rds.block |= V4L2_RDS_BLOCK_CORRECTED;
+                               break;
+                       case 1:
+                               rds.block |= V4L2_RDS_BLOCK_INVALID;
+                               break;
+                       case 2:
+                               rds.block |= V4L2_RDS_BLOCK_ERROR;
+                               rds.lsb = prandom_u32_max(256);
+                               rds.msb = prandom_u32_max(256);
+                               break;
+                       case 3: /* Skip block altogether */
+                               if (i)
+                                       continue;
+                               /*
+                                * Must make sure at least one block is
+                                * returned, otherwise the application
+                                * might think that end-of-file occurred.
+                                */
+                               break;
+                       }
+               }
+               if (copy_to_user(buf + i, &rds, sizeof(rds))) {
+                       i = -EFAULT;
+                       break;
+               }
+               i += sizeof(rds);
+       }
+       mutex_unlock(&dev->mutex);
+       return i;
+}
+
+unsigned int vivid_radio_rx_poll(struct file *file, struct poll_table_struct *wait)
+{
+       return POLLIN | POLLRDNORM | v4l2_ctrl_poll(file, wait);
+}
+
+int vivid_radio_rx_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band)
+{
+       if (band->tuner != 0)
+               return -EINVAL;
+
+       if (band->index >= TOT_BANDS)
+               return -EINVAL;
+
+       *band = vivid_radio_bands[band->index];
+       return 0;
+}
+
+int vivid_radio_rx_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       unsigned low, high;
+       unsigned freq;
+       unsigned spacing;
+       unsigned band;
+
+       if (a->tuner)
+               return -EINVAL;
+       if (a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_BOUNDED)
+               return -EINVAL;
+
+       if (!a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_WRAP)
+               return -EINVAL;
+       if (!a->rangelow ^ !a->rangehigh)
+               return -EINVAL;
+
+       if (file->f_flags & O_NONBLOCK)
+               return -EWOULDBLOCK;
+
+       if (a->rangelow) {
+               for (band = 0; band < TOT_BANDS; band++)
+                       if (a->rangelow >= vivid_radio_bands[band].rangelow &&
+                           a->rangehigh <= vivid_radio_bands[band].rangehigh)
+                               break;
+               if (band == TOT_BANDS)
+                       return -EINVAL;
+               if (!dev->radio_rx_hw_seek_prog_lim &&
+                   (a->rangelow != vivid_radio_bands[band].rangelow ||
+                    a->rangehigh != vivid_radio_bands[band].rangehigh))
+                       return -EINVAL;
+               low = a->rangelow;
+               high = a->rangehigh;
+       } else {
+               for (band = 0; band < TOT_BANDS; band++)
+                       if (dev->radio_rx_freq >= vivid_radio_bands[band].rangelow &&
+                           dev->radio_rx_freq <= vivid_radio_bands[band].rangehigh)
+                               break;
+               low = vivid_radio_bands[band].rangelow;
+               high = vivid_radio_bands[band].rangehigh;
+       }
+       spacing = band == BAND_AM ? 1600 : 16000;
+       freq = clamp(dev->radio_rx_freq, low, high);
+
+       if (a->seek_upward) {
+               freq = spacing * (freq / spacing) + spacing;
+               if (freq > high) {
+                       if (!a->wrap_around)
+                               return -ENODATA;
+                       freq = spacing * (low / spacing) + spacing;
+                       if (freq >= dev->radio_rx_freq)
+                               return -ENODATA;
+               }
+       } else {
+               freq = spacing * ((freq + spacing - 1) / spacing) - spacing;
+               if (freq < low) {
+                       if (!a->wrap_around)
+                               return -ENODATA;
+                       freq = spacing * ((high + spacing - 1) / spacing) - spacing;
+                       if (freq <= dev->radio_rx_freq)
+                               return -ENODATA;
+               }
+       }
+       return 0;
+}
+
+int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       int delta = 800;
+       int sig_qual;
+
+       if (vt->index > 0)
+               return -EINVAL;
+
+       strlcpy(vt->name, "AM/FM/SW Receiver", sizeof(vt->name));
+       vt->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                        V4L2_TUNER_CAP_FREQ_BANDS | V4L2_TUNER_CAP_RDS |
+                        (dev->radio_rx_rds_controls ?
+                               V4L2_TUNER_CAP_RDS_CONTROLS :
+                               V4L2_TUNER_CAP_RDS_BLOCK_IO) |
+                        (dev->radio_rx_hw_seek_prog_lim ?
+                               V4L2_TUNER_CAP_HWSEEK_PROG_LIM : 0);
+       switch (dev->radio_rx_hw_seek_mode) {
+       case VIVID_HW_SEEK_BOUNDED:
+               vt->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
+               break;
+       case VIVID_HW_SEEK_WRAP:
+               vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP;
+               break;
+       case VIVID_HW_SEEK_BOTH:
+               vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP |
+                                 V4L2_TUNER_CAP_HWSEEK_BOUNDED;
+               break;
+       }
+       vt->rangelow = AM_FREQ_RANGE_LOW;
+       vt->rangehigh = FM_FREQ_RANGE_HIGH;
+       sig_qual = dev->radio_rx_sig_qual;
+       vt->signal = abs(sig_qual) > delta ? 0 :
+                    0xffff - (abs(sig_qual) * 0xffff) / delta;
+       vt->afc = sig_qual > delta ? 0 : sig_qual;
+       if (abs(sig_qual) > delta)
+               vt->rxsubchans = 0;
+       else if (dev->radio_rx_freq < FM_FREQ_RANGE_LOW || vt->signal < 0x8000)
+               vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+       else if (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_STEREO))
+               vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+       else
+               vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+       if (dev->radio_rx_rds_enabled &&
+           (!dev->radio_rds_loop || (dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) &&
+           dev->radio_rx_freq >= FM_FREQ_RANGE_LOW && vt->signal >= 0xc000)
+               vt->rxsubchans |= V4L2_TUNER_SUB_RDS;
+       if (dev->radio_rx_rds_controls)
+               vivid_radio_rds_init(dev);
+       vt->audmode = dev->radio_rx_audmode;
+       return 0;
+}
+
+int vivid_radio_rx_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (vt->index)
+               return -EINVAL;
+       dev->radio_rx_audmode = vt->audmode >= V4L2_TUNER_MODE_STEREO;
+       return 0;
+}
diff --git a/drivers/media/platform/vivid/vivid-radio-rx.h b/drivers/media/platform/vivid/vivid-radio-rx.h
new file mode 100644 (file)
index 0000000..1077d8f
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * vivid-radio-rx.h - radio receiver support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_RADIO_RX_H_
+#define _VIVID_RADIO_RX_H_
+
+ssize_t vivid_radio_rx_read(struct file *, char __user *, size_t, loff_t *);
+unsigned int vivid_radio_rx_poll(struct file *file, struct poll_table_struct *wait);
+
+int vivid_radio_rx_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band);
+int vivid_radio_rx_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a);
+int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt);
+int vivid_radio_rx_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-radio-tx.c b/drivers/media/platform/vivid/vivid-radio-tx.c
new file mode 100644 (file)
index 0000000..8c59d4f
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * vivid-radio-tx.c - radio transmitter support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-ctrls.h"
+#include "vivid-radio-common.h"
+#include "vivid-radio-tx.h"
+
+ssize_t vivid_radio_tx_write(struct file *file, const char __user *buf,
+                         size_t size, loff_t *offset)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_rds_data *data = dev->rds_gen.data;
+       struct timespec ts;
+       unsigned blk;
+       int i;
+
+       if (dev->radio_tx_rds_controls)
+               return -EINVAL;
+
+       if (size < sizeof(*data))
+               return -EINVAL;
+       size = sizeof(*data) * (size / sizeof(*data));
+
+       if (mutex_lock_interruptible(&dev->mutex))
+               return -ERESTARTSYS;
+       if (dev->radio_tx_rds_owner &&
+           file->private_data != dev->radio_tx_rds_owner) {
+               mutex_unlock(&dev->mutex);
+               return -EBUSY;
+       }
+       dev->radio_tx_rds_owner = file->private_data;
+
+retry:
+       ktime_get_ts(&ts);
+       ts = timespec_sub(ts, dev->radio_rds_init_ts);
+       blk = ts.tv_sec * 100 + ts.tv_nsec / 10000000;
+       blk = (blk * VIVID_RDS_GEN_BLOCKS) / 500;
+       if (blk - VIVID_RDS_GEN_BLOCKS >= dev->radio_tx_rds_last_block)
+               dev->radio_tx_rds_last_block = blk - VIVID_RDS_GEN_BLOCKS + 1;
+
+       /*
+        * No data is available if there hasn't been time to get new data,
+        * or if the RDS receiver has been disabled, or if we use the data
+        * from the RDS transmitter and that RDS transmitter has been disabled,
+        * or if the signal quality is too weak.
+        */
+       if (blk == dev->radio_tx_rds_last_block ||
+           !(dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) {
+               mutex_unlock(&dev->mutex);
+               if (file->f_flags & O_NONBLOCK)
+                       return -EWOULDBLOCK;
+               if (msleep_interruptible(20) && signal_pending(current))
+                       return -EINTR;
+               if (mutex_lock_interruptible(&dev->mutex))
+                       return -ERESTARTSYS;
+               goto retry;
+       }
+
+       for (i = 0; i < size && blk > dev->radio_tx_rds_last_block;
+                       dev->radio_tx_rds_last_block++) {
+               unsigned data_blk = dev->radio_tx_rds_last_block % VIVID_RDS_GEN_BLOCKS;
+               struct v4l2_rds_data rds;
+
+               if (copy_from_user(&rds, buf + i, sizeof(rds))) {
+                       i = -EFAULT;
+                       break;
+               }
+               i += sizeof(rds);
+               if (!dev->radio_rds_loop)
+                       continue;
+               if ((rds.block & V4L2_RDS_BLOCK_MSK) == V4L2_RDS_BLOCK_INVALID ||
+                   (rds.block & V4L2_RDS_BLOCK_ERROR))
+                       continue;
+               rds.block &= V4L2_RDS_BLOCK_MSK;
+               data[data_blk] = rds;
+       }
+       mutex_unlock(&dev->mutex);
+       return i;
+}
+
+unsigned int vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wait)
+{
+       return POLLOUT | POLLWRNORM | v4l2_ctrl_poll(file, wait);
+}
+
+int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (a->index > 0)
+               return -EINVAL;
+
+       strlcpy(a->name, "AM/FM/SW Transmitter", sizeof(a->name));
+       a->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                       V4L2_TUNER_CAP_FREQ_BANDS | V4L2_TUNER_CAP_RDS |
+                       (dev->radio_tx_rds_controls ?
+                               V4L2_TUNER_CAP_RDS_CONTROLS :
+                               V4L2_TUNER_CAP_RDS_BLOCK_IO);
+       a->rangelow = AM_FREQ_RANGE_LOW;
+       a->rangehigh = FM_FREQ_RANGE_HIGH;
+       a->txsubchans = dev->radio_tx_subchans;
+       return 0;
+}
+
+int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (a->index)
+               return -EINVAL;
+       if (a->txsubchans & ~0x13)
+               return -EINVAL;
+       dev->radio_tx_subchans = a->txsubchans;
+       return 0;
+}
diff --git a/drivers/media/platform/vivid/vivid-radio-tx.h b/drivers/media/platform/vivid/vivid-radio-tx.h
new file mode 100644 (file)
index 0000000..7f8ff75
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * vivid-radio-tx.h - radio transmitter support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_RADIO_TX_H_
+#define _VIVID_RADIO_TX_H_
+
+ssize_t vivid_radio_tx_write(struct file *, const char __user *, size_t, loff_t *);
+unsigned int vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wait);
+
+int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a);
+int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-rds-gen.c b/drivers/media/platform/vivid/vivid-rds-gen.c
new file mode 100644 (file)
index 0000000..c382343
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * vivid-rds-gen.c - rds (radio data system) generator support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+
+#include "vivid-rds-gen.h"
+
+static u8 vivid_get_di(const struct vivid_rds_gen *rds, unsigned grp)
+{
+       switch (grp) {
+       case 0:
+               return (rds->dyn_pty << 2) | (grp & 3);
+       case 1:
+               return (rds->compressed << 2) | (grp & 3);
+       case 2:
+               return (rds->art_head << 2) | (grp & 3);
+       case 3:
+               return (rds->mono_stereo << 2) | (grp & 3);
+       }
+       return 0;
+}
+
+/*
+ * This RDS generator creates 57 RDS groups (one group == four RDS blocks).
+ * Groups 0-3, 22-25 and 44-47 (spaced 22 groups apart) are filled with a
+ * standard 0B group containing the PI code and PS name.
+ *
+ * Groups 4-19 and 26-41 use group 2A for the radio text.
+ *
+ * Group 56 contains the time (group 4A).
+ *
+ * All remaining groups use a filler group 15B block that just repeats
+ * the PI and PTY codes.
+ */
+void vivid_rds_generate(struct vivid_rds_gen *rds)
+{
+       struct v4l2_rds_data *data = rds->data;
+       unsigned grp;
+       struct tm tm;
+       unsigned date;
+       unsigned time;
+       int l;
+
+       for (grp = 0; grp < VIVID_RDS_GEN_GROUPS; grp++, data += VIVID_RDS_GEN_BLKS_PER_GRP) {
+               data[0].lsb = rds->picode & 0xff;
+               data[0].msb = rds->picode >> 8;
+               data[0].block = V4L2_RDS_BLOCK_A | (V4L2_RDS_BLOCK_A << 3);
+               data[1].lsb = rds->pty << 5;
+               data[1].msb = (rds->pty >> 3) | (rds->tp << 2);
+               data[1].block = V4L2_RDS_BLOCK_B | (V4L2_RDS_BLOCK_B << 3);
+               data[3].block = V4L2_RDS_BLOCK_D | (V4L2_RDS_BLOCK_D << 3);
+
+               switch (grp) {
+               case 0 ... 3:
+               case 22 ... 25:
+               case 44 ... 47: /* Group 0B */
+                       data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
+                       data[1].lsb |= vivid_get_di(rds, grp % 22);
+                       data[1].msb |= 1 << 3;
+                       data[2].lsb = rds->picode & 0xff;
+                       data[2].msb = rds->picode >> 8;
+                       data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
+                       data[3].lsb = rds->psname[2 * (grp % 22) + 1];
+                       data[3].msb = rds->psname[2 * (grp % 22)];
+                       break;
+               case 4 ... 19:
+               case 26 ... 41: /* Group 2A */
+                       data[1].lsb |= (grp - 4) % 22;
+                       data[1].msb |= 4 << 3;
+                       data[2].msb = rds->radiotext[4 * ((grp - 4) % 22)];
+                       data[2].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 1];
+                       data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
+                       data[3].msb = rds->radiotext[4 * ((grp - 4) % 22) + 2];
+                       data[3].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 3];
+                       break;
+               case 56:
+                       /*
+                        * Group 4A
+                        *
+                        * Uses the algorithm from Annex G of the RDS standard
+                        * EN 50067:1998 to convert a UTC date to an RDS Modified
+                        * Julian Day.
+                        */
+                       time_to_tm(get_seconds(), 0, &tm);
+                       l = tm.tm_mon <= 1;
+                       date = 14956 + tm.tm_mday + ((tm.tm_year - l) * 1461) / 4 +
+                               ((tm.tm_mon + 2 + l * 12) * 306001) / 10000;
+                       time = (tm.tm_hour << 12) |
+                              (tm.tm_min << 6) |
+                              (sys_tz.tz_minuteswest >= 0 ? 0x20 : 0) |
+                              (abs(sys_tz.tz_minuteswest) / 30);
+                       data[1].lsb &= ~3;
+                       data[1].lsb |= date >> 15;
+                       data[1].msb |= 8 << 3;
+                       data[2].lsb = (date << 1) & 0xfe;
+                       data[2].lsb |= (time >> 16) & 1;
+                       data[2].msb = (date >> 7) & 0xff;
+                       data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
+                       data[3].lsb = time & 0xff;
+                       data[3].msb = (time >> 8) & 0xff;
+                       break;
+               default: /* Group 15B */
+                       data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
+                       data[1].lsb |= vivid_get_di(rds, grp % 22);
+                       data[1].msb |= 0x1f << 3;
+                       data[2].lsb = rds->picode & 0xff;
+                       data[2].msb = rds->picode >> 8;
+                       data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
+                       data[3].lsb = rds->pty << 5;
+                       data[3].lsb |= (rds->ta << 4) | (rds->ms << 3);
+                       data[3].lsb |= vivid_get_di(rds, grp % 22);
+                       data[3].msb |= rds->pty >> 3;
+                       data[3].msb |= 0x1f << 3;
+                       break;
+               }
+       }
+}
+
+void vivid_rds_gen_fill(struct vivid_rds_gen *rds, unsigned freq,
+                         bool alt)
+{
+       /* Alternate PTY between Info and Weather */
+       if (rds->use_rbds) {
+               rds->picode = 0x2e75; /* 'KLNX' call sign */
+               rds->pty = alt ? 29 : 2;
+       } else {
+               rds->picode = 0x8088;
+               rds->pty = alt ? 16 : 3;
+       }
+       rds->mono_stereo = true;
+       rds->art_head = false;
+       rds->compressed = false;
+       rds->dyn_pty = false;
+       rds->tp = true;
+       rds->ta = alt;
+       rds->ms = true;
+       snprintf(rds->psname, sizeof(rds->psname), "%6d.%1d",
+                freq / 16, ((freq & 0xf) * 10) / 16);
+       if (alt)
+               strlcpy(rds->radiotext,
+                       " The Radio Data System can switch between different Radio Texts ",
+                       sizeof(rds->radiotext));
+       else
+               strlcpy(rds->radiotext,
+                       "An example of Radio Text as transmitted by the Radio Data System",
+                       sizeof(rds->radiotext));
+}
diff --git a/drivers/media/platform/vivid/vivid-rds-gen.h b/drivers/media/platform/vivid/vivid-rds-gen.h
new file mode 100644 (file)
index 0000000..eff4bf5
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * vivid-rds-gen.h - rds (radio data system) generator support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_RDS_GEN_H_
+#define _VIVID_RDS_GEN_H_
+
+/*
+ * It takes almost exactly 5 seconds to transmit 57 RDS groups.
+ * Each group has 4 blocks and each block has a payload of 16 bits + a
+ * block identification. The driver will generate the contents of these
+ * 57 groups only when necessary and it will just be played continuously.
+ */
+#define VIVID_RDS_GEN_GROUPS 57
+#define VIVID_RDS_GEN_BLKS_PER_GRP 4
+#define VIVID_RDS_GEN_BLOCKS (VIVID_RDS_GEN_BLKS_PER_GRP * VIVID_RDS_GEN_GROUPS)
+
+struct vivid_rds_gen {
+       struct v4l2_rds_data    data[VIVID_RDS_GEN_BLOCKS];
+       bool                    use_rbds;
+       u16                     picode;
+       u8                      pty;
+       bool                    mono_stereo;
+       bool                    art_head;
+       bool                    compressed;
+       bool                    dyn_pty;
+       bool                    ta;
+       bool                    tp;
+       bool                    ms;
+       char                    psname[8 + 1];
+       char                    radiotext[64 + 1];
+};
+
+void vivid_rds_gen_fill(struct vivid_rds_gen *rds, unsigned freq,
+                   bool use_alternate);
+void vivid_rds_generate(struct vivid_rds_gen *rds);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
new file mode 100644 (file)
index 0000000..8c5d661
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * vivid-sdr-cap.c - software defined radio support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-ctrls.h"
+#include "vivid-sdr-cap.h"
+
+static const struct v4l2_frequency_band bands_adc[] = {
+       {
+               .tuner = 0,
+               .type = V4L2_TUNER_ADC,
+               .index = 0,
+               .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow   =  300000,
+               .rangehigh  =  300000,
+       },
+       {
+               .tuner = 0,
+               .type = V4L2_TUNER_ADC,
+               .index = 1,
+               .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow   =  900001,
+               .rangehigh  = 2800000,
+       },
+       {
+               .tuner = 0,
+               .type = V4L2_TUNER_ADC,
+               .index = 2,
+               .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow   = 3200000,
+               .rangehigh  = 3200000,
+       },
+};
+
+/* ADC band midpoints */
+#define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2)
+#define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2)
+
+static const struct v4l2_frequency_band bands_fm[] = {
+       {
+               .tuner = 1,
+               .type = V4L2_TUNER_RF,
+               .index = 0,
+               .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow   =    50000000,
+               .rangehigh  =  2000000000,
+       },
+};
+
+static void vivid_thread_sdr_cap_tick(struct vivid_dev *dev)
+{
+       struct vivid_buffer *sdr_cap_buf = NULL;
+
+       dprintk(dev, 1, "SDR Capture Thread Tick\n");
+
+       /* Drop a certain percentage of buffers. */
+       if (dev->perc_dropped_buffers &&
+           prandom_u32_max(100) < dev->perc_dropped_buffers)
+               return;
+
+       spin_lock(&dev->slock);
+       if (!list_empty(&dev->sdr_cap_active)) {
+               sdr_cap_buf = list_entry(dev->sdr_cap_active.next,
+                                        struct vivid_buffer, list);
+               list_del(&sdr_cap_buf->list);
+       }
+       spin_unlock(&dev->slock);
+
+       if (sdr_cap_buf) {
+               sdr_cap_buf->vb.v4l2_buf.sequence = dev->sdr_cap_seq_count;
+               vivid_sdr_cap_process(dev, sdr_cap_buf);
+               v4l2_get_timestamp(&sdr_cap_buf->vb.v4l2_buf.timestamp);
+               sdr_cap_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+               vb2_buffer_done(&sdr_cap_buf->vb, dev->dqbuf_error ?
+                               VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+               dev->dqbuf_error = false;
+       }
+}
+
+static int vivid_thread_sdr_cap(void *data)
+{
+       struct vivid_dev *dev = data;
+       u64 samples_since_start;
+       u64 buffers_since_start;
+       u64 next_jiffies_since_start;
+       unsigned long jiffies_since_start;
+       unsigned long cur_jiffies;
+       unsigned wait_jiffies;
+
+       dprintk(dev, 1, "SDR Capture Thread Start\n");
+
+       set_freezable();
+
+       /* Resets frame counters */
+       dev->sdr_cap_seq_offset = 0;
+       if (dev->seq_wrap)
+               dev->sdr_cap_seq_offset = 0xffffff80U;
+       dev->jiffies_sdr_cap = jiffies;
+       dev->sdr_cap_seq_resync = false;
+
+       for (;;) {
+               try_to_freeze();
+               if (kthread_should_stop())
+                       break;
+
+               mutex_lock(&dev->mutex);
+               cur_jiffies = jiffies;
+               if (dev->sdr_cap_seq_resync) {
+                       dev->jiffies_sdr_cap = cur_jiffies;
+                       dev->sdr_cap_seq_offset = dev->sdr_cap_seq_count + 1;
+                       dev->sdr_cap_seq_count = 0;
+                       dev->sdr_cap_seq_resync = false;
+               }
+               /* Calculate the number of jiffies since we started streaming */
+               jiffies_since_start = cur_jiffies - dev->jiffies_sdr_cap;
+               /* Get the number of buffers streamed since the start */
+               buffers_since_start = (u64)jiffies_since_start * dev->sdr_adc_freq +
+                                     (HZ * SDR_CAP_SAMPLES_PER_BUF) / 2;
+               do_div(buffers_since_start, HZ * SDR_CAP_SAMPLES_PER_BUF);
+
+               /*
+                * After more than 0xf0000000 (rounded down to a multiple of
+                * 'jiffies-per-day' to ease jiffies_to_msecs calculation)
+                * jiffies have passed since we started streaming reset the
+                * counters and keep track of the sequence offset.
+                */
+               if (jiffies_since_start > JIFFIES_RESYNC) {
+                       dev->jiffies_sdr_cap = cur_jiffies;
+                       dev->sdr_cap_seq_offset = buffers_since_start;
+                       buffers_since_start = 0;
+               }
+               dev->sdr_cap_seq_count = buffers_since_start + dev->sdr_cap_seq_offset;
+
+               vivid_thread_sdr_cap_tick(dev);
+               mutex_unlock(&dev->mutex);
+
+               /*
+                * Calculate the number of samples streamed since we started,
+                * not including the current buffer.
+                */
+               samples_since_start = buffers_since_start * SDR_CAP_SAMPLES_PER_BUF;
+
+               /* And the number of jiffies since we started */
+               jiffies_since_start = jiffies - dev->jiffies_sdr_cap;
+
+               /* Increase by the number of samples in one buffer */
+               samples_since_start += SDR_CAP_SAMPLES_PER_BUF;
+               /*
+                * Calculate when that next buffer is supposed to start
+                * in jiffies since we started streaming.
+                */
+               next_jiffies_since_start = samples_since_start * HZ +
+                                          dev->sdr_adc_freq / 2;
+               do_div(next_jiffies_since_start, dev->sdr_adc_freq);
+               /* If it is in the past, then just schedule asap */
+               if (next_jiffies_since_start < jiffies_since_start)
+                       next_jiffies_since_start = jiffies_since_start;
+
+               wait_jiffies = next_jiffies_since_start - jiffies_since_start;
+               schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1);
+       }
+       dprintk(dev, 1, "SDR Capture Thread End\n");
+       return 0;
+}
+
+static int sdr_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+                      unsigned *nbuffers, unsigned *nplanes,
+                      unsigned sizes[], void *alloc_ctxs[])
+{
+       /* 2 = max 16-bit sample returned */
+       sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2;
+       *nplanes = 1;
+       return 0;
+}
+
+static int sdr_cap_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       unsigned size = SDR_CAP_SAMPLES_PER_BUF * 2;
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       if (dev->buf_prepare_error) {
+               /*
+                * Error injection: test what happens if buf_prepare() returns
+                * an error.
+                */
+               dev->buf_prepare_error = false;
+               return -EINVAL;
+       }
+       if (vb2_plane_size(vb, 0) < size) {
+               dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n",
+                               __func__, vb2_plane_size(vb, 0), size);
+               return -EINVAL;
+       }
+       vb2_set_plane_payload(vb, 0, size);
+
+       return 0;
+}
+
+static void sdr_cap_buf_queue(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb);
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       spin_lock(&dev->slock);
+       list_add_tail(&buf->list, &dev->sdr_cap_active);
+       spin_unlock(&dev->slock);
+}
+
+static int sdr_cap_start_streaming(struct vb2_queue *vq, unsigned count)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+       int err = 0;
+
+       dprintk(dev, 1, "%s\n", __func__);
+       dev->sdr_cap_seq_count = 0;
+       if (dev->start_streaming_error) {
+               dev->start_streaming_error = false;
+               err = -EINVAL;
+       } else if (dev->kthread_sdr_cap == NULL) {
+               dev->kthread_sdr_cap = kthread_run(vivid_thread_sdr_cap, dev,
+                               "%s-sdr-cap", dev->v4l2_dev.name);
+
+               if (IS_ERR(dev->kthread_sdr_cap)) {
+                       v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+                       err = PTR_ERR(dev->kthread_sdr_cap);
+                       dev->kthread_sdr_cap = NULL;
+               }
+       }
+       if (err) {
+               struct vivid_buffer *buf, *tmp;
+
+               list_for_each_entry_safe(buf, tmp, &dev->sdr_cap_active, list) {
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+               }
+       }
+       return err;
+}
+
+/* abort streaming and wait for last buffer */
+static void sdr_cap_stop_streaming(struct vb2_queue *vq)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+
+       if (dev->kthread_sdr_cap == NULL)
+               return;
+
+       while (!list_empty(&dev->sdr_cap_active)) {
+               struct vivid_buffer *buf;
+
+               buf = list_entry(dev->sdr_cap_active.next, struct vivid_buffer, list);
+               list_del(&buf->list);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+       }
+
+       /* shutdown control thread */
+       mutex_unlock(&dev->mutex);
+       kthread_stop(dev->kthread_sdr_cap);
+       dev->kthread_sdr_cap = NULL;
+       mutex_lock(&dev->mutex);
+}
+
+const struct vb2_ops vivid_sdr_cap_qops = {
+       .queue_setup            = sdr_cap_queue_setup,
+       .buf_prepare            = sdr_cap_buf_prepare,
+       .buf_queue              = sdr_cap_buf_queue,
+       .start_streaming        = sdr_cap_start_streaming,
+       .stop_streaming         = sdr_cap_stop_streaming,
+       .wait_prepare           = vivid_unlock,
+       .wait_finish            = vivid_lock,
+};
+
+int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band)
+{
+       switch (band->tuner) {
+       case 0:
+               if (band->index >= ARRAY_SIZE(bands_adc))
+                       return -EINVAL;
+               *band = bands_adc[band->index];
+               return 0;
+       case 1:
+               if (band->index >= ARRAY_SIZE(bands_fm))
+                       return -EINVAL;
+               *band = bands_fm[band->index];
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+int vivid_sdr_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       switch (vf->tuner) {
+       case 0:
+               vf->frequency = dev->sdr_adc_freq;
+               vf->type = V4L2_TUNER_ADC;
+               return 0;
+       case 1:
+               vf->frequency = dev->sdr_fm_freq;
+               vf->type = V4L2_TUNER_RF;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+int vivid_sdr_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       unsigned freq = vf->frequency;
+       unsigned band;
+
+       switch (vf->tuner) {
+       case 0:
+               if (vf->type != V4L2_TUNER_ADC)
+                       return -EINVAL;
+               if (freq < BAND_ADC_0)
+                       band = 0;
+               else if (freq < BAND_ADC_1)
+                       band = 1;
+               else
+                       band = 2;
+
+               freq = clamp_t(unsigned, freq,
+                               bands_adc[band].rangelow,
+                               bands_adc[band].rangehigh);
+
+               if (vb2_is_streaming(&dev->vb_sdr_cap_q) &&
+                   freq != dev->sdr_adc_freq) {
+                       /* resync the thread's timings */
+                       dev->sdr_cap_seq_resync = true;
+               }
+               dev->sdr_adc_freq = freq;
+               return 0;
+       case 1:
+               if (vf->type != V4L2_TUNER_RF)
+                       return -EINVAL;
+               dev->sdr_fm_freq = clamp_t(unsigned, freq,
+                               bands_fm[0].rangelow,
+                               bands_fm[0].rangehigh);
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+       switch (vt->index) {
+       case 0:
+               strlcpy(vt->name, "ADC", sizeof(vt->name));
+               vt->type = V4L2_TUNER_ADC;
+               vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+               vt->rangelow = bands_adc[0].rangelow;
+               vt->rangehigh = bands_adc[2].rangehigh;
+               return 0;
+       case 1:
+               strlcpy(vt->name, "RF", sizeof(vt->name));
+               vt->type = V4L2_TUNER_RF;
+               vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+               vt->rangelow = bands_fm[0].rangelow;
+               vt->rangehigh = bands_fm[0].rangehigh;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
+{
+       if (vt->index > 1)
+               return -EINVAL;
+       return 0;
+}
+
+int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+       if (f->index)
+               return -EINVAL;
+       f->pixelformat = V4L2_SDR_FMT_CU8;
+       strlcpy(f->description, "IQ U8", sizeof(f->description));
+       return 0;
+}
+
+int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+       f->fmt.sdr.pixelformat = V4L2_SDR_FMT_CU8;
+       f->fmt.sdr.buffersize = SDR_CAP_SAMPLES_PER_BUF * 2;
+       memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+       return 0;
+}
+
+#define FIXP_FRAC    (1 << 15)
+#define FIXP_PI      ((int)(FIXP_FRAC * 3.141592653589))
+
+/* cos() from cx88 driver: cx88-dsp.c */
+static s32 fixp_cos(unsigned int x)
+{
+       u32 t2, t4, t6, t8;
+       u16 period = x / FIXP_PI;
+
+       if (period % 2)
+               return -fixp_cos(x - FIXP_PI);
+       x = x % FIXP_PI;
+       if (x > FIXP_PI/2)
+               return -fixp_cos(FIXP_PI/2 - (x % (FIXP_PI/2)));
+       /* Now x is between 0 and FIXP_PI/2.
+        * To calculate cos(x) we use it's Taylor polinom. */
+       t2 = x*x/FIXP_FRAC/2;
+       t4 = t2*x/FIXP_FRAC*x/FIXP_FRAC/3/4;
+       t6 = t4*x/FIXP_FRAC*x/FIXP_FRAC/5/6;
+       t8 = t6*x/FIXP_FRAC*x/FIXP_FRAC/7/8;
+       return FIXP_FRAC-t2+t4-t6+t8;
+}
+
+static inline s32 fixp_sin(unsigned int x)
+{
+       return -fixp_cos(x + (FIXP_PI / 2));
+}
+
+void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
+{
+       u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0);
+       unsigned long i;
+       unsigned long plane_size = vb2_plane_size(&buf->vb, 0);
+       int fixp_src_phase_step, fixp_i, fixp_q;
+
+       /*
+        * TODO: Generated beep tone goes very crackly when sample rate is
+        * increased to ~1Msps or more. That is because of huge rounding error
+        * of phase angle caused by used cosine implementation.
+        */
+
+       /* calculate phase step */
+       #define BEEP_FREQ 1000 /* 1kHz beep */
+       fixp_src_phase_step = DIV_ROUND_CLOSEST(2 * FIXP_PI * BEEP_FREQ,
+                       dev->sdr_adc_freq);
+
+       for (i = 0; i < plane_size; i += 2) {
+               dev->sdr_fixp_mod_phase += fixp_cos(dev->sdr_fixp_src_phase);
+               dev->sdr_fixp_src_phase += fixp_src_phase_step;
+
+               /*
+                * Transfer phases to [0 / 2xPI] in order to avoid variable
+                * overflow and make it suitable for cosine implementation
+                * used, which does not support negative angles.
+                */
+               while (dev->sdr_fixp_mod_phase < (0 * FIXP_PI))
+                       dev->sdr_fixp_mod_phase += (2 * FIXP_PI);
+               while (dev->sdr_fixp_mod_phase > (2 * FIXP_PI))
+                       dev->sdr_fixp_mod_phase -= (2 * FIXP_PI);
+
+               while (dev->sdr_fixp_src_phase > (2 * FIXP_PI))
+                       dev->sdr_fixp_src_phase -= (2 * FIXP_PI);
+
+               fixp_i = fixp_cos(dev->sdr_fixp_mod_phase);
+               fixp_q = fixp_sin(dev->sdr_fixp_mod_phase);
+
+               /* convert 'fixp float' to u8 */
+               /* u8 = X * 127.5f + 127.5f; where X is float [-1.0 / +1.0] */
+               fixp_i = fixp_i * 1275 + FIXP_FRAC * 1275;
+               fixp_q = fixp_q * 1275 + FIXP_FRAC * 1275;
+               *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10);
+               *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10);
+       }
+}
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.h b/drivers/media/platform/vivid/vivid-sdr-cap.h
new file mode 100644 (file)
index 0000000..79c1890
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * vivid-sdr-cap.h - software defined radio support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_SDR_CAP_H_
+#define _VIVID_SDR_CAP_H_
+
+int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band);
+int vivid_sdr_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
+int vivid_sdr_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf);
+int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt);
+int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt);
+int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f);
+int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f);
+void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf);
+
+extern const struct vb2_ops vivid_sdr_cap_qops;
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-tpg-colors.c b/drivers/media/platform/vivid/vivid-tpg-colors.c
new file mode 100644 (file)
index 0000000..2adddc0
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * vivid-color.c - A table that converts colors to various colorspaces
+ *
+ * The test pattern generator uses the tpg_colors for its test patterns.
+ * For testing colorspaces the first 8 colors of that table need to be
+ * converted to their equivalent in the target colorspace.
+ *
+ * The tpg_csc_colors[] table is the result of that conversion and since
+ * it is precalculated the colorspace conversion is just a simple table
+ * lookup.
+ *
+ * This source also contains the code used to generate the tpg_csc_colors
+ * table. Run the following command to compile it:
+ *
+ *     gcc vivid-colors.c -DCOMPILE_APP -o gen-colors -lm
+ *
+ * and run the utility.
+ *
+ * Note that the converted colors are in the range 0x000-0xff0 (so times 16)
+ * in order to preserve precision.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/videodev2.h>
+
+#include "vivid-tpg-colors.h"
+
+/* sRGB colors with range [0-255] */
+const struct color tpg_colors[TPG_COLOR_MAX] = {
+       /*
+        * Colors to test colorspace conversion: converting these colors
+        * to other colorspaces will never lead to out-of-gamut colors.
+        */
+       { 191, 191, 191 }, /* TPG_COLOR_CSC_WHITE */
+       { 191, 191,  50 }, /* TPG_COLOR_CSC_YELLOW */
+       {  50, 191, 191 }, /* TPG_COLOR_CSC_CYAN */
+       {  50, 191,  50 }, /* TPG_COLOR_CSC_GREEN */
+       { 191,  50, 191 }, /* TPG_COLOR_CSC_MAGENTA */
+       { 191,  50,  50 }, /* TPG_COLOR_CSC_RED */
+       {  50,  50, 191 }, /* TPG_COLOR_CSC_BLUE */
+       {  50,  50,  50 }, /* TPG_COLOR_CSC_BLACK */
+
+       /* 75% colors */
+       { 191, 191,   0 }, /* TPG_COLOR_75_YELLOW */
+       {   0, 191, 191 }, /* TPG_COLOR_75_CYAN */
+       {   0, 191,   0 }, /* TPG_COLOR_75_GREEN */
+       { 191,   0, 191 }, /* TPG_COLOR_75_MAGENTA */
+       { 191,   0,   0 }, /* TPG_COLOR_75_RED */
+       {   0,   0, 191 }, /* TPG_COLOR_75_BLUE */
+
+       /* 100% colors */
+       { 255, 255, 255 }, /* TPG_COLOR_100_WHITE */
+       { 255, 255,   0 }, /* TPG_COLOR_100_YELLOW */
+       {   0, 255, 255 }, /* TPG_COLOR_100_CYAN */
+       {   0, 255,   0 }, /* TPG_COLOR_100_GREEN */
+       { 255,   0, 255 }, /* TPG_COLOR_100_MAGENTA */
+       { 255,   0,   0 }, /* TPG_COLOR_100_RED */
+       {   0,   0, 255 }, /* TPG_COLOR_100_BLUE */
+       {   0,   0,   0 }, /* TPG_COLOR_100_BLACK */
+
+       {   0,   0,   0 }, /* TPG_COLOR_RANDOM placeholder */
+};
+
+#ifndef COMPILE_APP
+
+/* Generated table */
+const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = {
+       [V4L2_COLORSPACE_SMPTE170M][0] = { 2953, 2939, 2939 },
+       [V4L2_COLORSPACE_SMPTE170M][1] = { 2954, 2963, 585 },
+       [V4L2_COLORSPACE_SMPTE170M][2] = { 84, 2967, 2937 },
+       [V4L2_COLORSPACE_SMPTE170M][3] = { 93, 2990, 575 },
+       [V4L2_COLORSPACE_SMPTE170M][4] = { 3030, 259, 2933 },
+       [V4L2_COLORSPACE_SMPTE170M][5] = { 3031, 406, 557 },
+       [V4L2_COLORSPACE_SMPTE170M][6] = { 544, 428, 2931 },
+       [V4L2_COLORSPACE_SMPTE170M][7] = { 551, 547, 547 },
+       [V4L2_COLORSPACE_SMPTE240M][0] = { 2926, 2926, 2926 },
+       [V4L2_COLORSPACE_SMPTE240M][1] = { 2926, 2926, 857 },
+       [V4L2_COLORSPACE_SMPTE240M][2] = { 1594, 2901, 2901 },
+       [V4L2_COLORSPACE_SMPTE240M][3] = { 1594, 2901, 774 },
+       [V4L2_COLORSPACE_SMPTE240M][4] = { 2484, 618, 2858 },
+       [V4L2_COLORSPACE_SMPTE240M][5] = { 2484, 618, 617 },
+       [V4L2_COLORSPACE_SMPTE240M][6] = { 507, 507, 2832 },
+       [V4L2_COLORSPACE_SMPTE240M][7] = { 507, 507, 507 },
+       [V4L2_COLORSPACE_REC709][0] = { 2939, 2939, 2939 },
+       [V4L2_COLORSPACE_REC709][1] = { 2939, 2939, 547 },
+       [V4L2_COLORSPACE_REC709][2] = { 547, 2939, 2939 },
+       [V4L2_COLORSPACE_REC709][3] = { 547, 2939, 547 },
+       [V4L2_COLORSPACE_REC709][4] = { 2939, 547, 2939 },
+       [V4L2_COLORSPACE_REC709][5] = { 2939, 547, 547 },
+       [V4L2_COLORSPACE_REC709][6] = { 547, 547, 2939 },
+       [V4L2_COLORSPACE_REC709][7] = { 547, 547, 547 },
+       [V4L2_COLORSPACE_470_SYSTEM_M][0] = { 2894, 2988, 2808 },
+       [V4L2_COLORSPACE_470_SYSTEM_M][1] = { 2847, 3070, 843 },
+       [V4L2_COLORSPACE_470_SYSTEM_M][2] = { 1656, 2962, 2783 },
+       [V4L2_COLORSPACE_470_SYSTEM_M][3] = { 1572, 3045, 763 },
+       [V4L2_COLORSPACE_470_SYSTEM_M][4] = { 2477, 229, 2743 },
+       [V4L2_COLORSPACE_470_SYSTEM_M][5] = { 2422, 672, 614 },
+       [V4L2_COLORSPACE_470_SYSTEM_M][6] = { 725, 63, 2718 },
+       [V4L2_COLORSPACE_470_SYSTEM_M][7] = { 534, 561, 509 },
+       [V4L2_COLORSPACE_470_SYSTEM_BG][0] = { 2939, 2939, 2939 },
+       [V4L2_COLORSPACE_470_SYSTEM_BG][1] = { 2939, 2939, 621 },
+       [V4L2_COLORSPACE_470_SYSTEM_BG][2] = { 786, 2939, 2939 },
+       [V4L2_COLORSPACE_470_SYSTEM_BG][3] = { 786, 2939, 621 },
+       [V4L2_COLORSPACE_470_SYSTEM_BG][4] = { 2879, 547, 2923 },
+       [V4L2_COLORSPACE_470_SYSTEM_BG][5] = { 2879, 547, 547 },
+       [V4L2_COLORSPACE_470_SYSTEM_BG][6] = { 547, 547, 2923 },
+       [V4L2_COLORSPACE_470_SYSTEM_BG][7] = { 547, 547, 547 },
+       [V4L2_COLORSPACE_SRGB][0] = { 3056, 3056, 3056 },
+       [V4L2_COLORSPACE_SRGB][1] = { 3056, 3056, 800 },
+       [V4L2_COLORSPACE_SRGB][2] = { 800, 3056, 3056 },
+       [V4L2_COLORSPACE_SRGB][3] = { 800, 3056, 800 },
+       [V4L2_COLORSPACE_SRGB][4] = { 3056, 800, 3056 },
+       [V4L2_COLORSPACE_SRGB][5] = { 3056, 800, 800 },
+       [V4L2_COLORSPACE_SRGB][6] = { 800, 800, 3056 },
+       [V4L2_COLORSPACE_SRGB][7] = { 800, 800, 800 },
+};
+
+#else
+
+/* This code generates the table above */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static const double rec709_to_ntsc1953[3][3] = {
+       { 0.6698, 0.2678,  0.0323 },
+       { 0.0185, 1.0742, -0.0603 },
+       { 0.0162, 0.0432,  0.8551 }
+};
+
+static const double rec709_to_ebu[3][3] = {
+       { 0.9578, 0.0422, 0      },
+       { 0     , 1     , 0      },
+       { 0     , 0.0118, 0.9882 }
+};
+
+static const double rec709_to_170m[3][3] = {
+       {  1.0654, -0.0554, -0.0010 },
+       { -0.0196,  1.0364, -0.0167 },
+       {  0.0016,  0.0044,  0.9940 }
+};
+
+static const double rec709_to_240m[3][3] = {
+       { 0.7151, 0.2849, 0      },
+       { 0.0179, 0.9821, 0      },
+       { 0.0177, 0.0472, 0.9350 }
+};
+
+
+static void mult_matrix(double *r, double *g, double *b, const double m[3][3])
+{
+       double ir, ig, ib;
+
+       ir = m[0][0] * (*r) + m[0][1] * (*g) + m[0][2] * (*b);
+       ig = m[1][0] * (*r) + m[1][1] * (*g) + m[1][2] * (*b);
+       ib = m[2][0] * (*r) + m[2][1] * (*g) + m[2][2] * (*b);
+       *r = ir;
+       *g = ig;
+       *b = ib;
+}
+
+static double transfer_srgb_to_rgb(double v)
+{
+       return (v <= 0.03928) ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4);
+}
+
+static double transfer_rgb_to_smpte240m(double v)
+{
+       return (v <= 0.0228) ? v * 4.0 : 1.1115 * pow(v, 0.45) - 0.1115;
+}
+
+static double transfer_rgb_to_rec709(double v)
+{
+       return (v < 0.018) ? v * 4.5 : 1.099 * pow(v, 0.45) - 0.099;
+}
+
+static double transfer_srgb_to_rec709(double v)
+{
+       return transfer_rgb_to_rec709(transfer_srgb_to_rgb(v));
+}
+
+static void csc(enum v4l2_colorspace colorspace, double *r, double *g, double *b)
+{
+       /* Convert the primaries of Rec. 709 Linear RGB */
+       switch (colorspace) {
+       case V4L2_COLORSPACE_SMPTE240M:
+               *r = transfer_srgb_to_rgb(*r);
+               *g = transfer_srgb_to_rgb(*g);
+               *b = transfer_srgb_to_rgb(*b);
+               mult_matrix(r, g, b, rec709_to_240m);
+               break;
+       case V4L2_COLORSPACE_SMPTE170M:
+               *r = transfer_srgb_to_rgb(*r);
+               *g = transfer_srgb_to_rgb(*g);
+               *b = transfer_srgb_to_rgb(*b);
+               mult_matrix(r, g, b, rec709_to_170m);
+               break;
+       case V4L2_COLORSPACE_470_SYSTEM_BG:
+               *r = transfer_srgb_to_rgb(*r);
+               *g = transfer_srgb_to_rgb(*g);
+               *b = transfer_srgb_to_rgb(*b);
+               mult_matrix(r, g, b, rec709_to_ebu);
+               break;
+       case V4L2_COLORSPACE_470_SYSTEM_M:
+               *r = transfer_srgb_to_rgb(*r);
+               *g = transfer_srgb_to_rgb(*g);
+               *b = transfer_srgb_to_rgb(*b);
+               mult_matrix(r, g, b, rec709_to_ntsc1953);
+               break;
+       case V4L2_COLORSPACE_SRGB:
+       case V4L2_COLORSPACE_REC709:
+       default:
+               break;
+       }
+
+       *r = ((*r) < 0) ? 0 : (((*r) > 1) ? 1 : (*r));
+       *g = ((*g) < 0) ? 0 : (((*g) > 1) ? 1 : (*g));
+       *b = ((*b) < 0) ? 0 : (((*b) > 1) ? 1 : (*b));
+
+       /* Encode to gamma corrected colorspace */
+       switch (colorspace) {
+       case V4L2_COLORSPACE_SMPTE240M:
+               *r = transfer_rgb_to_smpte240m(*r);
+               *g = transfer_rgb_to_smpte240m(*g);
+               *b = transfer_rgb_to_smpte240m(*b);
+               break;
+       case V4L2_COLORSPACE_SMPTE170M:
+       case V4L2_COLORSPACE_470_SYSTEM_M:
+       case V4L2_COLORSPACE_470_SYSTEM_BG:
+               *r = transfer_rgb_to_rec709(*r);
+               *g = transfer_rgb_to_rec709(*g);
+               *b = transfer_rgb_to_rec709(*b);
+               break;
+       case V4L2_COLORSPACE_SRGB:
+               break;
+       case V4L2_COLORSPACE_REC709:
+       default:
+               *r = transfer_srgb_to_rec709(*r);
+               *g = transfer_srgb_to_rec709(*g);
+               *b = transfer_srgb_to_rec709(*b);
+               break;
+       }
+}
+
+int main(int argc, char **argv)
+{
+       static const unsigned colorspaces[] = {
+               0,
+               V4L2_COLORSPACE_SMPTE170M,
+               V4L2_COLORSPACE_SMPTE240M,
+               V4L2_COLORSPACE_REC709,
+               0,
+               V4L2_COLORSPACE_470_SYSTEM_M,
+               V4L2_COLORSPACE_470_SYSTEM_BG,
+               0,
+               V4L2_COLORSPACE_SRGB,
+       };
+       static const char * const colorspace_names[] = {
+               "",
+               "V4L2_COLORSPACE_SMPTE170M",
+               "V4L2_COLORSPACE_SMPTE240M",
+               "V4L2_COLORSPACE_REC709",
+               "",
+               "V4L2_COLORSPACE_470_SYSTEM_M",
+               "V4L2_COLORSPACE_470_SYSTEM_BG",
+               "",
+               "V4L2_COLORSPACE_SRGB",
+       };
+       int i;
+       int c;
+
+       printf("/* Generated table */\n");
+       printf("const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = {\n");
+       for (c = 0; c <= V4L2_COLORSPACE_SRGB; c++) {
+               for (i = 0; i <= TPG_COLOR_CSC_BLACK; i++) {
+                       double r, g, b;
+
+                       if (colorspaces[c] == 0)
+                               continue;
+
+                       r = tpg_colors[i].r / 255.0;
+                       g = tpg_colors[i].g / 255.0;
+                       b = tpg_colors[i].b / 255.0;
+
+                       csc(c, &r, &g, &b);
+
+                       printf("\t[%s][%d] = { %d, %d, %d },\n", colorspace_names[c], i,
+                               (int)(r * 4080), (int)(g * 4080), (int)(b * 4080));
+               }
+       }
+       printf("};\n\n");
+       return 0;
+}
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-tpg-colors.h b/drivers/media/platform/vivid/vivid-tpg-colors.h
new file mode 100644 (file)
index 0000000..a2678fb
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * vivid-color.h - Color definitions for the test pattern generator
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_COLORS_H_
+#define _VIVID_COLORS_H_
+
+struct color {
+       unsigned char r, g, b;
+};
+
+struct color16 {
+       int r, g, b;
+};
+
+enum tpg_color {
+       TPG_COLOR_CSC_WHITE,
+       TPG_COLOR_CSC_YELLOW,
+       TPG_COLOR_CSC_CYAN,
+       TPG_COLOR_CSC_GREEN,
+       TPG_COLOR_CSC_MAGENTA,
+       TPG_COLOR_CSC_RED,
+       TPG_COLOR_CSC_BLUE,
+       TPG_COLOR_CSC_BLACK,
+       TPG_COLOR_75_YELLOW,
+       TPG_COLOR_75_CYAN,
+       TPG_COLOR_75_GREEN,
+       TPG_COLOR_75_MAGENTA,
+       TPG_COLOR_75_RED,
+       TPG_COLOR_75_BLUE,
+       TPG_COLOR_100_WHITE,
+       TPG_COLOR_100_YELLOW,
+       TPG_COLOR_100_CYAN,
+       TPG_COLOR_100_GREEN,
+       TPG_COLOR_100_MAGENTA,
+       TPG_COLOR_100_RED,
+       TPG_COLOR_100_BLUE,
+       TPG_COLOR_100_BLACK,
+       TPG_COLOR_TEXTFG,
+       TPG_COLOR_TEXTBG,
+       TPG_COLOR_RANDOM,
+       TPG_COLOR_RAMP,
+       TPG_COLOR_MAX = TPG_COLOR_RAMP + 256
+};
+
+extern const struct color tpg_colors[TPG_COLOR_MAX];
+extern const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1];
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c
new file mode 100644 (file)
index 0000000..0c6fa53
--- /dev/null
@@ -0,0 +1,1439 @@
+/*
+ * vivid-tpg.c - Test Pattern Generator
+ *
+ * Note: gen_twopix and tpg_gen_text are based on code from vivi.c. See the
+ * vivi.c source for the copyright information of those functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 "vivid-tpg.h"
+
+/* Must remain in sync with enum tpg_pattern */
+const char * const tpg_pattern_strings[] = {
+       "75% Colorbar",
+       "100% Colorbar",
+       "CSC Colorbar",
+       "Horizontal 100% Colorbar",
+       "100% Color Squares",
+       "100% Black",
+       "100% White",
+       "100% Red",
+       "100% Green",
+       "100% Blue",
+       "16x16 Checkers",
+       "1x1 Checkers",
+       "Alternating Hor Lines",
+       "Alternating Vert Lines",
+       "One Pixel Wide Cross",
+       "Two Pixels Wide Cross",
+       "Ten Pixels Wide Cross",
+       "Gray Ramp",
+       "Noise",
+       NULL
+};
+
+/* Must remain in sync with enum tpg_aspect */
+const char * const tpg_aspect_strings[] = {
+       "Source Width x Height",
+       "4x3",
+       "14x9",
+       "16x9",
+       "16x9 Anamorphic",
+       NULL
+};
+
+/*
+ * Sine table: sin[0] = 127 * sin(-180 degrees)
+ *             sin[128] = 127 * sin(0 degrees)
+ *             sin[256] = 127 * sin(180 degrees)
+ */
+static const s8 sin[257] = {
+          0,   -4,   -7,  -11,  -13,  -18,  -20,  -22,  -26,  -29,  -33,  -35,  -37,  -41,  -43,  -48,
+        -50,  -52,  -56,  -58,  -62,  -63,  -65,  -69,  -71,  -75,  -76,  -78,  -82,  -83,  -87,  -88,
+        -90,  -93,  -94,  -97,  -99, -101, -103, -104, -107, -108, -110, -111, -112, -114, -115, -117,
+       -118, -119, -120, -121, -122, -123, -123, -124, -125, -125, -126, -126, -127, -127, -127, -127,
+       -127, -127, -127, -127, -126, -126, -125, -125, -124, -124, -123, -122, -121, -120, -119, -118,
+       -117, -116, -114, -113, -111, -110, -109, -107, -105, -103, -101, -100,  -97,  -96,  -93,  -91,
+        -90,  -87,  -85,  -82,  -80,  -76,  -75,  -73,  -69,  -67,  -63,  -62,  -60,  -56,  -54,  -50,
+        -48,  -46,  -41,  -39,  -35,  -33,  -31,  -26,  -24,  -20,  -18,  -15,  -11,   -9,   -4,   -2,
+          0,    2,    4,    9,   11,   15,   18,   20,   24,   26,   31,   33,   35,   39,   41,   46,
+         48,   50,   54,   56,   60,   62,   64,   67,   69,   73,   75,   76,   80,   82,   85,   87,
+         90,   91,   93,   96,   97,  100,  101,  103,  105,  107,  109,  110,  111,  113,  114,  116,
+        117,  118,  119,  120,  121,  122,  123,  124,  124,  125,  125,  126,  126,  127,  127,  127,
+        127,  127,  127,  127,  127,  126,  126,  125,  125,  124,  123,  123,  122,  121,  120,  119,
+        118,  117,  115,  114,  112,  111,  110,  108,  107,  104,  103,  101,   99,   97,   94,   93,
+         90,   88,   87,   83,   82,   78,   76,   75,   71,   69,   65,   64,   62,   58,   56,   52,
+         50,   48,   43,   41,   37,   35,   33,   29,   26,   22,   20,   18,   13,   11,    7,    4,
+          0,
+};
+
+#define cos(idx) sin[((idx) + 64) % sizeof(sin)]
+
+/* Global font descriptor */
+static const u8 *font8x16;
+
+void tpg_set_font(const u8 *f)
+{
+       font8x16 = f;
+}
+
+void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h)
+{
+       memset(tpg, 0, sizeof(*tpg));
+       tpg->scaled_width = tpg->src_width = w;
+       tpg->src_height = tpg->buf_height = h;
+       tpg->crop.width = tpg->compose.width = w;
+       tpg->crop.height = tpg->compose.height = h;
+       tpg->recalc_colors = true;
+       tpg->recalc_square_border = true;
+       tpg->brightness = 128;
+       tpg->contrast = 128;
+       tpg->saturation = 128;
+       tpg->hue = 0;
+       tpg->mv_hor_mode = TPG_MOVE_NONE;
+       tpg->mv_vert_mode = TPG_MOVE_NONE;
+       tpg->field = V4L2_FIELD_NONE;
+       tpg_s_fourcc(tpg, V4L2_PIX_FMT_RGB24);
+       tpg->colorspace = V4L2_COLORSPACE_SRGB;
+       tpg->perc_fill = 100;
+}
+
+int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
+{
+       unsigned pat;
+       unsigned plane;
+
+       tpg->max_line_width = max_w;
+       for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) {
+               for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+                       unsigned pixelsz = plane ? 1 : 4;
+
+                       tpg->lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
+                       if (!tpg->lines[pat][plane])
+                               return -ENOMEM;
+               }
+       }
+       for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+               unsigned pixelsz = plane ? 1 : 4;
+
+               tpg->contrast_line[plane] = vzalloc(max_w * pixelsz);
+               if (!tpg->contrast_line[plane])
+                       return -ENOMEM;
+               tpg->black_line[plane] = vzalloc(max_w * pixelsz);
+               if (!tpg->black_line[plane])
+                       return -ENOMEM;
+               tpg->random_line[plane] = vzalloc(max_w * pixelsz);
+               if (!tpg->random_line[plane])
+                       return -ENOMEM;
+       }
+       return 0;
+}
+
+void tpg_free(struct tpg_data *tpg)
+{
+       unsigned pat;
+       unsigned plane;
+
+       for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++)
+               for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+                       vfree(tpg->lines[pat][plane]);
+                       tpg->lines[pat][plane] = NULL;
+               }
+       for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+               vfree(tpg->contrast_line[plane]);
+               vfree(tpg->black_line[plane]);
+               vfree(tpg->random_line[plane]);
+               tpg->contrast_line[plane] = NULL;
+               tpg->black_line[plane] = NULL;
+               tpg->random_line[plane] = NULL;
+       }
+}
+
+bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
+{
+       tpg->fourcc = fourcc;
+       tpg->planes = 1;
+       tpg->recalc_colors = true;
+       switch (fourcc) {
+       case V4L2_PIX_FMT_RGB565:
+       case V4L2_PIX_FMT_RGB565X:
+       case V4L2_PIX_FMT_RGB555:
+       case V4L2_PIX_FMT_XRGB555:
+       case V4L2_PIX_FMT_ARGB555:
+       case V4L2_PIX_FMT_RGB555X:
+       case V4L2_PIX_FMT_RGB24:
+       case V4L2_PIX_FMT_BGR24:
+       case V4L2_PIX_FMT_RGB32:
+       case V4L2_PIX_FMT_BGR32:
+       case V4L2_PIX_FMT_XRGB32:
+       case V4L2_PIX_FMT_XBGR32:
+       case V4L2_PIX_FMT_ARGB32:
+       case V4L2_PIX_FMT_ABGR32:
+               tpg->is_yuv = false;
+               break;
+       case V4L2_PIX_FMT_NV16M:
+       case V4L2_PIX_FMT_NV61M:
+               tpg->planes = 2;
+               /* fall-through */
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_UYVY:
+       case V4L2_PIX_FMT_YVYU:
+       case V4L2_PIX_FMT_VYUY:
+               tpg->is_yuv = true;
+               break;
+       default:
+               return false;
+       }
+
+       switch (fourcc) {
+       case V4L2_PIX_FMT_RGB565:
+       case V4L2_PIX_FMT_RGB565X:
+       case V4L2_PIX_FMT_RGB555:
+       case V4L2_PIX_FMT_XRGB555:
+       case V4L2_PIX_FMT_ARGB555:
+       case V4L2_PIX_FMT_RGB555X:
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_UYVY:
+       case V4L2_PIX_FMT_YVYU:
+       case V4L2_PIX_FMT_VYUY:
+               tpg->twopixelsize[0] = 2 * 2;
+               break;
+       case V4L2_PIX_FMT_RGB24:
+       case V4L2_PIX_FMT_BGR24:
+               tpg->twopixelsize[0] = 2 * 3;
+               break;
+       case V4L2_PIX_FMT_RGB32:
+       case V4L2_PIX_FMT_BGR32:
+       case V4L2_PIX_FMT_XRGB32:
+       case V4L2_PIX_FMT_XBGR32:
+       case V4L2_PIX_FMT_ARGB32:
+       case V4L2_PIX_FMT_ABGR32:
+               tpg->twopixelsize[0] = 2 * 4;
+               break;
+       case V4L2_PIX_FMT_NV16M:
+       case V4L2_PIX_FMT_NV61M:
+               tpg->twopixelsize[0] = 2;
+               tpg->twopixelsize[1] = 2;
+               break;
+       }
+       return true;
+}
+
+void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
+               const struct v4l2_rect *compose)
+{
+       tpg->crop = *crop;
+       tpg->compose = *compose;
+       tpg->scaled_width = (tpg->src_width * tpg->compose.width +
+                                tpg->crop.width - 1) / tpg->crop.width;
+       tpg->scaled_width &= ~1;
+       if (tpg->scaled_width > tpg->max_line_width)
+               tpg->scaled_width = tpg->max_line_width;
+       if (tpg->scaled_width < 2)
+               tpg->scaled_width = 2;
+       tpg->recalc_lines = true;
+}
+
+void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
+                      u32 field)
+{
+       unsigned p;
+
+       tpg->src_width = width;
+       tpg->src_height = height;
+       tpg->field = field;
+       tpg->buf_height = height;
+       if (V4L2_FIELD_HAS_T_OR_B(field))
+               tpg->buf_height /= 2;
+       tpg->scaled_width = width;
+       tpg->crop.top = tpg->crop.left = 0;
+       tpg->crop.width = width;
+       tpg->crop.height = height;
+       tpg->compose.top = tpg->compose.left = 0;
+       tpg->compose.width = width;
+       tpg->compose.height = tpg->buf_height;
+       for (p = 0; p < tpg->planes; p++)
+               tpg->bytesperline[p] = width * tpg->twopixelsize[p] / 2;
+       tpg->recalc_square_border = true;
+}
+
+static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg)
+{
+       switch (tpg->pattern) {
+       case TPG_PAT_BLACK:
+               return TPG_COLOR_100_WHITE;
+       case TPG_PAT_CSC_COLORBAR:
+               return TPG_COLOR_CSC_BLACK;
+       default:
+               return TPG_COLOR_100_BLACK;
+       }
+}
+
+static enum tpg_color tpg_get_textfg_color(struct tpg_data *tpg)
+{
+       switch (tpg->pattern) {
+       case TPG_PAT_75_COLORBAR:
+       case TPG_PAT_CSC_COLORBAR:
+               return TPG_COLOR_CSC_WHITE;
+       case TPG_PAT_BLACK:
+               return TPG_COLOR_100_BLACK;
+       default:
+               return TPG_COLOR_100_WHITE;
+       }
+}
+
+static u16 color_to_y(struct tpg_data *tpg, int r, int g, int b)
+{
+       switch (tpg->colorspace) {
+       case V4L2_COLORSPACE_SMPTE170M:
+       case V4L2_COLORSPACE_470_SYSTEM_M:
+       case V4L2_COLORSPACE_470_SYSTEM_BG:
+               return ((16829 * r + 33039 * g + 6416 * b + 16 * 32768) >> 16) + (16 << 4);
+       case V4L2_COLORSPACE_SMPTE240M:
+               return ((11932 * r + 39455 * g + 4897 * b + 16 * 32768) >> 16) + (16 << 4);
+       case V4L2_COLORSPACE_REC709:
+       case V4L2_COLORSPACE_SRGB:
+       default:
+               return ((11966 * r + 40254 * g + 4064 * b + 16 * 32768) >> 16) + (16 << 4);
+       }
+}
+
+static u16 color_to_cb(struct tpg_data *tpg, int r, int g, int b)
+{
+       switch (tpg->colorspace) {
+       case V4L2_COLORSPACE_SMPTE170M:
+       case V4L2_COLORSPACE_470_SYSTEM_M:
+       case V4L2_COLORSPACE_470_SYSTEM_BG:
+               return ((-9714 * r - 19070 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
+       case V4L2_COLORSPACE_SMPTE240M:
+               return ((-6684 * r - 22100 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
+       case V4L2_COLORSPACE_REC709:
+       case V4L2_COLORSPACE_SRGB:
+       default:
+               return ((-6596 * r - 22189 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
+       }
+}
+
+static u16 color_to_cr(struct tpg_data *tpg, int r, int g, int b)
+{
+       switch (tpg->colorspace) {
+       case V4L2_COLORSPACE_SMPTE170M:
+       case V4L2_COLORSPACE_470_SYSTEM_M:
+       case V4L2_COLORSPACE_470_SYSTEM_BG:
+               return ((28784 * r - 24103 * g - 4681 * b + 16 * 32768) >> 16) + (128 << 4);
+       case V4L2_COLORSPACE_SMPTE240M:
+               return ((28784 * r - 25606 * g - 3178 * b + 16 * 32768) >> 16) + (128 << 4);
+       case V4L2_COLORSPACE_REC709:
+       case V4L2_COLORSPACE_SRGB:
+       default:
+               return ((28784 * r - 26145 * g - 2639 * b + 16 * 32768) >> 16) + (128 << 4);
+       }
+}
+
+static u16 ycbcr_to_r(struct tpg_data *tpg, int y, int cb, int cr)
+{
+       int r;
+
+       y -= 16 << 4;
+       cb -= 128 << 4;
+       cr -= 128 << 4;
+       switch (tpg->colorspace) {
+       case V4L2_COLORSPACE_SMPTE170M:
+       case V4L2_COLORSPACE_470_SYSTEM_M:
+       case V4L2_COLORSPACE_470_SYSTEM_BG:
+               r = 4769 * y + 6537 * cr;
+               break;
+       case V4L2_COLORSPACE_SMPTE240M:
+               r = 4769 * y + 7376 * cr;
+               break;
+       case V4L2_COLORSPACE_REC709:
+       case V4L2_COLORSPACE_SRGB:
+       default:
+               r = 4769 * y + 7343 * cr;
+               break;
+       }
+       return clamp(r >> 12, 0, 0xff0);
+}
+
+static u16 ycbcr_to_g(struct tpg_data *tpg, int y, int cb, int cr)
+{
+       int g;
+
+       y -= 16 << 4;
+       cb -= 128 << 4;
+       cr -= 128 << 4;
+       switch (tpg->colorspace) {
+       case V4L2_COLORSPACE_SMPTE170M:
+       case V4L2_COLORSPACE_470_SYSTEM_M:
+       case V4L2_COLORSPACE_470_SYSTEM_BG:
+               g = 4769 * y - 1605 * cb - 3330 * cr;
+               break;
+       case V4L2_COLORSPACE_SMPTE240M:
+               g = 4769 * y - 1055 * cb - 2341 * cr;
+               break;
+       case V4L2_COLORSPACE_REC709:
+       case V4L2_COLORSPACE_SRGB:
+       default:
+               g = 4769 * y - 873 * cb - 2183 * cr;
+               break;
+       }
+       return clamp(g >> 12, 0, 0xff0);
+}
+
+static u16 ycbcr_to_b(struct tpg_data *tpg, int y, int cb, int cr)
+{
+       int b;
+
+       y -= 16 << 4;
+       cb -= 128 << 4;
+       cr -= 128 << 4;
+       switch (tpg->colorspace) {
+       case V4L2_COLORSPACE_SMPTE170M:
+       case V4L2_COLORSPACE_470_SYSTEM_M:
+       case V4L2_COLORSPACE_470_SYSTEM_BG:
+               b = 4769 * y + 7343 * cb;
+               break;
+       case V4L2_COLORSPACE_SMPTE240M:
+               b = 4769 * y + 8552 * cb;
+               break;
+       case V4L2_COLORSPACE_REC709:
+       case V4L2_COLORSPACE_SRGB:
+       default:
+               b = 4769 * y + 8652 * cb;
+               break;
+       }
+       return clamp(b >> 12, 0, 0xff0);
+}
+
+/* precalculate color bar values to speed up rendering */
+static void precalculate_color(struct tpg_data *tpg, int k)
+{
+       int col = k;
+       int r = tpg_colors[col].r;
+       int g = tpg_colors[col].g;
+       int b = tpg_colors[col].b;
+
+       if (k == TPG_COLOR_TEXTBG) {
+               col = tpg_get_textbg_color(tpg);
+
+               r = tpg_colors[col].r;
+               g = tpg_colors[col].g;
+               b = tpg_colors[col].b;
+       } else if (k == TPG_COLOR_TEXTFG) {
+               col = tpg_get_textfg_color(tpg);
+
+               r = tpg_colors[col].r;
+               g = tpg_colors[col].g;
+               b = tpg_colors[col].b;
+       } else if (tpg->pattern == TPG_PAT_NOISE) {
+               r = g = b = prandom_u32_max(256);
+       } else if (k == TPG_COLOR_RANDOM) {
+               r = g = b = tpg->qual_offset + prandom_u32_max(196);
+       } else if (k >= TPG_COLOR_RAMP) {
+               r = g = b = k - TPG_COLOR_RAMP;
+       }
+
+       if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) {
+               r = tpg_csc_colors[tpg->colorspace][col].r;
+               g = tpg_csc_colors[tpg->colorspace][col].g;
+               b = tpg_csc_colors[tpg->colorspace][col].b;
+       } else {
+               r <<= 4;
+               g <<= 4;
+               b <<= 4;
+       }
+       if (tpg->qual == TPG_QUAL_GRAY)
+               r = g = b = color_to_y(tpg, r, g, b);
+
+       /*
+        * The assumption is that the RGB output is always full range,
+        * so only if the rgb_range overrides the 'real' rgb range do
+        * we need to convert the RGB values.
+        *
+        * Currently there is no way of signalling to userspace if you
+        * are actually giving it limited range RGB (or full range
+        * YUV for that matter).
+        *
+        * Remember that r, g and b are still in the 0 - 0xff0 range.
+        */
+       if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
+           tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL) {
+               /*
+                * Convert from full range (which is what r, g and b are)
+                * to limited range (which is the 'real' RGB range), which
+                * is then interpreted as full range.
+                */
+               r = (r * 219) / 255 + (16 << 4);
+               g = (g * 219) / 255 + (16 << 4);
+               b = (b * 219) / 255 + (16 << 4);
+       } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED &&
+                  tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED) {
+               /*
+                * Clamp r, g and b to the limited range and convert to full
+                * range since that's what we deliver.
+                */
+               r = clamp(r, 16 << 4, 235 << 4);
+               g = clamp(g, 16 << 4, 235 << 4);
+               b = clamp(b, 16 << 4, 235 << 4);
+               r = (r - (16 << 4)) * 255 / 219;
+               g = (g - (16 << 4)) * 255 / 219;
+               b = (b - (16 << 4)) * 255 / 219;
+       }
+
+       if (tpg->brightness != 128 || tpg->contrast != 128 ||
+           tpg->saturation != 128 || tpg->hue) {
+               /* Implement these operations */
+
+               /* First convert to YCbCr */
+               int y = color_to_y(tpg, r, g, b);       /* Luma */
+               int cb = color_to_cb(tpg, r, g, b);     /* Cb */
+               int cr = color_to_cr(tpg, r, g, b);     /* Cr */
+               int tmp_cb, tmp_cr;
+
+               y = (16 << 4) + ((y - (16 << 4)) * tpg->contrast) / 128;
+               y += (tpg->brightness << 4) - (128 << 4);
+
+               cb -= 128 << 4;
+               cr -= 128 << 4;
+               tmp_cb = (cb * cos(128 + tpg->hue)) / 127 + (cr * sin[128 + tpg->hue]) / 127;
+               tmp_cr = (cr * cos(128 + tpg->hue)) / 127 - (cb * sin[128 + tpg->hue]) / 127;
+
+               cb = (128 << 4) + (tmp_cb * tpg->contrast * tpg->saturation) / (128 * 128);
+               cr = (128 << 4) + (tmp_cr * tpg->contrast * tpg->saturation) / (128 * 128);
+               if (tpg->is_yuv) {
+                       tpg->colors[k][0] = clamp(y >> 4, 1, 254);
+                       tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
+                       tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
+                       return;
+               }
+               r = ycbcr_to_r(tpg, y, cb, cr);
+               g = ycbcr_to_g(tpg, y, cb, cr);
+               b = ycbcr_to_b(tpg, y, cb, cr);
+       }
+
+       if (tpg->is_yuv) {
+               /* Convert to YCbCr */
+               u16 y = color_to_y(tpg, r, g, b);       /* Luma */
+               u16 cb = color_to_cb(tpg, r, g, b);     /* Cb */
+               u16 cr = color_to_cr(tpg, r, g, b);     /* Cr */
+
+               tpg->colors[k][0] = clamp(y >> 4, 1, 254);
+               tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
+               tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
+       } else {
+               switch (tpg->fourcc) {
+               case V4L2_PIX_FMT_RGB565:
+               case V4L2_PIX_FMT_RGB565X:
+                       r >>= 7;
+                       g >>= 6;
+                       b >>= 7;
+                       break;
+               case V4L2_PIX_FMT_RGB555:
+               case V4L2_PIX_FMT_XRGB555:
+               case V4L2_PIX_FMT_ARGB555:
+               case V4L2_PIX_FMT_RGB555X:
+                       r >>= 7;
+                       g >>= 7;
+                       b >>= 7;
+                       break;
+               default:
+                       r >>= 4;
+                       g >>= 4;
+                       b >>= 4;
+                       break;
+               }
+
+               tpg->colors[k][0] = r;
+               tpg->colors[k][1] = g;
+               tpg->colors[k][2] = b;
+       }
+}
+
+static void tpg_precalculate_colors(struct tpg_data *tpg)
+{
+       int k;
+
+       for (k = 0; k < TPG_COLOR_MAX; k++)
+               precalculate_color(tpg, k);
+}
+
+/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
+static void gen_twopix(struct tpg_data *tpg,
+               u8 buf[TPG_MAX_PLANES][8], int color, bool odd)
+{
+       unsigned offset = odd * tpg->twopixelsize[0] / 2;
+       u8 alpha = tpg->alpha_component;
+       u8 r_y, g_u, b_v;
+
+       if (tpg->alpha_red_only && color != TPG_COLOR_CSC_RED &&
+                                  color != TPG_COLOR_100_RED &&
+                                  color != TPG_COLOR_75_RED)
+               alpha = 0;
+       if (color == TPG_COLOR_RANDOM)
+               precalculate_color(tpg, color);
+       r_y = tpg->colors[color][0]; /* R or precalculated Y */
+       g_u = tpg->colors[color][1]; /* G or precalculated U */
+       b_v = tpg->colors[color][2]; /* B or precalculated V */
+
+       switch (tpg->fourcc) {
+       case V4L2_PIX_FMT_NV16M:
+               buf[0][offset] = r_y;
+               buf[1][offset] = odd ? b_v : g_u;
+               break;
+       case V4L2_PIX_FMT_NV61M:
+               buf[0][offset] = r_y;
+               buf[1][offset] = odd ? g_u : b_v;
+               break;
+
+       case V4L2_PIX_FMT_YUYV:
+               buf[0][offset] = r_y;
+               buf[0][offset + 1] = odd ? b_v : g_u;
+               break;
+       case V4L2_PIX_FMT_UYVY:
+               buf[0][offset] = odd ? b_v : g_u;
+               buf[0][offset + 1] = r_y;
+               break;
+       case V4L2_PIX_FMT_YVYU:
+               buf[0][offset] = r_y;
+               buf[0][offset + 1] = odd ? g_u : b_v;
+               break;
+       case V4L2_PIX_FMT_VYUY:
+               buf[0][offset] = odd ? g_u : b_v;
+               buf[0][offset + 1] = r_y;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               buf[0][offset] = (g_u << 5) | b_v;
+               buf[0][offset + 1] = (r_y << 3) | (g_u >> 3);
+               break;
+       case V4L2_PIX_FMT_RGB565X:
+               buf[0][offset] = (r_y << 3) | (g_u >> 3);
+               buf[0][offset + 1] = (g_u << 5) | b_v;
+               break;
+       case V4L2_PIX_FMT_RGB555:
+       case V4L2_PIX_FMT_XRGB555:
+               alpha = 0;
+               /* fall through */
+       case V4L2_PIX_FMT_ARGB555:
+               buf[0][offset] = (g_u << 5) | b_v;
+               buf[0][offset + 1] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
+               break;
+       case V4L2_PIX_FMT_RGB555X:
+               buf[0][offset] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
+               buf[0][offset + 1] = (g_u << 5) | b_v;
+               break;
+       case V4L2_PIX_FMT_RGB24:
+               buf[0][offset] = r_y;
+               buf[0][offset + 1] = g_u;
+               buf[0][offset + 2] = b_v;
+               break;
+       case V4L2_PIX_FMT_BGR24:
+               buf[0][offset] = b_v;
+               buf[0][offset + 1] = g_u;
+               buf[0][offset + 2] = r_y;
+               break;
+       case V4L2_PIX_FMT_RGB32:
+       case V4L2_PIX_FMT_XRGB32:
+               alpha = 0;
+               /* fall through */
+       case V4L2_PIX_FMT_ARGB32:
+               buf[0][offset] = alpha;
+               buf[0][offset + 1] = r_y;
+               buf[0][offset + 2] = g_u;
+               buf[0][offset + 3] = b_v;
+               break;
+       case V4L2_PIX_FMT_BGR32:
+       case V4L2_PIX_FMT_XBGR32:
+               alpha = 0;
+               /* fall through */
+       case V4L2_PIX_FMT_ABGR32:
+               buf[0][offset] = b_v;
+               buf[0][offset + 1] = g_u;
+               buf[0][offset + 2] = r_y;
+               buf[0][offset + 3] = alpha;
+               break;
+       }
+}
+
+/* Return how many pattern lines are used by the current pattern. */
+static unsigned tpg_get_pat_lines(struct tpg_data *tpg)
+{
+       switch (tpg->pattern) {
+       case TPG_PAT_CHECKERS_16X16:
+       case TPG_PAT_CHECKERS_1X1:
+       case TPG_PAT_ALTERNATING_HLINES:
+       case TPG_PAT_CROSS_1_PIXEL:
+       case TPG_PAT_CROSS_2_PIXELS:
+       case TPG_PAT_CROSS_10_PIXELS:
+               return 2;
+       case TPG_PAT_100_COLORSQUARES:
+       case TPG_PAT_100_HCOLORBAR:
+               return 8;
+       default:
+               return 1;
+       }
+}
+
+/* Which pattern line should be used for the given frame line. */
+static unsigned tpg_get_pat_line(struct tpg_data *tpg, unsigned line)
+{
+       switch (tpg->pattern) {
+       case TPG_PAT_CHECKERS_16X16:
+               return (line >> 4) & 1;
+       case TPG_PAT_CHECKERS_1X1:
+       case TPG_PAT_ALTERNATING_HLINES:
+               return line & 1;
+       case TPG_PAT_100_COLORSQUARES:
+       case TPG_PAT_100_HCOLORBAR:
+               return (line * 8) / tpg->src_height;
+       case TPG_PAT_CROSS_1_PIXEL:
+               return line == tpg->src_height / 2;
+       case TPG_PAT_CROSS_2_PIXELS:
+               return (line + 1) / 2 == tpg->src_height / 4;
+       case TPG_PAT_CROSS_10_PIXELS:
+               return (line + 10) / 20 == tpg->src_height / 40;
+       default:
+               return 0;
+       }
+}
+
+/*
+ * Which color should be used for the given pattern line and X coordinate.
+ * Note: x is in the range 0 to 2 * tpg->src_width.
+ */
+static enum tpg_color tpg_get_color(struct tpg_data *tpg, unsigned pat_line, unsigned x)
+{
+       /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
+          should be modified */
+       static const enum tpg_color bars[3][8] = {
+               /* Standard ITU-R 75% color bar sequence */
+               { TPG_COLOR_CSC_WHITE,   TPG_COLOR_75_YELLOW,
+                 TPG_COLOR_75_CYAN,     TPG_COLOR_75_GREEN,
+                 TPG_COLOR_75_MAGENTA,  TPG_COLOR_75_RED,
+                 TPG_COLOR_75_BLUE,     TPG_COLOR_100_BLACK, },
+               /* Standard ITU-R 100% color bar sequence */
+               { TPG_COLOR_100_WHITE,   TPG_COLOR_100_YELLOW,
+                 TPG_COLOR_100_CYAN,    TPG_COLOR_100_GREEN,
+                 TPG_COLOR_100_MAGENTA, TPG_COLOR_100_RED,
+                 TPG_COLOR_100_BLUE,    TPG_COLOR_100_BLACK, },
+               /* Color bar sequence suitable to test CSC */
+               { TPG_COLOR_CSC_WHITE,   TPG_COLOR_CSC_YELLOW,
+                 TPG_COLOR_CSC_CYAN,    TPG_COLOR_CSC_GREEN,
+                 TPG_COLOR_CSC_MAGENTA, TPG_COLOR_CSC_RED,
+                 TPG_COLOR_CSC_BLUE,    TPG_COLOR_CSC_BLACK, },
+       };
+
+       switch (tpg->pattern) {
+       case TPG_PAT_75_COLORBAR:
+       case TPG_PAT_100_COLORBAR:
+       case TPG_PAT_CSC_COLORBAR:
+               return bars[tpg->pattern][((x * 8) / tpg->src_width) % 8];
+       case TPG_PAT_100_COLORSQUARES:
+               return bars[1][(pat_line + (x * 8) / tpg->src_width) % 8];
+       case TPG_PAT_100_HCOLORBAR:
+               return bars[1][pat_line];
+       case TPG_PAT_BLACK:
+               return TPG_COLOR_100_BLACK;
+       case TPG_PAT_WHITE:
+               return TPG_COLOR_100_WHITE;
+       case TPG_PAT_RED:
+               return TPG_COLOR_100_RED;
+       case TPG_PAT_GREEN:
+               return TPG_COLOR_100_GREEN;
+       case TPG_PAT_BLUE:
+               return TPG_COLOR_100_BLUE;
+       case TPG_PAT_CHECKERS_16X16:
+               return (((x >> 4) & 1) ^ (pat_line & 1)) ?
+                       TPG_COLOR_100_BLACK : TPG_COLOR_100_WHITE;
+       case TPG_PAT_CHECKERS_1X1:
+               return ((x & 1) ^ (pat_line & 1)) ?
+                       TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+       case TPG_PAT_ALTERNATING_HLINES:
+               return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+       case TPG_PAT_ALTERNATING_VLINES:
+               return (x & 1) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+       case TPG_PAT_CROSS_1_PIXEL:
+               if (pat_line || (x % tpg->src_width) == tpg->src_width / 2)
+                       return TPG_COLOR_100_BLACK;
+               return TPG_COLOR_100_WHITE;
+       case TPG_PAT_CROSS_2_PIXELS:
+               if (pat_line || ((x % tpg->src_width) + 1) / 2 == tpg->src_width / 4)
+                       return TPG_COLOR_100_BLACK;
+               return TPG_COLOR_100_WHITE;
+       case TPG_PAT_CROSS_10_PIXELS:
+               if (pat_line || ((x % tpg->src_width) + 10) / 20 == tpg->src_width / 40)
+                       return TPG_COLOR_100_BLACK;
+               return TPG_COLOR_100_WHITE;
+       case TPG_PAT_GRAY_RAMP:
+               return TPG_COLOR_RAMP + ((x % tpg->src_width) * 256) / tpg->src_width;
+       default:
+               return TPG_COLOR_100_RED;
+       }
+}
+
+/*
+ * Given the pixel aspect ratio and video aspect ratio calculate the
+ * coordinates of a centered square and the coordinates of the border of
+ * the active video area. The coordinates are relative to the source
+ * frame rectangle.
+ */
+static void tpg_calculate_square_border(struct tpg_data *tpg)
+{
+       unsigned w = tpg->src_width;
+       unsigned h = tpg->src_height;
+       unsigned sq_w, sq_h;
+
+       sq_w = (w * 2 / 5) & ~1;
+       if (((w - sq_w) / 2) & 1)
+               sq_w += 2;
+       sq_h = sq_w;
+       tpg->square.width = sq_w;
+       if (tpg->vid_aspect == TPG_VIDEO_ASPECT_16X9_ANAMORPHIC) {
+               unsigned ana_sq_w = (sq_w / 4) * 3;
+
+               if (((w - ana_sq_w) / 2) & 1)
+                       ana_sq_w += 2;
+               tpg->square.width = ana_sq_w;
+       }
+       tpg->square.left = (w - tpg->square.width) / 2;
+       if (tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC)
+               sq_h = sq_w * 10 / 11;
+       else if (tpg->pix_aspect == TPG_PIXEL_ASPECT_PAL)
+               sq_h = sq_w * 59 / 54;
+       tpg->square.height = sq_h;
+       tpg->square.top = (h - sq_h) / 2;
+       tpg->border.left = 0;
+       tpg->border.width = w;
+       tpg->border.top = 0;
+       tpg->border.height = h;
+       switch (tpg->vid_aspect) {
+       case TPG_VIDEO_ASPECT_4X3:
+               if (tpg->pix_aspect)
+                       return;
+               if (3 * w >= 4 * h) {
+                       tpg->border.width = ((4 * h) / 3) & ~1;
+                       if (((w - tpg->border.width) / 2) & ~1)
+                               tpg->border.width -= 2;
+                       tpg->border.left = (w - tpg->border.width) / 2;
+                       break;
+               }
+               tpg->border.height = ((3 * w) / 4) & ~1;
+               tpg->border.top = (h - tpg->border.height) / 2;
+               break;
+       case TPG_VIDEO_ASPECT_14X9_CENTRE:
+               if (tpg->pix_aspect) {
+                       tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 420 : 506;
+                       tpg->border.top = (h - tpg->border.height) / 2;
+                       break;
+               }
+               if (9 * w >= 14 * h) {
+                       tpg->border.width = ((14 * h) / 9) & ~1;
+                       if (((w - tpg->border.width) / 2) & ~1)
+                               tpg->border.width -= 2;
+                       tpg->border.left = (w - tpg->border.width) / 2;
+                       break;
+               }
+               tpg->border.height = ((9 * w) / 14) & ~1;
+               tpg->border.top = (h - tpg->border.height) / 2;
+               break;
+       case TPG_VIDEO_ASPECT_16X9_CENTRE:
+               if (tpg->pix_aspect) {
+                       tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 368 : 442;
+                       tpg->border.top = (h - tpg->border.height) / 2;
+                       break;
+               }
+               if (9 * w >= 16 * h) {
+                       tpg->border.width = ((16 * h) / 9) & ~1;
+                       if (((w - tpg->border.width) / 2) & ~1)
+                               tpg->border.width -= 2;
+                       tpg->border.left = (w - tpg->border.width) / 2;
+                       break;
+               }
+               tpg->border.height = ((9 * w) / 16) & ~1;
+               tpg->border.top = (h - tpg->border.height) / 2;
+               break;
+       default:
+               break;
+       }
+}
+
+static void tpg_precalculate_line(struct tpg_data *tpg)
+{
+       enum tpg_color contrast;
+       unsigned pat;
+       unsigned p;
+       unsigned x;
+
+       switch (tpg->pattern) {
+       case TPG_PAT_GREEN:
+               contrast = TPG_COLOR_100_RED;
+               break;
+       case TPG_PAT_CSC_COLORBAR:
+               contrast = TPG_COLOR_CSC_GREEN;
+               break;
+       default:
+               contrast = TPG_COLOR_100_GREEN;
+               break;
+       }
+
+       for (pat = 0; pat < tpg_get_pat_lines(tpg); pat++) {
+               /* Coarse scaling with Bresenham */
+               unsigned int_part = tpg->src_width / tpg->scaled_width;
+               unsigned fract_part = tpg->src_width % tpg->scaled_width;
+               unsigned src_x = 0;
+               unsigned error = 0;
+
+               for (x = 0; x < tpg->scaled_width * 2; x += 2) {
+                       unsigned real_x = src_x;
+                       enum tpg_color color1, color2;
+                       u8 pix[TPG_MAX_PLANES][8];
+
+                       real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
+                       color1 = tpg_get_color(tpg, pat, real_x);
+
+                       src_x += int_part;
+                       error += fract_part;
+                       if (error >= tpg->scaled_width) {
+                               error -= tpg->scaled_width;
+                               src_x++;
+                       }
+
+                       real_x = src_x;
+                       real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
+                       color2 = tpg_get_color(tpg, pat, real_x);
+
+                       src_x += int_part;
+                       error += fract_part;
+                       if (error >= tpg->scaled_width) {
+                               error -= tpg->scaled_width;
+                               src_x++;
+                       }
+
+                       gen_twopix(tpg, pix, tpg->hflip ? color2 : color1, 0);
+                       gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1);
+                       for (p = 0; p < tpg->planes; p++) {
+                               unsigned twopixsize = tpg->twopixelsize[p];
+                               u8 *pos = tpg->lines[pat][p] + x * twopixsize / 2;
+
+                               memcpy(pos, pix[p], twopixsize);
+                       }
+               }
+       }
+       for (x = 0; x < tpg->scaled_width; x += 2) {
+               u8 pix[TPG_MAX_PLANES][8];
+
+               gen_twopix(tpg, pix, contrast, 0);
+               gen_twopix(tpg, pix, contrast, 1);
+               for (p = 0; p < tpg->planes; p++) {
+                       unsigned twopixsize = tpg->twopixelsize[p];
+                       u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2;
+
+                       memcpy(pos, pix[p], twopixsize);
+               }
+       }
+       for (x = 0; x < tpg->scaled_width; x += 2) {
+               u8 pix[TPG_MAX_PLANES][8];
+
+               gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0);
+               gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1);
+               for (p = 0; p < tpg->planes; p++) {
+                       unsigned twopixsize = tpg->twopixelsize[p];
+                       u8 *pos = tpg->black_line[p] + x * twopixsize / 2;
+
+                       memcpy(pos, pix[p], twopixsize);
+               }
+       }
+       for (x = 0; x < tpg->scaled_width * 2; x += 2) {
+               u8 pix[TPG_MAX_PLANES][8];
+
+               gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0);
+               gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1);
+               for (p = 0; p < tpg->planes; p++) {
+                       unsigned twopixsize = tpg->twopixelsize[p];
+                       u8 *pos = tpg->random_line[p] + x * twopixsize / 2;
+
+                       memcpy(pos, pix[p], twopixsize);
+               }
+       }
+       gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0);
+       gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1);
+       gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0);
+       gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 1);
+}
+
+/* need this to do rgb24 rendering */
+typedef struct { u16 __; u8 _; } __packed x24;
+
+void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+               int y, int x, char *text)
+{
+       int line;
+       unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
+       unsigned div = step;
+       unsigned first = 0;
+       unsigned len = strlen(text);
+       unsigned p;
+
+       if (font8x16 == NULL || basep == NULL)
+               return;
+
+       /* Checks if it is possible to show string */
+       if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width)
+               return;
+
+       if (len > (tpg->compose.width - x) / 8)
+               len = (tpg->compose.width - x) / 8;
+       if (tpg->vflip)
+               y = tpg->compose.height - y - 16;
+       if (tpg->hflip)
+               x = tpg->compose.width - x - 8;
+       y += tpg->compose.top;
+       x += tpg->compose.left;
+       if (tpg->field == V4L2_FIELD_BOTTOM)
+               first = 1;
+       else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT)
+               div = 2;
+
+       for (p = 0; p < tpg->planes; p++) {
+               /* Print stream time */
+#define PRINTSTR(PIXTYPE) do { \
+       PIXTYPE fg;     \
+       PIXTYPE bg;     \
+       memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE));   \
+       memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE));   \
+       \
+       for (line = first; line < 16; line += step) {   \
+               int l = tpg->vflip ? 15 - line : line; \
+               PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
+                              ((y * step + l) / div) * tpg->bytesperline[p] + \
+                              x * sizeof(PIXTYPE));    \
+               unsigned s;     \
+       \
+               for (s = 0; s < len; s++) {     \
+                       u8 chr = font8x16[text[s] * 16 + line]; \
+       \
+                       if (tpg->hflip) { \
+                               pos[7] = (chr & (0x01 << 7) ? fg : bg); \
+                               pos[6] = (chr & (0x01 << 6) ? fg : bg); \
+                               pos[5] = (chr & (0x01 << 5) ? fg : bg); \
+                               pos[4] = (chr & (0x01 << 4) ? fg : bg); \
+                               pos[3] = (chr & (0x01 << 3) ? fg : bg); \
+                               pos[2] = (chr & (0x01 << 2) ? fg : bg); \
+                               pos[1] = (chr & (0x01 << 1) ? fg : bg); \
+                               pos[0] = (chr & (0x01 << 0) ? fg : bg); \
+                       } else { \
+                               pos[0] = (chr & (0x01 << 7) ? fg : bg); \
+                               pos[1] = (chr & (0x01 << 6) ? fg : bg); \
+                               pos[2] = (chr & (0x01 << 5) ? fg : bg); \
+                               pos[3] = (chr & (0x01 << 4) ? fg : bg); \
+                               pos[4] = (chr & (0x01 << 3) ? fg : bg); \
+                               pos[5] = (chr & (0x01 << 2) ? fg : bg); \
+                               pos[6] = (chr & (0x01 << 1) ? fg : bg); \
+                               pos[7] = (chr & (0x01 << 0) ? fg : bg); \
+                       } \
+       \
+                       pos += tpg->hflip ? -8 : 8;     \
+               }       \
+       }       \
+} while (0)
+
+               switch (tpg->twopixelsize[p]) {
+               case 2:
+                       PRINTSTR(u8); break;
+               case 4:
+                       PRINTSTR(u16); break;
+               case 6:
+                       PRINTSTR(x24); break;
+               case 8:
+                       PRINTSTR(u32); break;
+               }
+       }
+}
+
+void tpg_update_mv_step(struct tpg_data *tpg)
+{
+       int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1;
+
+       if (tpg->hflip)
+               factor = -factor;
+       switch (tpg->mv_hor_mode) {
+       case TPG_MOVE_NEG_FAST:
+       case TPG_MOVE_POS_FAST:
+               tpg->mv_hor_step = ((tpg->src_width + 319) / 320) * 4;
+               break;
+       case TPG_MOVE_NEG:
+       case TPG_MOVE_POS:
+               tpg->mv_hor_step = ((tpg->src_width + 639) / 640) * 4;
+               break;
+       case TPG_MOVE_NEG_SLOW:
+       case TPG_MOVE_POS_SLOW:
+               tpg->mv_hor_step = 2;
+               break;
+       case TPG_MOVE_NONE:
+               tpg->mv_hor_step = 0;
+               break;
+       }
+       if (factor < 0)
+               tpg->mv_hor_step = tpg->src_width - tpg->mv_hor_step;
+
+       factor = tpg->mv_vert_mode > TPG_MOVE_NONE ? -1 : 1;
+       switch (tpg->mv_vert_mode) {
+       case TPG_MOVE_NEG_FAST:
+       case TPG_MOVE_POS_FAST:
+               tpg->mv_vert_step = ((tpg->src_width + 319) / 320) * 4;
+               break;
+       case TPG_MOVE_NEG:
+       case TPG_MOVE_POS:
+               tpg->mv_vert_step = ((tpg->src_width + 639) / 640) * 4;
+               break;
+       case TPG_MOVE_NEG_SLOW:
+       case TPG_MOVE_POS_SLOW:
+               tpg->mv_vert_step = 1;
+               break;
+       case TPG_MOVE_NONE:
+               tpg->mv_vert_step = 0;
+               break;
+       }
+       if (factor < 0)
+               tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step;
+}
+
+/* Map the line number relative to the crop rectangle to a frame line number */
+static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y,
+                                   unsigned field)
+{
+       switch (field) {
+       case V4L2_FIELD_TOP:
+               return tpg->crop.top + src_y * 2;
+       case V4L2_FIELD_BOTTOM:
+               return tpg->crop.top + src_y * 2 + 1;
+       default:
+               return src_y + tpg->crop.top;
+       }
+}
+
+/*
+ * Map the line number relative to the compose rectangle to a destination
+ * buffer line number.
+ */
+static unsigned tpg_calc_buffer_line(struct tpg_data *tpg, unsigned y,
+                                   unsigned field)
+{
+       y += tpg->compose.top;
+       switch (field) {
+       case V4L2_FIELD_SEQ_TB:
+               if (y & 1)
+                       return tpg->buf_height / 2 + y / 2;
+               return y / 2;
+       case V4L2_FIELD_SEQ_BT:
+               if (y & 1)
+                       return y / 2;
+               return tpg->buf_height / 2 + y / 2;
+       default:
+               return y;
+       }
+}
+
+static void tpg_recalc(struct tpg_data *tpg)
+{
+       if (tpg->recalc_colors) {
+               tpg->recalc_colors = false;
+               tpg->recalc_lines = true;
+               tpg_precalculate_colors(tpg);
+       }
+       if (tpg->recalc_square_border) {
+               tpg->recalc_square_border = false;
+               tpg_calculate_square_border(tpg);
+       }
+       if (tpg->recalc_lines) {
+               tpg->recalc_lines = false;
+               tpg_precalculate_line(tpg);
+       }
+}
+
+void tpg_calc_text_basep(struct tpg_data *tpg,
+               u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf)
+{
+       unsigned stride = tpg->bytesperline[p];
+
+       tpg_recalc(tpg);
+
+       basep[p][0] = vbuf;
+       basep[p][1] = vbuf;
+       if (tpg->field == V4L2_FIELD_SEQ_TB)
+               basep[p][1] += tpg->buf_height * stride / 2;
+       else if (tpg->field == V4L2_FIELD_SEQ_BT)
+               basep[p][0] += tpg->buf_height * stride / 2;
+}
+
+void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
+{
+       bool is_tv = std;
+       bool is_60hz = is_tv && (std & V4L2_STD_525_60);
+       unsigned mv_hor_old = tpg->mv_hor_count % tpg->src_width;
+       unsigned mv_hor_new = (tpg->mv_hor_count + tpg->mv_hor_step) % tpg->src_width;
+       unsigned mv_vert_old = tpg->mv_vert_count % tpg->src_height;
+       unsigned mv_vert_new = (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height;
+       unsigned wss_width;
+       unsigned f;
+       int hmax = (tpg->compose.height * tpg->perc_fill) / 100;
+       int h;
+       unsigned twopixsize = tpg->twopixelsize[p];
+       unsigned img_width = tpg->compose.width * twopixsize / 2;
+       unsigned line_offset;
+       unsigned left_pillar_width = 0;
+       unsigned right_pillar_start = img_width;
+       unsigned stride = tpg->bytesperline[p];
+       unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
+       u8 *orig_vbuf = vbuf;
+
+       /* Coarse scaling with Bresenham */
+       unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height;
+       unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height;
+       unsigned src_y = 0;
+       unsigned error = 0;
+
+       tpg_recalc(tpg);
+
+       mv_hor_old = (mv_hor_old * tpg->scaled_width / tpg->src_width) & ~1;
+       mv_hor_new = (mv_hor_new * tpg->scaled_width / tpg->src_width) & ~1;
+       wss_width = tpg->crop.left < tpg->src_width / 2 ?
+                       tpg->src_width / 2 - tpg->crop.left : 0;
+       if (wss_width > tpg->crop.width)
+               wss_width = tpg->crop.width;
+       wss_width = wss_width * tpg->scaled_width / tpg->src_width;
+
+       vbuf += tpg->compose.left * twopixsize / 2;
+       line_offset = tpg->crop.left * tpg->scaled_width / tpg->src_width;
+       line_offset = (line_offset & ~1) * twopixsize / 2;
+       if (tpg->crop.left < tpg->border.left) {
+               left_pillar_width = tpg->border.left - tpg->crop.left;
+               if (left_pillar_width > tpg->crop.width)
+                       left_pillar_width = tpg->crop.width;
+               left_pillar_width = (left_pillar_width * tpg->scaled_width) / tpg->src_width;
+               left_pillar_width = (left_pillar_width & ~1) * twopixsize / 2;
+       }
+       if (tpg->crop.left + tpg->crop.width > tpg->border.left + tpg->border.width) {
+               right_pillar_start = tpg->border.left + tpg->border.width - tpg->crop.left;
+               right_pillar_start = (right_pillar_start * tpg->scaled_width) / tpg->src_width;
+               right_pillar_start = (right_pillar_start & ~1) * twopixsize / 2;
+               if (right_pillar_start > img_width)
+                       right_pillar_start = img_width;
+       }
+
+       f = tpg->field == (is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+
+       for (h = 0; h < tpg->compose.height; h++) {
+               bool even;
+               bool fill_blank = false;
+               unsigned frame_line;
+               unsigned buf_line;
+               unsigned pat_line_old;
+               unsigned pat_line_new;
+               u8 *linestart_older;
+               u8 *linestart_newer;
+               u8 *linestart_top;
+               u8 *linestart_bottom;
+
+               frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
+               even = !(frame_line & 1);
+               buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
+               src_y += int_part;
+               error += fract_part;
+               if (error >= tpg->compose.height) {
+                       error -= tpg->compose.height;
+                       src_y++;
+               }
+
+               if (h >= hmax) {
+                       if (hmax == tpg->compose.height)
+                               continue;
+                       if (!tpg->perc_fill_blank)
+                               continue;
+                       fill_blank = true;
+               }
+
+               if (tpg->vflip)
+                       frame_line = tpg->src_height - frame_line - 1;
+
+               if (fill_blank) {
+                       linestart_older = tpg->contrast_line[p];
+                       linestart_newer = tpg->contrast_line[p];
+               } else if (tpg->qual != TPG_QUAL_NOISE &&
+                          (frame_line < tpg->border.top ||
+                           frame_line >= tpg->border.top + tpg->border.height)) {
+                       linestart_older = tpg->black_line[p];
+                       linestart_newer = tpg->black_line[p];
+               } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) {
+                       linestart_older = tpg->random_line[p] +
+                                         twopixsize * prandom_u32_max(tpg->src_width / 2);
+                       linestart_newer = tpg->random_line[p] +
+                                         twopixsize * prandom_u32_max(tpg->src_width / 2);
+               } else {
+                       pat_line_old = tpg_get_pat_line(tpg,
+                                               (frame_line + mv_vert_old) % tpg->src_height);
+                       pat_line_new = tpg_get_pat_line(tpg,
+                                               (frame_line + mv_vert_new) % tpg->src_height);
+                       linestart_older = tpg->lines[pat_line_old][p] +
+                                         mv_hor_old * twopixsize / 2;
+                       linestart_newer = tpg->lines[pat_line_new][p] +
+                                         mv_hor_new * twopixsize / 2;
+                       linestart_older += line_offset;
+                       linestart_newer += line_offset;
+               }
+               if (is_60hz) {
+                       linestart_top = linestart_newer;
+                       linestart_bottom = linestart_older;
+               } else {
+                       linestart_top = linestart_older;
+                       linestart_bottom = linestart_newer;
+               }
+
+               switch (tpg->field) {
+               case V4L2_FIELD_INTERLACED:
+               case V4L2_FIELD_INTERLACED_TB:
+               case V4L2_FIELD_SEQ_TB:
+               case V4L2_FIELD_SEQ_BT:
+                       if (even)
+                               memcpy(vbuf + buf_line * stride, linestart_top, img_width);
+                       else
+                               memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
+                       break;
+               case V4L2_FIELD_INTERLACED_BT:
+                       if (even)
+                               memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
+                       else
+                               memcpy(vbuf + buf_line * stride, linestart_top, img_width);
+                       break;
+               case V4L2_FIELD_TOP:
+                       memcpy(vbuf + buf_line * stride, linestart_top, img_width);
+                       break;
+               case V4L2_FIELD_BOTTOM:
+                       memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
+                       break;
+               case V4L2_FIELD_NONE:
+               default:
+                       memcpy(vbuf + buf_line * stride, linestart_older, img_width);
+                       break;
+               }
+
+               if (is_tv && !is_60hz && frame_line == 0 && wss_width) {
+                       /*
+                        * Replace the first half of the top line of a 50 Hz frame
+                        * with random data to simulate a WSS signal.
+                        */
+                       u8 *wss = tpg->random_line[p] +
+                                 twopixsize * prandom_u32_max(tpg->src_width / 2);
+
+                       memcpy(vbuf + buf_line * stride, wss, wss_width * twopixsize / 2);
+               }
+       }
+
+       vbuf = orig_vbuf;
+       vbuf += tpg->compose.left * twopixsize / 2;
+       src_y = 0;
+       error = 0;
+       for (h = 0; h < tpg->compose.height; h++) {
+               unsigned frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
+               unsigned buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
+               const struct v4l2_rect *sq = &tpg->square;
+               const struct v4l2_rect *b = &tpg->border;
+               const struct v4l2_rect *c = &tpg->crop;
+
+               src_y += int_part;
+               error += fract_part;
+               if (error >= tpg->compose.height) {
+                       error -= tpg->compose.height;
+                       src_y++;
+               }
+
+               if (tpg->show_border && frame_line >= b->top &&
+                   frame_line < b->top + b->height) {
+                       unsigned bottom = b->top + b->height - 1;
+                       unsigned left = left_pillar_width;
+                       unsigned right = right_pillar_start;
+
+                       if (frame_line == b->top || frame_line == b->top + 1 ||
+                           frame_line == bottom || frame_line == bottom - 1) {
+                               memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p],
+                                               right - left);
+                       } else {
+                               if (b->left >= c->left &&
+                                   b->left < c->left + c->width)
+                                       memcpy(vbuf + buf_line * stride + left,
+                                               tpg->contrast_line[p], twopixsize);
+                               if (b->left + b->width > c->left &&
+                                   b->left + b->width <= c->left + c->width)
+                                       memcpy(vbuf + buf_line * stride + right - twopixsize,
+                                               tpg->contrast_line[p], twopixsize);
+                       }
+               }
+               if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
+                   frame_line < b->top + b->height) {
+                       memcpy(vbuf + buf_line * stride, tpg->black_line[p], left_pillar_width);
+                       memcpy(vbuf + buf_line * stride + right_pillar_start, tpg->black_line[p],
+                              img_width - right_pillar_start);
+               }
+               if (tpg->show_square && frame_line >= sq->top &&
+                   frame_line < sq->top + sq->height &&
+                   sq->left < c->left + c->width &&
+                   sq->left + sq->width >= c->left) {
+                       unsigned left = sq->left;
+                       unsigned width = sq->width;
+
+                       if (c->left > left) {
+                               width -= c->left - left;
+                               left = c->left;
+                       }
+                       if (c->left + c->width < left + width)
+                               width -= left + width - c->left - c->width;
+                       left -= c->left;
+                       left = (left * tpg->scaled_width) / tpg->src_width;
+                       left = (left & ~1) * twopixsize / 2;
+                       width = (width * tpg->scaled_width) / tpg->src_width;
+                       width = (width & ~1) * twopixsize / 2;
+                       memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], width);
+               }
+               if (tpg->insert_sav) {
+                       unsigned offset = (tpg->compose.width / 6) * twopixsize;
+                       u8 *p = vbuf + buf_line * stride + offset;
+                       unsigned vact = 0, hact = 0;
+
+                       p[0] = 0xff;
+                       p[1] = 0;
+                       p[2] = 0;
+                       p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
+                               ((hact ^ vact) << 3) |
+                               ((hact ^ f) << 2) |
+                               ((f ^ vact) << 1) |
+                               (hact ^ vact ^ f);
+               }
+               if (tpg->insert_eav) {
+                       unsigned offset = (tpg->compose.width / 6) * 2 * twopixsize;
+                       u8 *p = vbuf + buf_line * stride + offset;
+                       unsigned vact = 0, hact = 1;
+
+                       p[0] = 0xff;
+                       p[1] = 0;
+                       p[2] = 0;
+                       p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
+                               ((hact ^ vact) << 3) |
+                               ((hact ^ f) << 2) |
+                               ((f ^ vact) << 1) |
+                               (hact ^ vact ^ f);
+               }
+       }
+}
diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h
new file mode 100644 (file)
index 0000000..8ef3e52
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * vivid-tpg.h - Test Pattern Generator
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_TPG_H_
+#define _VIVID_TPG_H_
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+
+#include "vivid-tpg-colors.h"
+
+enum tpg_pattern {
+       TPG_PAT_75_COLORBAR,
+       TPG_PAT_100_COLORBAR,
+       TPG_PAT_CSC_COLORBAR,
+       TPG_PAT_100_HCOLORBAR,
+       TPG_PAT_100_COLORSQUARES,
+       TPG_PAT_BLACK,
+       TPG_PAT_WHITE,
+       TPG_PAT_RED,
+       TPG_PAT_GREEN,
+       TPG_PAT_BLUE,
+       TPG_PAT_CHECKERS_16X16,
+       TPG_PAT_CHECKERS_1X1,
+       TPG_PAT_ALTERNATING_HLINES,
+       TPG_PAT_ALTERNATING_VLINES,
+       TPG_PAT_CROSS_1_PIXEL,
+       TPG_PAT_CROSS_2_PIXELS,
+       TPG_PAT_CROSS_10_PIXELS,
+       TPG_PAT_GRAY_RAMP,
+
+       /* Must be the last pattern */
+       TPG_PAT_NOISE,
+};
+
+extern const char * const tpg_pattern_strings[];
+
+enum tpg_quality {
+       TPG_QUAL_COLOR,
+       TPG_QUAL_GRAY,
+       TPG_QUAL_NOISE
+};
+
+enum tpg_video_aspect {
+       TPG_VIDEO_ASPECT_IMAGE,
+       TPG_VIDEO_ASPECT_4X3,
+       TPG_VIDEO_ASPECT_14X9_CENTRE,
+       TPG_VIDEO_ASPECT_16X9_CENTRE,
+       TPG_VIDEO_ASPECT_16X9_ANAMORPHIC,
+};
+
+enum tpg_pixel_aspect {
+       TPG_PIXEL_ASPECT_SQUARE,
+       TPG_PIXEL_ASPECT_NTSC,
+       TPG_PIXEL_ASPECT_PAL,
+};
+
+enum tpg_move_mode {
+       TPG_MOVE_NEG_FAST,
+       TPG_MOVE_NEG,
+       TPG_MOVE_NEG_SLOW,
+       TPG_MOVE_NONE,
+       TPG_MOVE_POS_SLOW,
+       TPG_MOVE_POS,
+       TPG_MOVE_POS_FAST,
+};
+
+extern const char * const tpg_aspect_strings[];
+
+#define TPG_MAX_PLANES 2
+#define TPG_MAX_PAT_LINES 8
+
+struct tpg_data {
+       /* Source frame size */
+       unsigned                        src_width, src_height;
+       /* Buffer height */
+       unsigned                        buf_height;
+       /* Scaled output frame size */
+       unsigned                        scaled_width;
+       u32                             field;
+       /* crop coordinates are frame-based */
+       struct v4l2_rect                crop;
+       /* compose coordinates are format-based */
+       struct v4l2_rect                compose;
+       /* border and square coordinates are frame-based */
+       struct v4l2_rect                border;
+       struct v4l2_rect                square;
+
+       /* Color-related fields */
+       enum tpg_quality                qual;
+       unsigned                        qual_offset;
+       u8                              alpha_component;
+       bool                            alpha_red_only;
+       u8                              brightness;
+       u8                              contrast;
+       u8                              saturation;
+       s16                             hue;
+       u32                             fourcc;
+       bool                            is_yuv;
+       u32                             colorspace;
+       enum tpg_video_aspect           vid_aspect;
+       enum tpg_pixel_aspect           pix_aspect;
+       unsigned                        rgb_range;
+       unsigned                        real_rgb_range;
+       unsigned                        planes;
+       /* Used to store the colors in native format, either RGB or YUV */
+       u8                              colors[TPG_COLOR_MAX][3];
+       u8                              textfg[TPG_MAX_PLANES][8], textbg[TPG_MAX_PLANES][8];
+       /* size in bytes for two pixels in each plane */
+       unsigned                        twopixelsize[TPG_MAX_PLANES];
+       unsigned                        bytesperline[TPG_MAX_PLANES];
+
+       /* Configuration */
+       enum tpg_pattern                pattern;
+       bool                            hflip;
+       bool                            vflip;
+       unsigned                        perc_fill;
+       bool                            perc_fill_blank;
+       bool                            show_border;
+       bool                            show_square;
+       bool                            insert_sav;
+       bool                            insert_eav;
+
+       /* Test pattern movement */
+       enum tpg_move_mode              mv_hor_mode;
+       int                             mv_hor_count;
+       int                             mv_hor_step;
+       enum tpg_move_mode              mv_vert_mode;
+       int                             mv_vert_count;
+       int                             mv_vert_step;
+
+       bool                            recalc_colors;
+       bool                            recalc_lines;
+       bool                            recalc_square_border;
+
+       /* Used to store TPG_MAX_PAT_LINES lines, each with up to two planes */
+       unsigned                        max_line_width;
+       u8                              *lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES];
+       u8                              *random_line[TPG_MAX_PLANES];
+       u8                              *contrast_line[TPG_MAX_PLANES];
+       u8                              *black_line[TPG_MAX_PLANES];
+};
+
+void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h);
+int tpg_alloc(struct tpg_data *tpg, unsigned max_w);
+void tpg_free(struct tpg_data *tpg);
+void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
+                      u32 field);
+
+void tpg_set_font(const u8 *f);
+void tpg_gen_text(struct tpg_data *tpg,
+               u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text);
+void tpg_calc_text_basep(struct tpg_data *tpg,
+               u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf);
+void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf);
+bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc);
+void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
+               const struct v4l2_rect *compose);
+
+static inline void tpg_s_pattern(struct tpg_data *tpg, enum tpg_pattern pattern)
+{
+       if (tpg->pattern == pattern)
+               return;
+       tpg->pattern = pattern;
+       tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_quality(struct tpg_data *tpg,
+                                   enum tpg_quality qual, unsigned qual_offset)
+{
+       if (tpg->qual == qual && tpg->qual_offset == qual_offset)
+               return;
+       tpg->qual = qual;
+       tpg->qual_offset = qual_offset;
+       tpg->recalc_colors = true;
+}
+
+static inline enum tpg_quality tpg_g_quality(const struct tpg_data *tpg)
+{
+       return tpg->qual;
+}
+
+static inline void tpg_s_alpha_component(struct tpg_data *tpg,
+                                           u8 alpha_component)
+{
+       if (tpg->alpha_component == alpha_component)
+               return;
+       tpg->alpha_component = alpha_component;
+       tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_alpha_mode(struct tpg_data *tpg,
+                                           bool red_only)
+{
+       if (tpg->alpha_red_only == red_only)
+               return;
+       tpg->alpha_red_only = red_only;
+       tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_brightness(struct tpg_data *tpg,
+                                       u8 brightness)
+{
+       if (tpg->brightness == brightness)
+               return;
+       tpg->brightness = brightness;
+       tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_contrast(struct tpg_data *tpg,
+                                       u8 contrast)
+{
+       if (tpg->contrast == contrast)
+               return;
+       tpg->contrast = contrast;
+       tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_saturation(struct tpg_data *tpg,
+                                       u8 saturation)
+{
+       if (tpg->saturation == saturation)
+               return;
+       tpg->saturation = saturation;
+       tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_hue(struct tpg_data *tpg,
+                                       s16 hue)
+{
+       if (tpg->hue == hue)
+               return;
+       tpg->hue = hue;
+       tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_rgb_range(struct tpg_data *tpg,
+                                       unsigned rgb_range)
+{
+       if (tpg->rgb_range == rgb_range)
+               return;
+       tpg->rgb_range = rgb_range;
+       tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_real_rgb_range(struct tpg_data *tpg,
+                                       unsigned rgb_range)
+{
+       if (tpg->real_rgb_range == rgb_range)
+               return;
+       tpg->real_rgb_range = rgb_range;
+       tpg->recalc_colors = true;
+}
+
+static inline void tpg_s_colorspace(struct tpg_data *tpg, u32 colorspace)
+{
+       if (tpg->colorspace == colorspace)
+               return;
+       tpg->colorspace = colorspace;
+       tpg->recalc_colors = true;
+}
+
+static inline u32 tpg_g_colorspace(const struct tpg_data *tpg)
+{
+       return tpg->colorspace;
+}
+
+static inline unsigned tpg_g_planes(const struct tpg_data *tpg)
+{
+       return tpg->planes;
+}
+
+static inline unsigned tpg_g_twopixelsize(const struct tpg_data *tpg, unsigned plane)
+{
+       return tpg->twopixelsize[plane];
+}
+
+static inline unsigned tpg_g_bytesperline(const struct tpg_data *tpg, unsigned plane)
+{
+       return tpg->bytesperline[plane];
+}
+
+static inline void tpg_s_bytesperline(struct tpg_data *tpg, unsigned plane, unsigned bpl)
+{
+       tpg->bytesperline[plane] = bpl;
+}
+
+static inline void tpg_s_buf_height(struct tpg_data *tpg, unsigned h)
+{
+       tpg->buf_height = h;
+}
+
+static inline void tpg_s_field(struct tpg_data *tpg, unsigned field)
+{
+       tpg->field = field;
+}
+
+static inline void tpg_s_perc_fill(struct tpg_data *tpg,
+                                     unsigned perc_fill)
+{
+       tpg->perc_fill = perc_fill;
+}
+
+static inline unsigned tpg_g_perc_fill(const struct tpg_data *tpg)
+{
+       return tpg->perc_fill;
+}
+
+static inline void tpg_s_perc_fill_blank(struct tpg_data *tpg,
+                                        bool perc_fill_blank)
+{
+       tpg->perc_fill_blank = perc_fill_blank;
+}
+
+static inline void tpg_s_video_aspect(struct tpg_data *tpg,
+                                       enum tpg_video_aspect vid_aspect)
+{
+       if (tpg->vid_aspect == vid_aspect)
+               return;
+       tpg->vid_aspect = vid_aspect;
+       tpg->recalc_square_border = true;
+}
+
+static inline enum tpg_video_aspect tpg_g_video_aspect(const struct tpg_data *tpg)
+{
+       return tpg->vid_aspect;
+}
+
+static inline void tpg_s_pixel_aspect(struct tpg_data *tpg,
+                                       enum tpg_pixel_aspect pix_aspect)
+{
+       if (tpg->pix_aspect == pix_aspect)
+               return;
+       tpg->pix_aspect = pix_aspect;
+       tpg->recalc_square_border = true;
+}
+
+static inline void tpg_s_show_border(struct tpg_data *tpg,
+                                       bool show_border)
+{
+       tpg->show_border = show_border;
+}
+
+static inline void tpg_s_show_square(struct tpg_data *tpg,
+                                       bool show_square)
+{
+       tpg->show_square = show_square;
+}
+
+static inline void tpg_s_insert_sav(struct tpg_data *tpg, bool insert_sav)
+{
+       tpg->insert_sav = insert_sav;
+}
+
+static inline void tpg_s_insert_eav(struct tpg_data *tpg, bool insert_eav)
+{
+       tpg->insert_eav = insert_eav;
+}
+
+void tpg_update_mv_step(struct tpg_data *tpg);
+
+static inline void tpg_s_mv_hor_mode(struct tpg_data *tpg,
+                               enum tpg_move_mode mv_hor_mode)
+{
+       tpg->mv_hor_mode = mv_hor_mode;
+       tpg_update_mv_step(tpg);
+}
+
+static inline void tpg_s_mv_vert_mode(struct tpg_data *tpg,
+                               enum tpg_move_mode mv_vert_mode)
+{
+       tpg->mv_vert_mode = mv_vert_mode;
+       tpg_update_mv_step(tpg);
+}
+
+static inline void tpg_init_mv_count(struct tpg_data *tpg)
+{
+       tpg->mv_hor_count = tpg->mv_vert_count = 0;
+}
+
+static inline void tpg_update_mv_count(struct tpg_data *tpg, bool frame_is_field)
+{
+       tpg->mv_hor_count += tpg->mv_hor_step * (frame_is_field ? 1 : 2);
+       tpg->mv_vert_count += tpg->mv_vert_step * (frame_is_field ? 1 : 2);
+}
+
+static inline void tpg_s_hflip(struct tpg_data *tpg, bool hflip)
+{
+       if (tpg->hflip == hflip)
+               return;
+       tpg->hflip = hflip;
+       tpg_update_mv_step(tpg);
+       tpg->recalc_lines = true;
+}
+
+static inline bool tpg_g_hflip(const struct tpg_data *tpg)
+{
+       return tpg->hflip;
+}
+
+static inline void tpg_s_vflip(struct tpg_data *tpg, bool vflip)
+{
+       tpg->vflip = vflip;
+}
+
+static inline bool tpg_g_vflip(const struct tpg_data *tpg)
+{
+       return tpg->vflip;
+}
+
+static inline bool tpg_pattern_is_static(const struct tpg_data *tpg)
+{
+       return tpg->pattern != TPG_PAT_NOISE &&
+              tpg->mv_hor_mode == TPG_MOVE_NONE &&
+              tpg->mv_vert_mode == TPG_MOVE_NONE;
+}
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c
new file mode 100644 (file)
index 0000000..2166d0b
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * vivid-vbi-cap.c - vbi capture support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+
+#include "vivid-core.h"
+#include "vivid-kthread-cap.h"
+#include "vivid-vbi-cap.h"
+#include "vivid-vbi-gen.h"
+
+static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr)
+{
+       struct vivid_vbi_gen_data *vbi_gen = &dev->vbi_gen;
+       bool is_60hz = dev->std_cap & V4L2_STD_525_60;
+
+       vivid_vbi_gen_sliced(vbi_gen, is_60hz, seqnr);
+
+       if (!is_60hz) {
+               if (dev->loop_video) {
+                       if (dev->vbi_out_have_wss) {
+                               vbi_gen->data[12].data[0] = dev->vbi_out_wss[0];
+                               vbi_gen->data[12].data[1] = dev->vbi_out_wss[1];
+                       } else {
+                               vbi_gen->data[12].id = 0;
+                       }
+               } else {
+                       switch (tpg_g_video_aspect(&dev->tpg)) {
+                       case TPG_VIDEO_ASPECT_14X9_CENTRE:
+                               vbi_gen->data[12].data[0] = 0x01;
+                               break;
+                       case TPG_VIDEO_ASPECT_16X9_CENTRE:
+                               vbi_gen->data[12].data[0] = 0x0b;
+                               break;
+                       case TPG_VIDEO_ASPECT_16X9_ANAMORPHIC:
+                               vbi_gen->data[12].data[0] = 0x07;
+                               break;
+                       case TPG_VIDEO_ASPECT_4X3:
+                       default:
+                               vbi_gen->data[12].data[0] = 0x08;
+                               break;
+                       }
+               }
+       } else if (dev->loop_video && is_60hz) {
+               if (dev->vbi_out_have_cc[0]) {
+                       vbi_gen->data[0].data[0] = dev->vbi_out_cc[0][0];
+                       vbi_gen->data[0].data[1] = dev->vbi_out_cc[0][1];
+               } else {
+                       vbi_gen->data[0].id = 0;
+               }
+               if (dev->vbi_out_have_cc[1]) {
+                       vbi_gen->data[1].data[0] = dev->vbi_out_cc[1][0];
+                       vbi_gen->data[1].data[1] = dev->vbi_out_cc[1][1];
+               } else {
+                       vbi_gen->data[1].id = 0;
+               }
+       }
+}
+
+static void vivid_g_fmt_vbi_cap(struct vivid_dev *dev, struct v4l2_vbi_format *vbi)
+{
+       bool is_60hz = dev->std_cap & V4L2_STD_525_60;
+
+       vbi->sampling_rate = 27000000;
+       vbi->offset = 24;
+       vbi->samples_per_line = 1440;
+       vbi->sample_format = V4L2_PIX_FMT_GREY;
+       vbi->start[0] = is_60hz ? V4L2_VBI_ITU_525_F1_START + 9 : V4L2_VBI_ITU_625_F1_START + 5;
+       vbi->start[1] = is_60hz ? V4L2_VBI_ITU_525_F2_START + 9 : V4L2_VBI_ITU_625_F2_START + 5;
+       vbi->count[0] = vbi->count[1] = is_60hz ? 12 : 18;
+       vbi->flags = dev->vbi_cap_interlaced ? V4L2_VBI_INTERLACED : 0;
+       vbi->reserved[0] = 0;
+       vbi->reserved[1] = 0;
+}
+
+void vivid_raw_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
+{
+       struct v4l2_vbi_format vbi;
+       u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0);
+
+       vivid_g_fmt_vbi_cap(dev, &vbi);
+       buf->vb.v4l2_buf.sequence = dev->vbi_cap_seq_count;
+       if (dev->field_cap == V4L2_FIELD_ALTERNATE)
+               buf->vb.v4l2_buf.sequence /= 2;
+
+       vivid_sliced_vbi_cap_fill(dev, buf->vb.v4l2_buf.sequence);
+
+       memset(vbuf, 0x10, vb2_plane_size(&buf->vb, 0));
+
+       if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode))
+               vivid_vbi_gen_raw(&dev->vbi_gen, &vbi, vbuf);
+
+       v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+       buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+}
+
+
+void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
+{
+       struct v4l2_sliced_vbi_data *vbuf = vb2_plane_vaddr(&buf->vb, 0);
+
+       buf->vb.v4l2_buf.sequence = dev->vbi_cap_seq_count;
+       if (dev->field_cap == V4L2_FIELD_ALTERNATE)
+               buf->vb.v4l2_buf.sequence /= 2;
+
+       vivid_sliced_vbi_cap_fill(dev, buf->vb.v4l2_buf.sequence);
+
+       memset(vbuf, 0, vb2_plane_size(&buf->vb, 0));
+       if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode)) {
+               unsigned i;
+
+               for (i = 0; i < 25; i++)
+                       vbuf[i] = dev->vbi_gen.data[i];
+       }
+
+       v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+       buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset;
+}
+
+static int vbi_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+                      unsigned *nbuffers, unsigned *nplanes,
+                      unsigned sizes[], void *alloc_ctxs[])
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+       bool is_60hz = dev->std_cap & V4L2_STD_525_60;
+       unsigned size = vq->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE ?
+               36 * sizeof(struct v4l2_sliced_vbi_data) :
+               1440 * 2 * (is_60hz ? 12 : 18);
+
+       if (!vivid_is_sdtv_cap(dev))
+               return -EINVAL;
+
+       sizes[0] = size;
+
+       if (vq->num_buffers + *nbuffers < 2)
+               *nbuffers = 2 - vq->num_buffers;
+
+       *nplanes = 1;
+       return 0;
+}
+
+static int vbi_cap_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       bool is_60hz = dev->std_cap & V4L2_STD_525_60;
+       unsigned size = vb->vb2_queue->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE ?
+               36 * sizeof(struct v4l2_sliced_vbi_data) :
+               1440 * 2 * (is_60hz ? 12 : 18);
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       if (dev->buf_prepare_error) {
+               /*
+                * Error injection: test what happens if buf_prepare() returns
+                * an error.
+                */
+               dev->buf_prepare_error = false;
+               return -EINVAL;
+       }
+       if (vb2_plane_size(vb, 0) < size) {
+               dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n",
+                               __func__, vb2_plane_size(vb, 0), size);
+               return -EINVAL;
+       }
+       vb2_set_plane_payload(vb, 0, size);
+
+       return 0;
+}
+
+static void vbi_cap_buf_queue(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb);
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       spin_lock(&dev->slock);
+       list_add_tail(&buf->list, &dev->vbi_cap_active);
+       spin_unlock(&dev->slock);
+}
+
+static int vbi_cap_start_streaming(struct vb2_queue *vq, unsigned count)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+       int err;
+
+       dprintk(dev, 1, "%s\n", __func__);
+       dev->vbi_cap_seq_count = 0;
+       if (dev->start_streaming_error) {
+               dev->start_streaming_error = false;
+               err = -EINVAL;
+       } else {
+               err = vivid_start_generating_vid_cap(dev, &dev->vbi_cap_streaming);
+       }
+       if (err) {
+               struct vivid_buffer *buf, *tmp;
+
+               list_for_each_entry_safe(buf, tmp, &dev->vbi_cap_active, list) {
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+               }
+       }
+       return err;
+}
+
+/* abort streaming and wait for last buffer */
+static void vbi_cap_stop_streaming(struct vb2_queue *vq)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+
+       dprintk(dev, 1, "%s\n", __func__);
+       vivid_stop_generating_vid_cap(dev, &dev->vbi_cap_streaming);
+}
+
+const struct vb2_ops vivid_vbi_cap_qops = {
+       .queue_setup            = vbi_cap_queue_setup,
+       .buf_prepare            = vbi_cap_buf_prepare,
+       .buf_queue              = vbi_cap_buf_queue,
+       .start_streaming        = vbi_cap_start_streaming,
+       .stop_streaming         = vbi_cap_stop_streaming,
+       .wait_prepare           = vivid_unlock,
+       .wait_finish            = vivid_lock,
+};
+
+int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_vbi_format *vbi = &f->fmt.vbi;
+
+       if (!vivid_is_sdtv_cap(dev) || !dev->has_raw_vbi_cap)
+               return -EINVAL;
+
+       vivid_g_fmt_vbi_cap(dev, vbi);
+       return 0;
+}
+
+int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       int ret = vidioc_g_fmt_vbi_cap(file, priv, f);
+
+       if (ret)
+               return ret;
+       if (dev->stream_sliced_vbi_cap && vb2_is_busy(&dev->vb_vbi_cap_q))
+               return -EBUSY;
+       dev->stream_sliced_vbi_cap = false;
+       dev->vbi_cap_dev.queue->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+       return 0;
+}
+
+void vivid_fill_service_lines(struct v4l2_sliced_vbi_format *vbi, u32 service_set)
+{
+       vbi->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+       vbi->service_set = service_set;
+       memset(vbi->service_lines, 0, sizeof(vbi->service_lines));
+       memset(vbi->reserved, 0, sizeof(vbi->reserved));
+
+       if (vbi->service_set == 0)
+               return;
+
+       if (vbi->service_set & V4L2_SLICED_CAPTION_525) {
+               vbi->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+               vbi->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+       }
+       if (vbi->service_set & V4L2_SLICED_WSS_625) {
+               unsigned i;
+
+               for (i = 7; i <= 18; i++)
+                       vbi->service_lines[0][i] =
+                       vbi->service_lines[1][i] = V4L2_SLICED_TELETEXT_B;
+               vbi->service_lines[0][23] = V4L2_SLICED_WSS_625;
+       }
+}
+
+int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
+
+       if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap)
+               return -EINVAL;
+
+       vivid_fill_service_lines(vbi, dev->service_set_cap);
+       return 0;
+}
+
+int vidioc_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
+       bool is_60hz = dev->std_cap & V4L2_STD_525_60;
+       u32 service_set = vbi->service_set;
+
+       if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap)
+               return -EINVAL;
+
+       service_set &= is_60hz ? V4L2_SLICED_CAPTION_525 :
+                                V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
+       vivid_fill_service_lines(vbi, service_set);
+       return 0;
+}
+
+int vidioc_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
+       int ret = vidioc_try_fmt_sliced_vbi_cap(file, fh, fmt);
+
+       if (ret)
+               return ret;
+       if (!dev->stream_sliced_vbi_cap && vb2_is_busy(&dev->vb_vbi_cap_q))
+               return -EBUSY;
+       dev->service_set_cap = vbi->service_set;
+       dev->stream_sliced_vbi_cap = true;
+       dev->vbi_cap_dev.queue->type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+       return 0;
+}
+
+int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+       bool is_60hz;
+
+       if (vdev->vfl_dir == VFL_DIR_RX) {
+               is_60hz = dev->std_cap & V4L2_STD_525_60;
+               if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap ||
+                   cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+                       return -EINVAL;
+       } else {
+               is_60hz = dev->std_out & V4L2_STD_525_60;
+               if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out ||
+                   cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
+                       return -EINVAL;
+       }
+
+       cap->service_set = is_60hz ? V4L2_SLICED_CAPTION_525 :
+                                    V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
+       if (is_60hz) {
+               cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+               cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+       } else {
+               unsigned i;
+
+               for (i = 7; i <= 18; i++)
+                       cap->service_lines[0][i] =
+                       cap->service_lines[1][i] = V4L2_SLICED_TELETEXT_B;
+               cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+       }
+       return 0;
+}
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.h b/drivers/media/platform/vivid/vivid-vbi-cap.h
new file mode 100644 (file)
index 0000000..2d8ea0b
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * vivid-vbi-cap.h - vbi capture support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_VBI_CAP_H_
+#define _VIVID_VBI_CAP_H_
+
+void vivid_fill_time_of_day_packet(u8 *packet);
+void vivid_raw_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf);
+void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf);
+void vivid_sliced_vbi_out_process(struct vivid_dev *dev, struct vivid_buffer *buf);
+int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f);
+int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f);
+int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt);
+int vidioc_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt);
+int vidioc_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt);
+int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap);
+
+void vivid_fill_service_lines(struct v4l2_sliced_vbi_format *vbi, u32 service_set);
+
+extern const struct vb2_ops vivid_vbi_cap_qops;
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.c b/drivers/media/platform/vivid/vivid-vbi-gen.c
new file mode 100644 (file)
index 0000000..a2159de
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * vivid-vbi-gen.c - vbi generator support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+
+#include "vivid-vbi-gen.h"
+
+static void wss_insert(u8 *wss, u32 val, unsigned size)
+{
+       while (size--)
+               *wss++ = (val & (1 << size)) ? 0xc0 : 0x10;
+}
+
+static void vivid_vbi_gen_wss_raw(const struct v4l2_sliced_vbi_data *data,
+               u8 *buf, unsigned sampling_rate)
+{
+       const unsigned rate = 5000000;  /* WSS has a 5 MHz transmission rate */
+       u8 wss[29 + 24 + 24 + 24 + 18 + 18] = { 0 };
+       const unsigned zero = 0x07;
+       const unsigned one = 0x38;
+       unsigned bit = 0;
+       u16 wss_data;
+       int i;
+
+       wss_insert(wss + bit, 0x1f1c71c7, 29); bit += 29;
+       wss_insert(wss + bit, 0x1e3c1f, 24); bit += 24;
+
+       wss_data = (data->data[1] << 8) | data->data[0];
+       for (i = 0; i <= 13; i++, bit += 6)
+               wss_insert(wss + bit, (wss_data & (1 << i)) ? one : zero, 6);
+
+       for (i = 0, bit = 0; bit < sizeof(wss); bit++) {
+               unsigned n = ((bit + 1) * sampling_rate) / rate;
+
+               while (i < n)
+                       buf[i++] = wss[bit];
+       }
+}
+
+static void vivid_vbi_gen_teletext_raw(const struct v4l2_sliced_vbi_data *data,
+               u8 *buf, unsigned sampling_rate)
+{
+       const unsigned rate = 6937500 / 10;     /* Teletext has a 6.9375 MHz transmission rate */
+       u8 teletext[45] = { 0x55, 0x55, 0x27 };
+       unsigned bit = 0;
+       int i;
+
+       memcpy(teletext + 3, data->data, sizeof(teletext) - 3);
+       /* prevents 32 bit overflow */
+       sampling_rate /= 10;
+
+       for (i = 0, bit = 0; bit < sizeof(teletext) * 8; bit++) {
+               unsigned n = ((bit + 1) * sampling_rate) / rate;
+               u8 val = (teletext[bit / 8] & (1 << (bit & 7))) ? 0xc0 : 0x10;
+
+               while (i < n)
+                       buf[i++] = val;
+       }
+}
+
+static void cc_insert(u8 *cc, u8 ch)
+{
+       unsigned tot = 0;
+       unsigned i;
+
+       for (i = 0; i < 7; i++) {
+               cc[2 * i] = cc[2 * i + 1] = (ch & (1 << i)) ? 1 : 0;
+               tot += cc[2 * i];
+       }
+       cc[14] = cc[15] = !(tot & 1);
+}
+
+#define CC_PREAMBLE_BITS (14 + 4 + 2)
+
+static void vivid_vbi_gen_cc_raw(const struct v4l2_sliced_vbi_data *data,
+               u8 *buf, unsigned sampling_rate)
+{
+       const unsigned rate = 1000000;  /* CC has a 1 MHz transmission rate */
+
+       u8 cc[CC_PREAMBLE_BITS + 2 * 16] = {
+               /* Clock run-in: 7 cycles */
+               0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+               /* 2 cycles of 0 */
+               0, 0, 0, 0,
+               /* Start bit of 1 (each bit is two cycles) */
+               1, 1
+       };
+       unsigned bit, i;
+
+       cc_insert(cc + CC_PREAMBLE_BITS, data->data[0]);
+       cc_insert(cc + CC_PREAMBLE_BITS + 16, data->data[1]);
+
+       for (i = 0, bit = 0; bit < sizeof(cc); bit++) {
+               unsigned n = ((bit + 1) * sampling_rate) / rate;
+
+               while (i < n)
+                       buf[i++] = cc[bit] ? 0xc0 : 0x10;
+       }
+}
+
+void vivid_vbi_gen_raw(const struct vivid_vbi_gen_data *vbi,
+               const struct v4l2_vbi_format *vbi_fmt, u8 *buf)
+{
+       unsigned idx;
+
+       for (idx = 0; idx < 25; idx++) {
+               const struct v4l2_sliced_vbi_data *data = vbi->data + idx;
+               unsigned start_2nd_field;
+               unsigned line = data->line;
+               u8 *linebuf = buf;
+
+               start_2nd_field = (data->id & V4L2_SLICED_VBI_525) ? 263 : 313;
+               if (data->field)
+                       line += start_2nd_field;
+               line -= vbi_fmt->start[data->field];
+
+               if (vbi_fmt->flags & V4L2_VBI_INTERLACED)
+                       linebuf += (line * 2 + data->field) *
+                               vbi_fmt->samples_per_line;
+               else
+                       linebuf += (line + data->field * vbi_fmt->count[0]) *
+                               vbi_fmt->samples_per_line;
+               if (data->id == V4L2_SLICED_CAPTION_525)
+                       vivid_vbi_gen_cc_raw(data, linebuf, vbi_fmt->sampling_rate);
+               else if (data->id == V4L2_SLICED_WSS_625)
+                       vivid_vbi_gen_wss_raw(data, linebuf, vbi_fmt->sampling_rate);
+               else if (data->id == V4L2_SLICED_TELETEXT_B)
+                       vivid_vbi_gen_teletext_raw(data, linebuf, vbi_fmt->sampling_rate);
+       }
+}
+
+static const u8 vivid_cc_sequence1[30] = {
+       0x14, 0x20,     /* Resume Caption Loading */
+       'H',  'e',
+       'l',  'l',
+       'o',  ' ',
+       'w',  'o',
+       'r',  'l',
+       'd',  '!',
+       0x14, 0x2f,     /* End of Caption */
+};
+
+static const u8 vivid_cc_sequence2[30] = {
+       0x14, 0x20,     /* Resume Caption Loading */
+       'C',  'l',
+       'o',  's',
+       'e',  'd',
+       ' ',  'c',
+       'a',  'p',
+       't',  'i',
+       'o',  'n',
+       's',  ' ',
+       't',  'e',
+       's',  't',
+       0x14, 0x2f,     /* End of Caption */
+};
+
+static u8 calc_parity(u8 val)
+{
+       unsigned i;
+       unsigned tot = 0;
+
+       for (i = 0; i < 7; i++)
+               tot += (val & (1 << i)) ? 1 : 0;
+       return val | ((tot & 1) ? 0 : 0x80);
+}
+
+static void vivid_vbi_gen_set_time_of_day(u8 *packet)
+{
+       struct tm tm;
+       u8 checksum, i;
+
+       time_to_tm(get_seconds(), 0, &tm);
+       packet[0] = calc_parity(0x07);
+       packet[1] = calc_parity(0x01);
+       packet[2] = calc_parity(0x40 | tm.tm_min);
+       packet[3] = calc_parity(0x40 | tm.tm_hour);
+       packet[4] = calc_parity(0x40 | tm.tm_mday);
+       if (tm.tm_mday == 1 && tm.tm_mon == 2 &&
+           sys_tz.tz_minuteswest > tm.tm_min + tm.tm_hour * 60)
+               packet[4] = calc_parity(0x60 | tm.tm_mday);
+       packet[5] = calc_parity(0x40 | (1 + tm.tm_mon));
+       packet[6] = calc_parity(0x40 | (1 + tm.tm_wday));
+       packet[7] = calc_parity(0x40 | ((tm.tm_year - 90) & 0x3f));
+       packet[8] = calc_parity(0x0f);
+       for (checksum = i = 0; i <= 8; i++)
+               checksum += packet[i] & 0x7f;
+       packet[9] = calc_parity(0x100 - checksum);
+       checksum = 0;
+       packet[10] = calc_parity(0x07);
+       packet[11] = calc_parity(0x04);
+       if (sys_tz.tz_minuteswest >= 0)
+               packet[12] = calc_parity(0x40 | ((sys_tz.tz_minuteswest / 60) & 0x1f));
+       else
+               packet[12] = calc_parity(0x40 | ((24 + sys_tz.tz_minuteswest / 60) & 0x1f));
+       packet[13] = calc_parity(0);
+       packet[14] = calc_parity(0x0f);
+       for (checksum = 0, i = 10; i <= 14; i++)
+               checksum += packet[i] & 0x7f;
+       packet[15] = calc_parity(0x100 - checksum);
+}
+
+static const u8 hamming[16] = {
+       0x15, 0x02, 0x49, 0x5e, 0x64, 0x73, 0x38, 0x2f,
+       0xd0, 0xc7, 0x8c, 0x9b, 0xa1, 0xb6, 0xfd, 0xea
+};
+
+static void vivid_vbi_gen_teletext(u8 *packet, unsigned line, unsigned frame)
+{
+       unsigned offset = 2;
+       unsigned i;
+
+       packet[0] = hamming[1 + ((line & 1) << 3)];
+       packet[1] = hamming[line >> 1];
+       memset(packet + 2, 0x20, 40);
+       if (line == 0) {
+               /* subcode */
+               packet[2] = hamming[frame % 10];
+               packet[3] = hamming[frame / 10];
+               packet[4] = hamming[0];
+               packet[5] = hamming[0];
+               packet[6] = hamming[0];
+               packet[7] = hamming[0];
+               packet[8] = hamming[0];
+               packet[9] = hamming[1];
+               offset = 10;
+       }
+       packet += offset;
+       memcpy(packet, "Page: 100 Row: 10", 17);
+       packet[7] = '0' + frame / 10;
+       packet[8] = '0' + frame % 10;
+       packet[15] = '0' + line / 10;
+       packet[16] = '0' + line % 10;
+       for (i = 0; i < 42 - offset; i++)
+               packet[i] = calc_parity(packet[i]);
+}
+
+void vivid_vbi_gen_sliced(struct vivid_vbi_gen_data *vbi,
+               bool is_60hz, unsigned seqnr)
+{
+       struct v4l2_sliced_vbi_data *data0 = vbi->data;
+       struct v4l2_sliced_vbi_data *data1 = vbi->data + 1;
+       unsigned frame = seqnr % 60;
+
+       memset(vbi->data, 0, sizeof(vbi->data));
+
+       if (!is_60hz) {
+               unsigned i;
+
+               for (i = 0; i <= 11; i++) {
+                       data0->id = V4L2_SLICED_TELETEXT_B;
+                       data0->line = 7 + i;
+                       vivid_vbi_gen_teletext(data0->data, i, frame);
+                       data0++;
+               }
+               data0->id = V4L2_SLICED_WSS_625;
+               data0->line = 23;
+               /* 4x3 video aspect ratio */
+               data0->data[0] = 0x08;
+               data0++;
+               for (i = 0; i <= 11; i++) {
+                       data0->id = V4L2_SLICED_TELETEXT_B;
+                       data0->field = 1;
+                       data0->line = 7 + i;
+                       vivid_vbi_gen_teletext(data0->data, 12 + i, frame);
+                       data0++;
+               }
+               return;
+       }
+
+       data0->id = V4L2_SLICED_CAPTION_525;
+       data0->line = 21;
+       data1->id = V4L2_SLICED_CAPTION_525;
+       data1->field = 1;
+       data1->line = 21;
+
+       if (frame < 15) {
+               data0->data[0] = calc_parity(vivid_cc_sequence1[2 * frame]);
+               data0->data[1] = calc_parity(vivid_cc_sequence1[2 * frame + 1]);
+       } else if (frame >= 30 && frame < 45) {
+               frame -= 30;
+               data0->data[0] = calc_parity(vivid_cc_sequence2[2 * frame]);
+               data0->data[1] = calc_parity(vivid_cc_sequence2[2 * frame + 1]);
+       } else {
+               data0->data[0] = calc_parity(0);
+               data0->data[1] = calc_parity(0);
+       }
+
+       frame = seqnr % (30 * 60);
+       switch (frame) {
+       case 0:
+               vivid_vbi_gen_set_time_of_day(vbi->time_of_day_packet);
+               /* fall through */
+       case 1 ... 7:
+               data1->data[0] = vbi->time_of_day_packet[frame * 2];
+               data1->data[1] = vbi->time_of_day_packet[frame * 2 + 1];
+               break;
+       default:
+               data1->data[0] = calc_parity(0);
+               data1->data[1] = calc_parity(0);
+               break;
+       }
+}
diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.h b/drivers/media/platform/vivid/vivid-vbi-gen.h
new file mode 100644 (file)
index 0000000..8444abe
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * vivid-vbi-gen.h - vbi generator support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_VBI_GEN_H_
+#define _VIVID_VBI_GEN_H_
+
+struct vivid_vbi_gen_data {
+       struct v4l2_sliced_vbi_data data[25];
+       u8 time_of_day_packet[16];
+};
+
+void vivid_vbi_gen_sliced(struct vivid_vbi_gen_data *vbi,
+               bool is_60hz, unsigned seqnr);
+void vivid_vbi_gen_raw(const struct vivid_vbi_gen_data *vbi,
+               const struct v4l2_vbi_format *vbi_fmt, u8 *buf);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c
new file mode 100644 (file)
index 0000000..9d00a07
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * vivid-vbi-out.c - vbi output support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+
+#include "vivid-core.h"
+#include "vivid-kthread-out.h"
+#include "vivid-vbi-out.h"
+#include "vivid-vbi-cap.h"
+
+static int vbi_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+                      unsigned *nbuffers, unsigned *nplanes,
+                      unsigned sizes[], void *alloc_ctxs[])
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+       bool is_60hz = dev->std_out & V4L2_STD_525_60;
+       unsigned size = vq->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ?
+               36 * sizeof(struct v4l2_sliced_vbi_data) :
+               1440 * 2 * (is_60hz ? 12 : 18);
+
+       if (!vivid_is_svid_out(dev))
+               return -EINVAL;
+
+       sizes[0] = size;
+
+       if (vq->num_buffers + *nbuffers < 2)
+               *nbuffers = 2 - vq->num_buffers;
+
+       *nplanes = 1;
+       return 0;
+}
+
+static int vbi_out_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       bool is_60hz = dev->std_out & V4L2_STD_525_60;
+       unsigned size = vb->vb2_queue->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ?
+               36 * sizeof(struct v4l2_sliced_vbi_data) :
+               1440 * 2 * (is_60hz ? 12 : 18);
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       if (dev->buf_prepare_error) {
+               /*
+                * Error injection: test what happens if buf_prepare() returns
+                * an error.
+                */
+               dev->buf_prepare_error = false;
+               return -EINVAL;
+       }
+       if (vb2_plane_size(vb, 0) < size) {
+               dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n",
+                               __func__, vb2_plane_size(vb, 0), size);
+               return -EINVAL;
+       }
+       vb2_set_plane_payload(vb, 0, size);
+
+       return 0;
+}
+
+static void vbi_out_buf_queue(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb);
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       spin_lock(&dev->slock);
+       list_add_tail(&buf->list, &dev->vbi_out_active);
+       spin_unlock(&dev->slock);
+}
+
+static int vbi_out_start_streaming(struct vb2_queue *vq, unsigned count)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+       int err;
+
+       dprintk(dev, 1, "%s\n", __func__);
+       dev->vbi_out_seq_count = 0;
+       if (dev->start_streaming_error) {
+               dev->start_streaming_error = false;
+               err = -EINVAL;
+       } else {
+               err = vivid_start_generating_vid_out(dev, &dev->vbi_out_streaming);
+       }
+       if (err) {
+               struct vivid_buffer *buf, *tmp;
+
+               list_for_each_entry_safe(buf, tmp, &dev->vbi_out_active, list) {
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+               }
+       }
+       return err;
+}
+
+/* abort streaming and wait for last buffer */
+static void vbi_out_stop_streaming(struct vb2_queue *vq)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+
+       dprintk(dev, 1, "%s\n", __func__);
+       vivid_stop_generating_vid_out(dev, &dev->vbi_out_streaming);
+       dev->vbi_out_have_wss = false;
+       dev->vbi_out_have_cc[0] = false;
+       dev->vbi_out_have_cc[1] = false;
+}
+
+const struct vb2_ops vivid_vbi_out_qops = {
+       .queue_setup            = vbi_out_queue_setup,
+       .buf_prepare            = vbi_out_buf_prepare,
+       .buf_queue              = vbi_out_buf_queue,
+       .start_streaming        = vbi_out_start_streaming,
+       .stop_streaming         = vbi_out_stop_streaming,
+       .wait_prepare           = vivid_unlock,
+       .wait_finish            = vivid_lock,
+};
+
+int vidioc_g_fmt_vbi_out(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_vbi_format *vbi = &f->fmt.vbi;
+       bool is_60hz = dev->std_out & V4L2_STD_525_60;
+
+       if (!vivid_is_svid_out(dev) || !dev->has_raw_vbi_out)
+               return -EINVAL;
+
+       vbi->sampling_rate = 25000000;
+       vbi->offset = 24;
+       vbi->samples_per_line = 1440;
+       vbi->sample_format = V4L2_PIX_FMT_GREY;
+       vbi->start[0] = is_60hz ? V4L2_VBI_ITU_525_F1_START + 9 : V4L2_VBI_ITU_625_F1_START + 5;
+       vbi->start[1] = is_60hz ? V4L2_VBI_ITU_525_F2_START + 9 : V4L2_VBI_ITU_625_F2_START + 5;
+       vbi->count[0] = vbi->count[1] = is_60hz ? 12 : 18;
+       vbi->flags = dev->vbi_cap_interlaced ? V4L2_VBI_INTERLACED : 0;
+       vbi->reserved[0] = 0;
+       vbi->reserved[1] = 0;
+       return 0;
+}
+
+int vidioc_s_fmt_vbi_out(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       int ret = vidioc_g_fmt_vbi_out(file, priv, f);
+
+       if (ret)
+               return ret;
+       if (vb2_is_busy(&dev->vb_vbi_out_q))
+               return -EBUSY;
+       dev->stream_sliced_vbi_out = false;
+       dev->vbi_out_dev.queue->type = V4L2_BUF_TYPE_VBI_OUTPUT;
+       return 0;
+}
+
+int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
+
+       if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out)
+               return -EINVAL;
+
+       vivid_fill_service_lines(vbi, dev->service_set_out);
+       return 0;
+}
+
+int vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
+       bool is_60hz = dev->std_out & V4L2_STD_525_60;
+       u32 service_set = vbi->service_set;
+
+       if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out)
+               return -EINVAL;
+
+       service_set &= is_60hz ? V4L2_SLICED_CAPTION_525 :
+                                V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
+       vivid_fill_service_lines(vbi, service_set);
+       return 0;
+}
+
+int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced;
+       int ret = vidioc_try_fmt_sliced_vbi_out(file, fh, fmt);
+
+       if (ret)
+               return ret;
+       if (vb2_is_busy(&dev->vb_vbi_out_q))
+               return -EBUSY;
+       dev->service_set_out = vbi->service_set;
+       dev->stream_sliced_vbi_out = true;
+       dev->vbi_out_dev.queue->type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT;
+       return 0;
+}
+
+void vivid_sliced_vbi_out_process(struct vivid_dev *dev, struct vivid_buffer *buf)
+{
+       struct v4l2_sliced_vbi_data *vbi = vb2_plane_vaddr(&buf->vb, 0);
+       unsigned elems = vb2_get_plane_payload(&buf->vb, 0) / sizeof(*vbi);
+
+       dev->vbi_out_have_cc[0] = false;
+       dev->vbi_out_have_cc[1] = false;
+       dev->vbi_out_have_wss = false;
+       while (elems--) {
+               switch (vbi->id) {
+               case V4L2_SLICED_CAPTION_525:
+                       if ((dev->std_out & V4L2_STD_525_60) && vbi->line == 21) {
+                               dev->vbi_out_have_cc[!!vbi->field] = true;
+                               dev->vbi_out_cc[!!vbi->field][0] = vbi->data[0];
+                               dev->vbi_out_cc[!!vbi->field][1] = vbi->data[1];
+                       }
+                       break;
+               case V4L2_SLICED_WSS_625:
+                       if ((dev->std_out & V4L2_STD_625_50) &&
+                           vbi->field == 0 && vbi->line == 23) {
+                               dev->vbi_out_have_wss = true;
+                               dev->vbi_out_wss[0] = vbi->data[0];
+                               dev->vbi_out_wss[1] = vbi->data[1];
+                       }
+                       break;
+               }
+               vbi++;
+       }
+}
diff --git a/drivers/media/platform/vivid/vivid-vbi-out.h b/drivers/media/platform/vivid/vivid-vbi-out.h
new file mode 100644 (file)
index 0000000..6555ba9
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * vivid-vbi-out.h - vbi output support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_VBI_OUT_H_
+#define _VIVID_VBI_OUT_H_
+
+void vivid_sliced_vbi_out_process(struct vivid_dev *dev, struct vivid_buffer *buf);
+int vidioc_g_fmt_vbi_out(struct file *file, void *priv,
+                                       struct v4l2_format *f);
+int vidioc_s_fmt_vbi_out(struct file *file, void *priv,
+                                       struct v4l2_format *f);
+int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt);
+int vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt);
+int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt);
+
+extern const struct vb2_ops vivid_vbi_out_qops;
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
new file mode 100644 (file)
index 0000000..331c544
--- /dev/null
@@ -0,0 +1,1730 @@
+/*
+ * vivid-vid-cap.c - video capture support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-kthread-cap.h"
+#include "vivid-vid-cap.h"
+
+/* timeperframe: min/max and default */
+static const struct v4l2_fract
+       tpf_min     = {.numerator = 1,          .denominator = FPS_MAX},
+       tpf_max     = {.numerator = FPS_MAX,    .denominator = 1},
+       tpf_default = {.numerator = 1,          .denominator = 30};
+
+static const struct vivid_fmt formats_ovl[] = {
+       {
+               .name     = "RGB565 (LE)",
+               .fourcc   = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+               .depth    = 16,
+               .planes   = 1,
+       },
+       {
+               .name     = "XRGB555 (LE)",
+               .fourcc   = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */
+               .depth    = 16,
+               .planes   = 1,
+       },
+       {
+               .name     = "ARGB555 (LE)",
+               .fourcc   = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */
+               .depth    = 16,
+               .planes   = 1,
+       },
+};
+
+/* The number of discrete webcam framesizes */
+#define VIVID_WEBCAM_SIZES 3
+/* The number of discrete webcam frameintervals */
+#define VIVID_WEBCAM_IVALS (VIVID_WEBCAM_SIZES * 2)
+
+/* Sizes must be in increasing order */
+static const struct v4l2_frmsize_discrete webcam_sizes[VIVID_WEBCAM_SIZES] = {
+       {  320, 180 },
+       {  640, 360 },
+       { 1280, 720 },
+};
+
+/*
+ * Intervals must be in increasing order and there must be twice as many
+ * elements in this array as there are in webcam_sizes.
+ */
+static const struct v4l2_fract webcam_intervals[VIVID_WEBCAM_IVALS] = {
+       {  1, 10 },
+       {  1, 15 },
+       {  1, 25 },
+       {  1, 30 },
+       {  1, 50 },
+       {  1, 60 },
+};
+
+static const struct v4l2_discrete_probe webcam_probe = {
+       webcam_sizes,
+       VIVID_WEBCAM_SIZES
+};
+
+static int vid_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+                      unsigned *nbuffers, unsigned *nplanes,
+                      unsigned sizes[], void *alloc_ctxs[])
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+       unsigned planes = tpg_g_planes(&dev->tpg);
+       unsigned h = dev->fmt_cap_rect.height;
+       unsigned p;
+
+       if (dev->field_cap == V4L2_FIELD_ALTERNATE) {
+               /*
+                * You cannot use read() with FIELD_ALTERNATE since the field
+                * information (TOP/BOTTOM) cannot be passed back to the user.
+                */
+               if (vb2_fileio_is_active(vq))
+                       return -EINVAL;
+       }
+
+       if (dev->queue_setup_error) {
+               /*
+                * Error injection: test what happens if queue_setup() returns
+                * an error.
+                */
+               dev->queue_setup_error = false;
+               return -EINVAL;
+       }
+       if (fmt) {
+               const struct v4l2_pix_format_mplane *mp;
+               struct v4l2_format mp_fmt;
+               const struct vivid_fmt *vfmt;
+
+               if (!V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) {
+                       fmt_sp2mp(fmt, &mp_fmt);
+                       fmt = &mp_fmt;
+               }
+               mp = &fmt->fmt.pix_mp;
+               /*
+                * Check if the number of planes in the specified format match
+                * the number of planes in the current format. You can't mix that.
+                */
+               if (mp->num_planes != planes)
+                       return -EINVAL;
+               vfmt = vivid_get_format(dev, mp->pixelformat);
+               for (p = 0; p < planes; p++) {
+                       sizes[p] = mp->plane_fmt[p].sizeimage;
+                       if (sizes[0] < tpg_g_bytesperline(&dev->tpg, 0) * h +
+                                                       vfmt->data_offset[p])
+                               return -EINVAL;
+               }
+       } else {
+               for (p = 0; p < planes; p++)
+                       sizes[p] = tpg_g_bytesperline(&dev->tpg, p) * h +
+                                       dev->fmt_cap->data_offset[p];
+       }
+
+       if (vq->num_buffers + *nbuffers < 2)
+               *nbuffers = 2 - vq->num_buffers;
+
+       *nplanes = planes;
+
+       /*
+        * videobuf2-vmalloc allocator is context-less so no need to set
+        * alloc_ctxs array.
+        */
+
+       if (planes == 2)
+               dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__,
+                       *nbuffers, sizes[0], sizes[1]);
+       else
+               dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__,
+                       *nbuffers, sizes[0]);
+
+       return 0;
+}
+
+static int vid_cap_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       unsigned long size;
+       unsigned planes = tpg_g_planes(&dev->tpg);
+       unsigned p;
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       if (WARN_ON(NULL == dev->fmt_cap))
+               return -EINVAL;
+
+       if (dev->buf_prepare_error) {
+               /*
+                * Error injection: test what happens if buf_prepare() returns
+                * an error.
+                */
+               dev->buf_prepare_error = false;
+               return -EINVAL;
+       }
+       for (p = 0; p < planes; p++) {
+               size = tpg_g_bytesperline(&dev->tpg, p) * dev->fmt_cap_rect.height +
+                       dev->fmt_cap->data_offset[p];
+
+               if (vb2_plane_size(vb, 0) < size) {
+                       dprintk(dev, 1, "%s data will not fit into plane %u (%lu < %lu)\n",
+                                       __func__, p, vb2_plane_size(vb, 0), size);
+                       return -EINVAL;
+               }
+
+               vb2_set_plane_payload(vb, p, size);
+               vb->v4l2_planes[p].data_offset = dev->fmt_cap->data_offset[p];
+       }
+
+       return 0;
+}
+
+static void vid_cap_buf_finish(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct v4l2_timecode *tc = &vb->v4l2_buf.timecode;
+       unsigned fps = 25;
+       unsigned seq = vb->v4l2_buf.sequence;
+
+       if (!vivid_is_sdtv_cap(dev))
+               return;
+
+       /*
+        * Set the timecode. Rarely used, so it is interesting to
+        * test this.
+        */
+       vb->v4l2_buf.flags |= V4L2_BUF_FLAG_TIMECODE;
+       if (dev->std_cap & V4L2_STD_525_60)
+               fps = 30;
+       tc->type = (fps == 30) ? V4L2_TC_TYPE_30FPS : V4L2_TC_TYPE_25FPS;
+       tc->flags = 0;
+       tc->frames = seq % fps;
+       tc->seconds = (seq / fps) % 60;
+       tc->minutes = (seq / (60 * fps)) % 60;
+       tc->hours = (seq / (60 * 60 * fps)) % 24;
+}
+
+static void vid_cap_buf_queue(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb);
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       spin_lock(&dev->slock);
+       list_add_tail(&buf->list, &dev->vid_cap_active);
+       spin_unlock(&dev->slock);
+}
+
+static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+       unsigned i;
+       int err;
+
+       if (vb2_is_streaming(&dev->vb_vid_out_q))
+               dev->can_loop_video = vivid_vid_can_loop(dev);
+
+       if (dev->kthread_vid_cap)
+               return 0;
+
+       dev->vid_cap_seq_count = 0;
+       dprintk(dev, 1, "%s\n", __func__);
+       for (i = 0; i < VIDEO_MAX_FRAME; i++)
+               dev->must_blank[i] = tpg_g_perc_fill(&dev->tpg) < 100;
+       if (dev->start_streaming_error) {
+               dev->start_streaming_error = false;
+               err = -EINVAL;
+       } else {
+               err = vivid_start_generating_vid_cap(dev, &dev->vid_cap_streaming);
+       }
+       if (err) {
+               struct vivid_buffer *buf, *tmp;
+
+               list_for_each_entry_safe(buf, tmp, &dev->vid_cap_active, list) {
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+               }
+       }
+       return err;
+}
+
+/* abort streaming and wait for last buffer */
+static void vid_cap_stop_streaming(struct vb2_queue *vq)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+
+       dprintk(dev, 1, "%s\n", __func__);
+       vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming);
+       dev->can_loop_video = false;
+}
+
+const struct vb2_ops vivid_vid_cap_qops = {
+       .queue_setup            = vid_cap_queue_setup,
+       .buf_prepare            = vid_cap_buf_prepare,
+       .buf_finish             = vid_cap_buf_finish,
+       .buf_queue              = vid_cap_buf_queue,
+       .start_streaming        = vid_cap_start_streaming,
+       .stop_streaming         = vid_cap_stop_streaming,
+       .wait_prepare           = vivid_unlock,
+       .wait_finish            = vivid_lock,
+};
+
+/*
+ * Determine the 'picture' quality based on the current TV frequency: either
+ * COLOR for a good 'signal', GRAY (grayscale picture) for a slightly off
+ * signal or NOISE for no signal.
+ */
+void vivid_update_quality(struct vivid_dev *dev)
+{
+       unsigned freq_modulus;
+
+       if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) {
+               /*
+                * The 'noise' will only be replaced by the actual video
+                * if the output video matches the input video settings.
+                */
+               tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0);
+               return;
+       }
+       if (vivid_is_hdmi_cap(dev) && VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode)) {
+               tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0);
+               return;
+       }
+       if (vivid_is_sdtv_cap(dev) && VIVID_INVALID_SIGNAL(dev->std_signal_mode)) {
+               tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0);
+               return;
+       }
+       if (!vivid_is_tv_cap(dev)) {
+               tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0);
+               return;
+       }
+
+       /*
+        * There is a fake channel every 6 MHz at 49.25, 55.25, etc.
+        * From +/- 0.25 MHz around the channel there is color, and from
+        * +/- 1 MHz there is grayscale (chroma is lost).
+        * Everywhere else it is just noise.
+        */
+       freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16);
+       if (freq_modulus > 2 * 16) {
+               tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE,
+                       next_pseudo_random32(dev->tv_freq ^ 0x55) & 0x3f);
+               return;
+       }
+       if (freq_modulus < 12 /*0.75 * 16*/ || freq_modulus > 20 /*1.25 * 16*/)
+               tpg_s_quality(&dev->tpg, TPG_QUAL_GRAY, 0);
+       else
+               tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0);
+}
+
+/*
+ * Get the current picture quality and the associated afc value.
+ */
+static enum tpg_quality vivid_get_quality(struct vivid_dev *dev, s32 *afc)
+{
+       unsigned freq_modulus;
+
+       if (afc)
+               *afc = 0;
+       if (tpg_g_quality(&dev->tpg) == TPG_QUAL_COLOR ||
+           tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE)
+               return tpg_g_quality(&dev->tpg);
+
+       /*
+        * There is a fake channel every 6 MHz at 49.25, 55.25, etc.
+        * From +/- 0.25 MHz around the channel there is color, and from
+        * +/- 1 MHz there is grayscale (chroma is lost).
+        * Everywhere else it is just gray.
+        */
+       freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16);
+       if (afc)
+               *afc = freq_modulus - 1 * 16;
+       return TPG_QUAL_GRAY;
+}
+
+enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev)
+{
+       if (vivid_is_sdtv_cap(dev))
+               return dev->std_aspect_ratio;
+
+       if (vivid_is_hdmi_cap(dev))
+               return dev->dv_timings_aspect_ratio;
+
+       return TPG_VIDEO_ASPECT_IMAGE;
+}
+
+static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev)
+{
+       if (vivid_is_sdtv_cap(dev))
+               return (dev->std_cap & V4L2_STD_525_60) ?
+                       TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;
+
+       if (vivid_is_hdmi_cap(dev) &&
+           dev->src_rect.width == 720 && dev->src_rect.height <= 576)
+               return dev->src_rect.height == 480 ?
+                       TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;
+
+       return TPG_PIXEL_ASPECT_SQUARE;
+}
+
+/*
+ * Called whenever the format has to be reset which can occur when
+ * changing inputs, standard, timings, etc.
+ */
+void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
+{
+       struct v4l2_bt_timings *bt = &dev->dv_timings_cap.bt;
+       unsigned size;
+
+       switch (dev->input_type[dev->input]) {
+       case WEBCAM:
+       default:
+               dev->src_rect.width = webcam_sizes[dev->webcam_size_idx].width;
+               dev->src_rect.height = webcam_sizes[dev->webcam_size_idx].height;
+               dev->timeperframe_vid_cap = webcam_intervals[dev->webcam_ival_idx];
+               dev->field_cap = V4L2_FIELD_NONE;
+               tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO);
+               break;
+       case TV:
+       case SVID:
+               dev->field_cap = dev->tv_field_cap;
+               dev->src_rect.width = 720;
+               if (dev->std_cap & V4L2_STD_525_60) {
+                       dev->src_rect.height = 480;
+                       dev->timeperframe_vid_cap = (struct v4l2_fract) { 1001, 30000 };
+                       dev->service_set_cap = V4L2_SLICED_CAPTION_525;
+               } else {
+                       dev->src_rect.height = 576;
+                       dev->timeperframe_vid_cap = (struct v4l2_fract) { 1000, 25000 };
+                       dev->service_set_cap = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
+               }
+               tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO);
+               break;
+       case HDMI:
+               dev->src_rect.width = bt->width;
+               dev->src_rect.height = bt->height;
+               size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
+               dev->timeperframe_vid_cap = (struct v4l2_fract) {
+                       size / 100, (u32)bt->pixelclock / 100
+               };
+               if (bt->interlaced)
+                       dev->field_cap = V4L2_FIELD_ALTERNATE;
+               else
+                       dev->field_cap = V4L2_FIELD_NONE;
+
+               /*
+                * We can be called from within s_ctrl, in that case we can't
+                * set/get controls. Luckily we don't need to in that case.
+                */
+               if (keep_controls || !dev->colorspace)
+                       break;
+               if (bt->standards & V4L2_DV_BT_STD_CEA861) {
+                       if (bt->width == 720 && bt->height <= 576)
+                               v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M);
+                       else
+                               v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_REC709);
+                       v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 1);
+               } else {
+                       v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB);
+                       v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 0);
+               }
+               tpg_s_rgb_range(&dev->tpg, v4l2_ctrl_g_ctrl(dev->rgb_range_cap));
+               break;
+       }
+       vivid_update_quality(dev);
+       tpg_reset_source(&dev->tpg, dev->src_rect.width, dev->src_rect.height, dev->field_cap);
+       dev->crop_cap = dev->src_rect;
+       dev->crop_bounds_cap = dev->src_rect;
+       dev->compose_cap = dev->crop_cap;
+       if (V4L2_FIELD_HAS_T_OR_B(dev->field_cap))
+               dev->compose_cap.height /= 2;
+       dev->fmt_cap_rect = dev->compose_cap;
+       tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev));
+       tpg_s_pixel_aspect(&dev->tpg, vivid_get_pixel_aspect(dev));
+       tpg_update_mv_step(&dev->tpg);
+}
+
+/* Map the field to something that is valid for the current input */
+static enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field field)
+{
+       if (vivid_is_sdtv_cap(dev)) {
+               switch (field) {
+               case V4L2_FIELD_INTERLACED_TB:
+               case V4L2_FIELD_INTERLACED_BT:
+               case V4L2_FIELD_SEQ_TB:
+               case V4L2_FIELD_SEQ_BT:
+               case V4L2_FIELD_TOP:
+               case V4L2_FIELD_BOTTOM:
+               case V4L2_FIELD_ALTERNATE:
+                       return field;
+               case V4L2_FIELD_INTERLACED:
+               default:
+                       return V4L2_FIELD_INTERLACED;
+               }
+       }
+       if (vivid_is_hdmi_cap(dev))
+               return dev->dv_timings_cap.bt.interlaced ? V4L2_FIELD_ALTERNATE :
+                                                      V4L2_FIELD_NONE;
+       return V4L2_FIELD_NONE;
+}
+
+static unsigned vivid_colorspace_cap(struct vivid_dev *dev)
+{
+       if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+               return tpg_g_colorspace(&dev->tpg);
+       return dev->colorspace_out;
+}
+
+int vivid_g_fmt_vid_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
+       unsigned p;
+
+       mp->width        = dev->fmt_cap_rect.width;
+       mp->height       = dev->fmt_cap_rect.height;
+       mp->field        = dev->field_cap;
+       mp->pixelformat  = dev->fmt_cap->fourcc;
+       mp->colorspace   = vivid_colorspace_cap(dev);
+       mp->num_planes = dev->fmt_cap->planes;
+       for (p = 0; p < mp->num_planes; p++) {
+               mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p);
+               mp->plane_fmt[p].sizeimage =
+                       mp->plane_fmt[p].bytesperline * mp->height +
+                       dev->fmt_cap->data_offset[p];
+       }
+       return 0;
+}
+
+int vivid_try_fmt_vid_cap(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
+       struct v4l2_plane_pix_format *pfmt = mp->plane_fmt;
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct vivid_fmt *fmt;
+       unsigned bytesperline, max_bpl;
+       unsigned factor = 1;
+       unsigned w, h;
+       unsigned p;
+
+       fmt = vivid_get_format(dev, mp->pixelformat);
+       if (!fmt) {
+               dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
+                       mp->pixelformat);
+               mp->pixelformat = V4L2_PIX_FMT_YUYV;
+               fmt = vivid_get_format(dev, mp->pixelformat);
+       }
+
+       mp->field = vivid_field_cap(dev, mp->field);
+       if (vivid_is_webcam(dev)) {
+               const struct v4l2_frmsize_discrete *sz =
+                       v4l2_find_nearest_format(&webcam_probe, mp->width, mp->height);
+
+               w = sz->width;
+               h = sz->height;
+       } else if (vivid_is_sdtv_cap(dev)) {
+               w = 720;
+               h = (dev->std_cap & V4L2_STD_525_60) ? 480 : 576;
+       } else {
+               w = dev->src_rect.width;
+               h = dev->src_rect.height;
+       }
+       if (V4L2_FIELD_HAS_T_OR_B(mp->field))
+               factor = 2;
+       if (vivid_is_webcam(dev) ||
+           (!dev->has_scaler_cap && !dev->has_crop_cap && !dev->has_compose_cap)) {
+               mp->width = w;
+               mp->height = h / factor;
+       } else {
+               struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor };
+
+               rect_set_min_size(&r, &vivid_min_rect);
+               rect_set_max_size(&r, &vivid_max_rect);
+               if (dev->has_scaler_cap && !dev->has_compose_cap) {
+                       struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h };
+
+                       rect_set_max_size(&r, &max_r);
+               } else if (!dev->has_scaler_cap && dev->has_crop_cap && !dev->has_compose_cap) {
+                       rect_set_max_size(&r, &dev->src_rect);
+               } else if (!dev->has_scaler_cap && !dev->has_crop_cap) {
+                       rect_set_min_size(&r, &dev->src_rect);
+               }
+               mp->width = r.width;
+               mp->height = r.height / factor;
+       }
+
+       /* This driver supports custom bytesperline values */
+
+       /* Calculate the minimum supported bytesperline value */
+       bytesperline = (mp->width * fmt->depth) >> 3;
+       /* Calculate the maximum supported bytesperline value */
+       max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3;
+       mp->num_planes = fmt->planes;
+       for (p = 0; p < mp->num_planes; p++) {
+               if (pfmt[p].bytesperline > max_bpl)
+                       pfmt[p].bytesperline = max_bpl;
+               if (pfmt[p].bytesperline < bytesperline)
+                       pfmt[p].bytesperline = bytesperline;
+               pfmt[p].sizeimage = pfmt[p].bytesperline * mp->height +
+                       fmt->data_offset[p];
+               memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
+       }
+       mp->colorspace = vivid_colorspace_cap(dev);
+       memset(mp->reserved, 0, sizeof(mp->reserved));
+       return 0;
+}
+
+int vivid_s_fmt_vid_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_rect *crop = &dev->crop_cap;
+       struct v4l2_rect *compose = &dev->compose_cap;
+       struct vb2_queue *q = &dev->vb_vid_cap_q;
+       int ret = vivid_try_fmt_vid_cap(file, priv, f);
+       unsigned factor = 1;
+       unsigned i;
+
+       if (ret < 0)
+               return ret;
+
+       if (vb2_is_busy(q)) {
+               dprintk(dev, 1, "%s device busy\n", __func__);
+               return -EBUSY;
+       }
+
+       if (dev->overlay_cap_owner && dev->fb_cap.fmt.pixelformat != mp->pixelformat) {
+               dprintk(dev, 1, "overlay is active, can't change pixelformat\n");
+               return -EBUSY;
+       }
+
+       dev->fmt_cap = vivid_get_format(dev, mp->pixelformat);
+       if (V4L2_FIELD_HAS_T_OR_B(mp->field))
+               factor = 2;
+
+       /* Note: the webcam input doesn't support scaling, cropping or composing */
+
+       if (!vivid_is_webcam(dev) &&
+           (dev->has_scaler_cap || dev->has_crop_cap || dev->has_compose_cap)) {
+               struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+
+               if (dev->has_scaler_cap) {
+                       if (dev->has_compose_cap)
+                               rect_map_inside(compose, &r);
+                       else
+                               *compose = r;
+                       if (dev->has_crop_cap && !dev->has_compose_cap) {
+                               struct v4l2_rect min_r = {
+                                       0, 0,
+                                       r.width / MAX_ZOOM,
+                                       factor * r.height / MAX_ZOOM
+                               };
+                               struct v4l2_rect max_r = {
+                                       0, 0,
+                                       r.width * MAX_ZOOM,
+                                       factor * r.height * MAX_ZOOM
+                               };
+
+                               rect_set_min_size(crop, &min_r);
+                               rect_set_max_size(crop, &max_r);
+                               rect_map_inside(crop, &dev->crop_bounds_cap);
+                       } else if (dev->has_crop_cap) {
+                               struct v4l2_rect min_r = {
+                                       0, 0,
+                                       compose->width / MAX_ZOOM,
+                                       factor * compose->height / MAX_ZOOM
+                               };
+                               struct v4l2_rect max_r = {
+                                       0, 0,
+                                       compose->width * MAX_ZOOM,
+                                       factor * compose->height * MAX_ZOOM
+                               };
+
+                               rect_set_min_size(crop, &min_r);
+                               rect_set_max_size(crop, &max_r);
+                               rect_map_inside(crop, &dev->crop_bounds_cap);
+                       }
+               } else if (dev->has_crop_cap && !dev->has_compose_cap) {
+                       r.height *= factor;
+                       rect_set_size_to(crop, &r);
+                       rect_map_inside(crop, &dev->crop_bounds_cap);
+                       r = *crop;
+                       r.height /= factor;
+                       rect_set_size_to(compose, &r);
+               } else if (!dev->has_crop_cap) {
+                       rect_map_inside(compose, &r);
+               } else {
+                       r.height *= factor;
+                       rect_set_max_size(crop, &r);
+                       rect_map_inside(crop, &dev->crop_bounds_cap);
+                       compose->top *= factor;
+                       compose->height *= factor;
+                       rect_set_size_to(compose, crop);
+                       rect_map_inside(compose, &r);
+                       compose->top /= factor;
+                       compose->height /= factor;
+               }
+       } else if (vivid_is_webcam(dev)) {
+               /* Guaranteed to be a match */
+               for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++)
+                       if (webcam_sizes[i].width == mp->width &&
+                                       webcam_sizes[i].height == mp->height)
+                               break;
+               dev->webcam_size_idx = i;
+               if (dev->webcam_ival_idx >= 2 * (3 - i))
+                       dev->webcam_ival_idx = 2 * (3 - i) - 1;
+               vivid_update_format_cap(dev, false);
+       } else {
+               struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+
+               rect_set_size_to(compose, &r);
+               r.height *= factor;
+               rect_set_size_to(crop, &r);
+       }
+
+       dev->fmt_cap_rect.width = mp->width;
+       dev->fmt_cap_rect.height = mp->height;
+       tpg_s_buf_height(&dev->tpg, mp->height);
+       tpg_s_bytesperline(&dev->tpg, 0, mp->plane_fmt[0].bytesperline);
+       if (tpg_g_planes(&dev->tpg) > 1)
+               tpg_s_bytesperline(&dev->tpg, 1, mp->plane_fmt[1].bytesperline);
+       dev->field_cap = mp->field;
+       tpg_s_field(&dev->tpg, dev->field_cap);
+       tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap);
+       tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc);
+       if (vivid_is_sdtv_cap(dev))
+               dev->tv_field_cap = mp->field;
+       tpg_update_mv_step(&dev->tpg);
+       return 0;
+}
+
+int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!dev->multiplanar)
+               return -ENOTTY;
+       return vivid_g_fmt_vid_cap(file, priv, f);
+}
+
+int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!dev->multiplanar)
+               return -ENOTTY;
+       return vivid_try_fmt_vid_cap(file, priv, f);
+}
+
+int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!dev->multiplanar)
+               return -ENOTTY;
+       return vivid_s_fmt_vid_cap(file, priv, f);
+}
+
+int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (dev->multiplanar)
+               return -ENOTTY;
+       return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_cap);
+}
+
+int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (dev->multiplanar)
+               return -ENOTTY;
+       return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_cap);
+}
+
+int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (dev->multiplanar)
+               return -ENOTTY;
+       return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_cap);
+}
+
+int vivid_vid_cap_g_selection(struct file *file, void *priv,
+                             struct v4l2_selection *sel)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!dev->has_crop_cap && !dev->has_compose_cap)
+               return -ENOTTY;
+       if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       if (vivid_is_webcam(dev))
+               return -EINVAL;
+
+       sel->r.left = sel->r.top = 0;
+       switch (sel->target) {
+       case V4L2_SEL_TGT_CROP:
+               if (!dev->has_crop_cap)
+                       return -EINVAL;
+               sel->r = dev->crop_cap;
+               break;
+       case V4L2_SEL_TGT_CROP_DEFAULT:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
+               if (!dev->has_crop_cap)
+                       return -EINVAL;
+               sel->r = dev->src_rect;
+               break;
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+               if (!dev->has_compose_cap)
+                       return -EINVAL;
+               sel->r = vivid_max_rect;
+               break;
+       case V4L2_SEL_TGT_COMPOSE:
+               if (!dev->has_compose_cap)
+                       return -EINVAL;
+               sel->r = dev->compose_cap;
+               break;
+       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+               if (!dev->has_compose_cap)
+                       return -EINVAL;
+               sel->r = dev->fmt_cap_rect;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_rect *crop = &dev->crop_cap;
+       struct v4l2_rect *compose = &dev->compose_cap;
+       unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1;
+       int ret;
+
+       if (!dev->has_crop_cap && !dev->has_compose_cap)
+               return -ENOTTY;
+       if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       if (vivid_is_webcam(dev))
+               return -EINVAL;
+
+       switch (s->target) {
+       case V4L2_SEL_TGT_CROP:
+               if (!dev->has_crop_cap)
+                       return -EINVAL;
+               ret = vivid_vid_adjust_sel(s->flags, &s->r);
+               if (ret)
+                       return ret;
+               rect_set_min_size(&s->r, &vivid_min_rect);
+               rect_set_max_size(&s->r, &dev->src_rect);
+               rect_map_inside(&s->r, &dev->crop_bounds_cap);
+               s->r.top /= factor;
+               s->r.height /= factor;
+               if (dev->has_scaler_cap) {
+                       struct v4l2_rect fmt = dev->fmt_cap_rect;
+                       struct v4l2_rect max_rect = {
+                               0, 0,
+                               s->r.width * MAX_ZOOM,
+                               s->r.height * MAX_ZOOM
+                       };
+                       struct v4l2_rect min_rect = {
+                               0, 0,
+                               s->r.width / MAX_ZOOM,
+                               s->r.height / MAX_ZOOM
+                       };
+
+                       rect_set_min_size(&fmt, &min_rect);
+                       if (!dev->has_compose_cap)
+                               rect_set_max_size(&fmt, &max_rect);
+                       if (!rect_same_size(&dev->fmt_cap_rect, &fmt) &&
+                           vb2_is_busy(&dev->vb_vid_cap_q))
+                               return -EBUSY;
+                       if (dev->has_compose_cap) {
+                               rect_set_min_size(compose, &min_rect);
+                               rect_set_max_size(compose, &max_rect);
+                       }
+                       dev->fmt_cap_rect = fmt;
+                       tpg_s_buf_height(&dev->tpg, fmt.height);
+               } else if (dev->has_compose_cap) {
+                       struct v4l2_rect fmt = dev->fmt_cap_rect;
+
+                       rect_set_min_size(&fmt, &s->r);
+                       if (!rect_same_size(&dev->fmt_cap_rect, &fmt) &&
+                           vb2_is_busy(&dev->vb_vid_cap_q))
+                               return -EBUSY;
+                       dev->fmt_cap_rect = fmt;
+                       tpg_s_buf_height(&dev->tpg, fmt.height);
+                       rect_set_size_to(compose, &s->r);
+                       rect_map_inside(compose, &dev->fmt_cap_rect);
+               } else {
+                       if (!rect_same_size(&s->r, &dev->fmt_cap_rect) &&
+                           vb2_is_busy(&dev->vb_vid_cap_q))
+                               return -EBUSY;
+                       rect_set_size_to(&dev->fmt_cap_rect, &s->r);
+                       rect_set_size_to(compose, &s->r);
+                       rect_map_inside(compose, &dev->fmt_cap_rect);
+                       tpg_s_buf_height(&dev->tpg, dev->fmt_cap_rect.height);
+               }
+               s->r.top *= factor;
+               s->r.height *= factor;
+               *crop = s->r;
+               break;
+       case V4L2_SEL_TGT_COMPOSE:
+               if (!dev->has_compose_cap)
+                       return -EINVAL;
+               ret = vivid_vid_adjust_sel(s->flags, &s->r);
+               if (ret)
+                       return ret;
+               rect_set_min_size(&s->r, &vivid_min_rect);
+               rect_set_max_size(&s->r, &dev->fmt_cap_rect);
+               if (dev->has_scaler_cap) {
+                       struct v4l2_rect max_rect = {
+                               0, 0,
+                               dev->src_rect.width * MAX_ZOOM,
+                               (dev->src_rect.height / factor) * MAX_ZOOM
+                       };
+
+                       rect_set_max_size(&s->r, &max_rect);
+                       if (dev->has_crop_cap) {
+                               struct v4l2_rect min_rect = {
+                                       0, 0,
+                                       s->r.width / MAX_ZOOM,
+                                       (s->r.height * factor) / MAX_ZOOM
+                               };
+                               struct v4l2_rect max_rect = {
+                                       0, 0,
+                                       s->r.width * MAX_ZOOM,
+                                       (s->r.height * factor) * MAX_ZOOM
+                               };
+
+                               rect_set_min_size(crop, &min_rect);
+                               rect_set_max_size(crop, &max_rect);
+                               rect_map_inside(crop, &dev->crop_bounds_cap);
+                       }
+               } else if (dev->has_crop_cap) {
+                       s->r.top *= factor;
+                       s->r.height *= factor;
+                       rect_set_max_size(&s->r, &dev->src_rect);
+                       rect_set_size_to(crop, &s->r);
+                       rect_map_inside(crop, &dev->crop_bounds_cap);
+                       s->r.top /= factor;
+                       s->r.height /= factor;
+               } else {
+                       rect_set_size_to(&s->r, &dev->src_rect);
+                       s->r.height /= factor;
+               }
+               rect_map_inside(&s->r, &dev->fmt_cap_rect);
+               if (dev->bitmap_cap && (compose->width != s->r.width ||
+                                       compose->height != s->r.height)) {
+                       kfree(dev->bitmap_cap);
+                       dev->bitmap_cap = NULL;
+               }
+               *compose = s->r;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       tpg_s_crop_compose(&dev->tpg, crop, compose);
+       return 0;
+}
+
+int vivid_vid_cap_cropcap(struct file *file, void *priv,
+                             struct v4l2_cropcap *cap)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       switch (vivid_get_pixel_aspect(dev)) {
+       case TPG_PIXEL_ASPECT_NTSC:
+               cap->pixelaspect.numerator = 11;
+               cap->pixelaspect.denominator = 10;
+               break;
+       case TPG_PIXEL_ASPECT_PAL:
+               cap->pixelaspect.numerator = 54;
+               cap->pixelaspect.denominator = 59;
+               break;
+       case TPG_PIXEL_ASPECT_SQUARE:
+               cap->pixelaspect.numerator = 1;
+               cap->pixelaspect.denominator = 1;
+               break;
+       }
+       return 0;
+}
+
+int vidioc_enum_fmt_vid_overlay(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+{
+       const struct vivid_fmt *fmt;
+
+       if (f->index >= ARRAY_SIZE(formats_ovl))
+               return -EINVAL;
+
+       fmt = &formats_ovl[f->index];
+
+       strlcpy(f->description, fmt->name, sizeof(f->description));
+       f->pixelformat = fmt->fourcc;
+       return 0;
+}
+
+int vidioc_g_fmt_vid_overlay(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct v4l2_rect *compose = &dev->compose_cap;
+       struct v4l2_window *win = &f->fmt.win;
+       unsigned clipcount = win->clipcount;
+
+       win->w.top = dev->overlay_cap_top;
+       win->w.left = dev->overlay_cap_left;
+       win->w.width = compose->width;
+       win->w.height = compose->height;
+       win->field = dev->overlay_cap_field;
+       win->clipcount = dev->clipcount_cap;
+       if (clipcount > dev->clipcount_cap)
+               clipcount = dev->clipcount_cap;
+       if (dev->bitmap_cap == NULL)
+               win->bitmap = NULL;
+       else if (win->bitmap) {
+               if (copy_to_user(win->bitmap, dev->bitmap_cap,
+                   ((compose->width + 7) / 8) * compose->height))
+                       return -EFAULT;
+       }
+       if (clipcount && win->clips) {
+               if (copy_to_user(win->clips, dev->clips_cap,
+                                clipcount * sizeof(dev->clips_cap[0])))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+int vidioc_try_fmt_vid_overlay(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct v4l2_rect *compose = &dev->compose_cap;
+       struct v4l2_window *win = &f->fmt.win;
+       int i, j;
+
+       win->w.left = clamp_t(int, win->w.left,
+                             -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width);
+       win->w.top = clamp_t(int, win->w.top,
+                            -dev->fb_cap.fmt.height, dev->fb_cap.fmt.height);
+       win->w.width = compose->width;
+       win->w.height = compose->height;
+       if (win->field != V4L2_FIELD_BOTTOM && win->field != V4L2_FIELD_TOP)
+               win->field = V4L2_FIELD_ANY;
+       win->chromakey = 0;
+       win->global_alpha = 0;
+       if (win->clipcount && !win->clips)
+               win->clipcount = 0;
+       if (win->clipcount > MAX_CLIPS)
+               win->clipcount = MAX_CLIPS;
+       if (win->clipcount) {
+               if (copy_from_user(dev->try_clips_cap, win->clips,
+                                  win->clipcount * sizeof(dev->clips_cap[0])))
+                       return -EFAULT;
+               for (i = 0; i < win->clipcount; i++) {
+                       struct v4l2_rect *r = &dev->try_clips_cap[i].c;
+
+                       r->top = clamp_t(s32, r->top, 0, dev->fb_cap.fmt.height - 1);
+                       r->height = clamp_t(s32, r->height, 1, dev->fb_cap.fmt.height - r->top);
+                       r->left = clamp_t(u32, r->left, 0, dev->fb_cap.fmt.width - 1);
+                       r->width = clamp_t(u32, r->width, 1, dev->fb_cap.fmt.width - r->left);
+               }
+               /*
+                * Yeah, so sue me, it's an O(n^2) algorithm. But n is a small
+                * number and it's typically a one-time deal.
+                */
+               for (i = 0; i < win->clipcount - 1; i++) {
+                       struct v4l2_rect *r1 = &dev->try_clips_cap[i].c;
+
+                       for (j = i + 1; j < win->clipcount; j++) {
+                               struct v4l2_rect *r2 = &dev->try_clips_cap[j].c;
+
+                               if (rect_overlap(r1, r2))
+                                       return -EINVAL;
+                       }
+               }
+               if (copy_to_user(win->clips, dev->try_clips_cap,
+                                win->clipcount * sizeof(dev->clips_cap[0])))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+int vidioc_s_fmt_vid_overlay(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct v4l2_rect *compose = &dev->compose_cap;
+       struct v4l2_window *win = &f->fmt.win;
+       int ret = vidioc_try_fmt_vid_overlay(file, priv, f);
+       unsigned bitmap_size = ((compose->width + 7) / 8) * compose->height;
+       unsigned clips_size = win->clipcount * sizeof(dev->clips_cap[0]);
+       void *new_bitmap = NULL;
+
+       if (ret)
+               return ret;
+
+       if (win->bitmap) {
+               new_bitmap = vzalloc(bitmap_size);
+
+               if (new_bitmap == NULL)
+                       return -ENOMEM;
+               if (copy_from_user(new_bitmap, win->bitmap, bitmap_size)) {
+                       vfree(new_bitmap);
+                       return -EFAULT;
+               }
+       }
+
+       dev->overlay_cap_top = win->w.top;
+       dev->overlay_cap_left = win->w.left;
+       dev->overlay_cap_field = win->field;
+       vfree(dev->bitmap_cap);
+       dev->bitmap_cap = new_bitmap;
+       dev->clipcount_cap = win->clipcount;
+       if (dev->clipcount_cap)
+               memcpy(dev->clips_cap, dev->try_clips_cap, clips_size);
+       return 0;
+}
+
+int vivid_vid_cap_overlay(struct file *file, void *fh, unsigned i)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (i && dev->fb_vbase_cap == NULL)
+               return -EINVAL;
+
+       if (i && dev->fb_cap.fmt.pixelformat != dev->fmt_cap->fourcc) {
+               dprintk(dev, 1, "mismatch between overlay and video capture pixelformats\n");
+               return -EINVAL;
+       }
+
+       if (dev->overlay_cap_owner && dev->overlay_cap_owner != fh)
+               return -EBUSY;
+       dev->overlay_cap_owner = i ? fh : NULL;
+       return 0;
+}
+
+int vivid_vid_cap_g_fbuf(struct file *file, void *fh,
+                               struct v4l2_framebuffer *a)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       *a = dev->fb_cap;
+       a->capability = V4L2_FBUF_CAP_BITMAP_CLIPPING |
+                       V4L2_FBUF_CAP_LIST_CLIPPING;
+       a->flags = V4L2_FBUF_FLAG_PRIMARY;
+       a->fmt.field = V4L2_FIELD_NONE;
+       a->fmt.colorspace = V4L2_COLORSPACE_SRGB;
+       a->fmt.priv = 0;
+       return 0;
+}
+
+int vivid_vid_cap_s_fbuf(struct file *file, void *fh,
+                               const struct v4l2_framebuffer *a)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct vivid_fmt *fmt;
+
+       if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+               return -EPERM;
+
+       if (dev->overlay_cap_owner)
+               return -EBUSY;
+
+       if (a->base == NULL) {
+               dev->fb_cap.base = NULL;
+               dev->fb_vbase_cap = NULL;
+               return 0;
+       }
+
+       if (a->fmt.width < 48 || a->fmt.height < 32)
+               return -EINVAL;
+       fmt = vivid_get_format(dev, a->fmt.pixelformat);
+       if (!fmt || !fmt->can_do_overlay)
+               return -EINVAL;
+       if (a->fmt.bytesperline < (a->fmt.width * fmt->depth) / 8)
+               return -EINVAL;
+       if (a->fmt.height * a->fmt.bytesperline < a->fmt.sizeimage)
+               return -EINVAL;
+
+       dev->fb_vbase_cap = phys_to_virt((unsigned long)a->base);
+       dev->fb_cap = *a;
+       dev->overlay_cap_left = clamp_t(int, dev->overlay_cap_left,
+                                   -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width);
+       dev->overlay_cap_top = clamp_t(int, dev->overlay_cap_top,
+                                  -dev->fb_cap.fmt.height, dev->fb_cap.fmt.height);
+       return 0;
+}
+
+static const struct v4l2_audio vivid_audio_inputs[] = {
+       { 0, "TV", V4L2_AUDCAP_STEREO },
+       { 1, "Line-In", V4L2_AUDCAP_STEREO },
+};
+
+int vidioc_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *inp)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (inp->index >= dev->num_inputs)
+               return -EINVAL;
+
+       inp->type = V4L2_INPUT_TYPE_CAMERA;
+       switch (dev->input_type[inp->index]) {
+       case WEBCAM:
+               snprintf(inp->name, sizeof(inp->name), "Webcam %u",
+                               dev->input_name_counter[inp->index]);
+               inp->capabilities = 0;
+               break;
+       case TV:
+               snprintf(inp->name, sizeof(inp->name), "TV %u",
+                               dev->input_name_counter[inp->index]);
+               inp->type = V4L2_INPUT_TYPE_TUNER;
+               inp->std = V4L2_STD_ALL;
+               if (dev->has_audio_inputs)
+                       inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1;
+               inp->capabilities = V4L2_IN_CAP_STD;
+               break;
+       case SVID:
+               snprintf(inp->name, sizeof(inp->name), "S-Video %u",
+                               dev->input_name_counter[inp->index]);
+               inp->std = V4L2_STD_ALL;
+               if (dev->has_audio_inputs)
+                       inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1;
+               inp->capabilities = V4L2_IN_CAP_STD;
+               break;
+       case HDMI:
+               snprintf(inp->name, sizeof(inp->name), "HDMI %u",
+                               dev->input_name_counter[inp->index]);
+               inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+               if (dev->edid_blocks == 0 ||
+                   dev->dv_timings_signal_mode == NO_SIGNAL)
+                       inp->status |= V4L2_IN_ST_NO_SIGNAL;
+               else if (dev->dv_timings_signal_mode == NO_LOCK ||
+                        dev->dv_timings_signal_mode == OUT_OF_RANGE)
+                       inp->status |= V4L2_IN_ST_NO_H_LOCK;
+               break;
+       }
+       if (dev->sensor_hflip)
+               inp->status |= V4L2_IN_ST_HFLIP;
+       if (dev->sensor_vflip)
+               inp->status |= V4L2_IN_ST_VFLIP;
+       if (dev->input == inp->index && vivid_is_sdtv_cap(dev)) {
+               if (dev->std_signal_mode == NO_SIGNAL) {
+                       inp->status |= V4L2_IN_ST_NO_SIGNAL;
+               } else if (dev->std_signal_mode == NO_LOCK) {
+                       inp->status |= V4L2_IN_ST_NO_H_LOCK;
+               } else if (vivid_is_tv_cap(dev)) {
+                       switch (tpg_g_quality(&dev->tpg)) {
+                       case TPG_QUAL_GRAY:
+                               inp->status |= V4L2_IN_ST_COLOR_KILL;
+                               break;
+                       case TPG_QUAL_NOISE:
+                               inp->status |= V4L2_IN_ST_NO_H_LOCK;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+
+int vidioc_g_input(struct file *file, void *priv, unsigned *i)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       *i = dev->input;
+       return 0;
+}
+
+int vidioc_s_input(struct file *file, void *priv, unsigned i)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_bt_timings *bt = &dev->dv_timings_cap.bt;
+       unsigned brightness;
+
+       if (i >= dev->num_inputs)
+               return -EINVAL;
+
+       if (i == dev->input)
+               return 0;
+
+       if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q))
+               return -EBUSY;
+
+       dev->input = i;
+       dev->vid_cap_dev.tvnorms = 0;
+       if (dev->input_type[i] == TV || dev->input_type[i] == SVID) {
+               dev->tv_audio_input = (dev->input_type[i] == TV) ? 0 : 1;
+               dev->vid_cap_dev.tvnorms = V4L2_STD_ALL;
+       }
+       dev->vbi_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms;
+       vivid_update_format_cap(dev, false);
+
+       if (dev->colorspace) {
+               switch (dev->input_type[i]) {
+               case WEBCAM:
+                       v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB);
+                       break;
+               case TV:
+               case SVID:
+                       v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M);
+                       break;
+               case HDMI:
+                       if (bt->standards & V4L2_DV_BT_STD_CEA861) {
+                               if (dev->src_rect.width == 720 && dev->src_rect.height <= 576)
+                                       v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M);
+                               else
+                                       v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_REC709);
+                       } else {
+                               v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB);
+                       }
+                       break;
+               }
+       }
+
+       /*
+        * Modify the brightness range depending on the input.
+        * This makes it easy to use vivid to test if applications can
+        * handle control range modifications and is also how this is
+        * typically used in practice as different inputs may be hooked
+        * up to different receivers with different control ranges.
+        */
+       brightness = 128 * i + dev->input_brightness[i];
+       v4l2_ctrl_modify_range(dev->brightness,
+                       128 * i, 255 + 128 * i, 1, 128 + 128 * i);
+       v4l2_ctrl_s_ctrl(dev->brightness, brightness);
+       return 0;
+}
+
+int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+       if (vin->index >= ARRAY_SIZE(vivid_audio_inputs))
+               return -EINVAL;
+       *vin = vivid_audio_inputs[vin->index];
+       return 0;
+}
+
+int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_sdtv_cap(dev))
+               return -EINVAL;
+       *vin = vivid_audio_inputs[dev->tv_audio_input];
+       return 0;
+}
+
+int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_sdtv_cap(dev))
+               return -EINVAL;
+       if (vin->index >= ARRAY_SIZE(vivid_audio_inputs))
+               return -EINVAL;
+       dev->tv_audio_input = vin->index;
+       return 0;
+}
+
+int vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (vf->tuner != 0)
+               return -EINVAL;
+       vf->frequency = dev->tv_freq;
+       return 0;
+}
+
+int vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (vf->tuner != 0)
+               return -EINVAL;
+       dev->tv_freq = clamp_t(unsigned, vf->frequency, MIN_TV_FREQ, MAX_TV_FREQ);
+       if (vivid_is_tv_cap(dev))
+               vivid_update_quality(dev);
+       return 0;
+}
+
+int vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (vt->index != 0)
+               return -EINVAL;
+       if (vt->audmode > V4L2_TUNER_MODE_LANG1_LANG2)
+               return -EINVAL;
+       dev->tv_audmode = vt->audmode;
+       return 0;
+}
+
+int vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       enum tpg_quality qual;
+
+       if (vt->index != 0)
+               return -EINVAL;
+
+       vt->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+                        V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+       vt->audmode = dev->tv_audmode;
+       vt->rangelow = MIN_TV_FREQ;
+       vt->rangehigh = MAX_TV_FREQ;
+       qual = vivid_get_quality(dev, &vt->afc);
+       if (qual == TPG_QUAL_COLOR)
+               vt->signal = 0xffff;
+       else if (qual == TPG_QUAL_GRAY)
+               vt->signal = 0x8000;
+       else
+               vt->signal = 0;
+       if (qual == TPG_QUAL_NOISE) {
+               vt->rxsubchans = 0;
+       } else if (qual == TPG_QUAL_GRAY) {
+               vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+       } else {
+               unsigned channel_nr = dev->tv_freq / (6 * 16);
+               unsigned options = (dev->std_cap & V4L2_STD_NTSC_M) ? 4 : 3;
+
+               switch (channel_nr % options) {
+               case 0:
+                       vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+                       break;
+               case 1:
+                       vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+                       break;
+               case 2:
+                       if (dev->std_cap & V4L2_STD_NTSC_M)
+                               vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_SAP;
+                       else
+                               vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+                       break;
+               case 3:
+                       vt->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_SAP;
+                       break;
+               }
+       }
+       strlcpy(vt->name, "TV Tuner", sizeof(vt->name));
+       return 0;
+}
+
+/* Must remain in sync with the vivid_ctrl_standard_strings array */
+const v4l2_std_id vivid_standard[] = {
+       V4L2_STD_NTSC_M,
+       V4L2_STD_NTSC_M_JP,
+       V4L2_STD_NTSC_M_KR,
+       V4L2_STD_NTSC_443,
+       V4L2_STD_PAL_BG | V4L2_STD_PAL_H,
+       V4L2_STD_PAL_I,
+       V4L2_STD_PAL_DK,
+       V4L2_STD_PAL_M,
+       V4L2_STD_PAL_N,
+       V4L2_STD_PAL_Nc,
+       V4L2_STD_PAL_60,
+       V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
+       V4L2_STD_SECAM_DK,
+       V4L2_STD_SECAM_L,
+       V4L2_STD_SECAM_LC,
+       V4L2_STD_UNKNOWN
+};
+
+/* Must remain in sync with the vivid_standard array */
+const char * const vivid_ctrl_standard_strings[] = {
+       "NTSC-M",
+       "NTSC-M-JP",
+       "NTSC-M-KR",
+       "NTSC-443",
+       "PAL-BGH",
+       "PAL-I",
+       "PAL-DK",
+       "PAL-M",
+       "PAL-N",
+       "PAL-Nc",
+       "PAL-60",
+       "SECAM-BGH",
+       "SECAM-DK",
+       "SECAM-L",
+       "SECAM-Lc",
+       NULL,
+};
+
+int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *id)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_sdtv_cap(dev))
+               return -ENODATA;
+       if (dev->std_signal_mode == NO_SIGNAL ||
+           dev->std_signal_mode == NO_LOCK) {
+               *id = V4L2_STD_UNKNOWN;
+               return 0;
+       }
+       if (vivid_is_tv_cap(dev) && tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE) {
+               *id = V4L2_STD_UNKNOWN;
+       } else if (dev->std_signal_mode == CURRENT_STD) {
+               *id = dev->std_cap;
+       } else if (dev->std_signal_mode == SELECTED_STD) {
+               *id = dev->query_std;
+       } else {
+               *id = vivid_standard[dev->query_std_last];
+               dev->query_std_last = (dev->query_std_last + 1) % ARRAY_SIZE(vivid_standard);
+       }
+
+       return 0;
+}
+
+int vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_sdtv_cap(dev))
+               return -ENODATA;
+       if (dev->std_cap == id)
+               return 0;
+       if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q))
+               return -EBUSY;
+       dev->std_cap = id;
+       vivid_update_format_cap(dev, false);
+       return 0;
+}
+
+int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh,
+                                   struct v4l2_dv_timings *timings)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_hdmi_cap(dev))
+               return -ENODATA;
+       if (vb2_is_busy(&dev->vb_vid_cap_q))
+               return -EBUSY;
+       if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,
+                               0, NULL, NULL))
+               return -EINVAL;
+       if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap, 0))
+               return 0;
+       dev->dv_timings_cap = *timings;
+       vivid_update_format_cap(dev, false);
+       return 0;
+}
+
+int vidioc_query_dv_timings(struct file *file, void *_fh,
+                                   struct v4l2_dv_timings *timings)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_hdmi_cap(dev))
+               return -ENODATA;
+       if (dev->dv_timings_signal_mode == NO_SIGNAL ||
+           dev->edid_blocks == 0)
+               return -ENOLINK;
+       if (dev->dv_timings_signal_mode == NO_LOCK)
+               return -ENOLCK;
+       if (dev->dv_timings_signal_mode == OUT_OF_RANGE) {
+               timings->bt.pixelclock = vivid_dv_timings_cap.bt.max_pixelclock * 2;
+               return -ERANGE;
+       }
+       if (dev->dv_timings_signal_mode == CURRENT_DV_TIMINGS) {
+               *timings = dev->dv_timings_cap;
+       } else if (dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS) {
+               *timings = v4l2_dv_timings_presets[dev->query_dv_timings];
+       } else {
+               *timings = v4l2_dv_timings_presets[dev->query_dv_timings_last];
+               dev->query_dv_timings_last = (dev->query_dv_timings_last + 1) %
+                                               dev->query_dv_timings_size;
+       }
+       return 0;
+}
+
+int vidioc_s_edid(struct file *file, void *_fh,
+                        struct v4l2_edid *edid)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       memset(edid->reserved, 0, sizeof(edid->reserved));
+       if (edid->pad >= dev->num_inputs)
+               return -EINVAL;
+       if (dev->input_type[edid->pad] != HDMI || edid->start_block)
+               return -EINVAL;
+       if (edid->blocks == 0) {
+               dev->edid_blocks = 0;
+               return 0;
+       }
+       if (edid->blocks > dev->edid_max_blocks) {
+               edid->blocks = dev->edid_max_blocks;
+               return -E2BIG;
+       }
+       dev->edid_blocks = edid->blocks;
+       memcpy(dev->edid, edid->edid, edid->blocks * 128);
+       return 0;
+}
+
+int vidioc_enum_framesizes(struct file *file, void *fh,
+                                        struct v4l2_frmsizeenum *fsize)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_webcam(dev) && !dev->has_scaler_cap)
+               return -EINVAL;
+       if (vivid_get_format(dev, fsize->pixel_format) == NULL)
+               return -EINVAL;
+       if (vivid_is_webcam(dev)) {
+               if (fsize->index >= ARRAY_SIZE(webcam_sizes))
+                       return -EINVAL;
+               fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+               fsize->discrete = webcam_sizes[fsize->index];
+               return 0;
+       }
+       if (fsize->index)
+               return -EINVAL;
+       fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+       fsize->stepwise.min_width = MIN_WIDTH;
+       fsize->stepwise.max_width = MAX_WIDTH * MAX_ZOOM;
+       fsize->stepwise.step_width = 2;
+       fsize->stepwise.min_height = MIN_HEIGHT;
+       fsize->stepwise.max_height = MAX_HEIGHT * MAX_ZOOM;
+       fsize->stepwise.step_height = 2;
+       return 0;
+}
+
+/* timeperframe is arbitrary and continuous */
+int vidioc_enum_frameintervals(struct file *file, void *priv,
+                                            struct v4l2_frmivalenum *fival)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct vivid_fmt *fmt;
+       int i;
+
+       fmt = vivid_get_format(dev, fival->pixel_format);
+       if (!fmt)
+               return -EINVAL;
+
+       if (!vivid_is_webcam(dev)) {
+               static const struct v4l2_fract step = { 1, 1 };
+
+               if (fival->index)
+                       return -EINVAL;
+               if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH * MAX_ZOOM)
+                       return -EINVAL;
+               if (fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT * MAX_ZOOM)
+                       return -EINVAL;
+               fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
+               fival->stepwise.min = tpf_min;
+               fival->stepwise.max = tpf_max;
+               fival->stepwise.step = step;
+               return 0;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++)
+               if (fival->width == webcam_sizes[i].width &&
+                   fival->height == webcam_sizes[i].height)
+                       break;
+       if (i == ARRAY_SIZE(webcam_sizes))
+               return -EINVAL;
+       if (fival->index >= 2 * (3 - i))
+               return -EINVAL;
+       fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+       fival->discrete = webcam_intervals[fival->index];
+       return 0;
+}
+
+int vivid_vid_cap_g_parm(struct file *file, void *priv,
+                         struct v4l2_streamparm *parm)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (parm->type != (dev->multiplanar ?
+                          V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
+                          V4L2_BUF_TYPE_VIDEO_CAPTURE))
+               return -EINVAL;
+
+       parm->parm.capture.capability   = V4L2_CAP_TIMEPERFRAME;
+       parm->parm.capture.timeperframe = dev->timeperframe_vid_cap;
+       parm->parm.capture.readbuffers  = 1;
+       return 0;
+}
+
+#define FRACT_CMP(a, OP, b)    \
+       ((u64)(a).numerator * (b).denominator  OP  (u64)(b).numerator * (a).denominator)
+
+int vivid_vid_cap_s_parm(struct file *file, void *priv,
+                         struct v4l2_streamparm *parm)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       unsigned ival_sz = 2 * (3 - dev->webcam_size_idx);
+       struct v4l2_fract tpf;
+       unsigned i;
+
+       if (parm->type != (dev->multiplanar ?
+                          V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
+                          V4L2_BUF_TYPE_VIDEO_CAPTURE))
+               return -EINVAL;
+       if (!vivid_is_webcam(dev))
+               return vivid_vid_cap_g_parm(file, priv, parm);
+
+       tpf = parm->parm.capture.timeperframe;
+
+       if (tpf.denominator == 0)
+               tpf = webcam_intervals[ival_sz - 1];
+       for (i = 0; i < ival_sz; i++)
+               if (FRACT_CMP(tpf, >=, webcam_intervals[i]))
+                       break;
+       if (i == ival_sz)
+               i = ival_sz - 1;
+       dev->webcam_ival_idx = i;
+       tpf = webcam_intervals[dev->webcam_ival_idx];
+       tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
+       tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
+
+       /* resync the thread's timings */
+       dev->cap_seq_resync = true;
+       dev->timeperframe_vid_cap = tpf;
+       parm->parm.capture.timeperframe = tpf;
+       parm->parm.capture.readbuffers  = 1;
+       return 0;
+}
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.h b/drivers/media/platform/vivid/vivid-vid-cap.h
new file mode 100644 (file)
index 0000000..9407981
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * vivid-vid-cap.h - video capture support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_VID_CAP_H_
+#define _VIVID_VID_CAP_H_
+
+void vivid_update_quality(struct vivid_dev *dev);
+void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls);
+enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev);
+
+extern const v4l2_std_id vivid_standard[];
+extern const char * const vivid_ctrl_standard_strings[];
+
+extern const struct vb2_ops vivid_vid_cap_qops;
+
+int vivid_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_vid_cap_g_selection(struct file *file, void *priv, struct v4l2_selection *sel);
+int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s);
+int vivid_vid_cap_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap);
+int vidioc_enum_fmt_vid_overlay(struct file *file, void  *priv, struct v4l2_fmtdesc *f);
+int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_vid_cap_overlay(struct file *file, void *fh, unsigned i);
+int vivid_vid_cap_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a);
+int vivid_vid_cap_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a);
+int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp);
+int vidioc_g_input(struct file *file, void *priv, unsigned *i);
+int vidioc_s_input(struct file *file, void *priv, unsigned i);
+int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin);
+int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin);
+int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin);
+int vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
+int vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf);
+int vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt);
+int vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt);
+int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *id);
+int vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id);
+int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings);
+int vidioc_query_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings);
+int vidioc_s_edid(struct file *file, void *_fh, struct v4l2_edid *edid);
+int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize);
+int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival);
+int vivid_vid_cap_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parm);
+int vivid_vid_cap_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parm);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
new file mode 100644 (file)
index 0000000..16cd6d2
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * vivid-vid-common.c - common video support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+
+const struct v4l2_dv_timings_cap vivid_dv_timings_cap = {
+       .type = V4L2_DV_BT_656_1120,
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, MAX_WIDTH, 0, MAX_HEIGHT, 25000000, 600000000,
+               V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT,
+               V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_INTERLACED)
+};
+
+/* ------------------------------------------------------------------
+       Basic structures
+   ------------------------------------------------------------------*/
+
+struct vivid_fmt vivid_formats[] = {
+       {
+               .name     = "4:2:2, packed, YUYV",
+               .fourcc   = V4L2_PIX_FMT_YUYV,
+               .depth    = 16,
+               .is_yuv   = true,
+               .planes   = 1,
+               .data_offset = { PLANE0_DATA_OFFSET, 0 },
+       },
+       {
+               .name     = "4:2:2, packed, UYVY",
+               .fourcc   = V4L2_PIX_FMT_UYVY,
+               .depth    = 16,
+               .is_yuv   = true,
+               .planes   = 1,
+       },
+       {
+               .name     = "4:2:2, packed, YVYU",
+               .fourcc   = V4L2_PIX_FMT_YVYU,
+               .depth    = 16,
+               .is_yuv   = true,
+               .planes   = 1,
+       },
+       {
+               .name     = "4:2:2, packed, VYUY",
+               .fourcc   = V4L2_PIX_FMT_VYUY,
+               .depth    = 16,
+               .is_yuv   = true,
+               .planes   = 1,
+       },
+       {
+               .name     = "RGB565 (LE)",
+               .fourcc   = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+               .depth    = 16,
+               .planes   = 1,
+               .can_do_overlay = true,
+       },
+       {
+               .name     = "RGB565 (BE)",
+               .fourcc   = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+               .depth    = 16,
+               .planes   = 1,
+               .can_do_overlay = true,
+       },
+       {
+               .name     = "RGB555 (LE)",
+               .fourcc   = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+               .depth    = 16,
+               .planes   = 1,
+               .can_do_overlay = true,
+       },
+       {
+               .name     = "XRGB555 (LE)",
+               .fourcc   = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */
+               .depth    = 16,
+               .planes   = 1,
+               .can_do_overlay = true,
+       },
+       {
+               .name     = "ARGB555 (LE)",
+               .fourcc   = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */
+               .depth    = 16,
+               .planes   = 1,
+               .can_do_overlay = true,
+               .alpha_mask = 0x8000,
+       },
+       {
+               .name     = "RGB555 (BE)",
+               .fourcc   = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+               .depth    = 16,
+               .planes   = 1,
+               .can_do_overlay = true,
+       },
+       {
+               .name     = "RGB24 (LE)",
+               .fourcc   = V4L2_PIX_FMT_RGB24, /* rgb */
+               .depth    = 24,
+               .planes   = 1,
+       },
+       {
+               .name     = "RGB24 (BE)",
+               .fourcc   = V4L2_PIX_FMT_BGR24, /* bgr */
+               .depth    = 24,
+               .planes   = 1,
+       },
+       {
+               .name     = "RGB32 (LE)",
+               .fourcc   = V4L2_PIX_FMT_RGB32, /* argb */
+               .depth    = 32,
+               .planes   = 1,
+       },
+       {
+               .name     = "RGB32 (BE)",
+               .fourcc   = V4L2_PIX_FMT_BGR32, /* bgra */
+               .depth    = 32,
+               .planes   = 1,
+       },
+       {
+               .name     = "XRGB32 (LE)",
+               .fourcc   = V4L2_PIX_FMT_XRGB32, /* argb */
+               .depth    = 32,
+               .planes   = 1,
+       },
+       {
+               .name     = "XRGB32 (BE)",
+               .fourcc   = V4L2_PIX_FMT_XBGR32, /* bgra */
+               .depth    = 32,
+               .planes   = 1,
+       },
+       {
+               .name     = "ARGB32 (LE)",
+               .fourcc   = V4L2_PIX_FMT_ARGB32, /* argb */
+               .depth    = 32,
+               .planes   = 1,
+               .alpha_mask = 0x000000ff,
+       },
+       {
+               .name     = "ARGB32 (BE)",
+               .fourcc   = V4L2_PIX_FMT_ABGR32, /* bgra */
+               .depth    = 32,
+               .planes   = 1,
+               .alpha_mask = 0xff000000,
+       },
+       {
+               .name     = "4:2:2, planar, YUV",
+               .fourcc   = V4L2_PIX_FMT_NV16M,
+               .depth    = 8,
+               .is_yuv   = true,
+               .planes   = 2,
+               .data_offset = { PLANE0_DATA_OFFSET, 0 },
+       },
+       {
+               .name     = "4:2:2, planar, YVU",
+               .fourcc   = V4L2_PIX_FMT_NV61M,
+               .depth    = 8,
+               .is_yuv   = true,
+               .planes   = 2,
+               .data_offset = { 0, PLANE0_DATA_OFFSET },
+       },
+};
+
+/* There are 2 multiplanar formats in the list */
+#define VIVID_MPLANAR_FORMATS 2
+
+const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat)
+{
+       const struct vivid_fmt *fmt;
+       unsigned k;
+
+       for (k = 0; k < ARRAY_SIZE(vivid_formats); k++) {
+               fmt = &vivid_formats[k];
+               if (fmt->fourcc == pixelformat)
+                       if (fmt->planes == 1 || dev->multiplanar)
+                               return fmt;
+       }
+
+       return NULL;
+}
+
+bool vivid_vid_can_loop(struct vivid_dev *dev)
+{
+       if (dev->src_rect.width != dev->sink_rect.width ||
+           dev->src_rect.height != dev->sink_rect.height)
+               return false;
+       if (dev->fmt_cap->fourcc != dev->fmt_out->fourcc)
+               return false;
+       if (dev->field_cap != dev->field_out)
+               return false;
+       if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) {
+               if (!(dev->std_cap & V4L2_STD_525_60) !=
+                   !(dev->std_out & V4L2_STD_525_60))
+                       return false;
+               return true;
+       }
+       if (vivid_is_hdmi_cap(dev) && vivid_is_hdmi_out(dev))
+               return true;
+       return false;
+}
+
+void vivid_send_source_change(struct vivid_dev *dev, unsigned type)
+{
+       struct v4l2_event ev = {
+               .type = V4L2_EVENT_SOURCE_CHANGE,
+               .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+       };
+       unsigned i;
+
+       for (i = 0; i < dev->num_inputs; i++) {
+               ev.id = i;
+               if (dev->input_type[i] == type) {
+                       if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap)
+                               v4l2_event_queue(&dev->vid_cap_dev, &ev);
+                       if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap)
+                               v4l2_event_queue(&dev->vbi_cap_dev, &ev);
+               }
+       }
+}
+
+/*
+ * Conversion function that converts a single-planar format to a
+ * single-plane multiplanar format.
+ */
+void fmt_sp2mp(const struct v4l2_format *sp_fmt, struct v4l2_format *mp_fmt)
+{
+       struct v4l2_pix_format_mplane *mp = &mp_fmt->fmt.pix_mp;
+       struct v4l2_plane_pix_format *ppix = &mp->plane_fmt[0];
+       const struct v4l2_pix_format *pix = &sp_fmt->fmt.pix;
+       bool is_out = sp_fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+       memset(mp->reserved, 0, sizeof(mp->reserved));
+       mp_fmt->type = is_out ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
+                          V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+       mp->width = pix->width;
+       mp->height = pix->height;
+       mp->pixelformat = pix->pixelformat;
+       mp->field = pix->field;
+       mp->colorspace = pix->colorspace;
+       mp->num_planes = 1;
+       mp->flags = pix->flags;
+       ppix->sizeimage = pix->sizeimage;
+       ppix->bytesperline = pix->bytesperline;
+       memset(ppix->reserved, 0, sizeof(ppix->reserved));
+}
+
+int fmt_sp2mp_func(struct file *file, void *priv,
+               struct v4l2_format *f, fmtfunc func)
+{
+       struct v4l2_format fmt;
+       struct v4l2_pix_format_mplane *mp = &fmt.fmt.pix_mp;
+       struct v4l2_plane_pix_format *ppix = &mp->plane_fmt[0];
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       int ret;
+
+       /* Converts to a mplane format */
+       fmt_sp2mp(f, &fmt);
+       /* Passes it to the generic mplane format function */
+       ret = func(file, priv, &fmt);
+       /* Copies back the mplane data to the single plane format */
+       pix->width = mp->width;
+       pix->height = mp->height;
+       pix->pixelformat = mp->pixelformat;
+       pix->field = mp->field;
+       pix->colorspace = mp->colorspace;
+       pix->sizeimage = ppix->sizeimage;
+       pix->bytesperline = ppix->bytesperline;
+       pix->flags = mp->flags;
+       return ret;
+}
+
+/* v4l2_rect helper function: copy the width/height values */
+void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size)
+{
+       r->width = size->width;
+       r->height = size->height;
+}
+
+/* v4l2_rect helper function: width and height of r should be >= min_size */
+void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size)
+{
+       if (r->width < min_size->width)
+               r->width = min_size->width;
+       if (r->height < min_size->height)
+               r->height = min_size->height;
+}
+
+/* v4l2_rect helper function: width and height of r should be <= max_size */
+void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size)
+{
+       if (r->width > max_size->width)
+               r->width = max_size->width;
+       if (r->height > max_size->height)
+               r->height = max_size->height;
+}
+
+/* v4l2_rect helper function: r should be inside boundary */
+void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary)
+{
+       rect_set_max_size(r, boundary);
+       if (r->left < boundary->left)
+               r->left = boundary->left;
+       if (r->top < boundary->top)
+               r->top = boundary->top;
+       if (r->left + r->width > boundary->width)
+               r->left = boundary->width - r->width;
+       if (r->top + r->height > boundary->height)
+               r->top = boundary->height - r->height;
+}
+
+/* v4l2_rect helper function: return true if r1 has the same size as r2 */
+bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
+{
+       return r1->width == r2->width && r1->height == r2->height;
+}
+
+/* v4l2_rect helper function: calculate the intersection of two rects */
+struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b)
+{
+       struct v4l2_rect r;
+       int right, bottom;
+
+       r.top = max(a->top, b->top);
+       r.left = max(a->left, b->left);
+       bottom = min(a->top + a->height, b->top + b->height);
+       right = min(a->left + a->width, b->left + b->width);
+       r.height = max(0, bottom - r.top);
+       r.width = max(0, right - r.left);
+       return r;
+}
+
+/*
+ * v4l2_rect helper function: scale rect r by to->width / from->width and
+ * to->height / from->height.
+ */
+void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from,
+                                    const struct v4l2_rect *to)
+{
+       if (from->width == 0 || from->height == 0) {
+               r->left = r->top = r->width = r->height = 0;
+               return;
+       }
+       r->left = (((r->left - from->left) * to->width) / from->width) & ~1;
+       r->width = ((r->width * to->width) / from->width) & ~1;
+       r->top = ((r->top - from->top) * to->height) / from->height;
+       r->height = (r->height * to->height) / from->height;
+}
+
+bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
+{
+       /*
+        * IF the left side of r1 is to the right of the right side of r2 OR
+        *    the left side of r2 is to the right of the right side of r1 THEN
+        * they do not overlap.
+        */
+       if (r1->left >= r2->left + r2->width ||
+           r2->left >= r1->left + r1->width)
+               return false;
+       /*
+        * IF the top side of r1 is below the bottom of r2 OR
+        *    the top side of r2 is below the bottom of r1 THEN
+        * they do not overlap.
+        */
+       if (r1->top >= r2->top + r2->height ||
+           r2->top >= r1->top + r1->height)
+               return false;
+       return true;
+}
+int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r)
+{
+       unsigned w = r->width;
+       unsigned h = r->height;
+
+       if (!(flags & V4L2_SEL_FLAG_LE)) {
+               w++;
+               h++;
+               if (w < 2)
+                       w = 2;
+               if (h < 2)
+                       h = 2;
+       }
+       if (!(flags & V4L2_SEL_FLAG_GE)) {
+               if (w > MAX_WIDTH)
+                       w = MAX_WIDTH;
+               if (h > MAX_HEIGHT)
+                       h = MAX_HEIGHT;
+       }
+       w = w & ~1;
+       h = h & ~1;
+       if (w < 2 || h < 2)
+               return -ERANGE;
+       if (w > MAX_WIDTH || h > MAX_HEIGHT)
+               return -ERANGE;
+       if (r->top < 0)
+               r->top = 0;
+       if (r->left < 0)
+               r->left = 0;
+       r->left &= ~1;
+       r->top &= ~1;
+       if (r->left + w > MAX_WIDTH)
+               r->left = MAX_WIDTH - w;
+       if (r->top + h > MAX_HEIGHT)
+               r->top = MAX_HEIGHT - h;
+       if ((flags & (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE)) ==
+                       (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE) &&
+           (r->width != w || r->height != h))
+               return -ERANGE;
+       r->width = w;
+       r->height = h;
+       return 0;
+}
+
+int vivid_enum_fmt_vid(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct vivid_fmt *fmt;
+
+       if (f->index >= ARRAY_SIZE(vivid_formats) -
+           (dev->multiplanar ? 0 : VIVID_MPLANAR_FORMATS))
+               return -EINVAL;
+
+       fmt = &vivid_formats[f->index];
+
+       strlcpy(f->description, fmt->name, sizeof(f->description));
+       f->pixelformat = fmt->fourcc;
+       return 0;
+}
+
+int vidioc_enum_fmt_vid_mplane(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!dev->multiplanar)
+               return -ENOTTY;
+       return vivid_enum_fmt_vid(file, priv, f);
+}
+
+int vidioc_enum_fmt_vid(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (dev->multiplanar)
+               return -ENOTTY;
+       return vivid_enum_fmt_vid(file, priv, f);
+}
+
+int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX) {
+               if (!vivid_is_sdtv_cap(dev))
+                       return -ENODATA;
+               *id = dev->std_cap;
+       } else {
+               if (!vivid_is_svid_out(dev))
+                       return -ENODATA;
+               *id = dev->std_out;
+       }
+       return 0;
+}
+
+int vidioc_g_dv_timings(struct file *file, void *_fh,
+                                   struct v4l2_dv_timings *timings)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX) {
+               if (!vivid_is_hdmi_cap(dev))
+                       return -ENODATA;
+               *timings = dev->dv_timings_cap;
+       } else {
+               if (!vivid_is_hdmi_out(dev))
+                       return -ENODATA;
+               *timings = dev->dv_timings_out;
+       }
+       return 0;
+}
+
+int vidioc_enum_dv_timings(struct file *file, void *_fh,
+                                   struct v4l2_enum_dv_timings *timings)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX) {
+               if (!vivid_is_hdmi_cap(dev))
+                       return -ENODATA;
+       } else {
+               if (!vivid_is_hdmi_out(dev))
+                       return -ENODATA;
+       }
+       return v4l2_enum_dv_timings_cap(timings, &vivid_dv_timings_cap,
+                       NULL, NULL);
+}
+
+int vidioc_dv_timings_cap(struct file *file, void *_fh,
+                                   struct v4l2_dv_timings_cap *cap)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+
+       if (vdev->vfl_dir == VFL_DIR_RX) {
+               if (!vivid_is_hdmi_cap(dev))
+                       return -ENODATA;
+       } else {
+               if (!vivid_is_hdmi_out(dev))
+                       return -ENODATA;
+       }
+       *cap = vivid_dv_timings_cap;
+       return 0;
+}
+
+int vidioc_g_edid(struct file *file, void *_fh,
+                        struct v4l2_edid *edid)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
+
+       memset(edid->reserved, 0, sizeof(edid->reserved));
+       if (vdev->vfl_dir == VFL_DIR_RX) {
+               if (edid->pad >= dev->num_inputs)
+                       return -EINVAL;
+               if (dev->input_type[edid->pad] != HDMI)
+                       return -EINVAL;
+       } else {
+               if (edid->pad >= dev->num_outputs)
+                       return -EINVAL;
+               if (dev->output_type[edid->pad] != HDMI)
+                       return -EINVAL;
+       }
+       if (edid->start_block == 0 && edid->blocks == 0) {
+               edid->blocks = dev->edid_blocks;
+               return 0;
+       }
+       if (dev->edid_blocks == 0)
+               return -ENODATA;
+       if (edid->start_block >= dev->edid_blocks)
+               return -EINVAL;
+       if (edid->start_block + edid->blocks > dev->edid_blocks)
+               edid->blocks = dev->edid_blocks - edid->start_block;
+       memcpy(edid->edid, dev->edid, edid->blocks * 128);
+       return 0;
+}
diff --git a/drivers/media/platform/vivid/vivid-vid-common.h b/drivers/media/platform/vivid/vivid-vid-common.h
new file mode 100644 (file)
index 0000000..3ec4fa8
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * vivid-vid-common.h - common video support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_VID_COMMON_H_
+#define _VIVID_VID_COMMON_H_
+
+typedef int (*fmtfunc)(struct file *file, void *priv, struct v4l2_format *f);
+
+/*
+ * Conversion function that converts a single-planar format to a
+ * single-plane multiplanar format.
+ */
+void fmt_sp2mp(const struct v4l2_format *sp_fmt, struct v4l2_format *mp_fmt);
+int fmt_sp2mp_func(struct file *file, void *priv,
+               struct v4l2_format *f, fmtfunc func);
+
+extern const struct v4l2_dv_timings_cap vivid_dv_timings_cap;
+
+const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat);
+
+bool vivid_vid_can_loop(struct vivid_dev *dev);
+void vivid_send_source_change(struct vivid_dev *dev, unsigned type);
+
+bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2);
+void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size);
+void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size);
+void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size);
+void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary);
+bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2);
+struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b);
+void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from,
+                                    const struct v4l2_rect *to);
+int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r);
+
+int vivid_enum_fmt_vid(struct file *file, void  *priv, struct v4l2_fmtdesc *f);
+int vidioc_enum_fmt_vid_mplane(struct file *file, void  *priv, struct v4l2_fmtdesc *f);
+int vidioc_enum_fmt_vid(struct file *file, void  *priv, struct v4l2_fmtdesc *f);
+int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id);
+int vidioc_g_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings);
+int vidioc_enum_dv_timings(struct file *file, void *_fh, struct v4l2_enum_dv_timings *timings);
+int vidioc_dv_timings_cap(struct file *file, void *_fh, struct v4l2_dv_timings_cap *cap);
+int vidioc_g_edid(struct file *file, void *_fh, struct v4l2_edid *edid);
+int vidioc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
new file mode 100644 (file)
index 0000000..69c2dbd
--- /dev/null
@@ -0,0 +1,1146 @@
+/*
+ * vivid-vid-out.c - video output support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dv-timings.h>
+
+#include "vivid-core.h"
+#include "vivid-vid-common.h"
+#include "vivid-kthread-out.h"
+#include "vivid-vid-out.h"
+
+static int vid_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+                      unsigned *nbuffers, unsigned *nplanes,
+                      unsigned sizes[], void *alloc_ctxs[])
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+       unsigned planes = dev->fmt_out->planes;
+       unsigned h = dev->fmt_out_rect.height;
+       unsigned size = dev->bytesperline_out[0] * h;
+
+       if (dev->field_out == V4L2_FIELD_ALTERNATE) {
+               /*
+                * You cannot use write() with FIELD_ALTERNATE since the field
+                * information (TOP/BOTTOM) cannot be passed to the kernel.
+                */
+               if (vb2_fileio_is_active(vq))
+                       return -EINVAL;
+       }
+
+       if (dev->queue_setup_error) {
+               /*
+                * Error injection: test what happens if queue_setup() returns
+                * an error.
+                */
+               dev->queue_setup_error = false;
+               return -EINVAL;
+       }
+
+       if (fmt) {
+               const struct v4l2_pix_format_mplane *mp;
+               struct v4l2_format mp_fmt;
+
+               if (!V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) {
+                       fmt_sp2mp(fmt, &mp_fmt);
+                       fmt = &mp_fmt;
+               }
+               mp = &fmt->fmt.pix_mp;
+               /*
+                * Check if the number of planes in the specified format match
+                * the number of planes in the current format. You can't mix that.
+                */
+               if (mp->num_planes != planes)
+                       return -EINVAL;
+               sizes[0] = mp->plane_fmt[0].sizeimage;
+               if (planes == 2) {
+                       sizes[1] = mp->plane_fmt[1].sizeimage;
+                       if (sizes[0] < dev->bytesperline_out[0] * h ||
+                           sizes[1] < dev->bytesperline_out[1] * h)
+                               return -EINVAL;
+               } else if (sizes[0] < size) {
+                       return -EINVAL;
+               }
+       } else {
+               if (planes == 2) {
+                       sizes[0] = dev->bytesperline_out[0] * h;
+                       sizes[1] = dev->bytesperline_out[1] * h;
+               } else {
+                       sizes[0] = size;
+               }
+       }
+
+       if (vq->num_buffers + *nbuffers < 2)
+               *nbuffers = 2 - vq->num_buffers;
+
+       *nplanes = planes;
+
+       /*
+        * videobuf2-vmalloc allocator is context-less so no need to set
+        * alloc_ctxs array.
+        */
+
+       if (planes == 2)
+               dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__,
+                       *nbuffers, sizes[0], sizes[1]);
+       else
+               dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__,
+                       *nbuffers, sizes[0]);
+       return 0;
+}
+
+static int vid_out_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       unsigned long size;
+       unsigned planes = dev->fmt_out->planes;
+       unsigned p;
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       if (WARN_ON(NULL == dev->fmt_out))
+               return -EINVAL;
+
+       if (dev->buf_prepare_error) {
+               /*
+                * Error injection: test what happens if buf_prepare() returns
+                * an error.
+                */
+               dev->buf_prepare_error = false;
+               return -EINVAL;
+       }
+
+       if (dev->field_out != V4L2_FIELD_ALTERNATE)
+               vb->v4l2_buf.field = dev->field_out;
+       else if (vb->v4l2_buf.field != V4L2_FIELD_TOP &&
+                vb->v4l2_buf.field != V4L2_FIELD_BOTTOM)
+               return -EINVAL;
+
+       for (p = 0; p < planes; p++) {
+               size = dev->bytesperline_out[p] * dev->fmt_out_rect.height +
+                       vb->v4l2_planes[p].data_offset;
+
+               if (vb2_get_plane_payload(vb, p) < size) {
+                       dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %lu)\n",
+                                       __func__, p, vb2_get_plane_payload(vb, p), size);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static void vid_out_buf_queue(struct vb2_buffer *vb)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb);
+
+       dprintk(dev, 1, "%s\n", __func__);
+
+       spin_lock(&dev->slock);
+       list_add_tail(&buf->list, &dev->vid_out_active);
+       spin_unlock(&dev->slock);
+}
+
+static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+       int err;
+
+       if (vb2_is_streaming(&dev->vb_vid_cap_q))
+               dev->can_loop_video = vivid_vid_can_loop(dev);
+
+       if (dev->kthread_vid_out)
+               return 0;
+
+       dev->vid_out_seq_count = 0;
+       dprintk(dev, 1, "%s\n", __func__);
+       if (dev->start_streaming_error) {
+               dev->start_streaming_error = false;
+               err = -EINVAL;
+       } else {
+               err = vivid_start_generating_vid_out(dev, &dev->vid_out_streaming);
+       }
+       if (err) {
+               struct vivid_buffer *buf, *tmp;
+
+               list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) {
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+               }
+       }
+       return err;
+}
+
+/* abort streaming and wait for last buffer */
+static void vid_out_stop_streaming(struct vb2_queue *vq)
+{
+       struct vivid_dev *dev = vb2_get_drv_priv(vq);
+
+       dprintk(dev, 1, "%s\n", __func__);
+       vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming);
+       dev->can_loop_video = false;
+}
+
+const struct vb2_ops vivid_vid_out_qops = {
+       .queue_setup            = vid_out_queue_setup,
+       .buf_prepare            = vid_out_buf_prepare,
+       .buf_queue              = vid_out_buf_queue,
+       .start_streaming        = vid_out_start_streaming,
+       .stop_streaming         = vid_out_stop_streaming,
+       .wait_prepare           = vivid_unlock,
+       .wait_finish            = vivid_lock,
+};
+
+/*
+ * Called whenever the format has to be reset which can occur when
+ * changing outputs, standard, timings, etc.
+ */
+void vivid_update_format_out(struct vivid_dev *dev)
+{
+       struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
+       unsigned size;
+
+       switch (dev->output_type[dev->output]) {
+       case SVID:
+       default:
+               dev->field_out = dev->tv_field_out;
+               dev->sink_rect.width = 720;
+               if (dev->std_out & V4L2_STD_525_60) {
+                       dev->sink_rect.height = 480;
+                       dev->timeperframe_vid_out = (struct v4l2_fract) { 1001, 30000 };
+                       dev->service_set_out = V4L2_SLICED_CAPTION_525;
+               } else {
+                       dev->sink_rect.height = 576;
+                       dev->timeperframe_vid_out = (struct v4l2_fract) { 1000, 25000 };
+                       dev->service_set_out = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B;
+               }
+               dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;
+               break;
+       case HDMI:
+               dev->sink_rect.width = bt->width;
+               dev->sink_rect.height = bt->height;
+               size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
+               dev->timeperframe_vid_out = (struct v4l2_fract) {
+                       size / 100, (u32)bt->pixelclock / 100
+               };
+               if (bt->interlaced)
+                       dev->field_out = V4L2_FIELD_ALTERNATE;
+               else
+                       dev->field_out = V4L2_FIELD_NONE;
+               if (!dev->dvi_d_out && (bt->standards & V4L2_DV_BT_STD_CEA861)) {
+                       if (bt->width == 720 && bt->height <= 576)
+                               dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;
+                       else
+                               dev->colorspace_out = V4L2_COLORSPACE_REC709;
+               } else {
+                       dev->colorspace_out = V4L2_COLORSPACE_SRGB;
+               }
+               break;
+       }
+       dev->compose_out = dev->sink_rect;
+       dev->compose_bounds_out = dev->sink_rect;
+       dev->crop_out = dev->compose_out;
+       if (V4L2_FIELD_HAS_T_OR_B(dev->field_out))
+               dev->crop_out.height /= 2;
+       dev->fmt_out_rect = dev->crop_out;
+       dev->bytesperline_out[0] = (dev->sink_rect.width * dev->fmt_out->depth) / 8;
+       if (dev->fmt_out->planes == 2)
+               dev->bytesperline_out[1] = (dev->sink_rect.width * dev->fmt_out->depth) / 8;
+}
+
+/* Map the field to something that is valid for the current output */
+static enum v4l2_field vivid_field_out(struct vivid_dev *dev, enum v4l2_field field)
+{
+       if (vivid_is_svid_out(dev)) {
+               switch (field) {
+               case V4L2_FIELD_INTERLACED_TB:
+               case V4L2_FIELD_INTERLACED_BT:
+               case V4L2_FIELD_SEQ_TB:
+               case V4L2_FIELD_SEQ_BT:
+               case V4L2_FIELD_ALTERNATE:
+                       return field;
+               case V4L2_FIELD_INTERLACED:
+               default:
+                       return V4L2_FIELD_INTERLACED;
+               }
+       }
+       if (vivid_is_hdmi_out(dev))
+               return dev->dv_timings_out.bt.interlaced ? V4L2_FIELD_ALTERNATE :
+                                                      V4L2_FIELD_NONE;
+       return V4L2_FIELD_NONE;
+}
+
+static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev)
+{
+       if (vivid_is_svid_out(dev))
+               return (dev->std_out & V4L2_STD_525_60) ?
+                       TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;
+
+       if (vivid_is_hdmi_out(dev) &&
+           dev->sink_rect.width == 720 && dev->sink_rect.height <= 576)
+               return dev->sink_rect.height == 480 ?
+                       TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL;
+
+       return TPG_PIXEL_ASPECT_SQUARE;
+}
+
+int vivid_g_fmt_vid_out(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
+       unsigned p;
+
+       mp->width        = dev->fmt_out_rect.width;
+       mp->height       = dev->fmt_out_rect.height;
+       mp->field        = dev->field_out;
+       mp->pixelformat  = dev->fmt_out->fourcc;
+       mp->colorspace   = dev->colorspace_out;
+       mp->num_planes = dev->fmt_out->planes;
+       for (p = 0; p < mp->num_planes; p++) {
+               mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p];
+               mp->plane_fmt[p].sizeimage =
+                       mp->plane_fmt[p].bytesperline * mp->height;
+       }
+       return 0;
+}
+
+int vivid_try_fmt_vid_out(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
+       struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
+       struct v4l2_plane_pix_format *pfmt = mp->plane_fmt;
+       const struct vivid_fmt *fmt;
+       unsigned bytesperline, max_bpl;
+       unsigned factor = 1;
+       unsigned w, h;
+       unsigned p;
+
+       fmt = vivid_get_format(dev, mp->pixelformat);
+       if (!fmt) {
+               dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
+                       mp->pixelformat);
+               mp->pixelformat = V4L2_PIX_FMT_YUYV;
+               fmt = vivid_get_format(dev, mp->pixelformat);
+       }
+
+       mp->field = vivid_field_out(dev, mp->field);
+       if (vivid_is_svid_out(dev)) {
+               w = 720;
+               h = (dev->std_out & V4L2_STD_525_60) ? 480 : 576;
+       } else {
+               w = dev->sink_rect.width;
+               h = dev->sink_rect.height;
+       }
+       if (V4L2_FIELD_HAS_T_OR_B(mp->field))
+               factor = 2;
+       if (!dev->has_scaler_out && !dev->has_crop_out && !dev->has_compose_out) {
+               mp->width = w;
+               mp->height = h / factor;
+       } else {
+               struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor };
+
+               rect_set_min_size(&r, &vivid_min_rect);
+               rect_set_max_size(&r, &vivid_max_rect);
+               if (dev->has_scaler_out && !dev->has_crop_out) {
+                       struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h };
+
+                       rect_set_max_size(&r, &max_r);
+               } else if (!dev->has_scaler_out && dev->has_compose_out && !dev->has_crop_out) {
+                       rect_set_max_size(&r, &dev->sink_rect);
+               } else if (!dev->has_scaler_out && !dev->has_compose_out) {
+                       rect_set_min_size(&r, &dev->sink_rect);
+               }
+               mp->width = r.width;
+               mp->height = r.height / factor;
+       }
+
+       /* This driver supports custom bytesperline values */
+
+       /* Calculate the minimum supported bytesperline value */
+       bytesperline = (mp->width * fmt->depth) >> 3;
+       /* Calculate the maximum supported bytesperline value */
+       max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3;
+       mp->num_planes = fmt->planes;
+       for (p = 0; p < mp->num_planes; p++) {
+               if (pfmt[p].bytesperline > max_bpl)
+                       pfmt[p].bytesperline = max_bpl;
+               if (pfmt[p].bytesperline < bytesperline)
+                       pfmt[p].bytesperline = bytesperline;
+               pfmt[p].sizeimage = pfmt[p].bytesperline * mp->height;
+               memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
+       }
+       if (vivid_is_svid_out(dev))
+               mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
+       else if (dev->dvi_d_out || !(bt->standards & V4L2_DV_BT_STD_CEA861))
+               mp->colorspace = V4L2_COLORSPACE_SRGB;
+       else if (bt->width == 720 && bt->height <= 576)
+               mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
+       else if (mp->colorspace != V4L2_COLORSPACE_SMPTE170M &&
+                mp->colorspace != V4L2_COLORSPACE_REC709 &&
+                mp->colorspace != V4L2_COLORSPACE_SRGB)
+               mp->colorspace = V4L2_COLORSPACE_REC709;
+       memset(mp->reserved, 0, sizeof(mp->reserved));
+       return 0;
+}
+
+int vivid_s_fmt_vid_out(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_rect *crop = &dev->crop_out;
+       struct v4l2_rect *compose = &dev->compose_out;
+       struct vb2_queue *q = &dev->vb_vid_out_q;
+       int ret = vivid_try_fmt_vid_out(file, priv, f);
+       unsigned factor = 1;
+
+       if (ret < 0)
+               return ret;
+
+       if (vb2_is_busy(q) &&
+           (vivid_is_svid_out(dev) ||
+            mp->width != dev->fmt_out_rect.width ||
+            mp->height != dev->fmt_out_rect.height ||
+            mp->pixelformat != dev->fmt_out->fourcc ||
+            mp->field != dev->field_out)) {
+               dprintk(dev, 1, "%s device busy\n", __func__);
+               return -EBUSY;
+       }
+
+       /*
+        * Allow for changing the colorspace on the fly. Useful for testing
+        * purposes, and it is something that HDMI transmitters are able
+        * to do.
+        */
+       if (vb2_is_busy(q))
+               goto set_colorspace;
+
+       dev->fmt_out = vivid_get_format(dev, mp->pixelformat);
+       if (V4L2_FIELD_HAS_T_OR_B(mp->field))
+               factor = 2;
+
+       if (dev->has_scaler_out || dev->has_crop_out || dev->has_compose_out) {
+               struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+
+               if (dev->has_scaler_out) {
+                       if (dev->has_crop_out)
+                               rect_map_inside(crop, &r);
+                       else
+                               *crop = r;
+                       if (dev->has_compose_out && !dev->has_crop_out) {
+                               struct v4l2_rect min_r = {
+                                       0, 0,
+                                       r.width / MAX_ZOOM,
+                                       factor * r.height / MAX_ZOOM
+                               };
+                               struct v4l2_rect max_r = {
+                                       0, 0,
+                                       r.width * MAX_ZOOM,
+                                       factor * r.height * MAX_ZOOM
+                               };
+
+                               rect_set_min_size(compose, &min_r);
+                               rect_set_max_size(compose, &max_r);
+                               rect_map_inside(compose, &dev->compose_bounds_out);
+                       } else if (dev->has_compose_out) {
+                               struct v4l2_rect min_r = {
+                                       0, 0,
+                                       crop->width / MAX_ZOOM,
+                                       factor * crop->height / MAX_ZOOM
+                               };
+                               struct v4l2_rect max_r = {
+                                       0, 0,
+                                       crop->width * MAX_ZOOM,
+                                       factor * crop->height * MAX_ZOOM
+                               };
+
+                               rect_set_min_size(compose, &min_r);
+                               rect_set_max_size(compose, &max_r);
+                               rect_map_inside(compose, &dev->compose_bounds_out);
+                       }
+               } else if (dev->has_compose_out && !dev->has_crop_out) {
+                       rect_set_size_to(crop, &r);
+                       r.height *= factor;
+                       rect_set_size_to(compose, &r);
+                       rect_map_inside(compose, &dev->compose_bounds_out);
+               } else if (!dev->has_compose_out) {
+                       rect_map_inside(crop, &r);
+                       r.height /= factor;
+                       rect_set_size_to(compose, &r);
+               } else {
+                       r.height *= factor;
+                       rect_set_max_size(compose, &r);
+                       rect_map_inside(compose, &dev->compose_bounds_out);
+                       crop->top *= factor;
+                       crop->height *= factor;
+                       rect_set_size_to(crop, compose);
+                       rect_map_inside(crop, &r);
+                       crop->top /= factor;
+                       crop->height /= factor;
+               }
+       } else {
+               struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+
+               rect_set_size_to(crop, &r);
+               r.height /= factor;
+               rect_set_size_to(compose, &r);
+       }
+
+       dev->fmt_out_rect.width = mp->width;
+       dev->fmt_out_rect.height = mp->height;
+       dev->bytesperline_out[0] = mp->plane_fmt[0].bytesperline;
+       if (mp->num_planes > 1)
+               dev->bytesperline_out[1] = mp->plane_fmt[1].bytesperline;
+       dev->field_out = mp->field;
+       if (vivid_is_svid_out(dev))
+               dev->tv_field_out = mp->field;
+
+set_colorspace:
+       dev->colorspace_out = mp->colorspace;
+       if (dev->loop_video) {
+               vivid_send_source_change(dev, SVID);
+               vivid_send_source_change(dev, HDMI);
+       }
+       return 0;
+}
+
+int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!dev->multiplanar)
+               return -ENOTTY;
+       return vivid_g_fmt_vid_out(file, priv, f);
+}
+
+int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!dev->multiplanar)
+               return -ENOTTY;
+       return vivid_try_fmt_vid_out(file, priv, f);
+}
+
+int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!dev->multiplanar)
+               return -ENOTTY;
+       return vivid_s_fmt_vid_out(file, priv, f);
+}
+
+int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (dev->multiplanar)
+               return -ENOTTY;
+       return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_out);
+}
+
+int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (dev->multiplanar)
+               return -ENOTTY;
+       return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_out);
+}
+
+int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (dev->multiplanar)
+               return -ENOTTY;
+       return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_out);
+}
+
+int vivid_vid_out_g_selection(struct file *file, void *priv,
+                             struct v4l2_selection *sel)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!dev->has_crop_out && !dev->has_compose_out)
+               return -ENOTTY;
+       if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+
+       sel->r.left = sel->r.top = 0;
+       switch (sel->target) {
+       case V4L2_SEL_TGT_CROP:
+               if (!dev->has_crop_out)
+                       return -EINVAL;
+               sel->r = dev->crop_out;
+               break;
+       case V4L2_SEL_TGT_CROP_DEFAULT:
+               if (!dev->has_crop_out)
+                       return -EINVAL;
+               sel->r = dev->fmt_out_rect;
+               break;
+       case V4L2_SEL_TGT_CROP_BOUNDS:
+               if (!dev->has_compose_out)
+                       return -EINVAL;
+               sel->r = vivid_max_rect;
+               break;
+       case V4L2_SEL_TGT_COMPOSE:
+               if (!dev->has_compose_out)
+                       return -EINVAL;
+               sel->r = dev->compose_out;
+               break;
+       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+               if (!dev->has_compose_out)
+                       return -EINVAL;
+               sel->r = dev->sink_rect;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       struct v4l2_rect *crop = &dev->crop_out;
+       struct v4l2_rect *compose = &dev->compose_out;
+       unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_out) ? 2 : 1;
+       int ret;
+
+       if (!dev->has_crop_out && !dev->has_compose_out)
+               return -ENOTTY;
+       if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+
+       switch (s->target) {
+       case V4L2_SEL_TGT_CROP:
+               if (!dev->has_crop_out)
+                       return -EINVAL;
+               ret = vivid_vid_adjust_sel(s->flags, &s->r);
+               if (ret)
+                       return ret;
+               rect_set_min_size(&s->r, &vivid_min_rect);
+               rect_set_max_size(&s->r, &dev->fmt_out_rect);
+               if (dev->has_scaler_out) {
+                       struct v4l2_rect max_rect = {
+                               0, 0,
+                               dev->sink_rect.width * MAX_ZOOM,
+                               (dev->sink_rect.height / factor) * MAX_ZOOM
+                       };
+
+                       rect_set_max_size(&s->r, &max_rect);
+                       if (dev->has_compose_out) {
+                               struct v4l2_rect min_rect = {
+                                       0, 0,
+                                       s->r.width / MAX_ZOOM,
+                                       (s->r.height * factor) / MAX_ZOOM
+                               };
+                               struct v4l2_rect max_rect = {
+                                       0, 0,
+                                       s->r.width * MAX_ZOOM,
+                                       (s->r.height * factor) * MAX_ZOOM
+                               };
+
+                               rect_set_min_size(compose, &min_rect);
+                               rect_set_max_size(compose, &max_rect);
+                               rect_map_inside(compose, &dev->compose_bounds_out);
+                       }
+               } else if (dev->has_compose_out) {
+                       s->r.top *= factor;
+                       s->r.height *= factor;
+                       rect_set_max_size(&s->r, &dev->sink_rect);
+                       rect_set_size_to(compose, &s->r);
+                       rect_map_inside(compose, &dev->compose_bounds_out);
+                       s->r.top /= factor;
+                       s->r.height /= factor;
+               } else {
+                       rect_set_size_to(&s->r, &dev->sink_rect);
+                       s->r.height /= factor;
+               }
+               rect_map_inside(&s->r, &dev->fmt_out_rect);
+               *crop = s->r;
+               break;
+       case V4L2_SEL_TGT_COMPOSE:
+               if (!dev->has_compose_out)
+                       return -EINVAL;
+               ret = vivid_vid_adjust_sel(s->flags, &s->r);
+               if (ret)
+                       return ret;
+               rect_set_min_size(&s->r, &vivid_min_rect);
+               rect_set_max_size(&s->r, &dev->sink_rect);
+               rect_map_inside(&s->r, &dev->compose_bounds_out);
+               s->r.top /= factor;
+               s->r.height /= factor;
+               if (dev->has_scaler_out) {
+                       struct v4l2_rect fmt = dev->fmt_out_rect;
+                       struct v4l2_rect max_rect = {
+                               0, 0,
+                               s->r.width * MAX_ZOOM,
+                               s->r.height * MAX_ZOOM
+                       };
+                       struct v4l2_rect min_rect = {
+                               0, 0,
+                               s->r.width / MAX_ZOOM,
+                               s->r.height / MAX_ZOOM
+                       };
+
+                       rect_set_min_size(&fmt, &min_rect);
+                       if (!dev->has_crop_out)
+                               rect_set_max_size(&fmt, &max_rect);
+                       if (!rect_same_size(&dev->fmt_out_rect, &fmt) &&
+                           vb2_is_busy(&dev->vb_vid_out_q))
+                               return -EBUSY;
+                       if (dev->has_crop_out) {
+                               rect_set_min_size(crop, &min_rect);
+                               rect_set_max_size(crop, &max_rect);
+                       }
+                       dev->fmt_out_rect = fmt;
+               } else if (dev->has_crop_out) {
+                       struct v4l2_rect fmt = dev->fmt_out_rect;
+
+                       rect_set_min_size(&fmt, &s->r);
+                       if (!rect_same_size(&dev->fmt_out_rect, &fmt) &&
+                           vb2_is_busy(&dev->vb_vid_out_q))
+                               return -EBUSY;
+                       dev->fmt_out_rect = fmt;
+                       rect_set_size_to(crop, &s->r);
+                       rect_map_inside(crop, &dev->fmt_out_rect);
+               } else {
+                       if (!rect_same_size(&s->r, &dev->fmt_out_rect) &&
+                           vb2_is_busy(&dev->vb_vid_out_q))
+                               return -EBUSY;
+                       rect_set_size_to(&dev->fmt_out_rect, &s->r);
+                       rect_set_size_to(crop, &s->r);
+                       crop->height /= factor;
+                       rect_map_inside(crop, &dev->fmt_out_rect);
+               }
+               s->r.top *= factor;
+               s->r.height *= factor;
+               if (dev->bitmap_out && (compose->width != s->r.width ||
+                                       compose->height != s->r.height)) {
+                       kfree(dev->bitmap_out);
+                       dev->bitmap_out = NULL;
+               }
+               *compose = s->r;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int vivid_vid_out_cropcap(struct file *file, void *priv,
+                             struct v4l2_cropcap *cap)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+
+       switch (vivid_get_pixel_aspect(dev)) {
+       case TPG_PIXEL_ASPECT_NTSC:
+               cap->pixelaspect.numerator = 11;
+               cap->pixelaspect.denominator = 10;
+               break;
+       case TPG_PIXEL_ASPECT_PAL:
+               cap->pixelaspect.numerator = 54;
+               cap->pixelaspect.denominator = 59;
+               break;
+       case TPG_PIXEL_ASPECT_SQUARE:
+               cap->pixelaspect.numerator = 1;
+               cap->pixelaspect.denominator = 1;
+               break;
+       }
+       return 0;
+}
+
+int vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct v4l2_rect *compose = &dev->compose_out;
+       struct v4l2_window *win = &f->fmt.win;
+       unsigned clipcount = win->clipcount;
+
+       if (!dev->has_fb)
+               return -EINVAL;
+       win->w.top = dev->overlay_out_top;
+       win->w.left = dev->overlay_out_left;
+       win->w.width = compose->width;
+       win->w.height = compose->height;
+       win->clipcount = dev->clipcount_out;
+       win->field = V4L2_FIELD_ANY;
+       win->chromakey = dev->chromakey_out;
+       win->global_alpha = dev->global_alpha_out;
+       if (clipcount > dev->clipcount_out)
+               clipcount = dev->clipcount_out;
+       if (dev->bitmap_out == NULL)
+               win->bitmap = NULL;
+       else if (win->bitmap) {
+               if (copy_to_user(win->bitmap, dev->bitmap_out,
+                   ((dev->compose_out.width + 7) / 8) * dev->compose_out.height))
+                       return -EFAULT;
+       }
+       if (clipcount && win->clips) {
+               if (copy_to_user(win->clips, dev->clips_out,
+                                clipcount * sizeof(dev->clips_out[0])))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct v4l2_rect *compose = &dev->compose_out;
+       struct v4l2_window *win = &f->fmt.win;
+       int i, j;
+
+       if (!dev->has_fb)
+               return -EINVAL;
+       win->w.left = clamp_t(int, win->w.left,
+                             -dev->display_width, dev->display_width);
+       win->w.top = clamp_t(int, win->w.top,
+                            -dev->display_height, dev->display_height);
+       win->w.width = compose->width;
+       win->w.height = compose->height;
+       /*
+        * It makes no sense for an OSD to overlay only top or bottom fields,
+        * so always set this to ANY.
+        */
+       win->field = V4L2_FIELD_ANY;
+       if (win->clipcount && !win->clips)
+               win->clipcount = 0;
+       if (win->clipcount > MAX_CLIPS)
+               win->clipcount = MAX_CLIPS;
+       if (win->clipcount) {
+               if (copy_from_user(dev->try_clips_out, win->clips,
+                                  win->clipcount * sizeof(dev->clips_out[0])))
+                       return -EFAULT;
+               for (i = 0; i < win->clipcount; i++) {
+                       struct v4l2_rect *r = &dev->try_clips_out[i].c;
+
+                       r->top = clamp_t(s32, r->top, 0, dev->display_height - 1);
+                       r->height = clamp_t(s32, r->height, 1, dev->display_height - r->top);
+                       r->left = clamp_t(u32, r->left, 0, dev->display_width - 1);
+                       r->width = clamp_t(u32, r->width, 1, dev->display_width - r->left);
+               }
+               /*
+                * Yeah, so sue me, it's an O(n^2) algorithm. But n is a small
+                * number and it's typically a one-time deal.
+                */
+               for (i = 0; i < win->clipcount - 1; i++) {
+                       struct v4l2_rect *r1 = &dev->try_clips_out[i].c;
+
+                       for (j = i + 1; j < win->clipcount; j++) {
+                               struct v4l2_rect *r2 = &dev->try_clips_out[j].c;
+
+                               if (rect_overlap(r1, r2))
+                                       return -EINVAL;
+                       }
+               }
+               if (copy_to_user(win->clips, dev->try_clips_out,
+                                win->clipcount * sizeof(dev->clips_out[0])))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+int vidioc_s_fmt_vid_out_overlay(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const struct v4l2_rect *compose = &dev->compose_out;
+       struct v4l2_window *win = &f->fmt.win;
+       int ret = vidioc_try_fmt_vid_out_overlay(file, priv, f);
+       unsigned bitmap_size = ((compose->width + 7) / 8) * compose->height;
+       unsigned clips_size = win->clipcount * sizeof(dev->clips_out[0]);
+       void *new_bitmap = NULL;
+
+       if (ret)
+               return ret;
+
+       if (win->bitmap) {
+               new_bitmap = memdup_user(win->bitmap, bitmap_size);
+
+               if (IS_ERR(new_bitmap))
+                       return PTR_ERR(new_bitmap);
+       }
+
+       dev->overlay_out_top = win->w.top;
+       dev->overlay_out_left = win->w.left;
+       kfree(dev->bitmap_out);
+       dev->bitmap_out = new_bitmap;
+       dev->clipcount_out = win->clipcount;
+       if (dev->clipcount_out)
+               memcpy(dev->clips_out, dev->try_clips_out, clips_size);
+       dev->chromakey_out = win->chromakey;
+       dev->global_alpha_out = win->global_alpha;
+       return ret;
+}
+
+int vivid_vid_out_overlay(struct file *file, void *fh, unsigned i)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (i && !dev->fmt_out->can_do_overlay) {
+               dprintk(dev, 1, "unsupported output format for output overlay\n");
+               return -EINVAL;
+       }
+
+       dev->overlay_out_enabled = i;
+       return 0;
+}
+
+int vivid_vid_out_g_fbuf(struct file *file, void *fh,
+                               struct v4l2_framebuffer *a)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       a->capability = V4L2_FBUF_CAP_EXTERNOVERLAY |
+                       V4L2_FBUF_CAP_BITMAP_CLIPPING |
+                       V4L2_FBUF_CAP_LIST_CLIPPING |
+                       V4L2_FBUF_CAP_CHROMAKEY |
+                       V4L2_FBUF_CAP_SRC_CHROMAKEY |
+                       V4L2_FBUF_CAP_GLOBAL_ALPHA |
+                       V4L2_FBUF_CAP_LOCAL_ALPHA |
+                       V4L2_FBUF_CAP_LOCAL_INV_ALPHA;
+       a->flags = V4L2_FBUF_FLAG_OVERLAY | dev->fbuf_out_flags;
+       a->base = (void *)dev->video_pbase;
+       a->fmt.width = dev->display_width;
+       a->fmt.height = dev->display_height;
+       if (dev->fb_defined.green.length == 5)
+               a->fmt.pixelformat = V4L2_PIX_FMT_ARGB555;
+       else
+               a->fmt.pixelformat = V4L2_PIX_FMT_RGB565;
+       a->fmt.bytesperline = dev->display_byte_stride;
+       a->fmt.sizeimage = a->fmt.height * a->fmt.bytesperline;
+       a->fmt.field = V4L2_FIELD_NONE;
+       a->fmt.colorspace = V4L2_COLORSPACE_SRGB;
+       a->fmt.priv = 0;
+       return 0;
+}
+
+int vivid_vid_out_s_fbuf(struct file *file, void *fh,
+                               const struct v4l2_framebuffer *a)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+       const unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY |
+                                     V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+       const unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA |
+                                    V4L2_FBUF_FLAG_LOCAL_ALPHA |
+                                    V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
+
+
+       if ((a->flags & chroma_flags) == chroma_flags)
+               return -EINVAL;
+       switch (a->flags & alpha_flags) {
+       case 0:
+       case V4L2_FBUF_FLAG_GLOBAL_ALPHA:
+       case V4L2_FBUF_FLAG_LOCAL_ALPHA:
+       case V4L2_FBUF_FLAG_LOCAL_INV_ALPHA:
+               break;
+       default:
+               return -EINVAL;
+       }
+       dev->fbuf_out_flags &= ~(chroma_flags | alpha_flags);
+       dev->fbuf_out_flags = a->flags & (chroma_flags | alpha_flags);
+       return 0;
+}
+
+static const struct v4l2_audioout vivid_audio_outputs[] = {
+       { 0, "Line-Out 1" },
+       { 1, "Line-Out 2" },
+};
+
+int vidioc_enum_output(struct file *file, void *priv,
+                               struct v4l2_output *out)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (out->index >= dev->num_outputs)
+               return -EINVAL;
+
+       out->type = V4L2_OUTPUT_TYPE_ANALOG;
+       switch (dev->output_type[out->index]) {
+       case SVID:
+               snprintf(out->name, sizeof(out->name), "S-Video %u",
+                               dev->output_name_counter[out->index]);
+               out->std = V4L2_STD_ALL;
+               if (dev->has_audio_outputs)
+                       out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1;
+               out->capabilities = V4L2_OUT_CAP_STD;
+               break;
+       case HDMI:
+               snprintf(out->name, sizeof(out->name), "HDMI %u",
+                               dev->output_name_counter[out->index]);
+               out->capabilities = V4L2_OUT_CAP_DV_TIMINGS;
+               break;
+       }
+       return 0;
+}
+
+int vidioc_g_output(struct file *file, void *priv, unsigned *o)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       *o = dev->output;
+       return 0;
+}
+
+int vidioc_s_output(struct file *file, void *priv, unsigned o)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (o >= dev->num_outputs)
+               return -EINVAL;
+
+       if (o == dev->output)
+               return 0;
+
+       if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q))
+               return -EBUSY;
+
+       dev->output = o;
+       dev->tv_audio_output = 0;
+       if (dev->output_type[o] == SVID)
+               dev->vid_out_dev.tvnorms = V4L2_STD_ALL;
+       else
+               dev->vid_out_dev.tvnorms = 0;
+
+       dev->vbi_out_dev.tvnorms = dev->vid_out_dev.tvnorms;
+       vivid_update_format_out(dev);
+       return 0;
+}
+
+int vidioc_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vout)
+{
+       if (vout->index >= ARRAY_SIZE(vivid_audio_outputs))
+               return -EINVAL;
+       *vout = vivid_audio_outputs[vout->index];
+       return 0;
+}
+
+int vidioc_g_audout(struct file *file, void *fh, struct v4l2_audioout *vout)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_svid_out(dev))
+               return -EINVAL;
+       *vout = vivid_audio_outputs[dev->tv_audio_output];
+       return 0;
+}
+
+int vidioc_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_svid_out(dev))
+               return -EINVAL;
+       if (vout->index >= ARRAY_SIZE(vivid_audio_outputs))
+               return -EINVAL;
+       dev->tv_audio_output = vout->index;
+       return 0;
+}
+
+int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_svid_out(dev))
+               return -ENODATA;
+       if (dev->std_out == id)
+               return 0;
+       if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q))
+               return -EBUSY;
+       dev->std_out = id;
+       vivid_update_format_out(dev);
+       return 0;
+}
+
+int vivid_vid_out_s_dv_timings(struct file *file, void *_fh,
+                                   struct v4l2_dv_timings *timings)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (!vivid_is_hdmi_out(dev))
+               return -ENODATA;
+       if (vb2_is_busy(&dev->vb_vid_out_q))
+               return -EBUSY;
+       if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,
+                               0, NULL, NULL))
+               return -EINVAL;
+       if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0))
+               return 0;
+       dev->dv_timings_out = *timings;
+       vivid_update_format_out(dev);
+       return 0;
+}
+
+int vivid_vid_out_g_parm(struct file *file, void *priv,
+                         struct v4l2_streamparm *parm)
+{
+       struct vivid_dev *dev = video_drvdata(file);
+
+       if (parm->type != (dev->multiplanar ?
+                          V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
+                          V4L2_BUF_TYPE_VIDEO_OUTPUT))
+               return -EINVAL;
+
+       parm->parm.output.capability   = V4L2_CAP_TIMEPERFRAME;
+       parm->parm.output.timeperframe = dev->timeperframe_vid_out;
+       parm->parm.output.writebuffers  = 1;
+return 0;
+}
+
+int vidioc_subscribe_event(struct v4l2_fh *fh,
+                       const struct v4l2_event_subscription *sub)
+{
+       switch (sub->type) {
+       case V4L2_EVENT_CTRL:
+               return v4l2_ctrl_subscribe_event(fh, sub);
+       case V4L2_EVENT_SOURCE_CHANGE:
+               if (fh->vdev->vfl_dir == VFL_DIR_RX)
+                       return v4l2_src_change_event_subscribe(fh, sub);
+               break;
+       default:
+               break;
+       }
+       return -EINVAL;
+}
diff --git a/drivers/media/platform/vivid/vivid-vid-out.h b/drivers/media/platform/vivid/vivid-vid-out.h
new file mode 100644 (file)
index 0000000..dfa84db
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * vivid-vid-out.h - video output support functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may 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.
+ *
+ * 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 _VIVID_VID_OUT_H_
+#define _VIVID_VID_OUT_H_
+
+extern const struct vb2_ops vivid_vid_out_qops;
+
+void vivid_update_format_out(struct vivid_dev *dev);
+
+int vivid_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_vid_out_g_selection(struct file *file, void *priv, struct v4l2_selection *sel);
+int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s);
+int vivid_vid_out_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cap);
+int vidioc_enum_fmt_vid_out_overlay(struct file *file, void  *priv, struct v4l2_fmtdesc *f);
+int vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_format *f);
+int vidioc_s_fmt_vid_out_overlay(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_vid_out_overlay(struct file *file, void *fh, unsigned i);
+int vivid_vid_out_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *a);
+int vivid_vid_out_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *a);
+int vidioc_enum_output(struct file *file, void *priv, struct v4l2_output *out);
+int vidioc_g_output(struct file *file, void *priv, unsigned *i);
+int vidioc_s_output(struct file *file, void *priv, unsigned i);
+int vidioc_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vout);
+int vidioc_g_audout(struct file *file, void *fh, struct v4l2_audioout *vout);
+int vidioc_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout);
+int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id);
+int vivid_vid_out_s_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings);
+int vivid_vid_out_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parm);
+
+#endif
index 235c0e349820702cf53387f952262e506eee9e06..cff1eb144a5c71f5db9fa46f10c5c5363cf939d1 100644 (file)
@@ -332,7 +332,7 @@ static int __init gemtek_init(void)
 
 static void __exit gemtek_exit(void)
 {
-       hardmute = 1;   /* Turn off PLL */
+       hardmute = true;        /* Turn off PLL */
 #ifdef CONFIG_PNP
        pnp_unregister_driver(&gemtek_driver.pnp_driver);
 #endif
index d7ce8fe6b5ae1a89c5c578a9f2d88d20aaeecca7..28a89466cddc4a82985a6ab01785c093fa8c91ff 100644 (file)
@@ -56,7 +56,7 @@ struct fmi
 
 static struct fmi fmi_card;
 static struct pnp_dev *dev;
-bool pnp_attached;
+static bool pnp_attached;
 
 #define RSF16_MINFREQ (87U * 16000)
 #define RSF16_MAXFREQ (108U * 16000)
@@ -285,7 +285,7 @@ static int __init fmi_init(void)
                                io = isapnp_fmi_probe();
                                if (io < 0)
                                        continue;
-                               pnp_attached = 1;
+                               pnp_attached = true;
                        }
                        if (!request_region(io, 2, "radio-sf16fmi")) {
                                if (pnp_attached)
@@ -349,7 +349,7 @@ static int __init fmi_init(void)
        mutex_init(&fmi->lock);
 
        /* mute card and set default frequency */
-       fmi->mute = 1;
+       fmi->mute = true;
        fmi->curfreq = RSF16_MINFREQ;
        fmi_set_freq(fmi);
 
index 93d864eb830627898c82d3d33416466819ee5c76..b8d61cbc18cb5d7b9c2dd902c2900c32c399cf1a 100644 (file)
@@ -305,7 +305,7 @@ static void fmr2_pnp_remove(struct pnp_dev *pdev)
        pnp_set_drvdata(pdev, NULL);
 }
 
-struct isa_driver fmr2_isa_driver = {
+static struct isa_driver fmr2_isa_driver = {
        .match          = fmr2_isa_match,
        .remove         = fmr2_isa_remove,
        .driver         = {
@@ -313,7 +313,7 @@ struct isa_driver fmr2_isa_driver = {
        },
 };
 
-struct pnp_driver fmr2_pnp_driver = {
+static struct pnp_driver fmr2_pnp_driver = {
        .name           = "radio-sf16fmr2",
        .id_table       = fmr2_pnp_ids,
        .probe          = fmr2_pnp_probe,
index 925049654c5bc0f33429b45b5cab5fff3795f8e8..cc3990111411bd8b06b4d86ad3d4da9b72a54c94 100644 (file)
@@ -124,11 +124,11 @@ struct tea5764_regs {
 
 struct tea5764_write_regs {
        u8 intreg;                              /* INTMSK */
-       u16 frqset;                             /* FRQSETMSB & FRQSETLSB */
-       u16 tnctrl;                             /* TNCTRL1 & TNCTRL2 */
-       u16 testreg;                            /* TESTBITS & TESTMODE */
-       u16 rdsctrl;                            /* RDSCTRL1 & RDSCTRL2 */
-       u16 rdsbbl;                             /* PAUSEDET & RDSBBL */
+       __be16 frqset;                          /* FRQSETMSB & FRQSETLSB */
+       __be16 tnctrl;                          /* TNCTRL1 & TNCTRL2 */
+       __be16 testreg;                         /* TESTBITS & TESTMODE */
+       __be16 rdsctrl;                         /* RDSCTRL1 & RDSCTRL2 */
+       __be16 rdsbbl;                          /* PAUSEDET & RDSBBL */
 } __attribute__ ((packed));
 
 #ifdef CONFIG_RADIO_TEA5764_XTAL
@@ -165,7 +165,7 @@ static int tea5764_i2c_read(struct tea5764_device *radio)
        if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1)
                return -EIO;
        for (i = 0; i < sizeof(struct tea5764_regs) / sizeof(u16); i++)
-               p[i] = __be16_to_cpu(p[i]);
+               p[i] = __be16_to_cpu((__force __be16)p[i]);
 
        return 0;
 }
index 0e750aef656a6f8a053aafbd965b4e9e1fe64f23..909c3f92d83920de0561d9dc59aa31171826d973 100644 (file)
@@ -208,7 +208,7 @@ static int si470x_set_band(struct si470x_device *radio, int band)
 static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
 {
        int retval;
-       bool timed_out = 0;
+       bool timed_out = false;
 
        /* start tuning */
        radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
@@ -300,7 +300,7 @@ static int si470x_set_seek(struct si470x_device *radio,
 {
        int band, retval;
        unsigned int freq;
-       bool timed_out = 0;
+       bool timed_out = false;
 
        /* set band */
        if (seek->rangelow || seek->rangehigh) {
index 494fac061306ed498a02dff4049cb0223d45f621..57f0bc3b60e7d3a54943ee685c17b295050f79e4 100644 (file)
@@ -607,9 +607,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
        /* Set up interrupt endpoint information. */
        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_INT))
+               if (usb_endpoint_is_int_in(endpoint))
                        radio->int_in_endpoint = endpoint;
        }
        if (!radio->int_in_endpoint) {
index 4b2e9e8298e1f6e962a468eeb1fba4b91b5c68bd..6f28f6e02ea57e947a2a8805ccccee4cfaa30cc2 100644 (file)
@@ -440,7 +440,7 @@ static int fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type,     void *payload,
                 * command with u16 payload - convert to be16
                 */
                if (payload != NULL)
-                       *(u16 *)payload = cpu_to_be16(*(u16 *)payload);
+                       *(__be16 *)payload = cpu_to_be16(*(u16 *)payload);
 
        } else if (payload != NULL) {
                fm_cb(skb)->fm_op = *((u8 *)payload + 2);
@@ -595,7 +595,7 @@ static void fm_irq_handle_flag_getcmd_resp(struct fmdev *fmdev)
        skb_pull(skb, sizeof(struct fm_event_msg_hdr));
        memcpy(&fmdev->irq_info.flag, skb->data, fm_evt_hdr->dlen);
 
-       fmdev->irq_info.flag = be16_to_cpu(fmdev->irq_info.flag);
+       fmdev->irq_info.flag = be16_to_cpu((__force __be16)fmdev->irq_info.flag);
        fmdbg("irq: flag register(0x%x)\n", fmdev->irq_info.flag);
 
        /* Continue next function in interrupt handler table */
@@ -764,7 +764,7 @@ static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev)
                         * Extract PI code and store in local cache.
                         * We need this during AF switch processing.
                         */
-                       cur_picode = be16_to_cpu(rds_fmt.data.groupgeneral.pidata);
+                       cur_picode = be16_to_cpu((__force __be16)rds_fmt.data.groupgeneral.pidata);
                        if (fmdev->rx.stat_info.picode != cur_picode)
                                fmdev->rx.stat_info.picode = cur_picode;
 
@@ -989,7 +989,7 @@ static void fm_irq_afjump_rd_freq_resp(struct fmdev *fmdev)
        /* Skip header info and copy only response data */
        skb_pull(skb, sizeof(struct fm_event_msg_hdr));
        memcpy(&read_freq, skb->data, sizeof(read_freq));
-       read_freq = be16_to_cpu(read_freq);
+       read_freq = be16_to_cpu((__force __be16)read_freq);
        curr_freq = fmdev->rx.region.bot_freq + ((u32)read_freq * FM_FREQ_MUL);
 
        jumped_freq = fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx];
@@ -1317,7 +1317,8 @@ static int load_default_rx_configuration(struct fmdev *fmdev)
 /* Does FM power on sequence */
 static int fm_power_up(struct fmdev *fmdev, u8 mode)
 {
-       u16 payload, asic_id, asic_ver;
+       u16 payload;
+       __be16 asic_id, asic_ver;
        int resp_len, ret;
        u8 fw_name[50];
 
index ebf09a3927deea0f34070d54aa6e3c65b7bd1742..09632cb26cb6980b06238fc1b09bc2b218343e32 100644 (file)
@@ -116,7 +116,7 @@ int fm_rx_set_freq(struct fmdev *fmdev, u32 freq)
        if (ret < 0)
                goto exit;
 
-       curr_frq = be16_to_cpu(curr_frq);
+       curr_frq = be16_to_cpu((__force __be16)curr_frq);
        curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL));
 
        if (curr_frq_in_khz != freq) {
@@ -189,7 +189,7 @@ int fm_rx_seek(struct fmdev *fmdev, u32 seek_upward,
        if (ret < 0)
                return ret;
 
-       curr_frq = be16_to_cpu(curr_frq);
+       curr_frq = be16_to_cpu((__force __be16)curr_frq);
        last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
 
        /* Check the offset in order to be aligned to the channel spacing*/
@@ -285,7 +285,7 @@ again:
                if (ret < 0)
                        return ret;
 
-               curr_frq = be16_to_cpu(curr_frq);
+               curr_frq = be16_to_cpu((__force __be16)curr_frq);
                fmdev->rx.freq = (fmdev->rx.region.bot_freq +
                                ((u32)curr_frq * FM_FREQ_MUL));
 
@@ -517,7 +517,7 @@ int fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute)
 /* Returns the signal strength level of current channel */
 int fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl)
 {
-       u16 curr_rssi_lel;
+       __be16 curr_rssi_lel;
        u32 resp_len;
        int ret;
 
@@ -608,7 +608,7 @@ int fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
 /* Gets current RX stereo/mono mode */
 int fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode)
 {
-       u16 curr_mode;
+       __be16 curr_mode;
        u32 resp_len;
        int ret;
 
index 6ea33e09d63b23b306979142ace913d6806f561f..839970b0f313522a31edfc0430f0d15652f47526 100644 (file)
@@ -374,7 +374,7 @@ int fm_tx_get_tune_cap_val(struct fmdev *fmdev)
        if (ret < 0)
                return ret;
 
-       curr_val = be16_to_cpu(curr_val);
+       curr_val = be16_to_cpu((__force __be16)curr_val);
 
        return curr_val;
 }
index 5e626af8e3130b0a7fbed625c1f7b1101f696270..8ce08107a69d5801d6de12b5ad0f5e9aa7daeacc 100644 (file)
@@ -164,6 +164,16 @@ config IR_ENE
           To compile this driver as a module, choose M here: the
           module will be called ene_ir.
 
+config IR_HIX5HD2
+       tristate "Hisilicon hix5hd2 IR remote control"
+       depends on RC_CORE
+       help
+        Say Y here if you want to use hisilicon hix5hd2 remote control.
+        To compile this driver as a module, choose M here: the module will be
+        called ir-hix5hd2.
+
+        If you're not sure, select N here
+
 config IR_IMON
        tristate "SoundGraph iMON Receiver and Display"
        depends on USB_ARCH_HAS_HCD
@@ -333,7 +343,8 @@ config IR_GPIO_CIR
 
 config RC_ST
        tristate "ST remote control receiver"
-       depends on ARCH_STI && RC_CORE
+       depends on RC_CORE
+       depends on ARCH_STI || COMPILE_TEST
        help
         Say Y here if you want support for ST remote control driver
         which allows both IR and UHF RX.
@@ -344,7 +355,7 @@ config RC_ST
 config IR_SUNXI
     tristate "SUNXI IR remote control"
     depends on RC_CORE
-    depends on ARCH_SUNXI
+    depends on ARCH_SUNXI || COMPILE_TEST
     ---help---
       Say Y if you want to use sunXi internal IR Controller
 
index 9f9843a1af5f2d61ee2ee851d4ef4519135e949c..0989f940e9cfa8d213706ca4989974584561117d 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o
 
 # stand-alone IR receivers/transmitters
 obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
+obj-$(CONFIG_IR_HIX5HD2) += ir-hix5hd2.o
 obj-$(CONFIG_IR_IMON) += imon.o
 obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
 obj-$(CONFIG_IR_MCEUSB) += mceusb.o
index d16d9b496b92e4f1b1f66fa87b01af7d5757501e..e80f2c6c5f1abf040428f4f6c14666de271ee016 100644 (file)
@@ -979,7 +979,7 @@ static int ene_transmit(struct rc_dev *rdev, unsigned *buf, unsigned n)
        dev->tx_reg = 0;
        dev->tx_done = 0;
        dev->tx_sample = 0;
-       dev->tx_sample_pulse = 0;
+       dev->tx_sample_pulse = false;
 
        dbg("TX: %d samples", dev->tx_len);
 
index f0a1f7d31ee6578925308b9be608f762c5b37fde..b5167573240e3631cd8d3ae74ccc6ec1893eed08 100644 (file)
@@ -148,7 +148,6 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
        u8 vendor_major, vendor_minor;
        u8 portsel, ir_class;
        u16 vendor, chip;
-       int ret = 0;
 
        fintek_config_mode_enable(fintek);
 
@@ -208,7 +207,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
 
        spin_unlock_irqrestore(&fintek->fintek_lock, flags);
 
-       return ret;
+       return 0;
 }
 
 static void fintek_cir_ldev_init(struct fintek_dev *fintek)
@@ -644,7 +643,6 @@ static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state)
 
 static int fintek_resume(struct pnp_dev *pdev)
 {
-       int ret = 0;
        struct fintek_dev *fintek = pnp_get_drvdata(pdev);
 
        fit_dbg("%s called", __func__);
@@ -661,7 +659,7 @@ static int fintek_resume(struct pnp_dev *pdev)
 
        fintek_cir_regs_init(fintek);
 
-       return ret;
+       return 0;
 }
 
 static void fintek_shutdown(struct pnp_dev *pdev)
index bfb282a714e8e2ca8bc77777ed375961d6e01a5f..ec49f94425fc757ddc107578adeaa257a1ebd77a 100644 (file)
 /* Decoders lock (only modified to preprocess them) */
 static DEFINE_SPINLOCK(img_ir_decoders_lock);
 
-extern struct img_ir_decoder img_ir_nec;
-extern struct img_ir_decoder img_ir_jvc;
-extern struct img_ir_decoder img_ir_sony;
-extern struct img_ir_decoder img_ir_sharp;
-extern struct img_ir_decoder img_ir_sanyo;
-
 static bool img_ir_decoders_preprocessed;
 static struct img_ir_decoder *img_ir_decoders[] = {
 #ifdef CONFIG_IR_IMG_NEC
index 3e40ce87b898047f511877c547c382898c7db40e..8fcc16c32c5bcd5ce1edc3a1744b30eeb2f1497a 100644 (file)
@@ -168,6 +168,12 @@ struct img_ir_decoder {
                      struct img_ir_filter *out, u64 protocols);
 };
 
+extern struct img_ir_decoder img_ir_nec;
+extern struct img_ir_decoder img_ir_jvc;
+extern struct img_ir_decoder img_ir_sony;
+extern struct img_ir_decoder img_ir_sharp;
+extern struct img_ir_decoder img_ir_sanyo;
+
 /**
  * struct img_ir_reg_timings - Reg values for decoder timings at clock rate.
  * @ctrl:      Processed control register value.
index 7115e68ba6978d36689e96ac0d32628646fc9664..b8837dd39bb2a52740a36765d69d75af464f51fa 100644 (file)
@@ -87,6 +87,18 @@ static ssize_t lcd_write(struct file *file, const char __user *buf,
 
 /*** G L O B A L S ***/
 
+struct imon_panel_key_table {
+       u64 hw_code;
+       u32 keycode;
+};
+
+struct imon_usb_dev_descr {
+       __u16 flags;
+#define IMON_NO_FLAGS 0
+#define IMON_NEED_20MS_PKT_DELAY 1
+       struct imon_panel_key_table key_table[];
+};
+
 struct imon_context {
        struct device *dev;
        /* Newer devices have two interfaces */
@@ -150,6 +162,8 @@ struct imon_context {
        struct timer_list ttimer;       /* touch screen timer */
        int touch_x;                    /* x coordinate on touchscreen */
        int touch_y;                    /* y coordinate on touchscreen */
+       struct imon_usb_dev_descr *dev_descr; /* device description with key
+                                                table for front panels */
 };
 
 #define TOUCH_TIMEOUT  (HZ/30)
@@ -186,8 +200,132 @@ enum {
        IMON_KEY_PANEL  = 2,
 };
 
-enum {
-       IMON_NEED_20MS_PKT_DELAY = 1
+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,
+};
+
+/* imon receiver front panel/knob key table */
+static const struct imon_usb_dev_descr imon_default_table = {
+       .flags = IMON_NO_FLAGS,
+       .key_table = {
+               { 0x000000000f00ffeell, KEY_MEDIA }, /* Go */
+               { 0x000000001200ffeell, KEY_UP },
+               { 0x000000001300ffeell, KEY_DOWN },
+               { 0x000000001400ffeell, KEY_LEFT },
+               { 0x000000001500ffeell, KEY_RIGHT },
+               { 0x000000001600ffeell, KEY_ENTER },
+               { 0x000000001700ffeell, KEY_ESC },
+               { 0x000000001f00ffeell, KEY_AUDIO },
+               { 0x000000002000ffeell, KEY_VIDEO },
+               { 0x000000002100ffeell, KEY_CAMERA },
+               { 0x000000002700ffeell, KEY_DVD },
+               { 0x000000002300ffeell, KEY_TV },
+               { 0x000000002b00ffeell, KEY_EXIT },
+               { 0x000000002c00ffeell, KEY_SELECT },
+               { 0x000000002d00ffeell, KEY_MENU },
+               { 0x000000000500ffeell, KEY_PREVIOUS },
+               { 0x000000000700ffeell, KEY_REWIND },
+               { 0x000000000400ffeell, KEY_STOP },
+               { 0x000000003c00ffeell, KEY_PLAYPAUSE },
+               { 0x000000000800ffeell, KEY_FASTFORWARD },
+               { 0x000000000600ffeell, KEY_NEXT },
+               { 0x000000010000ffeell, KEY_RIGHT },
+               { 0x000001000000ffeell, KEY_LEFT },
+               { 0x000000003d00ffeell, KEY_SELECT },
+               { 0x000100000000ffeell, KEY_VOLUMEUP },
+               { 0x010000000000ffeell, KEY_VOLUMEDOWN },
+               { 0x000000000100ffeell, KEY_MUTE },
+               /* 0xffdc iMON MCE VFD */
+               { 0x00010000ffffffeell, KEY_VOLUMEUP },
+               { 0x01000000ffffffeell, KEY_VOLUMEDOWN },
+               { 0x00000001ffffffeell, KEY_MUTE },
+               { 0x0000000fffffffeell, KEY_MEDIA },
+               { 0x00000012ffffffeell, KEY_UP },
+               { 0x00000013ffffffeell, KEY_DOWN },
+               { 0x00000014ffffffeell, KEY_LEFT },
+               { 0x00000015ffffffeell, KEY_RIGHT },
+               { 0x00000016ffffffeell, KEY_ENTER },
+               { 0x00000017ffffffeell, KEY_ESC },
+               /* iMON Knob values */
+               { 0x000100ffffffffeell, KEY_VOLUMEUP },
+               { 0x010000ffffffffeell, KEY_VOLUMEDOWN },
+               { 0x000008ffffffffeell, KEY_MUTE },
+               { 0, KEY_RESERVED },
+       }
+};
+
+static const struct imon_usb_dev_descr imon_OEM_VFD = {
+       .flags = IMON_NEED_20MS_PKT_DELAY,
+       .key_table = {
+               { 0x000000000f00ffeell, KEY_MEDIA }, /* Go */
+               { 0x000000001200ffeell, KEY_UP },
+               { 0x000000001300ffeell, KEY_DOWN },
+               { 0x000000001400ffeell, KEY_LEFT },
+               { 0x000000001500ffeell, KEY_RIGHT },
+               { 0x000000001600ffeell, KEY_ENTER },
+               { 0x000000001700ffeell, KEY_ESC },
+               { 0x000000001f00ffeell, KEY_AUDIO },
+               { 0x000000002b00ffeell, KEY_EXIT },
+               { 0x000000002c00ffeell, KEY_SELECT },
+               { 0x000000002d00ffeell, KEY_MENU },
+               { 0x000000000500ffeell, KEY_PREVIOUS },
+               { 0x000000000700ffeell, KEY_REWIND },
+               { 0x000000000400ffeell, KEY_STOP },
+               { 0x000000003c00ffeell, KEY_PLAYPAUSE },
+               { 0x000000000800ffeell, KEY_FASTFORWARD },
+               { 0x000000000600ffeell, KEY_NEXT },
+               { 0x000000010000ffeell, KEY_RIGHT },
+               { 0x000001000000ffeell, KEY_LEFT },
+               { 0x000000003d00ffeell, KEY_SELECT },
+               { 0x000100000000ffeell, KEY_VOLUMEUP },
+               { 0x010000000000ffeell, KEY_VOLUMEDOWN },
+               { 0x000000000100ffeell, KEY_MUTE },
+               /* 0xffdc iMON MCE VFD */
+               { 0x00010000ffffffeell, KEY_VOLUMEUP },
+               { 0x01000000ffffffeell, KEY_VOLUMEDOWN },
+               { 0x00000001ffffffeell, KEY_MUTE },
+               { 0x0000000fffffffeell, KEY_MEDIA },
+               { 0x00000012ffffffeell, KEY_UP },
+               { 0x00000013ffffffeell, KEY_DOWN },
+               { 0x00000014ffffffeell, KEY_LEFT },
+               { 0x00000015ffffffeell, KEY_RIGHT },
+               { 0x00000016ffffffeell, KEY_ENTER },
+               { 0x00000017ffffffeell, KEY_ESC },
+               /* iMON Knob values */
+               { 0x000100ffffffffeell, KEY_VOLUMEUP },
+               { 0x010000ffffffffeell, KEY_VOLUMEDOWN },
+               { 0x000008ffffffffeell, KEY_MUTE },
+               { 0, KEY_RESERVED },
+       }
+};
+
+/* imon receiver front panel/knob key table for DH102*/
+static const struct imon_usb_dev_descr imon_DH102 = {
+       .flags = IMON_NO_FLAGS,
+       .key_table = {
+               { 0x000100000000ffeell, KEY_VOLUMEUP },
+               { 0x010000000000ffeell, KEY_VOLUMEDOWN },
+               { 0x000000010000ffeell, KEY_MUTE },
+               { 0x0000000f0000ffeell, KEY_MEDIA },
+               { 0x000000120000ffeell, KEY_UP },
+               { 0x000000130000ffeell, KEY_DOWN },
+               { 0x000000140000ffeell, KEY_LEFT },
+               { 0x000000150000ffeell, KEY_RIGHT },
+               { 0x000000160000ffeell, KEY_ENTER },
+               { 0x000000170000ffeell, KEY_ESC },
+               { 0x0000002b0000ffeell, KEY_EXIT },
+               { 0x0000002c0000ffeell, KEY_SELECT },
+               { 0x0000002d0000ffeell, KEY_MENU },
+               { 0, KEY_RESERVED }
+       }
 };
 
 /*
@@ -208,7 +346,8 @@ static struct usb_device_id imon_usb_id_table[] = {
         * SoundGraph iMON PAD (IR & LCD)
         * SoundGraph iMON Knob (IR only)
         */
-       { USB_DEVICE(0x15c2, 0xffdc) },
+       { USB_DEVICE(0x15c2, 0xffdc),
+         .driver_info = (unsigned long)&imon_default_table },
 
        /*
         * Newer devices, all driven by the latest iMON Windows driver, full
@@ -216,43 +355,62 @@ static struct usb_device_id imon_usb_id_table[] = {
         * Need user input to fill in details on unknown devices.
         */
        /* SoundGraph iMON OEM Touch LCD (IR & 7" VGA LCD) */
-       { USB_DEVICE(0x15c2, 0x0034) },
+       { USB_DEVICE(0x15c2, 0x0034),
+         .driver_info = (unsigned long)&imon_DH102 },
        /* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */
-       { USB_DEVICE(0x15c2, 0x0035) },
+       { USB_DEVICE(0x15c2, 0x0035),
+         .driver_info = (unsigned long)&imon_default_table},
        /* SoundGraph iMON OEM VFD (IR & VFD) */
-       { USB_DEVICE(0x15c2, 0x0036), .driver_info = IMON_NEED_20MS_PKT_DELAY },
+       { USB_DEVICE(0x15c2, 0x0036),
+         .driver_info = (unsigned long)&imon_OEM_VFD },
        /* device specifics unknown */
-       { USB_DEVICE(0x15c2, 0x0037) },
+       { USB_DEVICE(0x15c2, 0x0037),
+         .driver_info = (unsigned long)&imon_default_table},
        /* SoundGraph iMON OEM LCD (IR & LCD) */
-       { USB_DEVICE(0x15c2, 0x0038) },
+       { USB_DEVICE(0x15c2, 0x0038),
+         .driver_info = (unsigned long)&imon_default_table},
        /* SoundGraph iMON UltraBay (IR & LCD) */
-       { USB_DEVICE(0x15c2, 0x0039) },
+       { USB_DEVICE(0x15c2, 0x0039),
+         .driver_info = (unsigned long)&imon_default_table},
        /* device specifics unknown */
-       { USB_DEVICE(0x15c2, 0x003a) },
+       { USB_DEVICE(0x15c2, 0x003a),
+         .driver_info = (unsigned long)&imon_default_table},
        /* device specifics unknown */
-       { USB_DEVICE(0x15c2, 0x003b) },
+       { USB_DEVICE(0x15c2, 0x003b),
+         .driver_info = (unsigned long)&imon_default_table},
        /* SoundGraph iMON OEM Inside (IR only) */
-       { USB_DEVICE(0x15c2, 0x003c) },
+       { USB_DEVICE(0x15c2, 0x003c),
+         .driver_info = (unsigned long)&imon_default_table},
        /* device specifics unknown */
-       { USB_DEVICE(0x15c2, 0x003d) },
+       { USB_DEVICE(0x15c2, 0x003d),
+         .driver_info = (unsigned long)&imon_default_table},
        /* device specifics unknown */
-       { USB_DEVICE(0x15c2, 0x003e) },
+       { USB_DEVICE(0x15c2, 0x003e),
+         .driver_info = (unsigned long)&imon_default_table},
        /* device specifics unknown */
-       { USB_DEVICE(0x15c2, 0x003f) },
+       { USB_DEVICE(0x15c2, 0x003f),
+         .driver_info = (unsigned long)&imon_default_table},
        /* device specifics unknown */
-       { USB_DEVICE(0x15c2, 0x0040) },
+       { USB_DEVICE(0x15c2, 0x0040),
+         .driver_info = (unsigned long)&imon_default_table},
        /* SoundGraph iMON MINI (IR only) */
-       { USB_DEVICE(0x15c2, 0x0041) },
+       { USB_DEVICE(0x15c2, 0x0041),
+         .driver_info = (unsigned long)&imon_default_table},
        /* Antec Veris Multimedia Station EZ External (IR only) */
-       { USB_DEVICE(0x15c2, 0x0042) },
+       { USB_DEVICE(0x15c2, 0x0042),
+         .driver_info = (unsigned long)&imon_default_table},
        /* Antec Veris Multimedia Station Basic Internal (IR only) */
-       { USB_DEVICE(0x15c2, 0x0043) },
+       { USB_DEVICE(0x15c2, 0x0043),
+         .driver_info = (unsigned long)&imon_default_table},
        /* Antec Veris Multimedia Station Elite (IR & VFD) */
-       { USB_DEVICE(0x15c2, 0x0044) },
+       { USB_DEVICE(0x15c2, 0x0044),
+         .driver_info = (unsigned long)&imon_default_table},
        /* Antec Veris Multimedia Station Premiere (IR & LCD) */
-       { USB_DEVICE(0x15c2, 0x0045) },
+       { USB_DEVICE(0x15c2, 0x0045),
+         .driver_info = (unsigned long)&imon_default_table},
        /* device specifics unknown */
-       { USB_DEVICE(0x15c2, 0x0046) },
+       { USB_DEVICE(0x15c2, 0x0046),
+         .driver_info = (unsigned long)&imon_default_table},
        {}
 };
 
@@ -266,67 +424,6 @@ static struct usb_driver imon_driver = {
        .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,
-};
-
-/* imon receiver front panel/knob key table */
-static const struct {
-       u64 hw_code;
-       u32 keycode;
-} imon_panel_key_table[] = {
-       { 0x000000000f00ffeell, KEY_MEDIA }, /* Go */
-       { 0x000000001200ffeell, KEY_UP },
-       { 0x000000001300ffeell, KEY_DOWN },
-       { 0x000000001400ffeell, KEY_LEFT },
-       { 0x000000001500ffeell, KEY_RIGHT },
-       { 0x000000001600ffeell, KEY_ENTER },
-       { 0x000000001700ffeell, KEY_ESC },
-       { 0x000000001f00ffeell, KEY_AUDIO },
-       { 0x000000002000ffeell, KEY_VIDEO },
-       { 0x000000002100ffeell, KEY_CAMERA },
-       { 0x000000002700ffeell, KEY_DVD },
-       { 0x000000002300ffeell, KEY_TV },
-       { 0x000000002b00ffeell, KEY_EXIT },
-       { 0x000000002c00ffeell, KEY_SELECT },
-       { 0x000000002d00ffeell, KEY_MENU },
-       { 0x000000000500ffeell, KEY_PREVIOUS },
-       { 0x000000000700ffeell, KEY_REWIND },
-       { 0x000000000400ffeell, KEY_STOP },
-       { 0x000000003c00ffeell, KEY_PLAYPAUSE },
-       { 0x000000000800ffeell, KEY_FASTFORWARD },
-       { 0x000000000600ffeell, KEY_NEXT },
-       { 0x000000010000ffeell, KEY_RIGHT },
-       { 0x000001000000ffeell, KEY_LEFT },
-       { 0x000000003d00ffeell, KEY_SELECT },
-       { 0x000100000000ffeell, KEY_VOLUMEUP },
-       { 0x010000000000ffeell, KEY_VOLUMEDOWN },
-       { 0x000000000100ffeell, KEY_MUTE },
-       /* 0xffdc iMON MCE VFD */
-       { 0x00010000ffffffeell, KEY_VOLUMEUP },
-       { 0x01000000ffffffeell, KEY_VOLUMEDOWN },
-       { 0x00000001ffffffeell, KEY_MUTE },
-       { 0x0000000fffffffeell, KEY_MEDIA },
-       { 0x00000012ffffffeell, KEY_UP },
-       { 0x00000013ffffffeell, KEY_DOWN },
-       { 0x00000014ffffffeell, KEY_LEFT },
-       { 0x00000015ffffffeell, KEY_RIGHT },
-       { 0x00000016ffffffeell, KEY_ENTER },
-       { 0x00000017ffffffeell, KEY_ESC },
-       /* iMON Knob values */
-       { 0x000100ffffffffeell, KEY_VOLUMEUP },
-       { 0x010000ffffffffeell, KEY_VOLUMEDOWN },
-       { 0x000008ffffffffeell, KEY_MUTE },
-};
-
 /* to prevent races between open() and disconnect(), probing, etc */
 static DEFINE_MUTEX(driver_lock);
 
@@ -1210,18 +1307,19 @@ static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 scancode)
        return keycode;
 }
 
-static u32 imon_panel_key_lookup(u64 code)
+static u32 imon_panel_key_lookup(struct imon_context *ictx, u64 code)
 {
        int i;
        u32 keycode = KEY_RESERVED;
+       struct imon_panel_key_table *key_table = ictx->dev_descr->key_table;
 
-       for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) {
-               if (imon_panel_key_table[i].hw_code == (code | 0xffee)) {
-                       keycode = imon_panel_key_table[i].keycode;
+       for (i = 0; key_table[i].hw_code != 0; i++) {
+               if (key_table[i].hw_code == (code | 0xffee)) {
+                       keycode = key_table[i].keycode;
                        break;
                }
        }
-
+       ictx->release_code = false;
        return keycode;
 }
 
@@ -1340,7 +1438,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
                                }
                                buf[2] = dir & 0xFF;
                                buf[3] = (dir >> 8) & 0xFF;
-                               scancode = be32_to_cpu(*((u32 *)buf));
+                               scancode = be32_to_cpu(*((__be32 *)buf));
                        }
                } else {
                        /*
@@ -1404,7 +1502,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
                        }
                        buf[2] = dir & 0xFF;
                        buf[3] = (dir >> 8) & 0xFF;
-                       scancode = be32_to_cpu(*((u32 *)buf));
+                       scancode = be32_to_cpu(*((__be32 *)buf));
                } else {
                        /*
                         * Hack alert: instead of using keycodes, we have
@@ -1509,11 +1607,12 @@ static void imon_incoming_packet(struct imon_context *ictx,
 
        /* Figure out what key was pressed */
        if (len == 8 && buf[7] == 0xee) {
-               scancode = be64_to_cpu(*((u64 *)buf));
+               scancode = be64_to_cpu(*((__be64 *)buf));
                ktype = IMON_KEY_PANEL;
-               kc = imon_panel_key_lookup(scancode);
+               kc = imon_panel_key_lookup(ictx, scancode);
+               ictx->release_code = false;
        } else {
-               scancode = be32_to_cpu(*((u32 *)buf));
+               scancode = be32_to_cpu(*((__be32 *)buf));
                if (ictx->rc_type == RC_BIT_RC6_MCE) {
                        ktype = IMON_KEY_IMON;
                        if (buf[0] == 0x80)
@@ -1908,6 +2007,7 @@ out:
 
 static struct input_dev *imon_init_idev(struct imon_context *ictx)
 {
+       struct imon_panel_key_table *key_table = ictx->dev_descr->key_table;
        struct input_dev *idev;
        int ret, i;
 
@@ -1933,8 +2033,8 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
                BIT_MASK(REL_WHEEL);
 
        /* panel and/or knob code support */
-       for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) {
-               u32 kc = imon_panel_key_table[i].keycode;
+       for (i = 0; key_table[i].hw_code != 0; i++) {
+               u32 kc = key_table[i].keycode;
                __set_bit(kc, idev->keybit);
        }
 
@@ -2023,7 +2123,7 @@ static bool imon_find_endpoints(struct imon_context *ictx,
        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;
+               ep_type = usb_endpoint_type(ep);
 
                if (!ir_ep_found && ep_dir == USB_DIR_IN &&
                    ep_type == USB_ENDPOINT_XFER_INT) {
@@ -2135,9 +2235,11 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf,
        ictx->vendor  = le16_to_cpu(ictx->usbdev_intf0->descriptor.idVendor);
        ictx->product = le16_to_cpu(ictx->usbdev_intf0->descriptor.idProduct);
 
+       /* save drive info for later accessing the panel/knob key table */
+       ictx->dev_descr = (struct imon_usb_dev_descr *)id->driver_info;
        /* default send_packet delay is 5ms but some devices need more */
-       ictx->send_packet_delay = id->driver_info & IMON_NEED_20MS_PKT_DELAY ?
-                                 20 : 5;
+       ictx->send_packet_delay = ictx->dev_descr->flags &
+                                 IMON_NEED_20MS_PKT_DELAY ? 20 : 5;
 
        ret = -ENODEV;
        iface_desc = intf->cur_altsetting;
@@ -2181,6 +2283,7 @@ idev_setup_failed:
        usb_kill_urb(ictx->rx_urb_intf0);
 urb_submit_failed:
 find_endpoint_failed:
+       usb_put_dev(ictx->usbdev_intf0);
        mutex_unlock(&ictx->lock);
        usb_free_urb(tx_urb);
 tx_urb_alloc_failed:
@@ -2253,6 +2356,7 @@ urb_submit_failed:
                input_unregister_device(ictx->touch);
 touch_setup_failed:
 find_endpoint_failed:
+       usb_put_dev(ictx->usbdev_intf1);
        mutex_unlock(&ictx->lock);
        usb_free_urb(rx_urb);
 rx_urb_alloc_failed:
@@ -2366,11 +2470,13 @@ static int imon_probe(struct usb_interface *interface,
                 usbdev->bus->busnum, usbdev->devnum);
 
        mutex_unlock(&driver_lock);
+       usb_put_dev(usbdev);
 
        return 0;
 
 fail:
        mutex_unlock(&driver_lock);
+       usb_put_dev(usbdev);
        dev_err(dev, "unable to register, err %d\n", ret);
 
        return ret;
@@ -2410,6 +2516,7 @@ static void imon_disconnect(struct usb_interface *interface)
        if (ifnum == 0) {
                ictx->dev_present_intf0 = false;
                usb_kill_urb(ictx->rx_urb_intf0);
+               usb_put_dev(ictx->usbdev_intf0);
                input_unregister_device(ictx->idev);
                rc_unregister_device(ictx->rdev);
                if (ictx->display_supported) {
@@ -2421,6 +2528,7 @@ static void imon_disconnect(struct usb_interface *interface)
        } else {
                ictx->dev_present_intf1 = false;
                usb_kill_urb(ictx->rx_urb_intf1);
+               usb_put_dev(ictx->usbdev_intf1);
                if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
                        input_unregister_device(ictx->touch);
                        del_timer_sync(&ictx->ttimer);
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
new file mode 100644 (file)
index 0000000..08bbd4f
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <media/rc-core.h>
+
+/* Allow the driver to compile on all architectures */
+#ifndef writel_relaxed
+# define writel_relaxed writel
+#endif
+#ifndef readl_relaxed
+# define readl_relaxed readl
+#endif
+
+#define IR_ENABLE              0x00
+#define IR_CONFIG              0x04
+#define CNT_LEADS              0x08
+#define CNT_LEADE              0x0c
+#define CNT_SLEADE             0x10
+#define CNT0_B                 0x14
+#define CNT1_B                 0x18
+#define IR_BUSY                        0x1c
+#define IR_DATAH               0x20
+#define IR_DATAL               0x24
+#define IR_INTM                        0x28
+#define IR_INTS                        0x2c
+#define IR_INTC                        0x30
+#define IR_START               0x34
+
+/* interrupt mask */
+#define INTMS_SYMBRCV          (BIT(24) | BIT(8))
+#define INTMS_TIMEOUT          (BIT(25) | BIT(9))
+#define INTMS_OVERFLOW         (BIT(26) | BIT(10))
+#define INT_CLR_OVERFLOW       BIT(18)
+#define INT_CLR_TIMEOUT                BIT(17)
+#define INT_CLR_RCV            BIT(16)
+#define INT_CLR_RCVTIMEOUT     (BIT(16) | BIT(17))
+
+#define IR_CLK                 0x48
+#define IR_CLK_ENABLE          BIT(4)
+#define IR_CLK_RESET           BIT(5)
+
+#define IR_CFG_WIDTH_MASK      0xffff
+#define IR_CFG_WIDTH_SHIFT     16
+#define IR_CFG_FORMAT_MASK     0x3
+#define IR_CFG_FORMAT_SHIFT    14
+#define IR_CFG_INT_LEVEL_MASK  0x3f
+#define IR_CFG_INT_LEVEL_SHIFT 8
+/* only support raw mode */
+#define IR_CFG_MODE_RAW                BIT(7)
+#define IR_CFG_FREQ_MASK       0x7f
+#define IR_CFG_FREQ_SHIFT      0
+#define IR_CFG_INT_THRESHOLD   1
+/* symbol start from low to high, symbol stream end at high*/
+#define IR_CFG_SYMBOL_FMT      0
+#define IR_CFG_SYMBOL_MAXWIDTH 0x3e80
+
+#define IR_HIX5HD2_NAME                "hix5hd2-ir"
+
+struct hix5hd2_ir_priv {
+       int                     irq;
+       void volatile __iomem   *base;
+       struct device           *dev;
+       struct rc_dev           *rdev;
+       struct regmap           *regmap;
+       struct clk              *clock;
+       unsigned long           rate;
+};
+
+static void hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on)
+{
+       u32 val;
+
+       regmap_read(dev->regmap, IR_CLK, &val);
+       if (on) {
+               val &= ~IR_CLK_RESET;
+               val |= IR_CLK_ENABLE;
+       } else {
+               val &= ~IR_CLK_ENABLE;
+               val |= IR_CLK_RESET;
+       }
+       regmap_write(dev->regmap, IR_CLK, val);
+}
+
+static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv)
+{
+       int timeout = 10000;
+       u32 val, rate;
+
+       writel_relaxed(0x01, priv->base + IR_ENABLE);
+       while (readl_relaxed(priv->base + IR_BUSY)) {
+               if (timeout--) {
+                       udelay(1);
+               } else {
+                       dev_err(priv->dev, "IR_BUSY timeout\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
+       /* Now only support raw mode, with symbol start from low to high */
+       rate = DIV_ROUND_CLOSEST(priv->rate, 1000000);
+       val = IR_CFG_SYMBOL_MAXWIDTH & IR_CFG_WIDTH_MASK << IR_CFG_WIDTH_SHIFT;
+       val |= IR_CFG_SYMBOL_FMT & IR_CFG_FORMAT_MASK << IR_CFG_FORMAT_SHIFT;
+       val |= (IR_CFG_INT_THRESHOLD - 1) & IR_CFG_INT_LEVEL_MASK
+              << IR_CFG_INT_LEVEL_SHIFT;
+       val |= IR_CFG_MODE_RAW;
+       val |= (rate - 1) & IR_CFG_FREQ_MASK << IR_CFG_FREQ_SHIFT;
+       writel_relaxed(val, priv->base + IR_CONFIG);
+
+       writel_relaxed(0x00, priv->base + IR_INTM);
+       /* write arbitrary value to start  */
+       writel_relaxed(0x01, priv->base + IR_START);
+       return 0;
+}
+
+static int hix5hd2_ir_open(struct rc_dev *rdev)
+{
+       struct hix5hd2_ir_priv *priv = rdev->priv;
+
+       hix5hd2_ir_enable(priv, true);
+       return hix5hd2_ir_config(priv);
+}
+
+static void hix5hd2_ir_close(struct rc_dev *rdev)
+{
+       struct hix5hd2_ir_priv *priv = rdev->priv;
+
+       hix5hd2_ir_enable(priv, false);
+}
+
+static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data)
+{
+       u32 symb_num, symb_val, symb_time;
+       u32 data_l, data_h;
+       u32 irq_sr, i;
+       struct hix5hd2_ir_priv *priv = data;
+
+       irq_sr = readl_relaxed(priv->base + IR_INTS);
+       if (irq_sr & INTMS_OVERFLOW) {
+               /*
+                * we must read IR_DATAL first, then we can clean up
+                * IR_INTS availably since logic would not clear
+                * fifo when overflow, drv do the job
+                */
+               ir_raw_event_reset(priv->rdev);
+               symb_num = readl_relaxed(priv->base + IR_DATAH);
+               for (i = 0; i < symb_num; i++)
+                       readl_relaxed(priv->base + IR_DATAL);
+
+               writel_relaxed(INT_CLR_OVERFLOW, priv->base + IR_INTC);
+               dev_info(priv->dev, "overflow, level=%d\n",
+                        IR_CFG_INT_THRESHOLD);
+       }
+
+       if ((irq_sr & INTMS_SYMBRCV) || (irq_sr & INTMS_TIMEOUT)) {
+               DEFINE_IR_RAW_EVENT(ev);
+
+               symb_num = readl_relaxed(priv->base + IR_DATAH);
+               for (i = 0; i < symb_num; i++) {
+                       symb_val = readl_relaxed(priv->base + IR_DATAL);
+                       data_l = ((symb_val & 0xffff) * 10);
+                       data_h =  ((symb_val >> 16) & 0xffff) * 10;
+                       symb_time = (data_l + data_h) / 10;
+
+                       ev.duration = US_TO_NS(data_l);
+                       ev.pulse = true;
+                       ir_raw_event_store(priv->rdev, &ev);
+
+                       if (symb_time < IR_CFG_SYMBOL_MAXWIDTH) {
+                               ev.duration = US_TO_NS(data_h);
+                               ev.pulse = false;
+                               ir_raw_event_store(priv->rdev, &ev);
+                       } else {
+                               ir_raw_event_set_idle(priv->rdev, true);
+                       }
+               }
+
+               if (irq_sr & INTMS_SYMBRCV)
+                       writel_relaxed(INT_CLR_RCV, priv->base + IR_INTC);
+               if (irq_sr & INTMS_TIMEOUT)
+                       writel_relaxed(INT_CLR_TIMEOUT, priv->base + IR_INTC);
+       }
+
+       /* Empty software fifo */
+       ir_raw_event_handle(priv->rdev);
+       return IRQ_HANDLED;
+}
+
+static int hix5hd2_ir_probe(struct platform_device *pdev)
+{
+       struct rc_dev *rdev;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct hix5hd2_ir_priv *priv;
+       struct device_node *node = pdev->dev.of_node;
+       const char *map_name;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->regmap = syscon_regmap_lookup_by_phandle(node,
+                                                      "hisilicon,power-syscon");
+       if (IS_ERR(priv->regmap)) {
+               dev_err(dev, "no power-reg\n");
+               return -EINVAL;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR((__force void *)priv->base))
+               return PTR_ERR((__force void *)priv->base);
+
+       priv->irq = platform_get_irq(pdev, 0);
+       if (priv->irq < 0) {
+               dev_err(dev, "irq can not get\n");
+               return priv->irq;
+       }
+
+       rdev = rc_allocate_device();
+       if (!rdev)
+               return -ENOMEM;
+
+       priv->clock = devm_clk_get(dev, NULL);
+       if (IS_ERR(priv->clock)) {
+               dev_err(dev, "clock not found\n");
+               ret = PTR_ERR(priv->clock);
+               goto err;
+       }
+       clk_prepare_enable(priv->clock);
+       priv->rate = clk_get_rate(priv->clock);
+
+       rdev->driver_type = RC_DRIVER_IR_RAW;
+       rdev->allowed_protocols = RC_BIT_ALL;
+       rdev->priv = priv;
+       rdev->open = hix5hd2_ir_open;
+       rdev->close = hix5hd2_ir_close;
+       rdev->driver_name = IR_HIX5HD2_NAME;
+       map_name = of_get_property(node, "linux,rc-map-name", NULL);
+       rdev->map_name = map_name ?: RC_MAP_EMPTY;
+       rdev->input_name = IR_HIX5HD2_NAME;
+       rdev->input_phys = IR_HIX5HD2_NAME "/input0";
+       rdev->input_id.bustype = BUS_HOST;
+       rdev->input_id.vendor = 0x0001;
+       rdev->input_id.product = 0x0001;
+       rdev->input_id.version = 0x0100;
+       rdev->rx_resolution = US_TO_NS(10);
+       rdev->timeout = US_TO_NS(IR_CFG_SYMBOL_MAXWIDTH * 10);
+
+       ret = rc_register_device(rdev);
+       if (ret < 0)
+               goto clkerr;
+
+       if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt,
+                            IRQF_NO_SUSPEND, pdev->name, priv) < 0) {
+               dev_err(dev, "IRQ %d register failed\n", priv->irq);
+               ret = -EINVAL;
+               goto regerr;
+       }
+
+       priv->rdev = rdev;
+       priv->dev = dev;
+       platform_set_drvdata(pdev, priv);
+
+       return ret;
+
+regerr:
+       rc_unregister_device(rdev);
+       rdev = NULL;
+clkerr:
+       clk_disable_unprepare(priv->clock);
+err:
+       rc_free_device(rdev);
+       dev_err(dev, "Unable to register device (%d)\n", ret);
+       return ret;
+}
+
+static int hix5hd2_ir_remove(struct platform_device *pdev)
+{
+       struct hix5hd2_ir_priv *priv = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(priv->clock);
+       rc_unregister_device(priv->rdev);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int hix5hd2_ir_suspend(struct device *dev)
+{
+       struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(priv->clock);
+       hix5hd2_ir_enable(priv, false);
+
+       return 0;
+}
+
+static int hix5hd2_ir_resume(struct device *dev)
+{
+       struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
+
+       hix5hd2_ir_enable(priv, true);
+       clk_prepare_enable(priv->clock);
+
+       writel_relaxed(0x01, priv->base + IR_ENABLE);
+       writel_relaxed(0x00, priv->base + IR_INTM);
+       writel_relaxed(0xff, priv->base + IR_INTC);
+       writel_relaxed(0x01, priv->base + IR_START);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend,
+                        hix5hd2_ir_resume);
+
+static struct of_device_id hix5hd2_ir_table[] = {
+       { .compatible = "hisilicon,hix5hd2-ir", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, hix5hd2_ir_table);
+
+static struct platform_driver hix5hd2_ir_driver = {
+       .driver = {
+               .name = IR_HIX5HD2_NAME,
+               .of_match_table = hix5hd2_ir_table,
+               .pm     = &hix5hd2_ir_pm_ops,
+       },
+       .probe = hix5hd2_ir_probe,
+       .remove = hix5hd2_ir_remove,
+};
+
+module_platform_driver(hix5hd2_ir_driver);
+
+MODULE_DESCRIPTION("IR controller driver for hix5hd2 platforms");
+MODULE_AUTHOR("Guoxiong Yan <yanguoxiong@huawei.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hix5hd2-ir");
index 447fe35862dc2d5ad00cce91eb7fd61edaf9e4b5..56abf9120cc27491d20857958fa10b1111ce573e 100644 (file)
@@ -1666,7 +1666,6 @@ static int ite_suspend(struct pnp_dev *pdev, pm_message_t state)
 
 static int ite_resume(struct pnp_dev *pdev)
 {
-       int ret = 0;
        struct ite_dev *dev = pnp_get_drvdata(pdev);
        unsigned long flags;
 
@@ -1681,7 +1680,7 @@ static int ite_resume(struct pnp_dev *pdev)
 
        spin_unlock_irqrestore(&dev->lock, flags);
 
-       return ret;
+       return 0;
 }
 
 static void ite_shutdown(struct pnp_dev *pdev)
index 0b8c54919010cec79ac916700374f6eca5849f37..abf60794223d3607fce92d6d94aa1e064e23735c 100644 (file)
@@ -28,6 +28,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
                        rc-dm1105-nec.o \
                        rc-dntv-live-dvb-t.o \
                        rc-dntv-live-dvbt-pro.o \
+                       rc-dvbsky.o \
                        rc-em-terratec.o \
                        rc-encore-enltv2.o \
                        rc-encore-enltv.o \
diff --git a/drivers/media/rc/keymaps/rc-dvbsky.c b/drivers/media/rc/keymaps/rc-dvbsky.c
new file mode 100644 (file)
index 0000000..c5115a1
--- /dev/null
@@ -0,0 +1,78 @@
+/* rc-dvbsky.c - Keytable for DVBSky Remote Controllers
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ *
+ * Copyright (c) 2010-2012 by Nibble Max <nibble.max@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+/*
+ * This table contains the complete RC5 code, instead of just the data part
+ */
+
+static struct rc_map_table rc5_dvbsky[] = {
+       { 0x0000, KEY_0 },
+       { 0x0001, KEY_1 },
+       { 0x0002, KEY_2 },
+       { 0x0003, KEY_3 },
+       { 0x0004, KEY_4 },
+       { 0x0005, KEY_5 },
+       { 0x0006, KEY_6 },
+       { 0x0007, KEY_7 },
+       { 0x0008, KEY_8 },
+       { 0x0009, KEY_9 },
+       { 0x000a, KEY_MUTE },
+       { 0x000d, KEY_OK },
+       { 0x000b, KEY_STOP },
+       { 0x000c, KEY_EXIT },
+       { 0x000e, KEY_CAMERA }, /*Snap shot*/
+       { 0x000f, KEY_SUBTITLE }, /*PIP*/
+       { 0x0010, KEY_VOLUMEUP },
+       { 0x0011, KEY_VOLUMEDOWN },
+       { 0x0012, KEY_FAVORITES },
+       { 0x0013, KEY_LIST }, /*Info*/
+       { 0x0016, KEY_PAUSE },
+       { 0x0017, KEY_PLAY },
+       { 0x001f, KEY_RECORD },
+       { 0x0020, KEY_CHANNELDOWN },
+       { 0x0021, KEY_CHANNELUP },
+       { 0x0025, KEY_POWER2 },
+       { 0x0026, KEY_REWIND },
+       { 0x0027, KEY_FASTFORWARD },
+       { 0x0029, KEY_LAST },
+       { 0x002b, KEY_MENU },
+       { 0x002c, KEY_EPG },
+       { 0x002d, KEY_ZOOM },
+};
+
+static struct rc_map_list rc5_dvbsky_map = {
+       .map = {
+               .scan    = rc5_dvbsky,
+               .size    = ARRAY_SIZE(rc5_dvbsky),
+               .rc_type = RC_TYPE_RC5,
+               .name    = RC_MAP_DVBSKY,
+       }
+};
+
+static int __init init_rc_map_rc5_dvbsky(void)
+{
+       return rc_map_register(&rc5_dvbsky_map);
+}
+
+static void __exit exit_rc_map_rc5_dvbsky(void)
+{
+       rc_map_unregister(&rc5_dvbsky_map);
+}
+
+module_init(init_rc_map_rc5_dvbsky)
+module_exit(exit_rc_map_rc5_dvbsky)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nibble Max <nibble.max@gmail.com>");
index dc5cbffcd5a26a4702b578e97c995e527b644236..249d2fbc8f3748b69fa95ff39e783a458cb18a8f 100644 (file)
@@ -595,7 +595,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
        switch (cmd) {
        case LIRC_GET_FEATURES:
-               result = put_user(ir->d.features, (__u32 *)arg);
+               result = put_user(ir->d.features, (__u32 __user *)arg);
                break;
        case LIRC_GET_REC_MODE:
                if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
@@ -605,7 +605,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                result = put_user(LIRC_REC2MODE
                                  (ir->d.features & LIRC_CAN_REC_MASK),
-                                 (__u32 *)arg);
+                                 (__u32 __user *)arg);
                break;
        case LIRC_SET_REC_MODE:
                if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
@@ -613,7 +613,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                        break;
                }
 
-               result = get_user(mode, (__u32 *)arg);
+               result = get_user(mode, (__u32 __user *)arg);
                if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
                        result = -EINVAL;
                /*
@@ -622,7 +622,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                 */
                break;
        case LIRC_GET_LENGTH:
-               result = put_user(ir->d.code_length, (__u32 *)arg);
+               result = put_user(ir->d.code_length, (__u32 __user *)arg);
                break;
        case LIRC_GET_MIN_TIMEOUT:
                if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
@@ -631,7 +631,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                        break;
                }
 
-               result = put_user(ir->d.min_timeout, (__u32 *)arg);
+               result = put_user(ir->d.min_timeout, (__u32 __user *)arg);
                break;
        case LIRC_GET_MAX_TIMEOUT:
                if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
@@ -640,7 +640,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                        break;
                }
 
-               result = put_user(ir->d.max_timeout, (__u32 *)arg);
+               result = put_user(ir->d.max_timeout, (__u32 __user *)arg);
                break;
        default:
                result = -EINVAL;
@@ -736,7 +736,7 @@ ssize_t lirc_dev_fop_read(struct file *file,
                        }
                } else {
                        lirc_buffer_read(ir->buf, buf);
-                       ret = copy_to_user((void *)buffer+written, buf,
+                       ret = copy_to_user((void __user *)buffer+written, buf,
                                           ir->buf->chunk_size);
                        if (!ret)
                                written += ir->buf->chunk_size;
index 45b0894288e511209a9c63ba314614fc0c382bba..2cdb740cde481e2e8884b7ddb05c90f6f399a954 100644 (file)
@@ -397,6 +397,10 @@ static struct usb_device_id mceusb_dev_table[] = {
          .driver_info = HAUPPAUGE_CX_HYBRID_TV },
        { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb131),
          .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+       { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb138),
+         .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+       { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb139),
+         .driver_info = HAUPPAUGE_CX_HYBRID_TV },
        { USB_DEVICE(VENDOR_PCTV, 0x0259),
          .driver_info = HAUPPAUGE_CX_HYBRID_TV },
        { USB_DEVICE(VENDOR_PCTV, 0x025e),
@@ -1198,10 +1202,9 @@ static void mceusb_flash_led(struct mceusb_dev *ir)
        mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED));
 }
 
-static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir,
-                                        struct usb_interface *intf)
+static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
 {
-       struct usb_device *udev = usb_get_dev(interface_to_usbdev(intf));
+       struct usb_device *udev = ir->usbdev;
        struct device *dev = ir->dev;
        struct rc_dev *rc;
        int ret;
@@ -1341,7 +1344,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
        if (!ir->urb_in)
                goto urb_in_alloc_fail;
 
-       ir->usbdev = dev;
+       ir->usbdev = usb_get_dev(dev);
        ir->dev = &intf->dev;
        ir->len_in = maxp;
        ir->flags.microsoft_gen1 = is_microsoft_gen1;
@@ -1362,7 +1365,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
                snprintf(name + strlen(name), sizeof(name) - strlen(name),
                         " %s", buf);
 
-       ir->rc = mceusb_init_rc_dev(ir, intf);
+       ir->rc = mceusb_init_rc_dev(ir);
        if (!ir->rc)
                goto rc_dev_fail;
 
@@ -1408,6 +1411,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
 
        /* Error-handling path */
 rc_dev_fail:
+       usb_put_dev(ir->usbdev);
        usb_free_urb(ir->urb_in);
 urb_in_alloc_fail:
        usb_free_coherent(dev, maxp, ir->buf_in, ir->dma_in);
@@ -1435,6 +1439,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf)
        usb_kill_urb(ir->urb_in);
        usb_free_urb(ir->urb_in);
        usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
+       usb_put_dev(dev);
 
        kfree(ir);
 }
index 7f4fd859bba5795e209a2c778de94be479e3b1c8..9c2c8635ff3326841178f8f04d65466886424e8b 100644 (file)
@@ -229,7 +229,6 @@ static int nvt_hw_detect(struct nvt_dev *nvt)
 {
        unsigned long flags;
        u8 chip_major, chip_minor;
-       int ret = 0;
        char chip_id[12];
        bool chip_unknown = false;
 
@@ -285,7 +284,7 @@ static int nvt_hw_detect(struct nvt_dev *nvt)
        nvt->chip_minor = chip_minor;
        spin_unlock_irqrestore(&nvt->nvt_lock, flags);
 
-       return ret;
+       return 0;
 }
 
 static void nvt_cir_ldev_init(struct nvt_dev *nvt)
@@ -1177,7 +1176,6 @@ static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state)
 
 static int nvt_resume(struct pnp_dev *pdev)
 {
-       int ret = 0;
        struct nvt_dev *nvt = pnp_get_drvdata(pdev);
 
        nvt_dbg("%s called", __func__);
@@ -1195,7 +1193,7 @@ static int nvt_resume(struct pnp_dev *pdev)
        nvt_cir_regs_init(nvt);
        nvt_cir_wake_regs_init(nvt);
 
-       return ret;
+       return 0;
 }
 
 static void nvt_shutdown(struct pnp_dev *pdev)
index 5c151351afa4c72f48ccf9e48b88abe48cfc8331..0e758ae2e52978b0b76a026d43799a9b75c42fe0 100644 (file)
@@ -22,8 +22,8 @@ struct st_rc_device {
        int                             irq;
        int                             irq_wake;
        struct clk                      *sys_clock;
-       void                            *base;  /* Register base address */
-       void                            *rx_base;/* RX Register base address */
+       volatile void __iomem           *base;  /* Register base address */
+       volatile void __iomem           *rx_base;/* RX Register base address */
        struct rc_dev                   *rdev;
        bool                            overclocking;
        int                             sample_mult;
@@ -267,8 +267,8 @@ static int st_rc_probe(struct platform_device *pdev)
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
        rc_dev->base = devm_ioremap_resource(dev, res);
-       if (IS_ERR(rc_dev->base)) {
-               ret = PTR_ERR(rc_dev->base);
+       if (IS_ERR((__force void *)rc_dev->base)) {
+               ret = PTR_ERR((__force void *)rc_dev->base);
                goto err;
        }
 
@@ -278,7 +278,7 @@ static int st_rc_probe(struct platform_device *pdev)
                rc_dev->rx_base = rc_dev->base;
 
 
-       rc_dev->rstc = reset_control_get(dev, NULL);
+       rc_dev->rstc = reset_control_get_optional(dev, NULL);
        if (IS_ERR(rc_dev->rstc))
                rc_dev->rstc = NULL;
 
@@ -376,9 +376,10 @@ static int st_rc_resume(struct device *dev)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(st_rc_pm_ops, st_rc_suspend, st_rc_resume);
 #endif
 
+static SIMPLE_DEV_PM_OPS(st_rc_pm_ops, st_rc_suspend, st_rc_resume);
+
 #ifdef CONFIG_OF
 static struct of_device_id st_rc_match[] = {
        { .compatible = "st,comms-irb", },
@@ -391,11 +392,8 @@ MODULE_DEVICE_TABLE(of, st_rc_match);
 static struct platform_driver st_rc_driver = {
        .driver = {
                .name = IR_ST_NAME,
-               .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(st_rc_match),
-#ifdef CONFIG_PM
                .pm     = &st_rc_pm_ops,
-#endif
        },
        .probe = st_rc_probe,
        .remove = st_rc_remove,
index 80c4feeb01ea7a47ea52e5b6ccc106b878d045d5..bf4a44272f0e3e00a01c0bc3fb44720e1732ec27 100644 (file)
@@ -362,16 +362,14 @@ static int streamzap_probe(struct usb_interface *intf,
        }
 
        sz->endpoint = &(iface_host->endpoint[0].desc);
-       if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
-           != USB_DIR_IN) {
+       if (!usb_endpoint_dir_in(sz->endpoint)) {
                dev_err(&intf->dev, "%s: endpoint doesn't match input device "
                        "02%02x\n", __func__, sz->endpoint->bEndpointAddress);
                retval = -ENODEV;
                goto free_sz;
        }
 
-       if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-           != USB_ENDPOINT_XFER_INT) {
+       if (!usb_endpoint_xfer_int(sz->endpoint)) {
                dev_err(&intf->dev, "%s: endpoint attributes don't match xfer "
                        "02%02x\n", __func__, sz->endpoint->bmAttributes);
                retval = -ENODEV;
index d79fd1ce5a18b0953d405871cf0c9569adde562a..f039dc2a21cf0fb3538d57fc42905f676b2cbe17 100644 (file)
@@ -204,6 +204,7 @@ config MEDIA_TUNER_FC0013
 config MEDIA_TUNER_TDA18212
        tristate "NXP TDA18212 silicon tuner"
        depends on MEDIA_SUPPORT && I2C
+       select REGMAP_I2C
        default m if !MEDIA_SUBDRV_AUTOSELECT
        help
          NXP TDA18212 silicon tuner driver.
@@ -226,6 +227,7 @@ config MEDIA_TUNER_FC2580
 config MEDIA_TUNER_M88TS2022
        tristate "Montage M88TS2022 silicon tuner"
        depends on MEDIA_SUPPORT && I2C
+       select REGMAP_I2C
        default m if !MEDIA_SUBDRV_AUTOSELECT
        help
          Montage M88TS2022 silicon tuner driver.
@@ -247,6 +249,7 @@ config MEDIA_TUNER_SI2157
 config MEDIA_TUNER_IT913X
        tristate "ITE Tech IT913x silicon tuner"
        depends on MEDIA_SUPPORT && I2C
+       select REGMAP_I2C
        default m if !MEDIA_SUBDRV_AUTOSELECT
        help
          ITE Tech IT913x silicon tuner driver.
@@ -257,4 +260,18 @@ config MEDIA_TUNER_R820T
        default m if !MEDIA_SUBDRV_AUTOSELECT
        help
          Rafael Micro R820T silicon tuner driver.
+
+config MEDIA_TUNER_MXL301RF
+       tristate "MaxLinear MxL301RF tuner"
+       depends on MEDIA_SUPPORT && I2C
+       default m if !MEDIA_SUBDRV_AUTOSELECT
+       help
+         MaxLinear MxL301RF OFDM tuner driver.
+
+config MEDIA_TUNER_QM1D1C0042
+       tristate "Sharp QM1D1C0042 tuner"
+       depends on MEDIA_SUPPORT && I2C
+       default m if !MEDIA_SUBDRV_AUTOSELECT
+       help
+         Sharp QM1D1C0042 trellis coded 8PSK tuner driver.
 endmenu
index 5591699755baced61dc7247e476d605a18a1220e..49fcf8033848dcf2e1029349d624268b907c2fc2 100644 (file)
@@ -37,8 +37,10 @@ obj-$(CONFIG_MEDIA_TUNER_M88TS2022) += m88ts2022.o
 obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
 obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o
 obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o
-obj-$(CONFIG_MEDIA_TUNER_IT913X) += tuner_it913x.o
+obj-$(CONFIG_MEDIA_TUNER_IT913X) += it913x.o
 obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o
+obj-$(CONFIG_MEDIA_TUNER_MXL301RF) += mxl301rf.o
+obj-$(CONFIG_MEDIA_TUNER_QM1D1C0042) += qm1d1c0042.o
 
 ccflags-y += -I$(srctree)/drivers/media/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
index 90d93348f20c5f111aea684ba15b798bc683c6b1..510239f80c0d9f5bb173a8f71d2c784de8b60201 100644 (file)
@@ -26,7 +26,7 @@ static int e4000_init(struct dvb_frontend *fe)
        struct e4000 *s = fe->tuner_priv;
        int ret;
 
-       dev_dbg(&s->client->dev, "%s:\n", __func__);
+       dev_dbg(&s->client->dev, "\n");
 
        /* dummy I2C to ensure I2C wakes up */
        ret = regmap_write(s->regmap, 0x02, 0x40);
@@ -87,7 +87,7 @@ static int e4000_init(struct dvb_frontend *fe)
        s->active = true;
 err:
        if (ret)
-               dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&s->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -97,7 +97,7 @@ static int e4000_sleep(struct dvb_frontend *fe)
        struct e4000 *s = fe->tuner_priv;
        int ret;
 
-       dev_dbg(&s->client->dev, "%s:\n", __func__);
+       dev_dbg(&s->client->dev, "\n");
 
        s->active = false;
 
@@ -106,7 +106,7 @@ static int e4000_sleep(struct dvb_frontend *fe)
                goto err;
 err:
        if (ret)
-               dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&s->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -121,9 +121,8 @@ static int e4000_set_params(struct dvb_frontend *fe)
        u8 buf[5], i_data[4], q_data[4];
 
        dev_dbg(&s->client->dev,
-                       "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n",
-                       __func__, c->delivery_system, c->frequency,
-                       c->bandwidth_hz);
+                       "delivery_system=%d frequency=%u bandwidth_hz=%u\n",
+                       c->delivery_system, c->frequency, c->bandwidth_hz);
 
        /* gain control manual */
        ret = regmap_write(s->regmap, 0x1a, 0x00);
@@ -150,9 +149,8 @@ static int e4000_set_params(struct dvb_frontend *fe)
        buf[3] = 0x00;
        buf[4] = e4000_pll_lut[i].div;
 
-       dev_dbg(&s->client->dev,
-                       "%s: f_vco=%llu pll div=%d sigma_delta=%04x\n",
-                       __func__, f_vco, buf[0], sigma_delta);
+       dev_dbg(&s->client->dev, "f_vco=%llu pll div=%d sigma_delta=%04x\n",
+                       f_vco, buf[0], sigma_delta);
 
        ret = regmap_bulk_write(s->regmap, 0x09, buf, 5);
        if (ret)
@@ -253,7 +251,7 @@ static int e4000_set_params(struct dvb_frontend *fe)
                goto err;
 err:
        if (ret)
-               dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&s->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -262,7 +260,7 @@ static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
        struct e4000 *s = fe->tuner_priv;
 
-       dev_dbg(&s->client->dev, "%s:\n", __func__);
+       dev_dbg(&s->client->dev, "\n");
 
        *frequency = 0; /* Zero-IF */
 
@@ -276,10 +274,9 @@ static int e4000_set_lna_gain(struct dvb_frontend *fe)
        int ret;
        u8 u8tmp;
 
-       dev_dbg(&s->client->dev, "%s: lna auto=%d->%d val=%d->%d\n",
-                       __func__, s->lna_gain_auto->cur.val,
-                       s->lna_gain_auto->val, s->lna_gain->cur.val,
-                       s->lna_gain->val);
+       dev_dbg(&s->client->dev, "lna auto=%d->%d val=%d->%d\n",
+                       s->lna_gain_auto->cur.val, s->lna_gain_auto->val,
+                       s->lna_gain->cur.val, s->lna_gain->val);
 
        if (s->lna_gain_auto->val && s->if_gain_auto->cur.val)
                u8tmp = 0x17;
@@ -301,7 +298,7 @@ static int e4000_set_lna_gain(struct dvb_frontend *fe)
        }
 err:
        if (ret)
-               dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&s->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -312,10 +309,9 @@ static int e4000_set_mixer_gain(struct dvb_frontend *fe)
        int ret;
        u8 u8tmp;
 
-       dev_dbg(&s->client->dev, "%s: mixer auto=%d->%d val=%d->%d\n",
-                       __func__, s->mixer_gain_auto->cur.val,
-                       s->mixer_gain_auto->val, s->mixer_gain->cur.val,
-                       s->mixer_gain->val);
+       dev_dbg(&s->client->dev, "mixer auto=%d->%d val=%d->%d\n",
+                       s->mixer_gain_auto->cur.val, s->mixer_gain_auto->val,
+                       s->mixer_gain->cur.val, s->mixer_gain->val);
 
        if (s->mixer_gain_auto->val)
                u8tmp = 0x15;
@@ -333,7 +329,7 @@ static int e4000_set_mixer_gain(struct dvb_frontend *fe)
        }
 err:
        if (ret)
-               dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&s->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -345,10 +341,9 @@ static int e4000_set_if_gain(struct dvb_frontend *fe)
        u8 buf[2];
        u8 u8tmp;
 
-       dev_dbg(&s->client->dev, "%s: if auto=%d->%d val=%d->%d\n",
-                       __func__, s->if_gain_auto->cur.val,
-                       s->if_gain_auto->val, s->if_gain->cur.val,
-                       s->if_gain->val);
+       dev_dbg(&s->client->dev, "if auto=%d->%d val=%d->%d\n",
+                       s->if_gain_auto->cur.val, s->if_gain_auto->val,
+                       s->if_gain->cur.val, s->if_gain->val);
 
        if (s->if_gain_auto->val && s->lna_gain_auto->cur.val)
                u8tmp = 0x17;
@@ -372,7 +367,7 @@ static int e4000_set_if_gain(struct dvb_frontend *fe)
        }
 err:
        if (ret)
-               dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&s->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -390,7 +385,7 @@ static int e4000_pll_lock(struct dvb_frontend *fe)
        s->pll_lock->val = (utmp & 0x01);
 err:
        if (ret)
-               dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&s->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -400,7 +395,7 @@ static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
        struct e4000 *s = container_of(ctrl->handler, struct e4000, hdl);
        int ret;
 
-       if (s->active == false)
+       if (!s->active)
                return 0;
 
        switch (ctrl->id) {
@@ -408,8 +403,8 @@ static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
                ret = e4000_pll_lock(s->fe);
                break;
        default:
-               dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
-                               __func__, ctrl->id, ctrl->name);
+               dev_dbg(&s->client->dev, "unknown ctrl: id=%d name=%s\n",
+                               ctrl->id, ctrl->name);
                ret = -EINVAL;
        }
 
@@ -423,7 +418,7 @@ static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int ret;
 
-       if (s->active == false)
+       if (!s->active)
                return 0;
 
        switch (ctrl->id) {
@@ -445,8 +440,8 @@ static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
                ret = e4000_set_if_gain(s->fe);
                break;
        default:
-               dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
-                               __func__, ctrl->id, ctrl->name);
+               dev_dbg(&s->client->dev, "unknown ctrl: id=%d name=%s\n",
+                               ctrl->id, ctrl->name);
                ret = -EINVAL;
        }
 
@@ -494,7 +489,7 @@ static int e4000_probe(struct i2c_client *client,
        s = kzalloc(sizeof(struct e4000), GFP_KERNEL);
        if (!s) {
                ret = -ENOMEM;
-               dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+               dev_err(&client->dev, "kzalloc() failed\n");
                goto err;
        }
 
@@ -512,7 +507,7 @@ static int e4000_probe(struct i2c_client *client,
        if (ret)
                goto err;
 
-       dev_dbg(&s->client->dev, "%s: chip id=%02x\n", __func__, utmp);
+       dev_dbg(&s->client->dev, "chip id=%02x\n", utmp);
 
        if (utmp != 0x40) {
                ret = -ENODEV;
@@ -559,9 +554,7 @@ static int e4000_probe(struct i2c_client *client,
        s->sd.ctrl_handler = &s->hdl;
 #endif
 
-       dev_info(&s->client->dev,
-                       "%s: Elonics E4000 successfully identified\n",
-                       KBUILD_MODNAME);
+       dev_info(&s->client->dev, "Elonics E4000 successfully identified\n");
 
        fe->tuner_priv = s;
        memcpy(&fe->ops.tuner_ops, &e4000_tuner_ops,
@@ -573,7 +566,7 @@ static int e4000_probe(struct i2c_client *client,
        return 0;
 err:
        if (ret) {
-               dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&client->dev, "failed=%d\n", ret);
                kfree(s);
        }
 
@@ -586,7 +579,7 @@ static int e4000_remove(struct i2c_client *client)
        struct e4000 *s = container_of(sd, struct e4000, sd);
        struct dvb_frontend *fe = s->fe;
 
-       dev_dbg(&client->dev, "%s:\n", __func__);
+       dev_dbg(&client->dev, "\n");
 
 #if IS_ENABLED(CONFIG_VIDEO_V4L2)
        v4l2_ctrl_handler_free(&s->hdl);
diff --git a/drivers/media/tuners/it913x.c b/drivers/media/tuners/it913x.c
new file mode 100644 (file)
index 0000000..a076c87
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ * ITE IT913X silicon tuner driver
+ *
+ *  Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
+ *  IT9137 Copyright (C) ITE Tech 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 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 "it913x.h"
+#include <linux/regmap.h>
+
+struct it913x_dev {
+       struct i2c_client *client;
+       struct regmap *regmap;
+       struct dvb_frontend *fe;
+       u8 chip_ver:2;
+       u8 role:2;
+       u16 xtal;
+       u8 fdiv;
+       u8 clk_mode;
+       u32 fn_min;
+       bool active;
+};
+
+static int it913x_init(struct dvb_frontend *fe)
+{
+       struct it913x_dev *dev = fe->tuner_priv;
+       int ret;
+       unsigned int utmp;
+       u8 iqik_m_cal, nv_val, buf[2];
+       static const u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2};
+       unsigned long timeout;
+
+       dev_dbg(&dev->client->dev, "role %u\n", dev->role);
+
+       ret = regmap_write(dev->regmap, 0x80ec4c, 0x68);
+       if (ret)
+               goto err;
+
+       usleep_range(10000, 100000);
+
+       ret = regmap_read(dev->regmap, 0x80ec86, &utmp);
+       if (ret)
+               goto err;
+
+       switch (utmp) {
+       case 0:
+               /* 12.000 MHz */
+               dev->clk_mode = utmp;
+               dev->xtal = 2000;
+               dev->fdiv = 3;
+               iqik_m_cal = 16;
+               break;
+       case 1:
+               /* 20.480 MHz */
+               dev->clk_mode = utmp;
+               dev->xtal = 640;
+               dev->fdiv = 1;
+               iqik_m_cal = 6;
+               break;
+       default:
+               dev_err(&dev->client->dev, "unknown clock identifier %d\n", utmp);
+               goto err;
+       }
+
+       ret = regmap_read(dev->regmap, 0x80ed03,  &utmp);
+       if (ret)
+               goto err;
+
+       else if (utmp < ARRAY_SIZE(nv))
+               nv_val = nv[utmp];
+       else
+               nv_val = 2;
+
+       #define TIMEOUT 50
+       timeout = jiffies + msecs_to_jiffies(TIMEOUT);
+       while (!time_after(jiffies, timeout)) {
+               ret = regmap_bulk_read(dev->regmap, 0x80ed23, buf, 2);
+               if (ret)
+                       goto err;
+
+               utmp = (buf[1] << 8) | (buf[0] << 0);
+               if (utmp)
+                       break;
+       }
+
+       dev_dbg(&dev->client->dev, "r_fbc_m_bdry took %u ms, val %u\n",
+                       jiffies_to_msecs(jiffies) -
+                       (jiffies_to_msecs(timeout) - TIMEOUT), utmp);
+
+       dev->fn_min = dev->xtal * utmp;
+       dev->fn_min /= (dev->fdiv * nv_val);
+       dev->fn_min *= 1000;
+       dev_dbg(&dev->client->dev, "fn_min %u\n", dev->fn_min);
+
+       /*
+        * Chip version BX never sets that flag so we just wait 50ms in that
+        * case. It is possible poll BX similarly than AX and then timeout in
+        * order to get 50ms delay, but that causes about 120 extra I2C
+        * messages. As for now, we just wait and reduce IO.
+        */
+       if (dev->chip_ver == 1) {
+               #define TIMEOUT 50
+               timeout = jiffies + msecs_to_jiffies(TIMEOUT);
+               while (!time_after(jiffies, timeout)) {
+                       ret = regmap_read(dev->regmap, 0x80ec82, &utmp);
+                       if (ret)
+                               goto err;
+
+                       if (utmp)
+                               break;
+               }
+
+               dev_dbg(&dev->client->dev, "p_tsm_init_mode took %u ms, val %u\n",
+                               jiffies_to_msecs(jiffies) -
+                               (jiffies_to_msecs(timeout) - TIMEOUT), utmp);
+       } else {
+               msleep(50);
+       }
+
+       ret = regmap_write(dev->regmap, 0x80ed81, iqik_m_cal);
+       if (ret)
+               goto err;
+
+       ret = regmap_write(dev->regmap, 0x80ec57, 0x00);
+       if (ret)
+               goto err;
+
+       ret = regmap_write(dev->regmap, 0x80ec58, 0x00);
+       if (ret)
+               goto err;
+
+       ret = regmap_write(dev->regmap, 0x80ec40, 0x01);
+       if (ret)
+               goto err;
+
+       dev->active = true;
+
+       return 0;
+err:
+       dev_dbg(&dev->client->dev, "failed %d\n", ret);
+       return ret;
+}
+
+static int it913x_sleep(struct dvb_frontend *fe)
+{
+       struct it913x_dev *dev = fe->tuner_priv;
+       int ret, len;
+
+       dev_dbg(&dev->client->dev, "role %u\n", dev->role);
+
+       dev->active = false;
+
+       ret  = regmap_bulk_write(dev->regmap, 0x80ec40, "\x00", 1);
+       if (ret)
+               goto err;
+
+       /*
+        * Writing '0x00' to master tuner register '0x80ec08' causes slave tuner
+        * communication lost. Due to that, we cannot put master full sleep.
+        */
+       if (dev->role == IT913X_ROLE_DUAL_MASTER)
+               len = 4;
+       else
+               len = 15;
+
+       dev_dbg(&dev->client->dev, "role %u, len %d\n", dev->role, len);
+
+       ret = regmap_bulk_write(dev->regmap, 0x80ec02,
+                       "\x3f\x1f\x3f\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+                       len);
+       if (ret)
+               goto err;
+
+       ret = regmap_bulk_write(dev->regmap, 0x80ec12, "\x00\x00\x00\x00", 4);
+       if (ret)
+               goto err;
+
+       ret = regmap_bulk_write(dev->regmap, 0x80ec17,
+                       "\x00\x00\x00\x00\x00\x00\x00\x00\x00", 9);
+       if (ret)
+               goto err;
+
+       ret = regmap_bulk_write(dev->regmap, 0x80ec22,
+                       "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10);
+       if (ret)
+               goto err;
+
+       ret = regmap_bulk_write(dev->regmap, 0x80ec20, "\x00", 1);
+       if (ret)
+               goto err;
+
+       ret = regmap_bulk_write(dev->regmap, 0x80ec3f, "\x01", 1);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       dev_dbg(&dev->client->dev, "failed %d\n", ret);
+       return ret;
+}
+
+static int it913x_set_params(struct dvb_frontend *fe)
+{
+       struct it913x_dev *dev = fe->tuner_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int ret;
+       unsigned int utmp;
+       u32 pre_lo_freq, t_cal_freq;
+       u16 iqik_m_cal, n_div;
+       u8 u8tmp, n, l_band, lna_band;
+
+       dev_dbg(&dev->client->dev, "role=%u, frequency %u, bandwidth_hz %u\n",
+                       dev->role, c->frequency, c->bandwidth_hz);
+
+       if (!dev->active) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (c->frequency <=         74000000) {
+               n_div = 48;
+               n = 0;
+       } else if (c->frequency <= 111000000) {
+               n_div = 32;
+               n = 1;
+       } else if (c->frequency <= 148000000) {
+               n_div = 24;
+               n = 2;
+       } else if (c->frequency <= 222000000) {
+               n_div = 16;
+               n = 3;
+       } else if (c->frequency <= 296000000) {
+               n_div = 12;
+               n = 4;
+       } else if (c->frequency <= 445000000) {
+               n_div = 8;
+               n = 5;
+       } else if (c->frequency <= dev->fn_min) {
+               n_div = 6;
+               n = 6;
+       } else if (c->frequency <= 950000000) {
+               n_div = 4;
+               n = 7;
+       } else {
+               n_div = 2;
+               n = 0;
+       }
+
+       ret = regmap_read(dev->regmap, 0x80ed81, &utmp);
+       if (ret)
+               goto err;
+
+       iqik_m_cal = utmp * n_div;
+
+       if (utmp < 0x20) {
+               if (dev->clk_mode == 0)
+                       iqik_m_cal = (iqik_m_cal * 9) >> 5;
+               else
+                       iqik_m_cal >>= 1;
+       } else {
+               iqik_m_cal = 0x40 - iqik_m_cal;
+               if (dev->clk_mode == 0)
+                       iqik_m_cal = ~((iqik_m_cal * 9) >> 5);
+               else
+                       iqik_m_cal = ~(iqik_m_cal >> 1);
+       }
+
+       t_cal_freq = (c->frequency / 1000) * n_div * dev->fdiv;
+       pre_lo_freq = t_cal_freq / dev->xtal;
+       utmp = pre_lo_freq * dev->xtal;
+
+       if ((t_cal_freq - utmp) >= (dev->xtal >> 1))
+               pre_lo_freq++;
+
+       pre_lo_freq += (u32) n << 13;
+       /* Frequency OMEGA_IQIK_M_CAL_MID*/
+       t_cal_freq = pre_lo_freq + (u32)iqik_m_cal;
+       dev_dbg(&dev->client->dev, "t_cal_freq %u, pre_lo_freq %u\n",
+                       t_cal_freq, pre_lo_freq);
+
+       if (c->frequency <=         440000000) {
+               l_band = 0;
+               lna_band = 0;
+       } else if (c->frequency <=  484000000) {
+               l_band = 1;
+               lna_band = 1;
+       } else if (c->frequency <=  533000000) {
+               l_band = 1;
+               lna_band = 2;
+       } else if (c->frequency <=  587000000) {
+               l_band = 1;
+               lna_band = 3;
+       } else if (c->frequency <=  645000000) {
+               l_band = 1;
+               lna_band = 4;
+       } else if (c->frequency <=  710000000) {
+               l_band = 1;
+               lna_band = 5;
+       } else if (c->frequency <=  782000000) {
+               l_band = 1;
+               lna_band = 6;
+       } else if (c->frequency <=  860000000) {
+               l_band = 1;
+               lna_band = 7;
+       } else if (c->frequency <= 1492000000) {
+               l_band = 1;
+               lna_band = 0;
+       } else if (c->frequency <= 1685000000) {
+               l_band = 1;
+               lna_band = 1;
+       } else {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* XXX: latest windows driver does not set that at all */
+       ret = regmap_write(dev->regmap, 0x80ee06, lna_band);
+       if (ret)
+               goto err;
+
+       if (c->bandwidth_hz <=      5000000)
+               u8tmp = 0;
+       else if (c->bandwidth_hz <= 6000000)
+               u8tmp = 2;
+       else if (c->bandwidth_hz <= 7000000)
+               u8tmp = 4;
+       else
+               u8tmp = 6;       /* 8000000 */
+
+       ret = regmap_write(dev->regmap, 0x80ec56, u8tmp);
+       if (ret)
+               goto err;
+
+       /* XXX: latest windows driver sets different value (a8 != 68) */
+       ret = regmap_write(dev->regmap, 0x80ec4c, 0xa0 | (l_band << 3));
+       if (ret)
+               goto err;
+
+       ret = regmap_write(dev->regmap, 0x80ec4d, (t_cal_freq >> 0) & 0xff);
+       if (ret)
+               goto err;
+
+       ret = regmap_write(dev->regmap, 0x80ec4e, (t_cal_freq >> 8) & 0xff);
+       if (ret)
+               goto err;
+
+       ret = regmap_write(dev->regmap, 0x80011e, (pre_lo_freq >> 0) & 0xff);
+       if (ret)
+               goto err;
+
+       ret = regmap_write(dev->regmap, 0x80011f, (pre_lo_freq >> 8) & 0xff);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       dev_dbg(&dev->client->dev, "failed %d\n", ret);
+       return ret;
+}
+
+static const struct dvb_tuner_ops it913x_tuner_ops = {
+       .info = {
+               .name           = "ITE IT913X",
+               .frequency_min  = 174000000,
+               .frequency_max  = 862000000,
+       },
+
+       .init = it913x_init,
+       .sleep = it913x_sleep,
+       .set_params = it913x_set_params,
+};
+
+static int it913x_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct it913x_config *cfg = client->dev.platform_data;
+       struct dvb_frontend *fe = cfg->fe;
+       struct it913x_dev *dev;
+       int ret;
+       char *chip_ver_str;
+       static const struct regmap_config regmap_config = {
+               .reg_bits = 24,
+               .val_bits = 8,
+       };
+
+       dev = kzalloc(sizeof(struct it913x_dev), GFP_KERNEL);
+       if (dev == NULL) {
+               ret = -ENOMEM;
+               dev_err(&client->dev, "kzalloc() failed\n");
+               goto err;
+       }
+
+       dev->client = client;
+       dev->fe = cfg->fe;
+       dev->chip_ver = cfg->chip_ver;
+       dev->role = cfg->role;
+       dev->regmap = regmap_init_i2c(client, &regmap_config);
+       if (IS_ERR(dev->regmap)) {
+               ret = PTR_ERR(dev->regmap);
+               goto err_kfree;
+       }
+
+       fe->tuner_priv = dev;
+       memcpy(&fe->ops.tuner_ops, &it913x_tuner_ops,
+                       sizeof(struct dvb_tuner_ops));
+       i2c_set_clientdata(client, dev);
+
+       if (dev->chip_ver == 1)
+               chip_ver_str = "AX";
+       else if (dev->chip_ver == 2)
+               chip_ver_str = "BX";
+       else
+               chip_ver_str = "??";
+
+       dev_info(&dev->client->dev, "ITE IT913X %s successfully attached\n",
+                       chip_ver_str);
+       dev_dbg(&dev->client->dev, "chip_ver %u, role %u\n",
+                       dev->chip_ver, dev->role);
+       return 0;
+
+err_kfree:
+       kfree(dev);
+err:
+       dev_dbg(&client->dev, "failed %d\n", ret);
+       return ret;
+}
+
+static int it913x_remove(struct i2c_client *client)
+{
+       struct it913x_dev *dev = i2c_get_clientdata(client);
+       struct dvb_frontend *fe = dev->fe;
+
+       dev_dbg(&client->dev, "\n");
+
+       memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
+       fe->tuner_priv = NULL;
+       regmap_exit(dev->regmap);
+       kfree(dev);
+
+       return 0;
+}
+
+static const struct i2c_device_id it913x_id_table[] = {
+       {"it913x", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, it913x_id_table);
+
+static struct i2c_driver it913x_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "it913x",
+       },
+       .probe          = it913x_probe,
+       .remove         = it913x_remove,
+       .id_table       = it913x_id_table,
+};
+
+module_i2c_driver(it913x_driver);
+
+MODULE_DESCRIPTION("ITE IT913X silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
similarity index 67%
rename from drivers/media/tuners/tuner_it913x.h
rename to drivers/media/tuners/it913x.h
index 12dd36bd9e79f6a6e4b99d3a3c50a8aa1f015204..33de53d4a566e1dd63b0de7e6883b7ebb16d7fe7 100644 (file)
 
 #include "dvb_frontend.h"
 
-#if defined(CONFIG_MEDIA_TUNER_IT913X) || \
-       (defined(CONFIG_MEDIA_TUNER_IT913X_MODULE) && defined(MODULE))
-extern struct dvb_frontend *it913x_attach(struct dvb_frontend *fe,
-       struct i2c_adapter *i2c_adap,
-       u8 i2c_addr,
-       u8 config);
-#else
-static inline struct dvb_frontend *it913x_attach(struct dvb_frontend *fe,
-       struct i2c_adapter *i2c_adap,
-       u8 i2c_addr,
-       u8 config)
-{
-       pr_warn("%s: driver disabled by Kconfig\n", __func__);
-       return NULL;
-}
-#endif
+/*
+ * I2C address
+ * 0x38, 0x3a, 0x3c, 0x3e
+ */
+struct it913x_config {
+       /*
+        * pointer to DVB frontend
+        */
+       struct dvb_frontend *fe;
+
+       /*
+        * chip version
+        * 1 = IT9135 AX
+        * 2 = IT9135 BX
+        */
+       unsigned int chip_ver:2;
+
+       /*
+        * tuner role
+        */
+#define IT913X_ROLE_SINGLE         0
+#define IT913X_ROLE_DUAL_MASTER    1
+#define IT913X_ROLE_DUAL_SLAVE     2
+       unsigned int role:2;
+};
 
 #endif
index 40c42dec721b9de556dacc6cadc12a80ba3cc06b..caa542346891f630074ec26d1aa11d046f85e261 100644 (file)
 
 #include "m88ts2022_priv.h"
 
-/* write multiple registers */
-static int m88ts2022_wr_regs(struct m88ts2022_priv *priv,
-               u8 reg, const u8 *val, int len)
+static int m88ts2022_cmd(struct m88ts2022_dev *dev, int op, int sleep, u8 reg,
+               u8 mask, u8 val, u8 *reg_val)
 {
-#define MAX_WR_LEN 3
-#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1)
-       int ret;
-       u8 buf[MAX_WR_XFER_LEN];
-       struct i2c_msg msg[1] = {
-               {
-                       .addr = priv->client->addr,
-                       .flags = 0,
-                       .len = 1 + len,
-                       .buf = buf,
-               }
-       };
-
-       if (WARN_ON(len > MAX_WR_LEN))
-               return -EINVAL;
-
-       buf[0] = reg;
-       memcpy(&buf[1], val, len);
-
-       ret = i2c_transfer(priv->client->adapter, msg, 1);
-       if (ret == 1) {
-               ret = 0;
-       } else {
-               dev_warn(&priv->client->dev,
-                               "%s: i2c wr failed=%d reg=%02x len=%d\n",
-                               KBUILD_MODNAME, ret, reg, len);
-               ret = -EREMOTEIO;
-       }
-
-       return ret;
-}
-
-/* read multiple registers */
-static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg,
-               u8 *val, int len)
-{
-#define MAX_RD_LEN 1
-#define MAX_RD_XFER_LEN (MAX_RD_LEN)
-       int ret;
-       u8 buf[MAX_RD_XFER_LEN];
-       struct i2c_msg msg[2] = {
-               {
-                       .addr = priv->client->addr,
-                       .flags = 0,
-                       .len = 1,
-                       .buf = &reg,
-               }, {
-                       .addr = priv->client->addr,
-                       .flags = I2C_M_RD,
-                       .len = len,
-                       .buf = buf,
-               }
-       };
-
-       if (WARN_ON(len > MAX_RD_LEN))
-               return -EINVAL;
-
-       ret = i2c_transfer(priv->client->adapter, msg, 2);
-       if (ret == 2) {
-               memcpy(val, buf, len);
-               ret = 0;
-       } else {
-               dev_warn(&priv->client->dev,
-                               "%s: i2c rd failed=%d reg=%02x len=%d\n",
-                               KBUILD_MODNAME, ret, reg, len);
-               ret = -EREMOTEIO;
-       }
-
-       return ret;
-}
-
-/* write single register */
-static int m88ts2022_wr_reg(struct m88ts2022_priv *priv, u8 reg, u8 val)
-{
-       return m88ts2022_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int m88ts2022_rd_reg(struct m88ts2022_priv *priv, u8 reg, u8 *val)
-{
-       return m88ts2022_rd_regs(priv, reg, val, 1);
-}
-
-/* write single register with mask */
-static int m88ts2022_wr_reg_mask(struct m88ts2022_priv *priv,
-               u8 reg, u8 val, u8 mask)
-{
-       int ret;
-       u8 u8tmp;
-
-       /* no need for read if whole reg is written */
-       if (mask != 0xff) {
-               ret = m88ts2022_rd_regs(priv, reg, &u8tmp, 1);
-               if (ret)
-                       return ret;
-
-               val &= mask;
-               u8tmp &= ~mask;
-               val |= u8tmp;
-       }
-
-       return m88ts2022_wr_regs(priv, reg, &val, 1);
-}
-
-static int m88ts2022_cmd(struct dvb_frontend *fe,
-               int op, int sleep, u8 reg, u8 mask, u8 val, u8 *reg_val)
-{
-       struct m88ts2022_priv *priv = fe->tuner_priv;
        int ret, i;
-       u8 u8tmp;
+       unsigned int utmp;
        struct m88ts2022_reg_val reg_vals[] = {
                {0x51, 0x1f - op},
                {0x51, 0x1f},
@@ -140,12 +31,12 @@ static int m88ts2022_cmd(struct dvb_frontend *fe,
        };
 
        for (i = 0; i < 2; i++) {
-               dev_dbg(&priv->client->dev,
-                               "%s: i=%d op=%02x reg=%02x mask=%02x val=%02x\n",
-                               __func__, i, op, reg, mask, val);
+               dev_dbg(&dev->client->dev,
+                               "i=%d op=%02x reg=%02x mask=%02x val=%02x\n",
+                               i, op, reg, mask, val);
 
                for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
-                       ret = m88ts2022_wr_reg(priv, reg_vals[i].reg,
+                       ret = regmap_write(dev->regmap, reg_vals[i].reg,
                                        reg_vals[i].val);
                        if (ret)
                                goto err;
@@ -153,37 +44,38 @@ static int m88ts2022_cmd(struct dvb_frontend *fe,
 
                usleep_range(sleep * 1000, sleep * 10000);
 
-               ret = m88ts2022_rd_reg(priv, reg, &u8tmp);
+               ret = regmap_read(dev->regmap, reg, &utmp);
                if (ret)
                        goto err;
 
-               if ((u8tmp & mask) != val)
+               if ((utmp & mask) != val)
                        break;
        }
 
        if (reg_val)
-               *reg_val = u8tmp;
+               *reg_val = utmp;
 err:
        return ret;
 }
 
 static int m88ts2022_set_params(struct dvb_frontend *fe)
 {
-       struct m88ts2022_priv *priv = fe->tuner_priv;
+       struct m88ts2022_dev *dev = fe->tuner_priv;
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int ret;
-       unsigned int frequency_khz, frequency_offset_khz, f_3db_hz;
+       unsigned int utmp, frequency_khz, frequency_offset_khz, f_3db_hz;
        unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n, gdiv28;
        u8 buf[3], u8tmp, cap_code, lpf_gm, lpf_mxdiv, div_max, div_min;
        u16 u16tmp;
-       dev_dbg(&priv->client->dev,
-                       "%s: frequency=%d symbol_rate=%d rolloff=%d\n",
-                       __func__, c->frequency, c->symbol_rate, c->rolloff);
+
+       dev_dbg(&dev->client->dev,
+                       "frequency=%d symbol_rate=%d rolloff=%d\n",
+                       c->frequency, c->symbol_rate, c->rolloff);
        /*
         * Integer-N PLL synthesizer
         * kHz is used for all calculations to keep calculations within 32-bit
         */
-       f_ref_khz = DIV_ROUND_CLOSEST(priv->cfg.clock, 1000);
+       f_ref_khz = DIV_ROUND_CLOSEST(dev->cfg.clock, 1000);
        div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000);
 
        if (c->symbol_rate < 5000000)
@@ -203,14 +95,14 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
 
        buf[0] = u8tmp;
        buf[1] = 0x40;
-       ret = m88ts2022_wr_regs(priv, 0x10, buf, 2);
+       ret = regmap_bulk_write(dev->regmap, 0x10, buf, 2);
        if (ret)
                goto err;
 
        f_vco_khz = frequency_khz * div_out;
        pll_n = f_vco_khz * div_ref / f_ref_khz;
        pll_n += pll_n % 2;
-       priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out;
+       dev->frequency_khz = pll_n * f_ref_khz / div_ref / div_out;
 
        if (pll_n < 4095)
                u16tmp = pll_n - 1024;
@@ -222,88 +114,87 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
        buf[0] = (u16tmp >> 8) & 0x3f;
        buf[1] = (u16tmp >> 0) & 0xff;
        buf[2] = div_ref - 8;
-       ret = m88ts2022_wr_regs(priv, 0x01, buf, 3);
+       ret = regmap_bulk_write(dev->regmap, 0x01, buf, 3);
        if (ret)
                goto err;
 
-       dev_dbg(&priv->client->dev,
-                       "%s: frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
-                       __func__, priv->frequency_khz,
-                       priv->frequency_khz - c->frequency, f_vco_khz, pll_n,
-                       div_ref, div_out);
+       dev_dbg(&dev->client->dev,
+                       "frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
+                       dev->frequency_khz, dev->frequency_khz - c->frequency,
+                       f_vco_khz, pll_n, div_ref, div_out);
 
-       ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL);
+       ret = m88ts2022_cmd(dev, 0x10, 5, 0x15, 0x40, 0x00, NULL);
        if (ret)
                goto err;
 
-       ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp);
+       ret = regmap_read(dev->regmap, 0x14, &utmp);
        if (ret)
                goto err;
 
-       u8tmp &= 0x7f;
-       if (u8tmp < 64) {
-               ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x80, 0x80);
+       utmp &= 0x7f;
+       if (utmp < 64) {
+               ret = regmap_update_bits(dev->regmap, 0x10, 0x80, 0x80);
                if (ret)
                        goto err;
 
-               ret = m88ts2022_wr_reg(priv, 0x11, 0x6f);
+               ret = regmap_write(dev->regmap, 0x11, 0x6f);
                if (ret)
                        goto err;
 
-               ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL);
+               ret = m88ts2022_cmd(dev, 0x10, 5, 0x15, 0x40, 0x00, NULL);
                if (ret)
                        goto err;
        }
 
-       ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp);
+       ret = regmap_read(dev->regmap, 0x14, &utmp);
        if (ret)
                goto err;
 
-       u8tmp &= 0x1f;
-       if (u8tmp > 19) {
-               ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x00, 0x02);
+       utmp &= 0x1f;
+       if (utmp > 19) {
+               ret = regmap_update_bits(dev->regmap, 0x10, 0x02, 0x00);
                if (ret)
                        goto err;
        }
 
-       ret = m88ts2022_cmd(fe, 0x08, 5, 0x3c, 0xff, 0x00, NULL);
+       ret = m88ts2022_cmd(dev, 0x08, 5, 0x3c, 0xff, 0x00, NULL);
        if (ret)
                goto err;
 
-       ret = m88ts2022_wr_reg(priv, 0x25, 0x00);
+       ret = regmap_write(dev->regmap, 0x25, 0x00);
        if (ret)
                goto err;
 
-       ret = m88ts2022_wr_reg(priv, 0x27, 0x70);
+       ret = regmap_write(dev->regmap, 0x27, 0x70);
        if (ret)
                goto err;
 
-       ret = m88ts2022_wr_reg(priv, 0x41, 0x09);
+       ret = regmap_write(dev->regmap, 0x41, 0x09);
        if (ret)
                goto err;
 
-       ret = m88ts2022_wr_reg(priv, 0x08, 0x0b);
+       ret = regmap_write(dev->regmap, 0x08, 0x0b);
        if (ret)
                goto err;
 
        /* filters */
        gdiv28 = DIV_ROUND_CLOSEST(f_ref_khz * 1694U, 1000000U);
 
-       ret = m88ts2022_wr_reg(priv, 0x04, gdiv28);
+       ret = regmap_write(dev->regmap, 0x04, gdiv28);
        if (ret)
                goto err;
 
-       ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
+       ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
        if (ret)
                goto err;
 
        cap_code = u8tmp & 0x3f;
 
-       ret = m88ts2022_wr_reg(priv, 0x41, 0x0d);
+       ret = regmap_write(dev->regmap, 0x41, 0x0d);
        if (ret)
                goto err;
 
-       ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
+       ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
        if (ret)
                goto err;
 
@@ -314,7 +205,7 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
        div_min = gdiv28 * 78 / 100;
        div_max = clamp_val(div_max, 0U, 63U);
 
-       f_3db_hz = c->symbol_rate * 135UL / 200UL;
+       f_3db_hz = mult_frac(c->symbol_rate, 135, 200);
        f_3db_hz +=  2000000U + (frequency_offset_khz * 1000U);
        f_3db_hz = clamp(f_3db_hz, 7000000U, 40000000U);
 
@@ -327,25 +218,25 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
                lpf_mxdiv = DIV_ROUND_CLOSEST(++lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz);
        lpf_mxdiv = clamp_val(lpf_mxdiv, 0U, div_max);
 
-       ret = m88ts2022_wr_reg(priv, 0x04, lpf_mxdiv);
+       ret = regmap_write(dev->regmap, 0x04, lpf_mxdiv);
        if (ret)
                goto err;
 
-       ret = m88ts2022_wr_reg(priv, 0x06, lpf_gm);
+       ret = regmap_write(dev->regmap, 0x06, lpf_gm);
        if (ret)
                goto err;
 
-       ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
+       ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
        if (ret)
                goto err;
 
        cap_code = u8tmp & 0x3f;
 
-       ret = m88ts2022_wr_reg(priv, 0x41, 0x09);
+       ret = regmap_write(dev->regmap, 0x41, 0x09);
        if (ret)
                goto err;
 
-       ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
+       ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
        if (ret)
                goto err;
 
@@ -353,31 +244,31 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
        cap_code = (cap_code + u8tmp) / 2;
 
        u8tmp = cap_code | 0x80;
-       ret = m88ts2022_wr_reg(priv, 0x25, u8tmp);
+       ret = regmap_write(dev->regmap, 0x25, u8tmp);
        if (ret)
                goto err;
 
-       ret = m88ts2022_wr_reg(priv, 0x27, 0x30);
+       ret = regmap_write(dev->regmap, 0x27, 0x30);
        if (ret)
                goto err;
 
-       ret = m88ts2022_wr_reg(priv, 0x08, 0x09);
+       ret = regmap_write(dev->regmap, 0x08, 0x09);
        if (ret)
                goto err;
 
-       ret = m88ts2022_cmd(fe, 0x01, 20, 0x21, 0xff, 0x00, NULL);
+       ret = m88ts2022_cmd(dev, 0x01, 20, 0x21, 0xff, 0x00, NULL);
        if (ret)
                goto err;
 err:
        if (ret)
-               dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
 static int m88ts2022_init(struct dvb_frontend *fe)
 {
-       struct m88ts2022_priv *priv = fe->tuner_priv;
+       struct m88ts2022_dev *dev = fe->tuner_priv;
        int ret, i;
        u8 u8tmp;
        static const struct m88ts2022_reg_val reg_vals[] = {
@@ -393,23 +284,24 @@ static int m88ts2022_init(struct dvb_frontend *fe)
                {0x24, 0x02},
                {0x12, 0xa0},
        };
-       dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
-       ret = m88ts2022_wr_reg(priv, 0x00, 0x01);
+       dev_dbg(&dev->client->dev, "\n");
+
+       ret = regmap_write(dev->regmap, 0x00, 0x01);
        if (ret)
                goto err;
 
-       ret = m88ts2022_wr_reg(priv, 0x00, 0x03);
+       ret = regmap_write(dev->regmap, 0x00, 0x03);
        if (ret)
                goto err;
 
-       switch (priv->cfg.clock_out) {
+       switch (dev->cfg.clock_out) {
        case M88TS2022_CLOCK_OUT_DISABLED:
                u8tmp = 0x60;
                break;
        case M88TS2022_CLOCK_OUT_ENABLED:
                u8tmp = 0x70;
-               ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div);
+               ret = regmap_write(dev->regmap, 0x05, dev->cfg.clock_out_div);
                if (ret)
                        goto err;
                break;
@@ -420,58 +312,61 @@ static int m88ts2022_init(struct dvb_frontend *fe)
                goto err;
        }
 
-       ret = m88ts2022_wr_reg(priv, 0x42, u8tmp);
+       ret = regmap_write(dev->regmap, 0x42, u8tmp);
        if (ret)
                goto err;
 
-       if (priv->cfg.loop_through)
+       if (dev->cfg.loop_through)
                u8tmp = 0xec;
        else
                u8tmp = 0x6c;
 
-       ret = m88ts2022_wr_reg(priv, 0x62, u8tmp);
+       ret = regmap_write(dev->regmap, 0x62, u8tmp);
        if (ret)
                goto err;
 
        for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
-               ret = m88ts2022_wr_reg(priv, reg_vals[i].reg, reg_vals[i].val);
+               ret = regmap_write(dev->regmap, reg_vals[i].reg, reg_vals[i].val);
                if (ret)
                        goto err;
        }
 err:
        if (ret)
-               dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&dev->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
 static int m88ts2022_sleep(struct dvb_frontend *fe)
 {
-       struct m88ts2022_priv *priv = fe->tuner_priv;
+       struct m88ts2022_dev *dev = fe->tuner_priv;
        int ret;
-       dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
-       ret = m88ts2022_wr_reg(priv, 0x00, 0x00);
+       dev_dbg(&dev->client->dev, "\n");
+
+       ret = regmap_write(dev->regmap, 0x00, 0x00);
        if (ret)
                goto err;
 err:
        if (ret)
-               dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&dev->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
 static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
-       struct m88ts2022_priv *priv = fe->tuner_priv;
-       dev_dbg(&priv->client->dev, "%s:\n", __func__);
+       struct m88ts2022_dev *dev = fe->tuner_priv;
 
-       *frequency = priv->frequency_khz;
+       dev_dbg(&dev->client->dev, "\n");
+
+       *frequency = dev->frequency_khz;
        return 0;
 }
 
 static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
-       struct m88ts2022_priv *priv = fe->tuner_priv;
-       dev_dbg(&priv->client->dev, "%s:\n", __func__);
+       struct m88ts2022_dev *dev = fe->tuner_priv;
+
+       dev_dbg(&dev->client->dev, "\n");
 
        *frequency = 0; /* Zero-IF */
        return 0;
@@ -479,31 +374,30 @@ static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 
 static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
 {
-       struct m88ts2022_priv *priv = fe->tuner_priv;
+       struct m88ts2022_dev *dev = fe->tuner_priv;
        int ret;
-       u8 u8tmp;
        u16 gain, u16tmp;
-       unsigned int gain1, gain2, gain3;
+       unsigned int utmp, gain1, gain2, gain3;
 
-       ret = m88ts2022_rd_reg(priv, 0x3d, &u8tmp);
+       ret = regmap_read(dev->regmap, 0x3d, &utmp);
        if (ret)
                goto err;
 
-       gain1 = (u8tmp >> 0) & 0x1f;
+       gain1 = (utmp >> 0) & 0x1f;
        gain1 = clamp(gain1, 0U, 15U);
 
-       ret = m88ts2022_rd_reg(priv, 0x21, &u8tmp);
+       ret = regmap_read(dev->regmap, 0x21, &utmp);
        if (ret)
                goto err;
 
-       gain2 = (u8tmp >> 0) & 0x1f;
+       gain2 = (utmp >> 0) & 0x1f;
        gain2 = clamp(gain2, 2U, 16U);
 
-       ret = m88ts2022_rd_reg(priv, 0x66, &u8tmp);
+       ret = regmap_read(dev->regmap, 0x66, &utmp);
        if (ret)
                goto err;
 
-       gain3 = (u8tmp >> 3) & 0x07;
+       gain3 = (utmp >> 3) & 0x07;
        gain3 = clamp(gain3, 0U, 6U);
 
        gain = gain1 * 265 + gain2 * 338 + gain3 * 285;
@@ -515,7 +409,7 @@ static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
        *strength = (u16tmp - 59000) * 0xffff / (61500 - 59000);
 err:
        if (ret)
-               dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&dev->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -540,46 +434,56 @@ static int m88ts2022_probe(struct i2c_client *client,
 {
        struct m88ts2022_config *cfg = client->dev.platform_data;
        struct dvb_frontend *fe = cfg->fe;
-       struct m88ts2022_priv *priv;
+       struct m88ts2022_dev *dev;
        int ret;
-       u8 chip_id, u8tmp;
+       u8 u8tmp;
+       unsigned int utmp;
+       static const struct regmap_config regmap_config = {
+               .reg_bits = 8,
+               .val_bits = 8,
+       };
 
-       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-       if (!priv) {
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev) {
                ret = -ENOMEM;
-               dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+               dev_err(&client->dev, "kzalloc() failed\n");
                goto err;
        }
 
-       memcpy(&priv->cfg, cfg, sizeof(struct m88ts2022_config));
-       priv->client = client;
+       memcpy(&dev->cfg, cfg, sizeof(struct m88ts2022_config));
+       dev->client = client;
+       dev->regmap = devm_regmap_init_i2c(client, &regmap_config);
+       if (IS_ERR(dev->regmap)) {
+               ret = PTR_ERR(dev->regmap);
+               goto err;
+       }
 
        /* check if the tuner is there */
-       ret = m88ts2022_rd_reg(priv, 0x00, &u8tmp);
+       ret = regmap_read(dev->regmap, 0x00, &utmp);
        if (ret)
                goto err;
 
-       if ((u8tmp & 0x03) == 0x00) {
-               ret = m88ts2022_wr_reg(priv, 0x00, 0x01);
-               if (ret < 0)
+       if ((utmp & 0x03) == 0x00) {
+               ret = regmap_write(dev->regmap, 0x00, 0x01);
+               if (ret)
                        goto err;
 
                usleep_range(2000, 50000);
        }
 
-       ret = m88ts2022_wr_reg(priv, 0x00, 0x03);
+       ret = regmap_write(dev->regmap, 0x00, 0x03);
        if (ret)
                goto err;
 
        usleep_range(2000, 50000);
 
-       ret = m88ts2022_rd_reg(priv, 0x00, &chip_id);
+       ret = regmap_read(dev->regmap, 0x00, &utmp);
        if (ret)
                goto err;
 
-       dev_dbg(&priv->client->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+       dev_dbg(&dev->client->dev, "chip_id=%02x\n", utmp);
 
-       switch (chip_id) {
+       switch (utmp) {
        case 0xc3:
        case 0x83:
                break;
@@ -587,13 +491,13 @@ static int m88ts2022_probe(struct i2c_client *client,
                goto err;
        }
 
-       switch (priv->cfg.clock_out) {
+       switch (dev->cfg.clock_out) {
        case M88TS2022_CLOCK_OUT_DISABLED:
                u8tmp = 0x60;
                break;
        case M88TS2022_CLOCK_OUT_ENABLED:
                u8tmp = 0x70;
-               ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div);
+               ret = regmap_write(dev->regmap, 0x05, dev->cfg.clock_out_div);
                if (ret)
                        goto err;
                break;
@@ -604,49 +508,48 @@ static int m88ts2022_probe(struct i2c_client *client,
                goto err;
        }
 
-       ret = m88ts2022_wr_reg(priv, 0x42, u8tmp);
+       ret = regmap_write(dev->regmap, 0x42, u8tmp);
        if (ret)
                goto err;
 
-       if (priv->cfg.loop_through)
+       if (dev->cfg.loop_through)
                u8tmp = 0xec;
        else
                u8tmp = 0x6c;
 
-       ret = m88ts2022_wr_reg(priv, 0x62, u8tmp);
+       ret = regmap_write(dev->regmap, 0x62, u8tmp);
        if (ret)
                goto err;
 
        /* sleep */
-       ret = m88ts2022_wr_reg(priv, 0x00, 0x00);
+       ret = regmap_write(dev->regmap, 0x00, 0x00);
        if (ret)
                goto err;
 
-       dev_info(&priv->client->dev,
-                       "%s: Montage M88TS2022 successfully identified\n",
-                       KBUILD_MODNAME);
+       dev_info(&dev->client->dev, "Montage M88TS2022 successfully identified\n");
 
-       fe->tuner_priv = priv;
+       fe->tuner_priv = dev;
        memcpy(&fe->ops.tuner_ops, &m88ts2022_tuner_ops,
                        sizeof(struct dvb_tuner_ops));
 
-       i2c_set_clientdata(client, priv);
+       i2c_set_clientdata(client, dev);
        return 0;
 err:
-       dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
-       kfree(priv);
+       dev_dbg(&client->dev, "failed=%d\n", ret);
+       kfree(dev);
        return ret;
 }
 
 static int m88ts2022_remove(struct i2c_client *client)
 {
-       struct m88ts2022_priv *priv = i2c_get_clientdata(client);
-       struct dvb_frontend *fe = priv->cfg.fe;
-       dev_dbg(&client->dev, "%s:\n", __func__);
+       struct m88ts2022_dev *dev = i2c_get_clientdata(client);
+       struct dvb_frontend *fe = dev->cfg.fe;
+
+       dev_dbg(&client->dev, "\n");
 
        memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
        fe->tuner_priv = NULL;
-       kfree(priv);
+       kfree(dev);
 
        return 0;
 }
index 0363dd866a2dab01ba9a19c2e7d4a85d59e53a99..feeb5ad6beef879c357612e708cc6c8e265c7f75 100644 (file)
 #define M88TS2022_PRIV_H
 
 #include "m88ts2022.h"
+#include <linux/regmap.h>
 
-struct m88ts2022_priv {
+struct m88ts2022_dev {
        struct m88ts2022_config cfg;
        struct i2c_client *client;
-       struct dvb_frontend *fe;
+       struct regmap *regmap;
        u32 frequency_khz;
 };
 
index ee99e372c943455a9aec8439dc3cab06c848867c..26019e73199314b779e788bfd6a57905c9c68457 100644 (file)
@@ -67,7 +67,8 @@ static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain,
 {
        int ret;
        u32 reg;
-       dev_dbg(&s->spi->dev, "%s: lna=%d mixer=%d if=%d\n", __func__,
+
+       dev_dbg(&s->spi->dev, "lna=%d mixer=%d if=%d\n",
                        lna_gain, mixer_gain, if_gain);
 
        reg = 1 << 0;
@@ -83,7 +84,7 @@ static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain,
 
        return 0;
 err:
-       dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret);
+       dev_dbg(&s->spi->dev, "failed %d\n", ret);
        return ret;
 };
 
@@ -94,6 +95,7 @@ static int msi001_set_tuner(struct msi001 *s)
        u32 reg;
        u64 f_vco, tmp64;
        u8 mode, filter_mode, lo_div;
+
        static const struct {
                u32 rf;
                u8 mode;
@@ -145,9 +147,7 @@ static int msi001_set_tuner(struct msi001 *s)
        #define R_REF 4
        #define F_OUT_STEP 1
 
-       dev_dbg(&s->spi->dev,
-                       "%s: f_rf=%d f_if=%d\n",
-                       __func__, f_rf, f_if);
+       dev_dbg(&s->spi->dev, "f_rf=%d f_if=%d\n", f_rf, f_if);
 
        for (i = 0; i < ARRAY_SIZE(band_lut); i++) {
                if (f_rf <= band_lut[i].rf) {
@@ -198,8 +198,7 @@ static int msi001_set_tuner(struct msi001 *s)
 
        s->bandwidth->val = bandwidth_lut[i].freq;
 
-       dev_dbg(&s->spi->dev, "%s: bandwidth selected=%d\n",
-                       __func__, bandwidth_lut[i].freq);
+       dev_dbg(&s->spi->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq);
 
        f_vco = (u64) (f_rf + f_if + f_if1) * lo_div;
        tmp64 = f_vco;
@@ -225,9 +224,8 @@ static int msi001_set_tuner(struct msi001 *s)
        tmp += 1ul * F_REF * R_REF * frac / thresh;
        tmp /= lo_div;
 
-       dev_dbg(&s->spi->dev,
-                       "%s: rf=%u:%u n=%d thresh=%d frac=%d\n",
-                               __func__, f_rf, tmp, n, thresh, frac);
+       dev_dbg(&s->spi->dev, "rf=%u:%u n=%d thresh=%d frac=%d\n",
+                               f_rf, tmp, n, thresh, frac);
 
        ret = msi001_wreg(s, 0x00000e);
        if (ret)
@@ -276,7 +274,7 @@ static int msi001_set_tuner(struct msi001 *s)
 
        return 0;
 err:
-       dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret);
+       dev_dbg(&s->spi->dev, "failed %d\n", ret);
        return ret;
 };
 
@@ -284,7 +282,8 @@ static int msi001_s_power(struct v4l2_subdev *sd, int on)
 {
        struct msi001 *s = sd_to_msi001(sd);
        int ret;
-       dev_dbg(&s->spi->dev, "%s: on=%d\n", __func__, on);
+
+       dev_dbg(&s->spi->dev, "on=%d\n", on);
 
        if (on)
                ret = 0;
@@ -301,7 +300,8 @@ static const struct v4l2_subdev_core_ops msi001_core_ops = {
 static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
 {
        struct msi001 *s = sd_to_msi001(sd);
-       dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index);
+
+       dev_dbg(&s->spi->dev, "index=%d\n", v->index);
 
        strlcpy(v->name, "Mirics MSi001", sizeof(v->name));
        v->type = V4L2_TUNER_RF;
@@ -315,14 +315,16 @@ static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
 static int msi001_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v)
 {
        struct msi001 *s = sd_to_msi001(sd);
-       dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index);
+
+       dev_dbg(&s->spi->dev, "index=%d\n", v->index);
        return 0;
 }
 
 static int msi001_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
 {
        struct msi001 *s = sd_to_msi001(sd);
-       dev_dbg(&s->spi->dev, "%s: tuner=%d\n", __func__, f->tuner);
+
+       dev_dbg(&s->spi->dev, "tuner=%d\n", f->tuner);
        f->frequency = s->f_tuner;
        return 0;
 }
@@ -332,8 +334,9 @@ static int msi001_s_frequency(struct v4l2_subdev *sd,
 {
        struct msi001 *s = sd_to_msi001(sd);
        unsigned int band;
-       dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d frequency=%u\n",
-                       __func__, f->tuner, f->type, f->frequency);
+
+       dev_dbg(&s->spi->dev, "tuner=%d type=%d frequency=%u\n",
+                       f->tuner, f->type, f->frequency);
 
        if (f->frequency < ((bands[0].rangehigh + bands[1].rangelow) / 2))
                band = 0;
@@ -349,8 +352,9 @@ static int msi001_enum_freq_bands(struct v4l2_subdev *sd,
                struct v4l2_frequency_band *band)
 {
        struct msi001 *s = sd_to_msi001(sd);
-       dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d index=%d\n",
-                       __func__, band->tuner, band->type, band->index);
+
+       dev_dbg(&s->spi->dev, "tuner=%d type=%d index=%d\n",
+                       band->tuner, band->type, band->index);
 
        if (band->index >= ARRAY_SIZE(bands))
                return -EINVAL;
@@ -380,9 +384,10 @@ static int msi001_s_ctrl(struct v4l2_ctrl *ctrl)
        struct msi001 *s = container_of(ctrl->handler, struct msi001, hdl);
 
        int ret;
+
        dev_dbg(&s->spi->dev,
-                       "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n",
-                       __func__, ctrl->id, ctrl->name, ctrl->val,
+                       "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n",
+                       ctrl->id, ctrl->name, ctrl->val,
                        ctrl->minimum, ctrl->maximum, ctrl->step);
 
        switch (ctrl->id) {
@@ -403,8 +408,7 @@ static int msi001_s_ctrl(struct v4l2_ctrl *ctrl)
                                s->mixer_gain->cur.val, s->if_gain->val);
                break;
        default:
-               dev_dbg(&s->spi->dev, "%s: unkown control %d\n",
-                               __func__, ctrl->id);
+               dev_dbg(&s->spi->dev, "unkown control %d\n", ctrl->id);
                ret = -EINVAL;
        }
 
@@ -419,7 +423,8 @@ static int msi001_probe(struct spi_device *spi)
 {
        struct msi001 *s;
        int ret;
-       dev_dbg(&spi->dev, "%s:\n", __func__);
+
+       dev_dbg(&spi->dev, "\n");
 
        s = kzalloc(sizeof(struct msi001), GFP_KERNEL);
        if (s == NULL) {
@@ -466,7 +471,8 @@ static int msi001_remove(struct spi_device *spi)
 {
        struct v4l2_subdev *sd = spi_get_drvdata(spi);
        struct msi001 *s = sd_to_msi001(sd);
-       dev_dbg(&spi->dev, "%s:\n", __func__);
+
+       dev_dbg(&spi->dev, "\n");
 
        /*
         * Registered by v4l2_spi_new_subdev() from master driver, but we must
index 13381de58a843a4b8f4997705bdfc62c40ae34f5..b87b2549d58d6eab5b202dca2abb8645a04a6141 100644 (file)
@@ -157,7 +157,6 @@ static int mt2060_set_params(struct dvb_frontend *fe)
 {
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        struct mt2060_priv *priv;
-       int ret=0;
        int i=0;
        u32 freq;
        u8  lnaband;
@@ -240,7 +239,7 @@ static int mt2060_set_params(struct dvb_frontend *fe)
        if (fe->ops.i2c_gate_ctrl)
                fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
 
-       return ret;
+       return 0;
 }
 
 static void mt2060_calibrate(struct mt2060_priv *priv)
index f640dcf4a81d8a7e204bba0a015f126b9d8756bc..9e9c5eb4cb66949a7e84a9789a9829f6f1f913d9 100644 (file)
@@ -1216,7 +1216,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
        if (status >= 0) {
                val =
                    (state->
-                    reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode]
+                    reg[MT2063_REG_PD1_TGT] & ~0x40) | (RFAGCEN[Mode]
                                                                   ? 0x40 :
                                                                   0x00);
                if (state->reg[MT2063_REG_PD1_TGT] != val)
@@ -1225,7 +1225,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
 
        /* LNARin */
        if (status >= 0) {
-               u8 val = (state->reg[MT2063_REG_CTRL_2C] & (u8) ~0x03) |
+               u8 val = (state->reg[MT2063_REG_CTRL_2C] & ~0x03) |
                         (LNARIN[Mode] & 0x03);
                if (state->reg[MT2063_REG_CTRL_2C] != val)
                        status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val);
@@ -1235,19 +1235,19 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
        if (status >= 0) {
                val =
                    (state->
-                    reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) |
+                    reg[MT2063_REG_FIFF_CTRL2] & ~0xF0) |
                    (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4);
                if (state->reg[MT2063_REG_FIFF_CTRL2] != val) {
                        status |=
                            mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val);
                        /* trigger FIFF calibration, needed after changing FIFFQ */
                        val =
-                           (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01);
+                           (state->reg[MT2063_REG_FIFF_CTRL] | 0x01);
                        status |=
                            mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
                        val =
                            (state->
-                            reg[MT2063_REG_FIFF_CTRL] & (u8) ~0x01);
+                            reg[MT2063_REG_FIFF_CTRL] & ~0x01);
                        status |=
                            mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
                }
@@ -1259,7 +1259,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
 
        /* acLNAmax */
        if (status >= 0) {
-               u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) |
+               u8 val = (state->reg[MT2063_REG_LNA_OV] & ~0x1F) |
                         (ACLNAMAX[Mode] & 0x1F);
                if (state->reg[MT2063_REG_LNA_OV] != val)
                        status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val);
@@ -1267,7 +1267,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
 
        /* LNATGT */
        if (status >= 0) {
-               u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) |
+               u8 val = (state->reg[MT2063_REG_LNA_TGT] & ~0x3F) |
                         (LNATGT[Mode] & 0x3F);
                if (state->reg[MT2063_REG_LNA_TGT] != val)
                        status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
@@ -1275,7 +1275,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
 
        /* ACRF */
        if (status >= 0) {
-               u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) |
+               u8 val = (state->reg[MT2063_REG_RF_OV] & ~0x1F) |
                         (ACRFMAX[Mode] & 0x1F);
                if (state->reg[MT2063_REG_RF_OV] != val)
                        status |= mt2063_setreg(state, MT2063_REG_RF_OV, val);
@@ -1283,7 +1283,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
 
        /* PD1TGT */
        if (status >= 0) {
-               u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) |
+               u8 val = (state->reg[MT2063_REG_PD1_TGT] & ~0x3F) |
                         (PD1TGT[Mode] & 0x3F);
                if (state->reg[MT2063_REG_PD1_TGT] != val)
                        status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
@@ -1294,7 +1294,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
                u8 val = ACFIFMAX[Mode];
                if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5)
                        val = 5;
-               val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) |
+               val = (state->reg[MT2063_REG_FIF_OV] & ~0x1F) |
                      (val & 0x1F);
                if (state->reg[MT2063_REG_FIF_OV] != val)
                        status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val);
@@ -1302,7 +1302,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
 
        /* PD2TGT */
        if (status >= 0) {
-               u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) |
+               u8 val = (state->reg[MT2063_REG_PD2_TGT] & ~0x3F) |
                    (PD2TGT[Mode] & 0x3F);
                if (state->reg[MT2063_REG_PD2_TGT] != val)
                        status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val);
@@ -1310,7 +1310,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
 
        /* Ignore ATN Overload */
        if (status >= 0) {
-               val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) |
+               val = (state->reg[MT2063_REG_LNA_TGT] & ~0x80) |
                      (RFOVDIS[Mode] ? 0x80 : 0x00);
                if (state->reg[MT2063_REG_LNA_TGT] != val)
                        status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
@@ -1318,7 +1318,7 @@ static u32 MT2063_SetReceiverMode(struct mt2063_state *state,
 
        /* Ignore FIF Overload */
        if (status >= 0) {
-               val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) |
+               val = (state->reg[MT2063_REG_PD1_TGT] & ~0x80) |
                      (FIFOVDIS[Mode] ? 0x80 : 0x00);
                if (state->reg[MT2063_REG_PD1_TGT] != val)
                        status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c
new file mode 100644 (file)
index 0000000..1575a5d
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * MaxLinear MxL301RF OFDM tuner driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+
+/*
+ * NOTICE:
+ * This driver is incomplete and lacks init/config of the chips,
+ * as the necessary info is not disclosed.
+ * Other features like get_if_frequency() are missing as well.
+ * It assumes that users of this driver (such as a PCI bridge of
+ * DTV receiver cards) properly init and configure the chip
+ * via I2C *before* calling this driver's init() function.
+ *
+ * Currently, PT3 driver is the only one that uses this driver,
+ * and contains init/config code in its firmware.
+ * Thus some part of the code might be dependent on PT3 specific config.
+ */
+
+#include <linux/kernel.h>
+#include "mxl301rf.h"
+
+struct mxl301rf_state {
+       struct mxl301rf_config cfg;
+       struct i2c_client *i2c;
+};
+
+static struct mxl301rf_state *cfg_to_state(struct mxl301rf_config *c)
+{
+       return container_of(c, struct mxl301rf_state, cfg);
+}
+
+static int raw_write(struct mxl301rf_state *state, const u8 *buf, int len)
+{
+       int ret;
+
+       ret = i2c_master_send(state->i2c, buf, len);
+       if (ret >= 0 && ret < len)
+               ret = -EIO;
+       return (ret == len) ? 0 : ret;
+}
+
+static int reg_write(struct mxl301rf_state *state, u8 reg, u8 val)
+{
+       u8 buf[2] = { reg, val };
+
+       return raw_write(state, buf, 2);
+}
+
+static int reg_read(struct mxl301rf_state *state, u8 reg, u8 *val)
+{
+       u8 wbuf[2] = { 0xfb, reg };
+       int ret;
+
+       ret = raw_write(state, wbuf, sizeof(wbuf));
+       if (ret == 0)
+               ret = i2c_master_recv(state->i2c, val, 1);
+       if (ret >= 0 && ret < 1)
+               ret = -EIO;
+       return (ret == 1) ? 0 : ret;
+}
+
+/* tuner_ops */
+
+/* get RSSI and update propery cache, set to *out in % */
+static int mxl301rf_get_rf_strength(struct dvb_frontend *fe, u16 *out)
+{
+       struct mxl301rf_state *state;
+       int ret;
+       u8  rf_in1, rf_in2, rf_off1, rf_off2;
+       u16 rf_in, rf_off;
+       s64 level;
+       struct dtv_fe_stats *rssi;
+
+       rssi = &fe->dtv_property_cache.strength;
+       rssi->len = 1;
+       rssi->stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       *out = 0;
+
+       state = fe->tuner_priv;
+       ret = reg_write(state, 0x14, 0x01);
+       if (ret < 0)
+               return ret;
+       usleep_range(1000, 2000);
+
+       ret = reg_read(state, 0x18, &rf_in1);
+       if (ret == 0)
+               ret = reg_read(state, 0x19, &rf_in2);
+       if (ret == 0)
+               ret = reg_read(state, 0xd6, &rf_off1);
+       if (ret == 0)
+               ret = reg_read(state, 0xd7, &rf_off2);
+       if (ret != 0)
+               return ret;
+
+       rf_in = (rf_in2 & 0x07) << 8 | rf_in1;
+       rf_off = (rf_off2 & 0x0f) << 5 | (rf_off1 >> 3);
+       level = rf_in - rf_off - (113 << 3); /* x8 dBm */
+       level = level * 1000 / 8;
+       rssi->stat[0].svalue = level;
+       rssi->stat[0].scale = FE_SCALE_DECIBEL;
+       /* *out = (level - min) * 100 / (max - min) */
+       *out = (rf_in - rf_off + (1 << 9) - 1) * 100 / ((5 << 9) - 2);
+       return 0;
+}
+
+/* spur shift parameters */
+struct shf {
+       u32     freq;           /* Channel center frequency */
+       u32     ofst_th;        /* Offset frequency threshold */
+       u8      shf_val;        /* Spur shift value */
+       u8      shf_dir;        /* Spur shift direction */
+};
+
+static const struct shf shf_tab[] = {
+       {  64500, 500, 0x92, 0x07 },
+       { 191500, 300, 0xe2, 0x07 },
+       { 205500, 500, 0x2c, 0x04 },
+       { 212500, 500, 0x1e, 0x04 },
+       { 226500, 500, 0xd4, 0x07 },
+       {  99143, 500, 0x9c, 0x07 },
+       { 173143, 500, 0xd4, 0x07 },
+       { 191143, 300, 0xd4, 0x07 },
+       { 207143, 500, 0xce, 0x07 },
+       { 225143, 500, 0xce, 0x07 },
+       { 243143, 500, 0xd4, 0x07 },
+       { 261143, 500, 0xd4, 0x07 },
+       { 291143, 500, 0xd4, 0x07 },
+       { 339143, 500, 0x2c, 0x04 },
+       { 117143, 500, 0x7a, 0x07 },
+       { 135143, 300, 0x7a, 0x07 },
+       { 153143, 500, 0x01, 0x07 }
+};
+
+struct reg_val {
+       u8 reg;
+       u8 val;
+} __attribute__ ((__packed__));
+
+static const struct reg_val set_idac[] = {
+       { 0x0d, 0x00 },
+       { 0x0c, 0x67 },
+       { 0x6f, 0x89 },
+       { 0x70, 0x0c },
+       { 0x6f, 0x8a },
+       { 0x70, 0x0e },
+       { 0x6f, 0x8b },
+       { 0x70, 0x1c },
+};
+
+static int mxl301rf_set_params(struct dvb_frontend *fe)
+{
+       struct reg_val tune0[] = {
+               { 0x13, 0x00 },         /* abort tuning */
+               { 0x3b, 0xc0 },
+               { 0x3b, 0x80 },
+               { 0x10, 0x95 },         /* BW */
+               { 0x1a, 0x05 },
+               { 0x61, 0x00 },         /* spur shift value (placeholder) */
+               { 0x62, 0xa0 }          /* spur shift direction (placeholder) */
+       };
+
+       struct reg_val tune1[] = {
+               { 0x11, 0x40 },         /* RF frequency L (placeholder) */
+               { 0x12, 0x0e },         /* RF frequency H (placeholder) */
+               { 0x13, 0x01 }          /* start tune */
+       };
+
+       struct mxl301rf_state *state;
+       u32 freq;
+       u16 f;
+       u32 tmp, div;
+       int i, ret;
+
+       state = fe->tuner_priv;
+       freq = fe->dtv_property_cache.frequency;
+
+       /* spur shift function (for analog) */
+       for (i = 0; i < ARRAY_SIZE(shf_tab); i++) {
+               if (freq >= (shf_tab[i].freq - shf_tab[i].ofst_th) * 1000 &&
+                   freq <= (shf_tab[i].freq + shf_tab[i].ofst_th) * 1000) {
+                       tune0[5].val = shf_tab[i].shf_val;
+                       tune0[6].val = 0xa0 | shf_tab[i].shf_dir;
+                       break;
+               }
+       }
+       ret = raw_write(state, (u8 *) tune0, sizeof(tune0));
+       if (ret < 0)
+               goto failed;
+       usleep_range(3000, 4000);
+
+       /* convert freq to 10.6 fixed point float [MHz] */
+       f = freq / 1000000;
+       tmp = freq % 1000000;
+       div = 1000000;
+       for (i = 0; i < 6; i++) {
+               f <<= 1;
+               div >>= 1;
+               if (tmp > div) {
+                       tmp -= div;
+                       f |= 1;
+               }
+       }
+       if (tmp > 7812)
+               f++;
+       tune1[0].val = f & 0xff;
+       tune1[1].val = f >> 8;
+       ret = raw_write(state, (u8 *) tune1, sizeof(tune1));
+       if (ret < 0)
+               goto failed;
+       msleep(31);
+
+       ret = reg_write(state, 0x1a, 0x0d);
+       if (ret < 0)
+               goto failed;
+       ret = raw_write(state, (u8 *) set_idac, sizeof(set_idac));
+       if (ret < 0)
+               goto failed;
+       return 0;
+
+failed:
+       dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
+               __func__, fe->dvb->num, fe->id);
+       return ret;
+}
+
+static const struct reg_val standby_data[] = {
+       { 0x01, 0x00 },
+       { 0x13, 0x00 }
+};
+
+static int mxl301rf_sleep(struct dvb_frontend *fe)
+{
+       struct mxl301rf_state *state;
+       int ret;
+
+       state = fe->tuner_priv;
+       ret = raw_write(state, (u8 *)standby_data, sizeof(standby_data));
+       if (ret < 0)
+               dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
+                       __func__, fe->dvb->num, fe->id);
+       return ret;
+}
+
+
+/* init sequence is not public.
+ * the parent must have init'ed the device.
+ * just wake up here.
+ */
+static int mxl301rf_init(struct dvb_frontend *fe)
+{
+       struct mxl301rf_state *state;
+       int ret;
+
+       state = fe->tuner_priv;
+
+       ret = reg_write(state, 0x01, 0x01);
+       if (ret < 0) {
+               dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
+                        __func__, fe->dvb->num, fe->id);
+               return ret;
+       }
+       return 0;
+}
+
+/* I2C driver functions */
+
+static const struct dvb_tuner_ops mxl301rf_ops = {
+       .info = {
+               .name = "MaxLinear MxL301RF",
+
+               .frequency_min =  93000000,
+               .frequency_max = 803142857,
+       },
+
+       .init = mxl301rf_init,
+       .sleep = mxl301rf_sleep,
+
+       .set_params = mxl301rf_set_params,
+       .get_rf_strength = mxl301rf_get_rf_strength,
+};
+
+
+static int mxl301rf_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       struct mxl301rf_state *state;
+       struct mxl301rf_config *cfg;
+       struct dvb_frontend *fe;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       state->i2c = client;
+       cfg = client->dev.platform_data;
+
+       memcpy(&state->cfg, cfg, sizeof(state->cfg));
+       fe = cfg->fe;
+       fe->tuner_priv = state;
+       memcpy(&fe->ops.tuner_ops, &mxl301rf_ops, sizeof(mxl301rf_ops));
+
+       i2c_set_clientdata(client, &state->cfg);
+       dev_info(&client->dev, "MaxLinear MxL301RF attached.\n");
+       return 0;
+}
+
+static int mxl301rf_remove(struct i2c_client *client)
+{
+       struct mxl301rf_state *state;
+
+       state = cfg_to_state(i2c_get_clientdata(client));
+       state->cfg.fe->tuner_priv = NULL;
+       kfree(state);
+       return 0;
+}
+
+
+static const struct i2c_device_id mxl301rf_id[] = {
+       {"mxl301rf", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, mxl301rf_id);
+
+static struct i2c_driver mxl301rf_driver = {
+       .driver = {
+               .name   = "mxl301rf",
+       },
+       .probe          = mxl301rf_probe,
+       .remove         = mxl301rf_remove,
+       .id_table       = mxl301rf_id,
+};
+
+module_i2c_driver(mxl301rf_driver);
+
+MODULE_DESCRIPTION("MaxLinear MXL301RF tuner");
+MODULE_AUTHOR("Akihiro TSUKADA");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/mxl301rf.h b/drivers/media/tuners/mxl301rf.h
new file mode 100644 (file)
index 0000000..19e6840
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * MaxLinear MxL301RF OFDM tuner driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+
+#ifndef MXL301RF_H
+#define MXL301RF_H
+
+#include "dvb_frontend.h"
+
+struct mxl301rf_config {
+       struct dvb_frontend *fe;
+};
+
+#endif /* MXL301RF_H */
index b473b76cb278b52f316cdb6249cba5da667e7d41..92a3be4fde870be4707ab5b8b3cecb8c734d635a 100644 (file)
@@ -1692,7 +1692,6 @@ static u16 MXL5005_TunerConfig(struct dvb_frontend *fe,
        )
 {
        struct mxl5005s_state *state = fe->tuner_priv;
-       u16 status = 0;
 
        state->Mode = Mode;
        state->IF_Mode = IF_mode;
@@ -1715,7 +1714,7 @@ static u16 MXL5005_TunerConfig(struct dvb_frontend *fe,
        /* Synthesizer LO frequency calculation */
        MXL_SynthIFLO_Calc(fe);
 
-       return status;
+       return 0;
 }
 
 static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe)
diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c
new file mode 100644 (file)
index 0000000..18bc745
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ * Sharp QM1D1C0042 8PSK tuner driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+
+/*
+ * NOTICE:
+ * As the disclosed information on the chip is very limited,
+ * this driver lacks some features, including chip config like IF freq.
+ * It assumes that users of this driver (such as a PCI bridge of
+ * DTV receiver cards) know the relevant info and
+ * configure the chip via I2C if necessary.
+ *
+ * Currently, PT3 driver is the only one that uses this driver,
+ * and contains init/config code in its firmware.
+ * Thus some part of the code might be dependent on PT3 specific config.
+ */
+
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include "qm1d1c0042.h"
+
+#define QM1D1C0042_NUM_REGS 0x20
+
+static const u8 reg_initval[QM1D1C0042_NUM_REGS] = {
+       0x48, 0x1c, 0xa0, 0x10, 0xbc, 0xc5, 0x20, 0x33,
+       0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+       0x00, 0xff, 0xf3, 0x00, 0x2a, 0x64, 0xa6, 0x86,
+       0x8c, 0xcf, 0xb8, 0xf1, 0xa8, 0xf2, 0x89, 0x00
+};
+
+static const struct qm1d1c0042_config default_cfg = {
+       .xtal_freq = 16000,
+       .lpf = 1,
+       .fast_srch = 0,
+       .lpf_wait = 20,
+       .fast_srch_wait = 4,
+       .normal_srch_wait = 15,
+};
+
+struct qm1d1c0042_state {
+       struct qm1d1c0042_config cfg;
+       struct i2c_client *i2c;
+       u8 regs[QM1D1C0042_NUM_REGS];
+};
+
+static struct qm1d1c0042_state *cfg_to_state(struct qm1d1c0042_config *c)
+{
+       return container_of(c, struct qm1d1c0042_state, cfg);
+}
+
+static int reg_write(struct qm1d1c0042_state *state, u8 reg, u8 val)
+{
+       u8 wbuf[2] = { reg, val };
+       int ret;
+
+       ret = i2c_master_send(state->i2c, wbuf, sizeof(wbuf));
+       if (ret >= 0 && ret < sizeof(wbuf))
+               ret = -EIO;
+       return (ret == sizeof(wbuf)) ? 0 : ret;
+}
+
+static int reg_read(struct qm1d1c0042_state *state, u8 reg, u8 *val)
+{
+       struct i2c_msg msgs[2] = {
+               {
+                       .addr = state->i2c->addr,
+                       .flags = 0,
+                       .buf = &reg,
+                       .len = 1,
+               },
+               {
+                       .addr = state->i2c->addr,
+                       .flags = I2C_M_RD,
+                       .buf = val,
+                       .len = 1,
+               },
+       };
+       int ret;
+
+       ret = i2c_transfer(state->i2c->adapter, msgs, ARRAY_SIZE(msgs));
+       if (ret >= 0 && ret < ARRAY_SIZE(msgs))
+               ret = -EIO;
+       return (ret == ARRAY_SIZE(msgs)) ? 0 : ret;
+}
+
+
+static int qm1d1c0042_set_srch_mode(struct qm1d1c0042_state *state, bool fast)
+{
+       if (fast)
+               state->regs[0x03] |= 0x01; /* set fast search mode */
+       else
+               state->regs[0x03] &= ~0x01 & 0xff;
+
+       return reg_write(state, 0x03, state->regs[0x03]);
+}
+
+static int qm1d1c0042_wakeup(struct qm1d1c0042_state *state)
+{
+       int ret;
+
+       state->regs[0x01] |= 1 << 3;             /* BB_Reg_enable */
+       state->regs[0x01] &= (~(1 << 0)) & 0xff; /* NORMAL (wake-up) */
+       state->regs[0x05] &= (~(1 << 3)) & 0xff; /* pfd_rst NORMAL */
+       ret = reg_write(state, 0x01, state->regs[0x01]);
+       if (ret == 0)
+               ret = reg_write(state, 0x05, state->regs[0x05]);
+
+       if (ret < 0)
+               dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
+                       __func__, state->cfg.fe->dvb->num, state->cfg.fe->id);
+       return ret;
+}
+
+/* tuner_ops */
+
+static int qm1d1c0042_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+       struct qm1d1c0042_state *state;
+       struct qm1d1c0042_config *cfg;
+
+       state = fe->tuner_priv;
+       cfg = priv_cfg;
+
+       if (cfg->fe)
+               state->cfg.fe = cfg->fe;
+
+       if (cfg->xtal_freq != QM1D1C0042_CFG_XTAL_DFLT)
+               dev_warn(&state->i2c->dev,
+                       "(%s) changing xtal_freq not supported. ", __func__);
+       state->cfg.xtal_freq = default_cfg.xtal_freq;
+
+       state->cfg.lpf = cfg->lpf;
+       state->cfg.fast_srch = cfg->fast_srch;
+
+       if (cfg->lpf_wait != QM1D1C0042_CFG_WAIT_DFLT)
+               state->cfg.lpf_wait = cfg->lpf_wait;
+       else
+               state->cfg.lpf_wait = default_cfg.lpf_wait;
+
+       if (cfg->fast_srch_wait != QM1D1C0042_CFG_WAIT_DFLT)
+               state->cfg.fast_srch_wait = cfg->fast_srch_wait;
+       else
+               state->cfg.fast_srch_wait = default_cfg.fast_srch_wait;
+
+       if (cfg->normal_srch_wait != QM1D1C0042_CFG_WAIT_DFLT)
+               state->cfg.normal_srch_wait = cfg->normal_srch_wait;
+       else
+               state->cfg.normal_srch_wait = default_cfg.normal_srch_wait;
+       return 0;
+}
+
+/* divisor, vco_band parameters */
+/*  {maxfreq,  param1(band?), param2(div?) */
+static const u32 conv_table[9][3] = {
+       { 2151000, 1, 7 },
+       { 1950000, 1, 6 },
+       { 1800000, 1, 5 },
+       { 1600000, 1, 4 },
+       { 1450000, 1, 3 },
+       { 1250000, 1, 2 },
+       { 1200000, 0, 7 },
+       {  975000, 0, 6 },
+       {  950000, 0, 0 }
+};
+
+static int qm1d1c0042_set_params(struct dvb_frontend *fe)
+{
+       struct qm1d1c0042_state *state;
+       u32 freq;
+       int i, ret;
+       u8 val, mask;
+       u32 a, sd;
+       s32 b;
+
+       state = fe->tuner_priv;
+       freq = fe->dtv_property_cache.frequency;
+
+       state->regs[0x08] &= 0xf0;
+       state->regs[0x08] |= 0x09;
+
+       state->regs[0x13] &= 0x9f;
+       state->regs[0x13] |= 0x20;
+
+       /* div2/vco_band */
+       val = state->regs[0x02] & 0x0f;
+       for (i = 0; i < 8; i++)
+               if (freq < conv_table[i][0] && freq >= conv_table[i + 1][0]) {
+                       val |= conv_table[i][1] << 7;
+                       val |= conv_table[i][2] << 4;
+                       break;
+               }
+       ret = reg_write(state, 0x02, val);
+       if (ret < 0)
+               return ret;
+
+       a = (freq + state->cfg.xtal_freq / 2) / state->cfg.xtal_freq;
+
+       state->regs[0x06] &= 0x40;
+       state->regs[0x06] |= (a - 12) / 4;
+       ret = reg_write(state, 0x06, state->regs[0x06]);
+       if (ret < 0)
+               return ret;
+
+       state->regs[0x07] &= 0xf0;
+       state->regs[0x07] |= (a - 4 * ((a - 12) / 4 + 1) - 5) & 0x0f;
+       ret = reg_write(state, 0x07, state->regs[0x07]);
+       if (ret < 0)
+               return ret;
+
+       /* LPF */
+       val = state->regs[0x08];
+       if (state->cfg.lpf) {
+               /* LPF_CLK, LPF_FC */
+               val &= 0xf0;
+               val |= 0x02;
+       }
+       ret = reg_write(state, 0x08, val);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * b = (freq / state->cfg.xtal_freq - a) << 20;
+        * sd = b          (b >= 0)
+        *      1<<22 + b  (b < 0)
+        */
+       b = (s32)div64_s64(((s64) freq) << 20, state->cfg.xtal_freq)
+                          - (((s64) a) << 20);
+
+       if (b >= 0)
+               sd = b;
+       else
+               sd = (1 << 22) + b;
+
+       state->regs[0x09] &= 0xc0;
+       state->regs[0x09] |= (sd >> 16) & 0x3f;
+       state->regs[0x0a] = (sd >> 8) & 0xff;
+       state->regs[0x0b] = sd & 0xff;
+       ret = reg_write(state, 0x09, state->regs[0x09]);
+       if (ret == 0)
+               ret = reg_write(state, 0x0a, state->regs[0x0a]);
+       if (ret == 0)
+               ret = reg_write(state, 0x0b, state->regs[0x0b]);
+       if (ret != 0)
+               return ret;
+
+       if (!state->cfg.lpf) {
+               /* CSEL_Offset */
+               ret = reg_write(state, 0x13, state->regs[0x13]);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* VCO_TM, LPF_TM */
+       mask = state->cfg.lpf ? 0x3f : 0x7f;
+       val = state->regs[0x0c] & mask;
+       ret = reg_write(state, 0x0c, val);
+       if (ret < 0)
+               return ret;
+       usleep_range(2000, 3000);
+       val = state->regs[0x0c] | ~mask;
+       ret = reg_write(state, 0x0c, val);
+       if (ret < 0)
+               return ret;
+
+       if (state->cfg.lpf)
+               msleep(state->cfg.lpf_wait);
+       else if (state->regs[0x03] & 0x01)
+               msleep(state->cfg.fast_srch_wait);
+       else
+               msleep(state->cfg.normal_srch_wait);
+
+       if (state->cfg.lpf) {
+               /* LPF_FC */
+               ret = reg_write(state, 0x08, 0x09);
+               if (ret < 0)
+                       return ret;
+
+               /* CSEL_Offset */
+               ret = reg_write(state, 0x13, state->regs[0x13]);
+               if (ret < 0)
+                       return ret;
+       }
+       return 0;
+}
+
+static int qm1d1c0042_sleep(struct dvb_frontend *fe)
+{
+       struct qm1d1c0042_state *state;
+       int ret;
+
+       state = fe->tuner_priv;
+       state->regs[0x01] &= (~(1 << 3)) & 0xff; /* BB_Reg_disable */
+       state->regs[0x01] |= 1 << 0;             /* STDBY */
+       state->regs[0x05] |= 1 << 3;             /* pfd_rst STANDBY */
+       ret = reg_write(state, 0x05, state->regs[0x05]);
+       if (ret == 0)
+               ret = reg_write(state, 0x01, state->regs[0x01]);
+       if (ret < 0)
+               dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
+                       __func__, fe->dvb->num, fe->id);
+       return ret;
+}
+
+static int qm1d1c0042_init(struct dvb_frontend *fe)
+{
+       struct qm1d1c0042_state *state;
+       u8 val;
+       int i, ret;
+
+       state = fe->tuner_priv;
+       memcpy(state->regs, reg_initval, sizeof(reg_initval));
+
+       reg_write(state, 0x01, 0x0c);
+       reg_write(state, 0x01, 0x0c);
+
+       ret = reg_write(state, 0x01, 0x0c); /* soft reset on */
+       if (ret < 0)
+               goto failed;
+       usleep_range(2000, 3000);
+
+       val = state->regs[0x01] | 0x10;
+       ret = reg_write(state, 0x01, val); /* soft reset off */
+       if (ret < 0)
+               goto failed;
+
+       /* check ID */
+       ret = reg_read(state, 0x00, &val);
+       if (ret < 0 || val != 0x48)
+               goto failed;
+       usleep_range(2000, 3000);
+
+       state->regs[0x0c] |= 0x40;
+       ret = reg_write(state, 0x0c, state->regs[0x0c]);
+       if (ret < 0)
+               goto failed;
+       msleep(state->cfg.lpf_wait);
+
+       /* set all writable registers */
+       for (i = 1; i <= 0x0c ; i++) {
+               ret = reg_write(state, i, state->regs[i]);
+               if (ret < 0)
+                       goto failed;
+       }
+       for (i = 0x11; i < QM1D1C0042_NUM_REGS; i++) {
+               ret = reg_write(state, i, state->regs[i]);
+               if (ret < 0)
+                       goto failed;
+       }
+
+       ret = qm1d1c0042_wakeup(state);
+       if (ret < 0)
+               goto failed;
+
+       ret = qm1d1c0042_set_srch_mode(state, state->cfg.fast_srch);
+       if (ret < 0)
+               goto failed;
+
+       return ret;
+
+failed:
+       dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
+               __func__, fe->dvb->num, fe->id);
+       return ret;
+}
+
+/* I2C driver functions */
+
+static const struct dvb_tuner_ops qm1d1c0042_ops = {
+       .info = {
+               .name = "Sharp QM1D1C0042",
+
+               .frequency_min =  950000,
+               .frequency_max = 2150000,
+       },
+
+       .init = qm1d1c0042_init,
+       .sleep = qm1d1c0042_sleep,
+       .set_config = qm1d1c0042_set_config,
+       .set_params = qm1d1c0042_set_params,
+};
+
+
+static int qm1d1c0042_probe(struct i2c_client *client,
+                           const struct i2c_device_id *id)
+{
+       struct qm1d1c0042_state *state;
+       struct qm1d1c0042_config *cfg;
+       struct dvb_frontend *fe;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+       state->i2c = client;
+
+       cfg = client->dev.platform_data;
+       fe = cfg->fe;
+       fe->tuner_priv = state;
+       qm1d1c0042_set_config(fe, cfg);
+       memcpy(&fe->ops.tuner_ops, &qm1d1c0042_ops, sizeof(qm1d1c0042_ops));
+
+       i2c_set_clientdata(client, &state->cfg);
+       dev_info(&client->dev, "Sharp QM1D1C0042 attached.\n");
+       return 0;
+}
+
+static int qm1d1c0042_remove(struct i2c_client *client)
+{
+       struct qm1d1c0042_state *state;
+
+       state = cfg_to_state(i2c_get_clientdata(client));
+       state->cfg.fe->tuner_priv = NULL;
+       kfree(state);
+       return 0;
+}
+
+
+static const struct i2c_device_id qm1d1c0042_id[] = {
+       {"qm1d1c0042", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id);
+
+static struct i2c_driver qm1d1c0042_driver = {
+       .driver = {
+               .name   = "qm1d1c0042",
+       },
+       .probe          = qm1d1c0042_probe,
+       .remove         = qm1d1c0042_remove,
+       .id_table       = qm1d1c0042_id,
+};
+
+module_i2c_driver(qm1d1c0042_driver);
+
+MODULE_DESCRIPTION("Sharp QM1D1C0042 tuner");
+MODULE_AUTHOR("Akihiro TSUKADA");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/qm1d1c0042.h b/drivers/media/tuners/qm1d1c0042.h
new file mode 100644 (file)
index 0000000..4f5c188
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Sharp QM1D1C0042 8PSK tuner driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ *
+ * 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.
+ */
+
+#ifndef QM1D1C0042_H
+#define QM1D1C0042_H
+
+#include "dvb_frontend.h"
+
+
+struct qm1d1c0042_config {
+       struct dvb_frontend *fe;
+
+       u32  xtal_freq;    /* [kHz] */ /* currently ignored */
+       bool lpf;          /* enable LPF */
+       bool fast_srch;    /* enable fast search mode, no LPF */
+       u32  lpf_wait;         /* wait in tuning with LPF enabled. [ms] */
+       u32  fast_srch_wait;   /* with fast-search mode, no LPF. [ms] */
+       u32  normal_srch_wait; /* with no LPF/fast-search mode. [ms] */
+};
+/* special values indicating to use the default in qm1d1c0042_config */
+#define QM1D1C0042_CFG_XTAL_DFLT 0
+#define QM1D1C0042_CFG_WAIT_DFLT 0
+
+#endif /* QM1D1C0042_H */
index 6c53edb73a632ed0c43ade3acf363c9b580bff3d..cf97142e01e6a34c3ebd30cf6e143c5599dab919 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Silicon Labs Si2157/2158 silicon tuner driver
+ * Silicon Labs Si2147/2157/2158 silicon tuner driver
  *
  * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
  *
@@ -55,8 +55,7 @@ static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd)
                                break;
                }
 
-               dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n",
-                               __func__,
+               dev_dbg(&s->client->dev, "cmd execution took %d ms\n",
                                jiffies_to_msecs(jiffies) -
                                (jiffies_to_msecs(timeout) - TIMEOUT));
 
@@ -75,7 +74,7 @@ err_mutex_unlock:
 
        return 0;
 err:
-       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&s->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -88,9 +87,12 @@ static int si2157_init(struct dvb_frontend *fe)
        u8 *fw_file;
        unsigned int chip_id;
 
-       dev_dbg(&s->client->dev, "%s:\n", __func__);
+       dev_dbg(&s->client->dev, "\n");
 
-       /* configure? */
+       if (s->fw_loaded)
+               goto warm;
+
+       /* power up */
        memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15);
        cmd.wlen = 15;
        cmd.rlen = 1;
@@ -111,45 +113,47 @@ static int si2157_init(struct dvb_frontend *fe)
 
        #define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0)
        #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0)
+       #define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0)
 
        switch (chip_id) {
        case SI2158_A20:
                fw_file = SI2158_A20_FIRMWARE;
                break;
        case SI2157_A30:
+       case SI2147_A30:
                goto skip_fw_download;
                break;
        default:
                dev_err(&s->client->dev,
-                               "%s: unkown chip version Si21%d-%c%c%c\n",
-                               KBUILD_MODNAME, cmd.args[2], cmd.args[1],
+                               "unknown chip version Si21%d-%c%c%c\n",
+                               cmd.args[2], cmd.args[1],
                                cmd.args[3], cmd.args[4]);
                ret = -EINVAL;
                goto err;
        }
 
        /* cold state - try to download firmware */
-       dev_info(&s->client->dev, "%s: found a '%s' in cold state\n",
-                       KBUILD_MODNAME, si2157_ops.info.name);
+       dev_info(&s->client->dev, "found a '%s' in cold state\n",
+                       si2157_ops.info.name);
 
        /* request the firmware, this will block and timeout */
        ret = request_firmware(&fw, fw_file, &s->client->dev);
        if (ret) {
-               dev_err(&s->client->dev, "%s: firmware file '%s' not found\n",
-                               KBUILD_MODNAME, fw_file);
+               dev_err(&s->client->dev, "firmware file '%s' not found\n",
+                               fw_file);
                goto err;
        }
 
        /* firmware should be n chunks of 17 bytes */
        if (fw->size % 17 != 0) {
-               dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n",
-                               KBUILD_MODNAME, fw_file);
+               dev_err(&s->client->dev, "firmware file '%s' is invalid\n",
+                               fw_file);
                ret = -EINVAL;
                goto err;
        }
 
-       dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n",
-                       KBUILD_MODNAME, fw_file);
+       dev_info(&s->client->dev, "downloading firmware from file '%s'\n",
+                       fw_file);
 
        for (remaining = fw->size; remaining > 0; remaining -= 17) {
                len = fw->data[fw->size - remaining];
@@ -159,8 +163,8 @@ static int si2157_init(struct dvb_frontend *fe)
                ret = si2157_cmd_execute(s, &cmd);
                if (ret) {
                        dev_err(&s->client->dev,
-                                       "%s: firmware download failed=%d\n",
-                                       KBUILD_MODNAME, ret);
+                                       "firmware download failed=%d\n",
+                                       ret);
                        goto err;
                }
        }
@@ -177,14 +181,17 @@ skip_fw_download:
        if (ret)
                goto err;
 
-       s->active = true;
+       s->fw_loaded = true;
 
+warm:
+       s->active = true;
        return 0;
+
 err:
        if (fw)
                release_firmware(fw);
 
-       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&s->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -194,20 +201,21 @@ static int si2157_sleep(struct dvb_frontend *fe)
        int ret;
        struct si2157_cmd cmd;
 
-       dev_dbg(&s->client->dev, "%s:\n", __func__);
+       dev_dbg(&s->client->dev, "\n");
 
        s->active = false;
 
-       memcpy(cmd.args, "\x13", 1);
-       cmd.wlen = 1;
-       cmd.rlen = 0;
+       /* standby */
+       memcpy(cmd.args, "\x16\x00", 2);
+       cmd.wlen = 2;
+       cmd.rlen = 1;
        ret = si2157_cmd_execute(s, &cmd);
        if (ret)
                goto err;
 
        return 0;
 err:
-       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&s->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -220,8 +228,8 @@ static int si2157_set_params(struct dvb_frontend *fe)
        u8 bandwidth, delivery_system;
 
        dev_dbg(&s->client->dev,
-                       "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n",
-                       __func__, c->delivery_system, c->frequency,
+                       "delivery_system=%d frequency=%u bandwidth_hz=%u\n",
+                       c->delivery_system, c->frequency,
                        c->bandwidth_hz);
 
        if (!s->active) {
@@ -239,6 +247,9 @@ static int si2157_set_params(struct dvb_frontend *fe)
                bandwidth = 0x0f;
 
        switch (c->delivery_system) {
+       case SYS_ATSC:
+                       delivery_system = 0x00;
+                       break;
        case SYS_DVBT:
        case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */
                        delivery_system = 0x20;
@@ -256,7 +267,14 @@ static int si2157_set_params(struct dvb_frontend *fe)
        if (s->inversion)
                cmd.args[5] = 0x01;
        cmd.wlen = 6;
-       cmd.rlen = 1;
+       cmd.rlen = 4;
+       ret = si2157_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x02\x07\x01\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 4;
        ret = si2157_cmd_execute(s, &cmd);
        if (ret)
                goto err;
@@ -275,7 +293,7 @@ static int si2157_set_params(struct dvb_frontend *fe)
 
        return 0;
 err:
-       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&s->client->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -310,13 +328,14 @@ static int si2157_probe(struct i2c_client *client,
        s = kzalloc(sizeof(struct si2157), GFP_KERNEL);
        if (!s) {
                ret = -ENOMEM;
-               dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+               dev_err(&client->dev, "kzalloc() failed\n");
                goto err;
        }
 
        s->client = client;
        s->fe = cfg->fe;
        s->inversion = cfg->inversion;
+       s->fw_loaded = false;
        mutex_init(&s->i2c_mutex);
 
        /* check if the tuner is there */
@@ -333,11 +352,10 @@ static int si2157_probe(struct i2c_client *client,
        i2c_set_clientdata(client, s);
 
        dev_info(&s->client->dev,
-                       "%s: Silicon Labs Si2157/Si2158 successfully attached\n",
-                       KBUILD_MODNAME);
+                       "Silicon Labs Si2157/Si2158 successfully attached\n");
        return 0;
 err:
-       dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&client->dev, "failed=%d\n", ret);
        kfree(s);
 
        return ret;
@@ -348,7 +366,7 @@ static int si2157_remove(struct i2c_client *client)
        struct si2157 *s = i2c_get_clientdata(client);
        struct dvb_frontend *fe = s->fe;
 
-       dev_dbg(&client->dev, "%s:\n", __func__);
+       dev_dbg(&client->dev, "\n");
 
        memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
        fe->tuner_priv = NULL;
index 6da4d5d1c8178b25ad5e3965e5a90b06dbd2865f..d3b19cadb4a16d04fa83fbafd7e483514bfc6e1e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Silicon Labs Si2157/2158 silicon tuner driver
+ * Silicon Labs Si2147/2157/2158 silicon tuner driver
  *
  * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
  *
index 3ddab5e6b50038cbb9034f556fb6f77edec5308f..e71ffafed951b804f7a272a5ecc12795e86f30d5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Silicon Labs Si2157/2158 silicon tuner driver
+ * Silicon Labs Si2147/2157/2158 silicon tuner driver
  *
  * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
  *
@@ -26,6 +26,7 @@ struct si2157 {
        struct i2c_client *client;
        struct dvb_frontend *fe;
        bool active;
+       bool fw_loaded;
        bool inversion;
 };
 
index 05a4ac9edb6b0559eedde3f39273bb958df9cfd8..d93e0667b46b65b78d67105f8acd7ffe929863d5 100644 (file)
  */
 
 #include "tda18212.h"
+#include <linux/regmap.h>
 
-/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE  64
-
-struct tda18212_priv {
-       struct tda18212_config *cfg;
-       struct i2c_adapter *i2c;
+struct tda18212_dev {
+       struct tda18212_config cfg;
+       struct i2c_client *client;
+       struct regmap *regmap;
 
        u32 if_frequency;
 };
 
-/* write multiple registers */
-static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
-       int len)
-{
-       int ret;
-       u8 buf[MAX_XFER_SIZE];
-       struct i2c_msg msg[1] = {
-               {
-                       .addr = priv->cfg->i2c_address,
-                       .flags = 0,
-                       .len = 1 + len,
-                       .buf = buf,
-               }
-       };
-
-       if (1 + len > sizeof(buf)) {
-               dev_warn(&priv->i2c->dev,
-                        "%s: i2c wr reg=%04x: len=%d is too big!\n",
-                        KBUILD_MODNAME, reg, len);
-               return -EINVAL;
-       }
-
-       buf[0] = reg;
-       memcpy(&buf[1], val, len);
-
-       ret = i2c_transfer(priv->i2c, msg, 1);
-       if (ret == 1) {
-               ret = 0;
-       } else {
-               dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
-                               "len=%d\n", KBUILD_MODNAME, ret, reg, len);
-               ret = -EREMOTEIO;
-       }
-       return ret;
-}
-
-/* read multiple registers */
-static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
-       int len)
-{
-       int ret;
-       u8 buf[MAX_XFER_SIZE];
-       struct i2c_msg msg[2] = {
-               {
-                       .addr = priv->cfg->i2c_address,
-                       .flags = 0,
-                       .len = 1,
-                       .buf = &reg,
-               }, {
-                       .addr = priv->cfg->i2c_address,
-                       .flags = I2C_M_RD,
-                       .len = len,
-                       .buf = buf,
-               }
-       };
-
-       if (len > sizeof(buf)) {
-               dev_warn(&priv->i2c->dev,
-                        "%s: i2c rd reg=%04x: len=%d is too big!\n",
-                        KBUILD_MODNAME, reg, len);
-               return -EINVAL;
-       }
-
-       ret = i2c_transfer(priv->i2c, msg, 2);
-       if (ret == 2) {
-               memcpy(val, buf, len);
-               ret = 0;
-       } else {
-               dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
-                               "len=%d\n", KBUILD_MODNAME, ret, reg, len);
-               ret = -EREMOTEIO;
-       }
-
-       return ret;
-}
-
-/* write single register */
-static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val)
-{
-       return tda18212_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val)
-{
-       return tda18212_rd_regs(priv, reg, val, 1);
-}
-
-#if 0 /* keep, useful when developing driver */
-static void tda18212_dump_regs(struct tda18212_priv *priv)
-{
-       int i;
-       u8 buf[256];
-
-       #define TDA18212_RD_LEN 32
-       for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN)
-               tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN);
-
-       print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf,
-               sizeof(buf), true);
-
-       return;
-}
-#endif
-
 static int tda18212_set_params(struct dvb_frontend *fe)
 {
-       struct tda18212_priv *priv = fe->tuner_priv;
+       struct tda18212_dev *dev = fe->tuner_priv;
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int ret, i;
        u32 if_khz;
@@ -166,9 +60,9 @@ static int tda18212_set_params(struct dvb_frontend *fe)
                [ATSC_QAM] = { 0x7d, 0x20, 0x63 },
        };
 
-       dev_dbg(&priv->i2c->dev,
-                       "%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n",
-                       __func__, c->delivery_system, c->frequency,
+       dev_dbg(&dev->client->dev,
+                       "delivery_system=%d frequency=%d bandwidth_hz=%d\n",
+                       c->delivery_system, c->frequency,
                        c->bandwidth_hz);
 
        if (fe->ops.i2c_gate_ctrl)
@@ -176,25 +70,25 @@ static int tda18212_set_params(struct dvb_frontend *fe)
 
        switch (c->delivery_system) {
        case SYS_ATSC:
-               if_khz = priv->cfg->if_atsc_vsb;
+               if_khz = dev->cfg.if_atsc_vsb;
                i = ATSC_VSB;
                break;
        case SYS_DVBC_ANNEX_B:
-               if_khz = priv->cfg->if_atsc_qam;
+               if_khz = dev->cfg.if_atsc_qam;
                i = ATSC_QAM;
                break;
        case SYS_DVBT:
                switch (c->bandwidth_hz) {
                case 6000000:
-                       if_khz = priv->cfg->if_dvbt_6;
+                       if_khz = dev->cfg.if_dvbt_6;
                        i = DVBT_6;
                        break;
                case 7000000:
-                       if_khz = priv->cfg->if_dvbt_7;
+                       if_khz = dev->cfg.if_dvbt_7;
                        i = DVBT_7;
                        break;
                case 8000000:
-                       if_khz = priv->cfg->if_dvbt_8;
+                       if_khz = dev->cfg.if_dvbt_8;
                        i = DVBT_8;
                        break;
                default:
@@ -205,15 +99,15 @@ static int tda18212_set_params(struct dvb_frontend *fe)
        case SYS_DVBT2:
                switch (c->bandwidth_hz) {
                case 6000000:
-                       if_khz = priv->cfg->if_dvbt2_6;
+                       if_khz = dev->cfg.if_dvbt2_6;
                        i = DVBT2_6;
                        break;
                case 7000000:
-                       if_khz = priv->cfg->if_dvbt2_7;
+                       if_khz = dev->cfg.if_dvbt2_7;
                        i = DVBT2_7;
                        break;
                case 8000000:
-                       if_khz = priv->cfg->if_dvbt2_8;
+                       if_khz = dev->cfg.if_dvbt2_8;
                        i = DVBT2_8;
                        break;
                default:
@@ -223,7 +117,7 @@ static int tda18212_set_params(struct dvb_frontend *fe)
                break;
        case SYS_DVBC_ANNEX_A:
        case SYS_DVBC_ANNEX_C:
-               if_khz = priv->cfg->if_dvbc;
+               if_khz = dev->cfg.if_dvbc;
                i = DVBC_8;
                break;
        default:
@@ -231,15 +125,15 @@ static int tda18212_set_params(struct dvb_frontend *fe)
                goto error;
        }
 
-       ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]);
+       ret = regmap_write(dev->regmap, 0x23, bw_params[i][2]);
        if (ret)
                goto error;
 
-       ret = tda18212_wr_reg(priv, 0x06, 0x00);
+       ret = regmap_write(dev->regmap, 0x06, 0x00);
        if (ret)
                goto error;
 
-       ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]);
+       ret = regmap_write(dev->regmap, 0x0f, bw_params[i][0]);
        if (ret)
                goto error;
 
@@ -252,12 +146,12 @@ static int tda18212_set_params(struct dvb_frontend *fe)
        buf[6] = ((c->frequency / 1000) >>  0) & 0xff;
        buf[7] = 0xc1;
        buf[8] = 0x01;
-       ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf));
+       ret = regmap_bulk_write(dev->regmap, 0x12, buf, sizeof(buf));
        if (ret)
                goto error;
 
        /* actual IF rounded as it is on register */
-       priv->if_frequency = buf[3] * 50 * 1000;
+       dev->if_frequency = buf[3] * 50 * 1000;
 
 exit:
        if (fe->ops.i2c_gate_ctrl)
@@ -266,26 +160,19 @@ exit:
        return ret;
 
 error:
-       dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
        goto exit;
 }
 
 static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
-       struct tda18212_priv *priv = fe->tuner_priv;
+       struct tda18212_dev *dev = fe->tuner_priv;
 
-       *frequency = priv->if_frequency;
+       *frequency = dev->if_frequency;
 
        return 0;
 }
 
-static int tda18212_release(struct dvb_frontend *fe)
-{
-       kfree(fe->tuner_priv);
-       fe->tuner_priv = NULL;
-       return 0;
-}
-
 static const struct dvb_tuner_ops tda18212_tuner_ops = {
        .info = {
                .name           = "NXP TDA18212",
@@ -295,53 +182,110 @@ static const struct dvb_tuner_ops tda18212_tuner_ops = {
                .frequency_step =      1000,
        },
 
-       .release       = tda18212_release,
-
        .set_params    = tda18212_set_params,
        .get_if_frequency = tda18212_get_if_frequency,
 };
 
-struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
-       struct i2c_adapter *i2c, struct tda18212_config *cfg)
+static int tda18212_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
 {
-       struct tda18212_priv *priv = NULL;
+       struct tda18212_config *cfg = client->dev.platform_data;
+       struct dvb_frontend *fe = cfg->fe;
+       struct tda18212_dev *dev;
        int ret;
-       u8 val;
+       unsigned int chip_id;
+       char *version;
+       static const struct regmap_config regmap_config = {
+               .reg_bits = 8,
+               .val_bits = 8,
+       };
 
-       priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL);
-       if (priv == NULL)
-               return NULL;
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL) {
+               ret = -ENOMEM;
+               dev_err(&client->dev, "kzalloc() failed\n");
+               goto err;
+       }
 
-       priv->cfg = cfg;
-       priv->i2c = i2c;
-       fe->tuner_priv = priv;
+       memcpy(&dev->cfg, cfg, sizeof(struct tda18212_config));
+       dev->client = client;
+       dev->regmap = devm_regmap_init_i2c(client, &regmap_config);
+       if (IS_ERR(dev->regmap)) {
+               ret = PTR_ERR(dev->regmap);
+               goto err;
+       }
 
+       /* check if the tuner is there */
        if (fe->ops.i2c_gate_ctrl)
                fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
 
-       /* check if the tuner is there */
-       ret = tda18212_rd_reg(priv, 0x00, &val);
+       ret = regmap_read(dev->regmap, 0x00, &chip_id);
+       dev_dbg(&dev->client->dev, "chip_id=%02x\n", chip_id);
 
        if (fe->ops.i2c_gate_ctrl)
                fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
 
-       if (!ret)
-               dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val);
-       if (ret || val != 0xc7) {
-               kfree(priv);
-               return NULL;
+       if (ret)
+               goto err;
+
+       switch (chip_id) {
+       case 0xc7:
+               version = "M"; /* master */
+               break;
+       case 0x47:
+               version = "S"; /* slave */
+               break;
+       default:
+               ret = -ENODEV;
+               goto err;
        }
 
-       dev_info(&priv->i2c->dev,
-                       "%s: NXP TDA18212HN successfully identified\n",
-                       KBUILD_MODNAME);
+       dev_info(&dev->client->dev,
+                       "NXP TDA18212HN/%s successfully identified\n", version);
 
+       fe->tuner_priv = dev;
        memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
-               sizeof(struct dvb_tuner_ops));
+                       sizeof(struct dvb_tuner_ops));
+       i2c_set_clientdata(client, dev);
 
-       return fe;
+       return 0;
+err:
+       dev_dbg(&client->dev, "failed=%d\n", ret);
+       kfree(dev);
+       return ret;
 }
-EXPORT_SYMBOL(tda18212_attach);
+
+static int tda18212_remove(struct i2c_client *client)
+{
+       struct tda18212_dev *dev = i2c_get_clientdata(client);
+       struct dvb_frontend *fe = dev->cfg.fe;
+
+       dev_dbg(&client->dev, "\n");
+
+       memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
+       fe->tuner_priv = NULL;
+       kfree(dev);
+
+       return 0;
+}
+
+static const struct i2c_device_id tda18212_id[] = {
+       {"tda18212", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, tda18212_id);
+
+static struct i2c_driver tda18212_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "tda18212",
+       },
+       .probe          = tda18212_probe,
+       .remove         = tda18212_remove,
+       .id_table       = tda18212_id,
+};
+
+module_i2c_driver(tda18212_driver);
 
 MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
index c36b49e4b2742a8f8cf12f1dd99e253c69d89ac6..e58c9096d79c5e5a08a6bcdb5a644fa19490edbf 100644 (file)
@@ -25,8 +25,6 @@
 #include "dvb_frontend.h"
 
 struct tda18212_config {
-       u8 i2c_address;
-
        u16 if_dvbt_6;
        u16 if_dvbt_7;
        u16 if_dvbt_8;
@@ -37,18 +35,11 @@ struct tda18212_config {
        u16 if_dvbc;
        u16 if_atsc_vsb;
        u16 if_atsc_qam;
-};
 
-#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA18212)
-extern struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
-       struct i2c_adapter *i2c, struct tda18212_config *cfg);
-#else
-static inline struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
-       struct i2c_adapter *i2c, struct tda18212_config *cfg)
-{
-       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
-       return NULL;
-}
-#endif
+       /*
+        * pointer to DVB frontend
+        */
+       struct dvb_frontend *fe;
+};
 
 #endif
index 18c77afe2e4ff8f32345a56d6aae199de1062c74..86e5e31101186ef37b30055b9f43e0725fc7ba01 100644 (file)
@@ -714,12 +714,11 @@ fail:
        return ret;
 }
 
-int _tda_printk(struct tda18271_priv *state, const char *level,
-               const char *func, const char *fmt, ...)
+void _tda_printk(struct tda18271_priv *state, const char *level,
+                const char *func, const char *fmt, ...)
 {
        struct va_format vaf;
        va_list args;
-       int rtn;
 
        va_start(args, fmt);
 
@@ -727,15 +726,13 @@ int _tda_printk(struct tda18271_priv *state, const char *level,
        vaf.va = &args;
 
        if (state)
-               rtn = printk("%s%s: [%d-%04x|%c] %pV",
-                            level, func, i2c_adapter_id(state->i2c_props.adap),
-                            state->i2c_props.addr,
-                            (state->role == TDA18271_MASTER) ? 'M' : 'S',
-                            &vaf);
+               printk("%s%s: [%d-%04x|%c] %pV",
+                      level, func, i2c_adapter_id(state->i2c_props.adap),
+                      state->i2c_props.addr,
+                      (state->role == TDA18271_MASTER) ? 'M' : 'S',
+                      &vaf);
        else
-               rtn = printk("%s%s: %pV", level, func, &vaf);
+               printk("%s%s: %pV", level, func, &vaf);
 
        va_end(args);
-
-       return rtn;
 }
index 454c152ccaa023d66423591108ca16616c058660..b36a7b75477253ad6cc1e678579833d8476354fb 100644 (file)
@@ -139,8 +139,8 @@ extern int tda18271_debug;
 #define DBG_CAL  16
 
 __attribute__((format(printf, 4, 5)))
-int _tda_printk(struct tda18271_priv *state, const char *level,
-               const char *func, const char *fmt, ...);
+void _tda_printk(struct tda18271_priv *state, const char *level,
+                const char *func, const char *fmt, ...);
 
 #define tda_printk(st, lvl, fmt, arg...)                       \
        _tda_printk(st, lvl, __func__, fmt, ##arg)
index 565eeebb3aebc990fc89b7e4fafc98c0e876e487..d12f5e4ad8bf42e0138de60316f1a16ae419a2b8 100644 (file)
@@ -178,67 +178,67 @@ static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
 #define dump_firm_type(t)      dump_firm_type_and_int_freq(t, 0)
 static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
 {
-        if (type & BASE)
+       if (type & BASE)
                printk("BASE ");
-        if (type & INIT1)
+       if (type & INIT1)
                printk("INIT1 ");
-        if (type & F8MHZ)
+       if (type & F8MHZ)
                printk("F8MHZ ");
-        if (type & MTS)
+       if (type & MTS)
                printk("MTS ");
-        if (type & D2620)
+       if (type & D2620)
                printk("D2620 ");
-        if (type & D2633)
+       if (type & D2633)
                printk("D2633 ");
-        if (type & DTV6)
+       if (type & DTV6)
                printk("DTV6 ");
-        if (type & QAM)
+       if (type & QAM)
                printk("QAM ");
-        if (type & DTV7)
+       if (type & DTV7)
                printk("DTV7 ");
-        if (type & DTV78)
+       if (type & DTV78)
                printk("DTV78 ");
-        if (type & DTV8)
+       if (type & DTV8)
                printk("DTV8 ");
-        if (type & FM)
+       if (type & FM)
                printk("FM ");
-        if (type & INPUT1)
+       if (type & INPUT1)
                printk("INPUT1 ");
-        if (type & LCD)
+       if (type & LCD)
                printk("LCD ");
-        if (type & NOGD)
+       if (type & NOGD)
                printk("NOGD ");
-        if (type & MONO)
+       if (type & MONO)
                printk("MONO ");
-        if (type & ATSC)
+       if (type & ATSC)
                printk("ATSC ");
-        if (type & IF)
+       if (type & IF)
                printk("IF ");
-        if (type & LG60)
+       if (type & LG60)
                printk("LG60 ");
-        if (type & ATI638)
+       if (type & ATI638)
                printk("ATI638 ");
-        if (type & OREN538)
+       if (type & OREN538)
                printk("OREN538 ");
-        if (type & OREN36)
+       if (type & OREN36)
                printk("OREN36 ");
-        if (type & TOYOTA388)
+       if (type & TOYOTA388)
                printk("TOYOTA388 ");
-        if (type & TOYOTA794)
+       if (type & TOYOTA794)
                printk("TOYOTA794 ");
-        if (type & DIBCOM52)
+       if (type & DIBCOM52)
                printk("DIBCOM52 ");
-        if (type & ZARLINK456)
+       if (type & ZARLINK456)
                printk("ZARLINK456 ");
-        if (type & CHINA)
+       if (type & CHINA)
                printk("CHINA ");
-        if (type & F6MHZ)
+       if (type & F6MHZ)
                printk("F6MHZ ");
-        if (type & INPUT2)
+       if (type & INPUT2)
                printk("INPUT2 ");
-        if (type & SCODE)
+       if (type & SCODE)
                printk("SCODE ");
-        if (type & HAS_IF)
+       if (type & HAS_IF)
                printk("HAS_IF_%d ", int_freq);
 }
 
diff --git a/drivers/media/tuners/tuner_it913x.c b/drivers/media/tuners/tuner_it913x.c
deleted file mode 100644 (file)
index 3d83c42..0000000
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * ITE Tech IT9137 silicon tuner driver
- *
- *  Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
- *  IT9137 Copyright (C) ITE Tech 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 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 "tuner_it913x_priv.h"
-
-struct it913x_state {
-       struct i2c_adapter *i2c_adap;
-       u8 i2c_addr;
-       u8 chip_ver;
-       u8 tuner_type;
-       u8 firmware_ver;
-       u16 tun_xtal;
-       u8 tun_fdiv;
-       u8 tun_clk_mode;
-       u32 tun_fn_min;
-};
-
-/* read multiple registers */
-static int it913x_rd_regs(struct it913x_state *state,
-               u32 reg, u8 *data, u8 count)
-{
-       int ret;
-       u8 b[3];
-       struct i2c_msg msg[2] = {
-               { .addr = state->i2c_addr, .flags = 0,
-                       .buf = b, .len = sizeof(b) },
-               { .addr = state->i2c_addr, .flags = I2C_M_RD,
-                       .buf = data, .len = count }
-       };
-       b[0] = (u8)(reg >> 16) & 0xff;
-       b[1] = (u8)(reg >> 8) & 0xff;
-       b[2] = (u8) reg & 0xff;
-       b[0] |= 0x80; /* All reads from demodulator */
-
-       ret = i2c_transfer(state->i2c_adap, msg, 2);
-
-       return ret;
-}
-
-/* read single register */
-static int it913x_rd_reg(struct it913x_state *state, u32 reg)
-{
-       int ret;
-       u8 b[1];
-       ret = it913x_rd_regs(state, reg, &b[0], sizeof(b));
-       return (ret < 0) ? -ENODEV : b[0];
-}
-
-/* write multiple registers */
-static int it913x_wr_regs(struct it913x_state *state,
-               u8 pro, u32 reg, u8 buf[], u8 count)
-{
-       u8 b[256];
-       struct i2c_msg msg[1] = {
-               { .addr = state->i2c_addr, .flags = 0,
-                 .buf = b, .len = 3 + count }
-       };
-       int ret;
-       b[0] = (u8)(reg >> 16) & 0xff;
-       b[1] = (u8)(reg >> 8) & 0xff;
-       b[2] = (u8) reg & 0xff;
-       memcpy(&b[3], buf, count);
-
-       if (pro == PRO_DMOD)
-               b[0] |= 0x80;
-
-       ret = i2c_transfer(state->i2c_adap, msg, 1);
-
-       if (ret < 0)
-               return -EIO;
-
-       return 0;
-}
-
-/* write single register */
-static int it913x_wr_reg(struct it913x_state *state,
-               u8 pro, u32 reg, u32 data)
-{
-       int ret;
-       u8 b[4];
-       u8 s;
-
-       b[0] = data >> 24;
-       b[1] = (data >> 16) & 0xff;
-       b[2] = (data >> 8) & 0xff;
-       b[3] = data & 0xff;
-       /* expand write as needed */
-       if (data < 0x100)
-               s = 3;
-       else if (data < 0x1000)
-               s = 2;
-       else if (data < 0x100000)
-               s = 1;
-       else
-               s = 0;
-
-       ret = it913x_wr_regs(state, pro, reg, &b[s], sizeof(b) - s);
-
-       return ret;
-}
-
-static int it913x_script_loader(struct it913x_state *state,
-               struct it913xset *loadscript)
-{
-       int ret, i;
-       if (loadscript == NULL)
-               return -EINVAL;
-
-       for (i = 0; i < 1000; ++i) {
-               if (loadscript[i].pro == 0xff)
-                       break;
-               ret = it913x_wr_regs(state, loadscript[i].pro,
-                       loadscript[i].address,
-                       loadscript[i].reg, loadscript[i].count);
-               if (ret < 0)
-                       return -ENODEV;
-       }
-       return 0;
-}
-
-static int it913x_init(struct dvb_frontend *fe)
-{
-       struct it913x_state *state = fe->tuner_priv;
-       int ret, i, reg;
-       u8 val, nv_val;
-       u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2};
-       u8 b[2];
-
-       reg = it913x_rd_reg(state, 0xec86);
-       switch (reg) {
-       case 0:
-               state->tun_clk_mode = reg;
-               state->tun_xtal = 2000;
-               state->tun_fdiv = 3;
-               val = 16;
-               break;
-       case -ENODEV:
-               return -ENODEV;
-       case 1:
-       default:
-               state->tun_clk_mode = reg;
-               state->tun_xtal = 640;
-               state->tun_fdiv = 1;
-               val = 6;
-               break;
-       }
-
-       reg = it913x_rd_reg(state, 0xed03);
-
-       if (reg < 0)
-               return -ENODEV;
-       else if (reg < ARRAY_SIZE(nv))
-               nv_val = nv[reg];
-       else
-               nv_val = 2;
-
-       for (i = 0; i < 50; i++) {
-               ret = it913x_rd_regs(state, 0xed23, &b[0], sizeof(b));
-               reg = (b[1] << 8) + b[0];
-               if (reg > 0)
-                       break;
-               if (ret < 0)
-                       return -ENODEV;
-               udelay(2000);
-       }
-       state->tun_fn_min = state->tun_xtal * reg;
-       state->tun_fn_min /= (state->tun_fdiv * nv_val);
-       dev_dbg(&state->i2c_adap->dev, "%s: Tuner fn_min %d\n", __func__,
-                       state->tun_fn_min);
-
-       if (state->chip_ver > 1)
-               msleep(50);
-       else {
-               for (i = 0; i < 50; i++) {
-                       reg = it913x_rd_reg(state, 0xec82);
-                       if (reg > 0)
-                               break;
-                       if (reg < 0)
-                               return -ENODEV;
-                       udelay(2000);
-               }
-       }
-
-       /* Power Up Tuner - common all versions */
-       ret = it913x_wr_reg(state, PRO_DMOD, 0xec40, 0x1);
-       ret |= it913x_wr_reg(state, PRO_DMOD, 0xfba8, 0x0);
-       ret |= it913x_wr_reg(state, PRO_DMOD, 0xec57, 0x0);
-       ret |= it913x_wr_reg(state, PRO_DMOD, 0xec58, 0x0);
-
-       return it913x_wr_reg(state, PRO_DMOD, 0xed81, val);
-}
-
-static int it9137_set_params(struct dvb_frontend *fe)
-{
-       struct it913x_state *state = fe->tuner_priv;
-       struct it913xset *set_tuner = set_it9137_template;
-       struct dtv_frontend_properties *p = &fe->dtv_property_cache;
-       u32 bandwidth = p->bandwidth_hz;
-       u32 frequency_m = p->frequency;
-       int ret, reg;
-       u32 frequency = frequency_m / 1000;
-       u32 freq, temp_f, tmp;
-       u16 iqik_m_cal;
-       u16 n_div;
-       u8 n;
-       u8 l_band;
-       u8 lna_band;
-       u8 bw;
-
-       if (state->firmware_ver == 1)
-               set_tuner = set_it9135_template;
-       else
-               set_tuner = set_it9137_template;
-
-       dev_dbg(&state->i2c_adap->dev, "%s: Tuner Frequency %d Bandwidth %d\n",
-                       __func__, frequency, bandwidth);
-
-       if (frequency >= 51000 && frequency <= 440000) {
-               l_band = 0;
-               lna_band = 0;
-       } else if (frequency > 440000 && frequency <= 484000) {
-               l_band = 1;
-               lna_band = 1;
-       } else if (frequency > 484000 && frequency <= 533000) {
-               l_band = 1;
-               lna_band = 2;
-       } else if (frequency > 533000 && frequency <= 587000) {
-               l_band = 1;
-               lna_band = 3;
-       } else if (frequency > 587000 && frequency <= 645000) {
-               l_band = 1;
-               lna_band = 4;
-       } else if (frequency > 645000 && frequency <= 710000) {
-               l_band = 1;
-               lna_band = 5;
-       } else if (frequency > 710000 && frequency <= 782000) {
-               l_band = 1;
-               lna_band = 6;
-       } else if (frequency > 782000 && frequency <= 860000) {
-               l_band = 1;
-               lna_band = 7;
-       } else if (frequency > 1450000 && frequency <= 1492000) {
-               l_band = 1;
-               lna_band = 0;
-       } else if (frequency > 1660000 && frequency <= 1685000) {
-               l_band = 1;
-               lna_band = 1;
-       } else
-               return -EINVAL;
-       set_tuner[0].reg[0] = lna_band;
-
-       switch (bandwidth) {
-       case 5000000:
-               bw = 0;
-               break;
-       case 6000000:
-               bw = 2;
-               break;
-       case 7000000:
-               bw = 4;
-               break;
-       default:
-       case 8000000:
-               bw = 6;
-               break;
-       }
-
-       set_tuner[1].reg[0] = bw;
-       set_tuner[2].reg[0] = 0xa0 | (l_band << 3);
-
-       if (frequency > 53000 && frequency <= 74000) {
-               n_div = 48;
-               n = 0;
-       } else if (frequency > 74000 && frequency <= 111000) {
-               n_div = 32;
-               n = 1;
-       } else if (frequency > 111000 && frequency <= 148000) {
-               n_div = 24;
-               n = 2;
-       } else if (frequency > 148000 && frequency <= 222000) {
-               n_div = 16;
-               n = 3;
-       } else if (frequency > 222000 && frequency <= 296000) {
-               n_div = 12;
-               n = 4;
-       } else if (frequency > 296000 && frequency <= 445000) {
-               n_div = 8;
-               n = 5;
-       } else if (frequency > 445000 && frequency <= state->tun_fn_min) {
-               n_div = 6;
-               n = 6;
-       } else if (frequency > state->tun_fn_min && frequency <= 950000) {
-               n_div = 4;
-               n = 7;
-       } else if (frequency > 1450000 && frequency <= 1680000) {
-               n_div = 2;
-               n = 0;
-       } else
-               return -EINVAL;
-
-       reg = it913x_rd_reg(state, 0xed81);
-       iqik_m_cal = (u16)reg * n_div;
-
-       if (reg < 0x20) {
-               if (state->tun_clk_mode == 0)
-                       iqik_m_cal = (iqik_m_cal * 9) >> 5;
-               else
-                       iqik_m_cal >>= 1;
-       } else {
-               iqik_m_cal = 0x40 - iqik_m_cal;
-               if (state->tun_clk_mode == 0)
-                       iqik_m_cal = ~((iqik_m_cal * 9) >> 5);
-               else
-                       iqik_m_cal = ~(iqik_m_cal >> 1);
-       }
-
-       temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv;
-       freq = temp_f / state->tun_xtal;
-       tmp = freq * state->tun_xtal;
-
-       if ((temp_f - tmp) >= (state->tun_xtal >> 1))
-               freq++;
-
-       freq += (u32) n << 13;
-       /* Frequency OMEGA_IQIK_M_CAL_MID*/
-       temp_f = freq + (u32)iqik_m_cal;
-
-       set_tuner[3].reg[0] =  temp_f & 0xff;
-       set_tuner[4].reg[0] =  (temp_f >> 8) & 0xff;
-
-       dev_dbg(&state->i2c_adap->dev, "%s: High Frequency = %04x\n",
-                       __func__, temp_f);
-
-       /* Lower frequency */
-       set_tuner[5].reg[0] =  freq & 0xff;
-       set_tuner[6].reg[0] =  (freq >> 8) & 0xff;
-
-       dev_dbg(&state->i2c_adap->dev, "%s: low Frequency = %04x\n",
-                       __func__, freq);
-
-       ret = it913x_script_loader(state, set_tuner);
-
-       return (ret < 0) ? -ENODEV : 0;
-}
-
-/* Power sequence */
-/* Power Up    Tuner on -> Frontend suspend off -> Tuner clk on */
-/* Power Down  Frontend suspend on -> Tuner clk off -> Tuner off */
-
-static int it913x_sleep(struct dvb_frontend *fe)
-{
-       struct it913x_state *state = fe->tuner_priv;
-       return it913x_script_loader(state, it9137_tuner_off);
-}
-
-static int it913x_release(struct dvb_frontend *fe)
-{
-       kfree(fe->tuner_priv);
-       return 0;
-}
-
-static const struct dvb_tuner_ops it913x_tuner_ops = {
-       .info = {
-               .name           = "ITE Tech IT913X",
-               .frequency_min  = 174000000,
-               .frequency_max  = 862000000,
-       },
-
-       .release = it913x_release,
-
-       .init = it913x_init,
-       .sleep = it913x_sleep,
-       .set_params = it9137_set_params,
-};
-
-struct dvb_frontend *it913x_attach(struct dvb_frontend *fe,
-               struct i2c_adapter *i2c_adap, u8 i2c_addr, u8 config)
-{
-       struct it913x_state *state = NULL;
-       int ret;
-
-       /* allocate memory for the internal state */
-       state = kzalloc(sizeof(struct it913x_state), GFP_KERNEL);
-       if (state == NULL)
-               return NULL;
-
-       state->i2c_adap = i2c_adap;
-       state->i2c_addr = i2c_addr;
-
-       switch (config) {
-       case AF9033_TUNER_IT9135_38:
-       case AF9033_TUNER_IT9135_51:
-       case AF9033_TUNER_IT9135_52:
-               state->chip_ver = 0x01;
-               break;
-       case AF9033_TUNER_IT9135_60:
-       case AF9033_TUNER_IT9135_61:
-       case AF9033_TUNER_IT9135_62:
-               state->chip_ver = 0x02;
-               break;
-       default:
-               dev_dbg(&i2c_adap->dev,
-                               "%s: invalid config=%02x\n", __func__, config);
-               goto error;
-       }
-
-       state->tuner_type = config;
-       state->firmware_ver = 1;
-
-       /* tuner RF initial */
-       ret = it913x_wr_reg(state, PRO_DMOD, 0xec4c, 0x68);
-       if (ret < 0)
-               goto error;
-
-       fe->tuner_priv = state;
-       memcpy(&fe->ops.tuner_ops, &it913x_tuner_ops,
-                       sizeof(struct dvb_tuner_ops));
-
-       dev_info(&i2c_adap->dev,
-                       "%s: ITE Tech IT913X successfully attached\n",
-                       KBUILD_MODNAME);
-       dev_dbg(&i2c_adap->dev, "%s: config=%02x chip_ver=%02x\n",
-                       __func__, config, state->chip_ver);
-
-       return fe;
-error:
-       kfree(state);
-       return NULL;
-}
-EXPORT_SYMBOL(it913x_attach);
-
-MODULE_DESCRIPTION("ITE Tech IT913X silicon tuner driver");
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/tuner_it913x_priv.h b/drivers/media/tuners/tuner_it913x_priv.h
deleted file mode 100644 (file)
index ce65210..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * ITE Tech IT9137 silicon tuner driver
- *
- *  Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
- *  IT9137 Copyright (C) ITE Tech 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 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 IT913X_PRIV_H
-#define IT913X_PRIV_H
-
-#include "tuner_it913x.h"
-#include "af9033.h"
-
-#define PRO_LINK               0x0
-#define PRO_DMOD               0x1
-#define TRIGGER_OFSM           0x0000
-
-struct it913xset {     u32 pro;
-                       u32 address;
-                       u8 reg[15];
-                       u8 count;
-};
-
-/* Tuner setting scripts (still keeping it9137) */
-static struct it913xset it9137_tuner_off[] = {
-       {PRO_DMOD, 0xfba8, {0x01}, 0x01}, /* Tuner Clock Off  */
-       {PRO_DMOD, 0xec40, {0x00}, 0x01}, /* Power Down Tuner */
-       {PRO_DMOD, 0xec02, {0x3f, 0x1f, 0x3f, 0x3f}, 0x04},
-       {PRO_DMOD, 0xec06, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00}, 0x0c},
-       {PRO_DMOD, 0xec12, {0x00, 0x00, 0x00, 0x00}, 0x04},
-       {PRO_DMOD, 0xec17, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00}, 0x09},
-       {PRO_DMOD, 0xec22, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00}, 0x0a},
-       {PRO_DMOD, 0xec20, {0x00}, 0x01},
-       {PRO_DMOD, 0xec3f, {0x01}, 0x01},
-       {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
-};
-
-static struct it913xset set_it9135_template[] = {
-       {PRO_DMOD, 0xee06, {0x00}, 0x01},
-       {PRO_DMOD, 0xec56, {0x00}, 0x01},
-       {PRO_DMOD, 0xec4c, {0x00}, 0x01},
-       {PRO_DMOD, 0xec4d, {0x00}, 0x01},
-       {PRO_DMOD, 0xec4e, {0x00}, 0x01},
-       {PRO_DMOD, 0x011e, {0x00}, 0x01}, /* Older Devices */
-       {PRO_DMOD, 0x011f, {0x00}, 0x01},
-       {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
-};
-
-static struct it913xset set_it9137_template[] = {
-       {PRO_DMOD, 0xee06, {0x00}, 0x01},
-       {PRO_DMOD, 0xec56, {0x00}, 0x01},
-       {PRO_DMOD, 0xec4c, {0x00}, 0x01},
-       {PRO_DMOD, 0xec4d, {0x00}, 0x01},
-       {PRO_DMOD, 0xec4e, {0x00}, 0x01},
-       {PRO_DMOD, 0xec4f, {0x00}, 0x01},
-       {PRO_DMOD, 0xec50, {0x00}, 0x01},
-       {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
-};
-
-#endif
index f9ab79e3432d9cb4018fcd017548a9f457724b9c..219ebafae70fd3f9b8d1f5541d43100e7a73aa64 100644 (file)
@@ -569,67 +569,67 @@ static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val)
 #define dump_firm_type(t)      dump_firm_type_and_int_freq(t, 0)
 static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
 {
-        if (type & BASE)
+       if (type & BASE)
                printk(KERN_CONT "BASE ");
-        if (type & INIT1)
+       if (type & INIT1)
                printk(KERN_CONT "INIT1 ");
-        if (type & F8MHZ)
+       if (type & F8MHZ)
                printk(KERN_CONT "F8MHZ ");
-        if (type & MTS)
+       if (type & MTS)
                printk(KERN_CONT "MTS ");
-        if (type & D2620)
+       if (type & D2620)
                printk(KERN_CONT "D2620 ");
-        if (type & D2633)
+       if (type & D2633)
                printk(KERN_CONT "D2633 ");
-        if (type & DTV6)
+       if (type & DTV6)
                printk(KERN_CONT "DTV6 ");
-        if (type & QAM)
+       if (type & QAM)
                printk(KERN_CONT "QAM ");
-        if (type & DTV7)
+       if (type & DTV7)
                printk(KERN_CONT "DTV7 ");
-        if (type & DTV78)
+       if (type & DTV78)
                printk(KERN_CONT "DTV78 ");
-        if (type & DTV8)
+       if (type & DTV8)
                printk(KERN_CONT "DTV8 ");
-        if (type & FM)
+       if (type & FM)
                printk(KERN_CONT "FM ");
-        if (type & INPUT1)
+       if (type & INPUT1)
                printk(KERN_CONT "INPUT1 ");
-        if (type & LCD)
+       if (type & LCD)
                printk(KERN_CONT "LCD ");
-        if (type & NOGD)
+       if (type & NOGD)
                printk(KERN_CONT "NOGD ");
-        if (type & MONO)
+       if (type & MONO)
                printk(KERN_CONT "MONO ");
-        if (type & ATSC)
+       if (type & ATSC)
                printk(KERN_CONT "ATSC ");
-        if (type & IF)
+       if (type & IF)
                printk(KERN_CONT "IF ");
-        if (type & LG60)
+       if (type & LG60)
                printk(KERN_CONT "LG60 ");
-        if (type & ATI638)
+       if (type & ATI638)
                printk(KERN_CONT "ATI638 ");
-        if (type & OREN538)
+       if (type & OREN538)
                printk(KERN_CONT "OREN538 ");
-        if (type & OREN36)
+       if (type & OREN36)
                printk(KERN_CONT "OREN36 ");
-        if (type & TOYOTA388)
+       if (type & TOYOTA388)
                printk(KERN_CONT "TOYOTA388 ");
-        if (type & TOYOTA794)
+       if (type & TOYOTA794)
                printk(KERN_CONT "TOYOTA794 ");
-        if (type & DIBCOM52)
+       if (type & DIBCOM52)
                printk(KERN_CONT "DIBCOM52 ");
-        if (type & ZARLINK456)
+       if (type & ZARLINK456)
                printk(KERN_CONT "ZARLINK456 ");
-        if (type & CHINA)
+       if (type & CHINA)
                printk(KERN_CONT "CHINA ");
-        if (type & F6MHZ)
+       if (type & F6MHZ)
                printk(KERN_CONT "F6MHZ ");
-        if (type & INPUT2)
+       if (type & INPUT2)
                printk(KERN_CONT "INPUT2 ");
-        if (type & SCODE)
+       if (type & SCODE)
                printk(KERN_CONT "SCODE ");
-        if (type & HAS_IF)
+       if (type & HAS_IF)
                printk(KERN_CONT "HAS_IF_%d ", int_freq);
 }
 
index e135760f7d486d5bb6061fb0452bbbb5f87545af..e44c8aba6074c0667181d9e7a7daab2f7db4d1bc 100644 (file)
@@ -59,6 +59,7 @@ struct xc5000_priv {
        u32 freq_hz, freq_offset;
        u32 bandwidth;
        u8  video_standard;
+       unsigned int mode;
        u8  rf_mode;
        u8  radio_input;
 
@@ -69,6 +70,8 @@ struct xc5000_priv {
 
        struct dvb_frontend *fe;
        struct delayed_work timer_sleep;
+
+       const struct firmware   *firmware;
 };
 
 /* Misc Defines */
@@ -712,9 +715,50 @@ static void xc_debug_dump(struct xc5000_priv *priv)
        }
 }
 
-static int xc5000_set_params(struct dvb_frontend *fe)
+static int xc5000_tune_digital(struct dvb_frontend *fe)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       int ret;
+       u32 bw = fe->dtv_property_cache.bandwidth_hz;
+
+       ret = xc_set_signal_source(priv, priv->rf_mode);
+       if (ret != 0) {
+               printk(KERN_ERR
+                       "xc5000: xc_set_signal_source(%d) failed\n",
+                       priv->rf_mode);
+               return -EREMOTEIO;
+       }
+
+       ret = xc_set_tv_standard(priv,
+               xc5000_standard[priv->video_standard].video_mode,
+               xc5000_standard[priv->video_standard].audio_mode, 0);
+       if (ret != 0) {
+               printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n");
+               return -EREMOTEIO;
+       }
+
+       ret = xc_set_IF_frequency(priv, priv->if_khz);
+       if (ret != 0) {
+               printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
+                      priv->if_khz);
+               return -EIO;
+       }
+
+       xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a);
+
+       xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL);
+
+       if (debug)
+               xc_debug_dump(priv);
+
+       priv->bandwidth = bw;
+
+       return 0;
+}
+
+static int xc5000_set_digital_params(struct dvb_frontend *fe)
 {
-       int ret, b;
+       int b;
        struct xc5000_priv *priv = fe->tuner_priv;
        u32 bw = fe->dtv_property_cache.bandwidth_hz;
        u32 freq = fe->dtv_property_cache.frequency;
@@ -794,43 +838,12 @@ static int xc5000_set_params(struct dvb_frontend *fe)
        }
 
        priv->freq_hz = freq - priv->freq_offset;
+       priv->mode = V4L2_TUNER_DIGITAL_TV;
 
        dprintk(1, "%s() frequency=%d (compensated to %d)\n",
                __func__, freq, priv->freq_hz);
 
-       ret = xc_set_signal_source(priv, priv->rf_mode);
-       if (ret != 0) {
-               printk(KERN_ERR
-                       "xc5000: xc_set_signal_source(%d) failed\n",
-                       priv->rf_mode);
-               return -EREMOTEIO;
-       }
-
-       ret = xc_set_tv_standard(priv,
-               xc5000_standard[priv->video_standard].video_mode,
-               xc5000_standard[priv->video_standard].audio_mode, 0);
-       if (ret != 0) {
-               printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n");
-               return -EREMOTEIO;
-       }
-
-       ret = xc_set_IF_frequency(priv, priv->if_khz);
-       if (ret != 0) {
-               printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
-                      priv->if_khz);
-               return -EIO;
-       }
-
-       xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a);
-
-       xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL);
-
-       if (debug)
-               xc_debug_dump(priv);
-
-       priv->bandwidth = bw;
-
-       return 0;
+       return xc5000_tune_digital(fe);
 }
 
 static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)
@@ -852,12 +865,10 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)
        return ret;
 }
 
-static int xc5000_set_tv_freq(struct dvb_frontend *fe,
-       struct analog_parameters *params)
+static void xc5000_config_tv(struct dvb_frontend *fe,
+                            struct analog_parameters *params)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
-       u16 pll_lock_status;
-       int ret;
 
        dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
                __func__, params->frequency);
@@ -876,42 +887,49 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe,
        if (params->std & V4L2_STD_MN) {
                /* default to BTSC audio standard */
                priv->video_standard = MN_NTSC_PAL_BTSC;
-               goto tune_channel;
+               return;
        }
 
        if (params->std & V4L2_STD_PAL_BG) {
                /* default to NICAM audio standard */
                priv->video_standard = BG_PAL_NICAM;
-               goto tune_channel;
+               return;
        }
 
        if (params->std & V4L2_STD_PAL_I) {
                /* default to NICAM audio standard */
                priv->video_standard = I_PAL_NICAM;
-               goto tune_channel;
+               return;
        }
 
        if (params->std & V4L2_STD_PAL_DK) {
                /* default to NICAM audio standard */
                priv->video_standard = DK_PAL_NICAM;
-               goto tune_channel;
+               return;
        }
 
        if (params->std & V4L2_STD_SECAM_DK) {
                /* default to A2 DK1 audio standard */
                priv->video_standard = DK_SECAM_A2DK1;
-               goto tune_channel;
+               return;
        }
 
        if (params->std & V4L2_STD_SECAM_L) {
                priv->video_standard = L_SECAM_NICAM;
-               goto tune_channel;
+               return;
        }
 
        if (params->std & V4L2_STD_SECAM_LC) {
                priv->video_standard = LC_SECAM_NICAM;
-               goto tune_channel;
+               return;
        }
+}
+
+static int xc5000_set_tv_freq(struct dvb_frontend *fe)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       u16 pll_lock_status;
+       int ret;
 
 tune_channel:
        ret = xc_set_signal_source(priv, priv->rf_mode);
@@ -955,12 +973,11 @@ tune_channel:
        return 0;
 }
 
-static int xc5000_set_radio_freq(struct dvb_frontend *fe,
-       struct analog_parameters *params)
+static int xc5000_config_radio(struct dvb_frontend *fe,
+                              struct analog_parameters *params)
+
 {
        struct xc5000_priv *priv = fe->tuner_priv;
-       int ret = -EINVAL;
-       u8 radio_input;
 
        dprintk(1, "%s() frequency=%d (in units of khz)\n",
                __func__, params->frequency);
@@ -970,6 +987,18 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe,
                return -EINVAL;
        }
 
+       priv->freq_hz = params->frequency * 125 / 2;
+       priv->rf_mode = XC_RF_MODE_AIR;
+
+       return 0;
+}
+
+static int xc5000_set_radio_freq(struct dvb_frontend *fe)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       int ret;
+       u8 radio_input;
+
        if (priv->radio_input == XC5000_RADIO_FM1)
                radio_input = FM_RADIO_INPUT1;
        else if  (priv->radio_input == XC5000_RADIO_FM2)
@@ -982,10 +1011,6 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe,
                return -EINVAL;
        }
 
-       priv->freq_hz = params->frequency * 125 / 2;
-
-       priv->rf_mode = XC_RF_MODE_AIR;
-
        ret = xc_set_tv_standard(priv, xc5000_standard[radio_input].video_mode,
                               xc5000_standard[radio_input].audio_mode, radio_input);
 
@@ -1013,34 +1038,53 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe,
        return 0;
 }
 
-static int xc5000_set_analog_params(struct dvb_frontend *fe,
-                            struct analog_parameters *params)
+static int xc5000_set_params(struct dvb_frontend *fe)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
-       int ret = -EINVAL;
-
-       if (priv->i2c_props.adap == NULL)
-               return -EINVAL;
 
        if (xc_load_fw_and_init_tuner(fe, 0) != 0) {
                dprintk(1, "Unable to load firmware and init tuner\n");
                return -EINVAL;
        }
 
+       switch (priv->mode) {
+       case V4L2_TUNER_RADIO:
+               return xc5000_set_radio_freq(fe);
+       case V4L2_TUNER_ANALOG_TV:
+               return xc5000_set_tv_freq(fe);
+       case V4L2_TUNER_DIGITAL_TV:
+               return xc5000_tune_digital(fe);
+       }
+
+       return 0;
+}
+
+static int xc5000_set_analog_params(struct dvb_frontend *fe,
+                            struct analog_parameters *params)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       int ret;
+
+       if (priv->i2c_props.adap == NULL)
+               return -EINVAL;
+
        switch (params->mode) {
        case V4L2_TUNER_RADIO:
-               ret = xc5000_set_radio_freq(fe, params);
+               ret = xc5000_config_radio(fe, params);
+               if (ret)
+                       return ret;
                break;
        case V4L2_TUNER_ANALOG_TV:
-       case V4L2_TUNER_DIGITAL_TV:
-               ret = xc5000_set_tv_freq(fe, params);
+               xc5000_config_tv(fe, params);
+               break;
+       default:
                break;
        }
+       priv->mode = params->mode;
 
-       return ret;
+       return xc5000_set_params(fe);
 }
 
-
 static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
@@ -1094,20 +1138,23 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force)
        if (!force && xc5000_is_firmware_loaded(fe) == 0)
                return 0;
 
-       ret = request_firmware(&fw, desired_fw->name,
-                              priv->i2c_props.adap->dev.parent);
-       if (ret) {
-               printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
-               return ret;
-       }
-
-       dprintk(1, "firmware read %Zu bytes.\n", fw->size);
+       if (!priv->firmware) {
+               ret = request_firmware(&fw, desired_fw->name,
+                                       priv->i2c_props.adap->dev.parent);
+               if (ret) {
+                       pr_err("xc5000: Upload failed. rc %d\n", ret);
+                       return ret;
+               }
+               dprintk(1, "firmware read %Zu bytes.\n", fw->size);
 
-       if (fw->size != desired_fw->size) {
-               printk(KERN_ERR "xc5000: Firmware file with incorrect size\n");
-               ret = -EINVAL;
-               goto err;
-       }
+               if (fw->size != desired_fw->size) {
+                       pr_err("xc5000: Firmware file with incorrect size\n");
+                       release_firmware(fw);
+                       return -EINVAL;
+               }
+               priv->firmware = fw;
+       } else
+               fw = priv->firmware;
 
        /* Try up to 5 times to load firmware */
        for (i = 0; i < 5; i++) {
@@ -1190,7 +1237,6 @@ err:
        else
                printk(KERN_CONT " - too many retries. Giving up\n");
 
-       release_firmware(fw);
        return ret;
 }
 
@@ -1229,6 +1275,38 @@ static int xc5000_sleep(struct dvb_frontend *fe)
        return 0;
 }
 
+static int xc5000_suspend(struct dvb_frontend *fe)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       int ret;
+
+       dprintk(1, "%s()\n", __func__);
+
+       cancel_delayed_work(&priv->timer_sleep);
+
+       ret = xc5000_tuner_reset(fe);
+       if (ret != 0)
+               printk(KERN_ERR
+                       "xc5000: %s() unable to shutdown tuner\n",
+                       __func__);
+
+       return 0;
+}
+
+static int xc5000_resume(struct dvb_frontend *fe)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+
+       dprintk(1, "%s()\n", __func__);
+
+       /* suspended before firmware is loaded.
+          Avoid firmware load in resume path. */
+       if (!priv->firmware)
+               return 0;
+
+       return xc5000_set_params(fe);
+}
+
 static int xc5000_init(struct dvb_frontend *fe)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
@@ -1256,6 +1334,8 @@ static int xc5000_release(struct dvb_frontend *fe)
        if (priv) {
                cancel_delayed_work(&priv->timer_sleep);
                hybrid_tuner_release_state(priv);
+               if (priv->firmware)
+                       release_firmware(priv->firmware);
        }
 
        mutex_unlock(&xc5000_list_mutex);
@@ -1293,9 +1373,11 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = {
        .release           = xc5000_release,
        .init              = xc5000_init,
        .sleep             = xc5000_sleep,
+       .suspend           = xc5000_suspend,
+       .resume            = xc5000_resume,
 
        .set_config        = xc5000_set_config,
-       .set_params        = xc5000_set_params,
+       .set_params        = xc5000_set_digital_params,
        .set_analog_params = xc5000_set_analog_params,
        .get_frequency     = xc5000_get_frequency,
        .get_if_frequency  = xc5000_get_if_frequency,
index 94d51e092db34a4df56fb49570dd5e2de3af0895..056181f2f5694864be2740c05f95069e8aa923d1 100644 (file)
@@ -46,6 +46,7 @@ source "drivers/media/usb/ttusb-budget/Kconfig"
 source "drivers/media/usb/ttusb-dec/Kconfig"
 source "drivers/media/usb/siano/Kconfig"
 source "drivers/media/usb/b2c2/Kconfig"
+source "drivers/media/usb/as102/Kconfig"
 endif
 
 if (MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT)
@@ -55,8 +56,9 @@ endif
 
 if MEDIA_SDR_SUPPORT
        comment "Software defined radio USB devices"
-source "drivers/media/usb/msi2500/Kconfig"
 source "drivers/media/usb/airspy/Kconfig"
+source "drivers/media/usb/hackrf/Kconfig"
+source "drivers/media/usb/msi2500/Kconfig"
 endif
 
 endif #MEDIA_USB_SUPPORT
index f438efffefc59e027a1241fc7a30b42774b416c8..6f2eb7c8416c47844eeb2977122f554b0513668c 100644 (file)
@@ -9,8 +9,9 @@ obj-y += zr364xx/ stkwebcam/ s2255/
 obj-$(CONFIG_USB_VIDEO_CLASS)  += uvc/
 obj-$(CONFIG_USB_GSPCA)         += gspca/
 obj-$(CONFIG_USB_PWC)           += pwc/
-obj-$(CONFIG_USB_MSI2500)       += msi2500/
 obj-$(CONFIG_USB_AIRSPY)        += airspy/
+obj-$(CONFIG_USB_HACKRF)        += hackrf/
+obj-$(CONFIG_USB_MSI2500)       += msi2500/
 obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
 obj-$(CONFIG_VIDEO_AU0828) += au0828/
 obj-$(CONFIG_VIDEO_HDPVR)      += hdpvr/
@@ -23,3 +24,4 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000/
 obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
 obj-$(CONFIG_VIDEO_USBTV) += usbtv/
 obj-$(CONFIG_VIDEO_GO7007) += go7007/
+obj-$(CONFIG_DVB_AS102) += as102/
index cb0e515d80ae378e7e26edecfa196c4024d7dd4b..4069234abed5432c7fbf2f867da9c0235d4eba37 100644 (file)
@@ -107,6 +107,7 @@ struct airspy {
 #define USB_STATE_URB_BUF  (1 << 3)
        unsigned long flags;
 
+       struct device *dev;
        struct usb_device *udev;
        struct video_device vdev;
        struct v4l2_device v4l2_dev;
@@ -154,16 +155,15 @@ struct airspy {
        unsigned int sample_measured;
 };
 
-#define airspy_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \
+#define airspy_dbg_usb_control_msg(_dev, _r, _t, _v, _i, _b, _l) { \
        char *_direction; \
        if (_t & USB_DIR_IN) \
                _direction = "<<<"; \
        else \
                _direction = ">>>"; \
-       dev_dbg(&_udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \
-                       "%s %*ph\n",  __func__, _t, _r, _v & 0xff, _v >> 8, \
-                       _i & 0xff, _i >> 8, _l & 0xff, _l >> 8, _direction, \
-                       _l, _b); \
+       dev_dbg(_dev, "%02x %02x %02x %02x %02x %02x %02x %02x %s %*ph\n", \
+                       _t, _r, _v & 0xff, _v >> 8, _i & 0xff, _i >> 8, \
+                       _l & 0xff, _l >> 8, _direction, _l, _b); \
 }
 
 /* execute firmware command */
@@ -192,7 +192,7 @@ static int airspy_ctrl_msg(struct airspy *s, u8 request, u16 value, u16 index,
                requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
                break;
        default:
-               dev_err(&s->udev->dev, "Unknown command %02x\n", request);
+               dev_err(s->dev, "Unknown command %02x\n", request);
                ret = -EINVAL;
                goto err;
        }
@@ -203,11 +203,10 @@ static int airspy_ctrl_msg(struct airspy *s, u8 request, u16 value, u16 index,
 
        ret = usb_control_msg(s->udev, pipe, request, requesttype, value,
                        index, s->buf, size, 1000);
-       airspy_dbg_usb_control_msg(s->udev, request, requesttype, value,
+       airspy_dbg_usb_control_msg(s->dev, request, requesttype, value,
                        index, s->buf, size);
        if (ret < 0) {
-               dev_err(&s->udev->dev,
-                               "usb_control_msg() failed %d request %02x\n",
+               dev_err(s->dev, "usb_control_msg() failed %d request %02x\n",
                                ret, request);
                goto err;
        }
@@ -224,7 +223,7 @@ err:
 /* Private functions */
 static struct airspy_frame_buf *airspy_get_next_fill_buf(struct airspy *s)
 {
-       unsigned long flags = 0;
+       unsigned long flags;
        struct airspy_frame_buf *buf = NULL;
 
        spin_lock_irqsave(&s->queued_bufs_lock, flags);
@@ -251,16 +250,18 @@ static unsigned int airspy_convert_stream(struct airspy *s,
                dst_len = 0;
        }
 
-       /* calculate samping rate and output it in 10 seconds intervals */
+       /* calculate sample rate and output it in 10 seconds intervals */
        if (unlikely(time_is_before_jiffies(s->jiffies_next))) {
                #define MSECS 10000UL
+               unsigned int msecs = jiffies_to_msecs(jiffies -
+                               s->jiffies_next + msecs_to_jiffies(MSECS));
                unsigned int samples = s->sample - s->sample_measured;
+
                s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
                s->sample_measured = s->sample;
-               dev_dbg(&s->udev->dev,
-                               "slen=%d samples=%u msecs=%lu sample rate=%lu\n",
-                               src_len, samples, MSECS,
-                               samples * 1000UL / MSECS);
+               dev_dbg(s->dev, "slen=%u samples=%u msecs=%u sample rate=%lu\n",
+                               src_len, samples, msecs,
+                               samples * 1000UL / msecs);
        }
 
        /* total number of samples */
@@ -278,9 +279,8 @@ static void airspy_urb_complete(struct urb *urb)
        struct airspy *s = urb->context;
        struct airspy_frame_buf *fbuf;
 
-       dev_dbg_ratelimited(&s->udev->dev,
-                       "%s: status=%d length=%d/%d errors=%d\n",
-                       __func__, urb->status, urb->actual_length,
+       dev_dbg_ratelimited(s->dev, "status=%d length=%d/%d errors=%d\n",
+                       urb->status, urb->actual_length,
                        urb->transfer_buffer_length, urb->error_count);
 
        switch (urb->status) {
@@ -292,8 +292,7 @@ static void airspy_urb_complete(struct urb *urb)
        case -ESHUTDOWN:
                return;
        default:            /* error */
-               dev_err_ratelimited(&s->udev->dev, "URB failed %d\n",
-                               urb->status);
+               dev_err_ratelimited(s->dev, "URB failed %d\n", urb->status);
                break;
        }
 
@@ -304,7 +303,7 @@ static void airspy_urb_complete(struct urb *urb)
                fbuf = airspy_get_next_fill_buf(s);
                if (unlikely(fbuf == NULL)) {
                        s->vb_full++;
-                       dev_notice_ratelimited(&s->udev->dev,
+                       dev_notice_ratelimited(s->dev,
                                        "videobuf is full, %d packets dropped\n",
                                        s->vb_full);
                        goto skip;
@@ -328,7 +327,7 @@ static int airspy_kill_urbs(struct airspy *s)
        int i;
 
        for (i = s->urbs_submitted - 1; i >= 0; i--) {
-               dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i);
+               dev_dbg(s->dev, "kill urb=%d\n", i);
                /* stop the URB */
                usb_kill_urb(s->urb_list[i]);
        }
@@ -342,11 +341,10 @@ static int airspy_submit_urbs(struct airspy *s)
        int i, ret;
 
        for (i = 0; i < s->urbs_initialized; i++) {
-               dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i);
+               dev_dbg(s->dev, "submit urb=%d\n", i);
                ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC);
                if (ret) {
-                       dev_err(&s->udev->dev,
-                                       "Could not submit URB no. %d - get them all back\n",
+                       dev_err(s->dev, "Could not submit URB no. %d - get them all back\n",
                                        i);
                        airspy_kill_urbs(s);
                        return ret;
@@ -362,8 +360,7 @@ static int airspy_free_stream_bufs(struct airspy *s)
        if (s->flags & USB_STATE_URB_BUF) {
                while (s->buf_num) {
                        s->buf_num--;
-                       dev_dbg(&s->udev->dev, "%s: free buf=%d\n",
-                                       __func__, s->buf_num);
+                       dev_dbg(s->dev, "free buf=%d\n", s->buf_num);
                        usb_free_coherent(s->udev, s->buf_size,
                                          s->buf_list[s->buf_num],
                                          s->dma_addr[s->buf_num]);
@@ -379,23 +376,20 @@ static int airspy_alloc_stream_bufs(struct airspy *s)
        s->buf_num = 0;
        s->buf_size = BULK_BUFFER_SIZE;
 
-       dev_dbg(&s->udev->dev,
-                       "%s: all in all I will use %u bytes for streaming\n",
-                       __func__,  MAX_BULK_BUFS * BULK_BUFFER_SIZE);
+       dev_dbg(s->dev, "all in all I will use %u bytes for streaming\n",
+                       MAX_BULK_BUFS * BULK_BUFFER_SIZE);
 
        for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) {
                s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev,
                                BULK_BUFFER_SIZE, GFP_ATOMIC,
                                &s->dma_addr[s->buf_num]);
                if (!s->buf_list[s->buf_num]) {
-                       dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n",
-                                       __func__, s->buf_num);
+                       dev_dbg(s->dev, "alloc buf=%d failed\n", s->buf_num);
                        airspy_free_stream_bufs(s);
                        return -ENOMEM;
                }
 
-               dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n",
-                               __func__, s->buf_num,
+               dev_dbg(s->dev, "alloc buf=%d %p (dma %llu)\n", s->buf_num,
                                s->buf_list[s->buf_num],
                                (long long)s->dma_addr[s->buf_num]);
                s->flags |= USB_STATE_URB_BUF;
@@ -412,8 +406,7 @@ static int airspy_free_urbs(struct airspy *s)
 
        for (i = s->urbs_initialized - 1; i >= 0; i--) {
                if (s->urb_list[i]) {
-                       dev_dbg(&s->udev->dev, "%s: free urb=%d\n",
-                                       __func__, i);
+                       dev_dbg(s->dev, "free urb=%d\n", i);
                        /* free the URBs */
                        usb_free_urb(s->urb_list[i]);
                }
@@ -429,10 +422,10 @@ static int airspy_alloc_urbs(struct airspy *s)
 
        /* allocate the URBs */
        for (i = 0; i < MAX_BULK_BUFS; i++) {
-               dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i);
+               dev_dbg(s->dev, "alloc urb=%d\n", i);
                s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
                if (!s->urb_list[i]) {
-                       dev_dbg(&s->udev->dev, "%s: failed\n", __func__);
+                       dev_dbg(s->dev, "failed\n");
                        for (j = 0; j < i; j++)
                                usb_free_urb(s->urb_list[j]);
                        return -ENOMEM;
@@ -455,13 +448,14 @@ static int airspy_alloc_urbs(struct airspy *s)
 /* Must be called with vb_queue_lock hold */
 static void airspy_cleanup_queued_bufs(struct airspy *s)
 {
-       unsigned long flags = 0;
+       unsigned long flags;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        spin_lock_irqsave(&s->queued_bufs_lock, flags);
        while (!list_empty(&s->queued_bufs)) {
                struct airspy_frame_buf *buf;
+
                buf = list_entry(s->queued_bufs.next,
                                struct airspy_frame_buf, list);
                list_del(&buf->list);
@@ -476,7 +470,7 @@ static void airspy_disconnect(struct usb_interface *intf)
        struct v4l2_device *v = usb_get_intfdata(intf);
        struct airspy *s = container_of(v, struct airspy, v4l2_dev);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        mutex_lock(&s->vb_queue_lock);
        mutex_lock(&s->v4l2_lock);
@@ -497,7 +491,7 @@ static int airspy_queue_setup(struct vb2_queue *vq,
 {
        struct airspy *s = vb2_get_drv_priv(vq);
 
-       dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers);
+       dev_dbg(s->dev, "nbuffers=%d\n", *nbuffers);
 
        /* Need at least 8 buffers */
        if (vq->num_buffers + *nbuffers < 8)
@@ -505,8 +499,7 @@ static int airspy_queue_setup(struct vb2_queue *vq,
        *nplanes = 1;
        sizes[0] = PAGE_ALIGN(s->buffersize);
 
-       dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
-                       __func__, *nbuffers, sizes[0]);
+       dev_dbg(s->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]);
        return 0;
 }
 
@@ -515,7 +508,7 @@ static void airspy_buf_queue(struct vb2_buffer *vb)
        struct airspy *s = vb2_get_drv_priv(vb->vb2_queue);
        struct airspy_frame_buf *buf =
                        container_of(vb, struct airspy_frame_buf, vb);
-       unsigned long flags = 0;
+       unsigned long flags;
 
        /* Check the device has not disconnected between prep and queuing */
        if (unlikely(!s->udev)) {
@@ -533,34 +526,56 @@ static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count)
        struct airspy *s = vb2_get_drv_priv(vq);
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        if (!s->udev)
                return -ENODEV;
 
        mutex_lock(&s->v4l2_lock);
 
-       set_bit(POWER_ON, &s->flags);
-
        s->sequence = 0;
 
+       set_bit(POWER_ON, &s->flags);
+
        ret = airspy_alloc_stream_bufs(s);
        if (ret)
-               goto err;
+               goto err_clear_bit;
 
        ret = airspy_alloc_urbs(s);
        if (ret)
-               goto err;
+               goto err_free_stream_bufs;
 
        ret = airspy_submit_urbs(s);
        if (ret)
-               goto err;
+               goto err_free_urbs;
 
        /* start hardware streaming */
        ret = airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 1, 0, NULL, 0);
        if (ret)
-               goto err;
-err:
+               goto err_kill_urbs;
+
+       goto exit_mutex_unlock;
+
+err_kill_urbs:
+       airspy_kill_urbs(s);
+err_free_urbs:
+       airspy_free_urbs(s);
+err_free_stream_bufs:
+       airspy_free_stream_bufs(s);
+err_clear_bit:
+       clear_bit(POWER_ON, &s->flags);
+
+       /* return all queued buffers to vb2 */
+       {
+               struct airspy_frame_buf *buf, *tmp;
+
+               list_for_each_entry_safe(buf, tmp, &s->queued_bufs, list) {
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+               }
+       }
+
+exit_mutex_unlock:
        mutex_unlock(&s->v4l2_lock);
 
        return ret;
@@ -570,7 +585,7 @@ static void airspy_stop_streaming(struct vb2_queue *vq)
 {
        struct airspy *s = vb2_get_drv_priv(vq);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        mutex_lock(&s->v4l2_lock);
 
@@ -602,8 +617,6 @@ static int airspy_querycap(struct file *file, void *fh,
 {
        struct airspy *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
-
        strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
        strlcpy(cap->card, s->vdev.name, sizeof(cap->card));
        usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info));
@@ -617,10 +630,6 @@ static int airspy_querycap(struct file *file, void *fh,
 static int airspy_enum_fmt_sdr_cap(struct file *file, void *priv,
                struct v4l2_fmtdesc *f)
 {
-       struct airspy *s = video_drvdata(file);
-
-       dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index);
-
        if (f->index >= NUM_FORMATS)
                return -EINVAL;
 
@@ -635,9 +644,6 @@ static int airspy_g_fmt_sdr_cap(struct file *file, void *priv,
 {
        struct airspy *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
-                       (char *)&s->pixelformat);
-
        f->fmt.sdr.pixelformat = s->pixelformat;
        f->fmt.sdr.buffersize = s->buffersize;
        memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
@@ -652,9 +658,6 @@ static int airspy_s_fmt_sdr_cap(struct file *file, void *priv,
        struct vb2_queue *q = &s->vb_queue;
        int i;
 
-       dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
-                       (char *)&f->fmt.sdr.pixelformat);
-
        if (vb2_is_busy(q))
                return -EBUSY;
 
@@ -679,12 +682,8 @@ static int airspy_s_fmt_sdr_cap(struct file *file, void *priv,
 static int airspy_try_fmt_sdr_cap(struct file *file, void *priv,
                struct v4l2_format *f)
 {
-       struct airspy *s = video_drvdata(file);
        int i;
 
-       dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
-                       (char *)&f->fmt.sdr.pixelformat);
-
        memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
        for (i = 0; i < NUM_FORMATS; i++) {
                if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
@@ -702,11 +701,8 @@ static int airspy_try_fmt_sdr_cap(struct file *file, void *priv,
 static int airspy_s_tuner(struct file *file, void *priv,
                const struct v4l2_tuner *v)
 {
-       struct airspy *s = video_drvdata(file);
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index);
-
        if (v->index == 0)
                ret = 0;
        else if (v->index == 1)
@@ -719,11 +715,8 @@ static int airspy_s_tuner(struct file *file, void *priv,
 
 static int airspy_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
 {
-       struct airspy *s = video_drvdata(file);
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index);
-
        if (v->index == 0) {
                strlcpy(v->name, "AirSpy ADC", sizeof(v->name));
                v->type = V4L2_TUNER_ADC;
@@ -749,17 +742,18 @@ static int airspy_g_frequency(struct file *file, void *priv,
                struct v4l2_frequency *f)
 {
        struct airspy *s = video_drvdata(file);
-       int ret  = 0;
-       dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n",
-                       __func__, f->tuner, f->type);
+       int ret;
 
        if (f->tuner == 0) {
                f->type = V4L2_TUNER_ADC;
                f->frequency = s->f_adc;
+               dev_dbg(s->dev, "ADC frequency=%u Hz\n", s->f_adc);
                ret = 0;
        } else if (f->tuner == 1) {
                f->type = V4L2_TUNER_RF;
                f->frequency = s->f_rf;
+               dev_dbg(s->dev, "RF frequency=%u Hz\n", s->f_rf);
+               ret = 0;
        } else {
                ret = -EINVAL;
        }
@@ -774,22 +768,17 @@ static int airspy_s_frequency(struct file *file, void *priv,
        int ret;
        u8 buf[4];
 
-       dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n",
-                       __func__, f->tuner, f->type, f->frequency);
-
        if (f->tuner == 0) {
                s->f_adc = clamp_t(unsigned int, f->frequency,
                                bands[0].rangelow,
                                bands[0].rangehigh);
-               dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n",
-                               __func__, s->f_adc);
+               dev_dbg(s->dev, "ADC frequency=%u Hz\n", s->f_adc);
                ret = 0;
        } else if (f->tuner == 1) {
                s->f_rf = clamp_t(unsigned int, f->frequency,
                                bands_rf[0].rangelow,
                                bands_rf[0].rangehigh);
-               dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n",
-                               __func__, s->f_rf);
+               dev_dbg(s->dev, "RF frequency=%u Hz\n", s->f_rf);
                buf[0] = (s->f_rf >>  0) & 0xff;
                buf[1] = (s->f_rf >>  8) & 0xff;
                buf[2] = (s->f_rf >> 16) & 0xff;
@@ -805,10 +794,7 @@ static int airspy_s_frequency(struct file *file, void *priv,
 static int airspy_enum_freq_bands(struct file *file, void *priv,
                struct v4l2_frequency_band *band)
 {
-       struct airspy *s = video_drvdata(file);
        int ret;
-       dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n",
-                       __func__, band->tuner, band->type, band->index);
 
        if (band->tuner == 0) {
                if (band->index >= ARRAY_SIZE(bands)) {
@@ -892,10 +878,9 @@ static int airspy_set_lna_gain(struct airspy *s)
        int ret;
        u8 u8tmp;
 
-       dev_dbg(&s->udev->dev, "%s: lna auto=%d->%d val=%d->%d\n",
-                       __func__, s->lna_gain_auto->cur.val,
-                       s->lna_gain_auto->val, s->lna_gain->cur.val,
-                       s->lna_gain->val);
+       dev_dbg(s->dev, "lna auto=%d->%d val=%d->%d\n",
+                       s->lna_gain_auto->cur.val, s->lna_gain_auto->val,
+                       s->lna_gain->cur.val, s->lna_gain->val);
 
        ret = airspy_ctrl_msg(s, CMD_SET_LNA_AGC, 0, s->lna_gain_auto->val,
                        &u8tmp, 1);
@@ -910,7 +895,7 @@ static int airspy_set_lna_gain(struct airspy *s)
        }
 err:
        if (ret)
-               dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(s->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -920,10 +905,9 @@ static int airspy_set_mixer_gain(struct airspy *s)
        int ret;
        u8 u8tmp;
 
-       dev_dbg(&s->udev->dev, "%s: mixer auto=%d->%d val=%d->%d\n",
-                       __func__, s->mixer_gain_auto->cur.val,
-                       s->mixer_gain_auto->val, s->mixer_gain->cur.val,
-                       s->mixer_gain->val);
+       dev_dbg(s->dev, "mixer auto=%d->%d val=%d->%d\n",
+                       s->mixer_gain_auto->cur.val, s->mixer_gain_auto->val,
+                       s->mixer_gain->cur.val, s->mixer_gain->val);
 
        ret = airspy_ctrl_msg(s, CMD_SET_MIXER_AGC, 0, s->mixer_gain_auto->val,
                        &u8tmp, 1);
@@ -938,7 +922,7 @@ static int airspy_set_mixer_gain(struct airspy *s)
        }
 err:
        if (ret)
-               dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(s->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -948,8 +932,7 @@ static int airspy_set_if_gain(struct airspy *s)
        int ret;
        u8 u8tmp;
 
-       dev_dbg(&s->udev->dev, "%s: val=%d->%d\n",
-                       __func__, s->if_gain->cur.val, s->if_gain->val);
+       dev_dbg(s->dev, "val=%d->%d\n", s->if_gain->cur.val, s->if_gain->val);
 
        ret = airspy_ctrl_msg(s, CMD_SET_VGA_GAIN, 0, s->if_gain->val,
                        &u8tmp, 1);
@@ -957,7 +940,7 @@ static int airspy_set_if_gain(struct airspy *s)
                goto err;
 err:
        if (ret)
-               dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(s->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -980,8 +963,8 @@ static int airspy_s_ctrl(struct v4l2_ctrl *ctrl)
                ret = airspy_set_if_gain(s);
                break;
        default:
-               dev_dbg(&s->udev->dev, "%s: unknown ctrl: id=%d name=%s\n",
-                               __func__, ctrl->id, ctrl->name);
+               dev_dbg(s->dev, "unknown ctrl: id=%d name=%s\n",
+                               ctrl->id, ctrl->name);
                ret = -EINVAL;
        }
 
@@ -995,15 +978,13 @@ static const struct v4l2_ctrl_ops airspy_ctrl_ops = {
 static int airspy_probe(struct usb_interface *intf,
                const struct usb_device_id *id)
 {
-       struct usb_device *udev = interface_to_usbdev(intf);
-       struct airspy *s = NULL;
+       struct airspy *s;
        int ret;
        u8 u8tmp, buf[BUF_SIZE];
 
        s = kzalloc(sizeof(struct airspy), GFP_KERNEL);
        if (s == NULL) {
-               dev_err(&udev->dev,
-                               "Could not allocate memory for airspy state\n");
+               dev_err(&intf->dev, "Could not allocate memory for state\n");
                return -ENOMEM;
        }
 
@@ -1011,7 +992,8 @@ static int airspy_probe(struct usb_interface *intf,
        mutex_init(&s->vb_queue_lock);
        spin_lock_init(&s->queued_bufs_lock);
        INIT_LIST_HEAD(&s->queued_bufs);
-       s->udev = udev;
+       s->dev = &intf->dev;
+       s->udev = interface_to_usbdev(intf);
        s->f_adc = bands[0].rangelow;
        s->f_rf = bands_rf[0].rangelow;
        s->pixelformat = formats[0].pixelformat;
@@ -1023,14 +1005,14 @@ static int airspy_probe(struct usb_interface *intf,
                ret = airspy_ctrl_msg(s, CMD_VERSION_STRING_READ, 0, 0,
                                buf, BUF_SIZE);
        if (ret) {
-               dev_err(&s->udev->dev, "Could not detect board\n");
+               dev_err(s->dev, "Could not detect board\n");
                goto err_free_mem;
        }
 
        buf[BUF_SIZE - 1] = '\0';
 
-       dev_info(&s->udev->dev, "Board ID: %02x\n", u8tmp);
-       dev_info(&s->udev->dev, "Firmware version: %s\n", buf);
+       dev_info(s->dev, "Board ID: %02x\n", u8tmp);
+       dev_info(s->dev, "Firmware version: %s\n", buf);
 
        /* Init videobuf2 queue structure */
        s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
@@ -1042,7 +1024,7 @@ static int airspy_probe(struct usb_interface *intf,
        s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        ret = vb2_queue_init(&s->vb_queue);
        if (ret) {
-               dev_err(&s->udev->dev, "Could not initialize vb2 queue\n");
+               dev_err(s->dev, "Could not initialize vb2 queue\n");
                goto err_free_mem;
        }
 
@@ -1056,8 +1038,7 @@ static int airspy_probe(struct usb_interface *intf,
        s->v4l2_dev.release = airspy_video_release;
        ret = v4l2_device_register(&intf->dev, &s->v4l2_dev);
        if (ret) {
-               dev_err(&s->udev->dev,
-                               "Failed to register v4l2-device (%d)\n", ret);
+               dev_err(s->dev, "Failed to register v4l2-device (%d)\n", ret);
                goto err_free_mem;
        }
 
@@ -1077,7 +1058,7 @@ static int airspy_probe(struct usb_interface *intf,
                        V4L2_CID_RF_TUNER_IF_GAIN, 0, 15, 1, 0);
        if (s->hdl.error) {
                ret = s->hdl.error;
-               dev_err(&s->udev->dev, "Could not initialize controls\n");
+               dev_err(s->dev, "Could not initialize controls\n");
                goto err_free_controls;
        }
 
@@ -1089,16 +1070,13 @@ static int airspy_probe(struct usb_interface *intf,
 
        ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
        if (ret) {
-               dev_err(&s->udev->dev,
-                               "Failed to register as video device (%d)\n",
+               dev_err(s->dev, "Failed to register as video device (%d)\n",
                                ret);
                goto err_unregister_v4l2_dev;
        }
-       dev_info(&s->udev->dev, "Registered as %s\n",
+       dev_info(s->dev, "Registered as %s\n",
                        video_device_node_name(&s->vdev));
-       dev_notice(&s->udev->dev,
-                       "%s: SDR API is still slightly experimental and functionality changes may follow\n",
-                       KBUILD_MODNAME);
+       dev_notice(s->dev, "SDR API is still slightly experimental and functionality changes may follow\n");
        return 0;
 
 err_free_controls:
similarity index 65%
rename from drivers/staging/media/as102/Makefile
rename to drivers/media/usb/as102/Makefile
index 8916d8a909bc6dec3c499f73309e8623a4da028a..22f43eee4a3bc5fa55fe7fc2675e9062a2b6fec2 100644 (file)
@@ -1,6 +1,7 @@
 dvb-as102-objs := as102_drv.o as102_fw.o as10x_cmd.o as10x_cmd_stream.o \
-               as102_fe.o as102_usb_drv.o as10x_cmd_cfg.o
+                 as102_usb_drv.o as10x_cmd_cfg.o
 
 obj-$(CONFIG_DVB_AS102) += dvb-as102.o
 
 ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
similarity index 66%
rename from drivers/staging/media/as102/as102_drv.c
rename to drivers/media/usb/as102/as102_drv.c
index 09d64cd675020f65669b3b8c7b9386414fb7738e..8be1474b2c36fbf2df6080e62afbadae648de711 100644 (file)
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 #include <linux/kernel.h>
 #include <linux/errno.h>
 
 /* header file for usb device driver*/
 #include "as102_drv.h"
+#include "as10x_cmd.h"
+#include "as102_fe.h"
 #include "as102_fw.h"
 #include "dvbdev.h"
 
-int as102_debug;
-module_param_named(debug, as102_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default: off)");
-
 int dual_tuner;
 module_param_named(dual_tuner, dual_tuner, int, 0644);
 MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)");
@@ -74,7 +68,8 @@ static void as102_stop_stream(struct as102_dev_t *dev)
                        return;
 
                if (as10x_cmd_stop_streaming(bus_adap) < 0)
-                       dprintk(debug, "as10x_cmd_stop_streaming failed\n");
+                       dev_dbg(&dev->bus_adap.usb_dev->dev,
+                               "as10x_cmd_stop_streaming failed\n");
 
                mutex_unlock(&dev->bus_adap.lock);
        }
@@ -112,14 +107,16 @@ static int as10x_pid_filter(struct as102_dev_t *dev,
        int ret = -EFAULT;
 
        if (mutex_lock_interruptible(&dev->bus_adap.lock)) {
-               dprintk(debug, "mutex_lock_interruptible(lock) failed !\n");
+               dev_dbg(&dev->bus_adap.usb_dev->dev,
+                       "amutex_lock_interruptible(lock) failed !\n");
                return -EBUSY;
        }
 
        switch (onoff) {
        case 0:
                ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid);
-               dprintk(debug, "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
+               dev_dbg(&dev->bus_adap.usb_dev->dev,
+                       "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
                        index, pid, ret);
                break;
        case 1:
@@ -131,7 +128,7 @@ static int as10x_pid_filter(struct as102_dev_t *dev,
                filter.pid = pid;
 
                ret = as10x_cmd_add_PID_filter(bus_adap, &filter);
-               dprintk(debug,
+               dev_dbg(&dev->bus_adap.usb_dev->dev,
                        "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n",
                        index, filter.idx, filter.pid, ret);
                break;
@@ -181,6 +178,119 @@ static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
        return 0;
 }
 
+static int as102_set_tune(void *priv, struct as10x_tune_args *tune_args)
+{
+       struct as10x_bus_adapter_t *bus_adap = priv;
+       int ret;
+
+       /* Set frontend arguments */
+       if (mutex_lock_interruptible(&bus_adap->lock))
+               return -EBUSY;
+
+       ret =  as10x_cmd_set_tune(bus_adap, tune_args);
+       if (ret != 0)
+               dev_dbg(&bus_adap->usb_dev->dev,
+                       "as10x_cmd_set_tune failed. (err = %d)\n", ret);
+
+       mutex_unlock(&bus_adap->lock);
+
+       return ret;
+}
+
+static int as102_get_tps(void *priv, struct as10x_tps *tps)
+{
+       struct as10x_bus_adapter_t *bus_adap = priv;
+       int ret;
+
+       if (mutex_lock_interruptible(&bus_adap->lock))
+               return -EBUSY;
+
+       /* send abilis command: GET_TPS */
+       ret = as10x_cmd_get_tps(bus_adap, tps);
+
+       mutex_unlock(&bus_adap->lock);
+
+       return ret;
+}
+
+static int as102_get_status(void *priv, struct as10x_tune_status *tstate)
+{
+       struct as10x_bus_adapter_t *bus_adap = priv;
+       int ret;
+
+       if (mutex_lock_interruptible(&bus_adap->lock))
+               return -EBUSY;
+
+       /* send abilis command: GET_TUNE_STATUS */
+       ret = as10x_cmd_get_tune_status(bus_adap, tstate);
+       if (ret < 0) {
+               dev_dbg(&bus_adap->usb_dev->dev,
+                       "as10x_cmd_get_tune_status failed (err = %d)\n",
+                       ret);
+       }
+
+       mutex_unlock(&bus_adap->lock);
+
+       return ret;
+}
+
+static int as102_get_stats(void *priv, struct as10x_demod_stats *demod_stats)
+{
+       struct as10x_bus_adapter_t *bus_adap = priv;
+       int ret;
+
+       if (mutex_lock_interruptible(&bus_adap->lock))
+               return -EBUSY;
+
+       /* send abilis command: GET_TUNE_STATUS */
+       ret = as10x_cmd_get_demod_stats(bus_adap, demod_stats);
+       if (ret < 0) {
+               dev_dbg(&bus_adap->usb_dev->dev,
+                       "as10x_cmd_get_demod_stats failed (probably not tuned)\n");
+       } else {
+               dev_dbg(&bus_adap->usb_dev->dev,
+                       "demod status: fc: 0x%08x, bad fc: 0x%08x, bytes corrected: 0x%08x , MER: 0x%04x\n",
+                       demod_stats->frame_count,
+                       demod_stats->bad_frame_count,
+                       demod_stats->bytes_fixed_by_rs,
+                       demod_stats->mer);
+       }
+       mutex_unlock(&bus_adap->lock);
+
+       return ret;
+}
+
+static int as102_stream_ctrl(void *priv, int acquire, uint32_t elna_cfg)
+{
+       struct as10x_bus_adapter_t *bus_adap = priv;
+       int ret;
+
+       if (mutex_lock_interruptible(&bus_adap->lock))
+               return -EBUSY;
+
+       if (acquire) {
+               if (elna_enable)
+                       as10x_cmd_set_context(bus_adap,
+                                             CONTEXT_LNA, elna_cfg);
+
+               ret = as10x_cmd_turn_on(bus_adap);
+       } else {
+               ret = as10x_cmd_turn_off(bus_adap);
+       }
+
+       mutex_unlock(&bus_adap->lock);
+
+       return ret;
+}
+
+static const struct as102_fe_ops as102_fe_ops = {
+       .set_tune = as102_set_tune,
+       .get_tps  = as102_get_tps,
+       .get_status = as102_get_status,
+       .get_stats = as102_get_stats,
+       .stream_ctrl = as102_stream_ctrl,
+};
+
 int as102_dvb_register(struct as102_dev_t *as102_dev)
 {
        struct device *dev = &as102_dev->bus_adap.usb_dev->dev;
@@ -221,7 +331,18 @@ int as102_dvb_register(struct as102_dev_t *as102_dev)
                goto edmxdinit;
        }
 
-       ret = as102_dvb_register_fe(as102_dev, &as102_dev->dvb_fe);
+       /* Attach the frontend */
+       as102_dev->dvb_fe = dvb_attach(as102_attach, as102_dev->name,
+                                      &as102_fe_ops,
+                                      &as102_dev->bus_adap,
+                                      as102_dev->elna_cfg);
+       if (!as102_dev->dvb_fe) {
+               dev_err(dev, "%s: as102_attach() failed: %d",
+                   __func__, ret);
+               goto efereg;
+       }
+
+       ret =  dvb_register_frontend(&as102_dev->dvb_adap, as102_dev->dvb_fe);
        if (ret < 0) {
                dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d",
                    __func__, ret);
@@ -257,7 +378,10 @@ edmxinit:
 void as102_dvb_unregister(struct as102_dev_t *as102_dev)
 {
        /* unregister as102 frontend */
-       as102_dvb_unregister_fe(&as102_dev->dvb_fe);
+       dvb_unregister_frontend(as102_dev->dvb_fe);
+
+       /* detach frontend */
+       dvb_frontend_detach(as102_dev->dvb_fe);
 
        /* unregister demux device */
        dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
similarity index 75%
rename from drivers/staging/media/as102/as102_drv.h
rename to drivers/media/usb/as102/as102_drv.h
index a06837dcc05d017b800baabc43b71527eb7112ba..aee2d76e8dfc996dd3fa4e1f09ca0c55be47e2ed 100644 (file)
  * 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 _AS102_DRV_H
+#define _AS102_DRV_H
 #include <linux/usb.h>
 #include <dvb_demux.h>
 #include <dvb_frontend.h>
 #include <dmxdev.h>
+#include "as10x_handle.h"
 #include "as10x_cmd.h"
 #include "as102_usb_drv.h"
 
 #define DRIVER_FULL_NAME "Abilis Systems as10x usb driver"
 #define DRIVER_NAME "as10x_usb"
 
-extern int as102_debug;
 #define debug  as102_debug
 extern struct usb_driver as102_usb_driver;
 extern int elna_enable;
 
-#define dprintk(debug, args...) \
-       do { if (debug) {       \
-               pr_debug("%s: ", __func__);     \
-               printk(args);   \
-       } } while (0)
-
 #define AS102_DEVICE_MAJOR     192
 
 #define AS102_USB_BUF_SIZE     512
@@ -71,17 +63,10 @@ struct as102_dev_t {
        uint8_t elna_cfg;
 
        struct dvb_adapter dvb_adap;
-       struct dvb_frontend dvb_fe;
+       struct dvb_frontend *dvb_fe;
        struct dvb_demux dvb_dmx;
        struct dmxdev dvb_dmxdev;
 
-       /* demodulator stats */
-       struct as10x_demod_stats demod_stats;
-       /* signal strength */
-       uint16_t signal_strength;
-       /* bit error rate */
-       uint32_t ber;
-
        /* timer handle to trig ts stream download */
        struct timer_list timer_handle;
 
@@ -95,5 +80,4 @@ struct as102_dev_t {
 int as102_dvb_register(struct as102_dev_t *dev);
 void as102_dvb_unregister(struct as102_dev_t *dev);
 
-int as102_dvb_register_fe(struct as102_dev_t *dev, struct dvb_frontend *fe);
-int as102_dvb_unregister_fe(struct dvb_frontend *dev);
+#endif
similarity index 96%
rename from drivers/staging/media/as102/as102_fw.c
rename to drivers/media/usb/as102/as102_fw.c
index f33f752c0aadfcf6788fce3cf87df07394e94c08..07d08c49f4d495d0fc40d26a3bde82051eae7c30 100644 (file)
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 #include <linux/kernel.h>
 #include <linux/errno.h>
similarity index 83%
rename from drivers/staging/media/as102/as102_fw.h
rename to drivers/media/usb/as102/as102_fw.h
index 4bfc6849d95a34de6878848fcb5c8597940535ed..2732b784216d6ef46cf65e9f24102f7722def233 100644 (file)
  * 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.
  */
 #define MAX_FW_PKT_SIZE        64
 
similarity index 90%
rename from drivers/staging/media/as102/as102_usb_drv.c
rename to drivers/media/usb/as102/as102_usb_drv.c
index e6f6278e97d6869c0f3343cf4b16707e58bb2d0a..3f669066ccf6837dd8b4022c7f03ab166e3a2f42 100644 (file)
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -104,21 +100,22 @@ static int as102_usb_xfer_cmd(struct as10x_bus_adapter_t *bus_adap,
                                      send_buf, send_buf_len,
                                      USB_CTRL_SET_TIMEOUT /* 200 */);
                if (ret < 0) {
-                       dprintk(debug, "usb_control_msg(send) failed, err %i\n",
-                                       ret);
+                       dev_dbg(&bus_adap->usb_dev->dev,
+                               "usb_control_msg(send) failed, err %i\n", ret);
                        return ret;
                }
 
                if (ret != send_buf_len) {
-                       dprintk(debug, "only wrote %d of %d bytes\n",
-                                       ret, send_buf_len);
+                       dev_dbg(&bus_adap->usb_dev->dev,
+                       "only wrote %d of %d bytes\n", ret, send_buf_len);
                        return -1;
                }
        }
 
        if (recv_buf != NULL) {
 #ifdef TRACE
-               dprintk(debug, "want to read: %d bytes\n", recv_buf_len);
+               dev_dbg(bus_adap->usb_dev->dev,
+                       "want to read: %d bytes\n", recv_buf_len);
 #endif
                ret = usb_control_msg(bus_adap->usb_dev,
                                      usb_rcvctrlpipe(bus_adap->usb_dev, 0),
@@ -130,12 +127,13 @@ static int as102_usb_xfer_cmd(struct as10x_bus_adapter_t *bus_adap,
                                      recv_buf, recv_buf_len,
                                      USB_CTRL_GET_TIMEOUT /* 200 */);
                if (ret < 0) {
-                       dprintk(debug, "usb_control_msg(recv) failed, err %i\n",
-                                       ret);
+                       dev_dbg(&bus_adap->usb_dev->dev,
+                               "usb_control_msg(recv) failed, err %i\n", ret);
                        return ret;
                }
 #ifdef TRACE
-               dprintk(debug, "read %d bytes\n", recv_buf_len);
+               dev_dbg(bus_adap->usb_dev->dev,
+                       "read %d bytes\n", recv_buf_len);
 #endif
        }
 
@@ -147,28 +145,29 @@ static int as102_send_ep1(struct as10x_bus_adapter_t *bus_adap,
                          int send_buf_len,
                          int swap32)
 {
-       int ret = 0, actual_len;
+       int ret, actual_len;
 
        ret = usb_bulk_msg(bus_adap->usb_dev,
                           usb_sndbulkpipe(bus_adap->usb_dev, 1),
                           send_buf, send_buf_len, &actual_len, 200);
        if (ret) {
-               dprintk(debug, "usb_bulk_msg(send) failed, err %i\n", ret);
+               dev_dbg(&bus_adap->usb_dev->dev,
+                       "usb_bulk_msg(send) failed, err %i\n", ret);
                return ret;
        }
 
        if (actual_len != send_buf_len) {
-               dprintk(debug, "only wrote %d of %d bytes\n",
-                               actual_len, send_buf_len);
+               dev_dbg(&bus_adap->usb_dev->dev, "only wrote %d of %d bytes\n",
+                       actual_len, send_buf_len);
                return -1;
        }
-       return ret ? ret : actual_len;
+       return actual_len;
 }
 
 static int as102_read_ep2(struct as10x_bus_adapter_t *bus_adap,
                   unsigned char *recv_buf, int recv_buf_len)
 {
-       int ret = 0, actual_len;
+       int ret, actual_len;
 
        if (recv_buf == NULL)
                return -EINVAL;
@@ -177,16 +176,17 @@ static int as102_read_ep2(struct as10x_bus_adapter_t *bus_adap,
                           usb_rcvbulkpipe(bus_adap->usb_dev, 2),
                           recv_buf, recv_buf_len, &actual_len, 200);
        if (ret) {
-               dprintk(debug, "usb_bulk_msg(recv) failed, err %i\n", ret);
+               dev_dbg(&bus_adap->usb_dev->dev,
+                       "usb_bulk_msg(recv) failed, err %i\n", ret);
                return ret;
        }
 
        if (actual_len != recv_buf_len) {
-               dprintk(debug, "only read %d of %d bytes\n",
-                               actual_len, recv_buf_len);
+               dev_dbg(&bus_adap->usb_dev->dev, "only read %d of %d bytes\n",
+                       actual_len, recv_buf_len);
                return -1;
        }
-       return ret ? ret : actual_len;
+       return actual_len;
 }
 
 static struct as102_priv_ops_t as102_priv_ops = {
@@ -211,7 +211,8 @@ static int as102_submit_urb_stream(struct as102_dev_t *dev, struct urb *urb)
 
        err = usb_submit_urb(urb, GFP_ATOMIC);
        if (err)
-               dprintk(debug, "%s: usb_submit_urb failed\n", __func__);
+               dev_dbg(&urb->dev->dev,
+                       "%s: usb_submit_urb failed\n", __func__);
 
        return err;
 }
@@ -256,7 +257,8 @@ static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
                                       GFP_KERNEL,
                                       &dev->dma_addr);
        if (!dev->stream) {
-               dprintk(debug, "%s: usb_buffer_alloc failed\n", __func__);
+               dev_dbg(&dev->bus_adap.usb_dev->dev,
+                       "%s: usb_buffer_alloc failed\n", __func__);
                return -ENOMEM;
        }
 
@@ -268,7 +270,8 @@ static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
 
                urb = usb_alloc_urb(0, GFP_ATOMIC);
                if (urb == NULL) {
-                       dprintk(debug, "%s: usb_alloc_urb failed\n", __func__);
+                       dev_dbg(&dev->bus_adap.usb_dev->dev,
+                               "%s: usb_alloc_urb failed\n", __func__);
                        as102_free_usb_stream_buffer(dev);
                        return -ENOMEM;
                }
similarity index 90%
rename from drivers/staging/media/as102/as102_usb_drv.h
rename to drivers/media/usb/as102/as102_usb_drv.h
index 1ad1ec52b11e82da540e6d53f594bfb25a2a89c6..4fb1baa8cac0ddbff64d818e179bc34031d27c74 100644 (file)
  * 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 _AS102_USB_DRV_H_
 #define _AS102_USB_DRV_H_
similarity index 92%
rename from drivers/staging/media/as102/as10x_cmd.c
rename to drivers/media/usb/as102/as10x_cmd.c
index 9e49f15a7c9f1df77f825231596cc8b9e6f79fa2..8706179944103db0095502c74a806e5d69a03e76 100644 (file)
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/kernel.h>
 #include "as102_drv.h"
-#include "as10x_types.h"
 #include "as10x_cmd.h"
 
 /**
@@ -126,7 +121,7 @@ int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap,
 
        /* fill command */
        preq->body.set_tune.req.proc_id = cpu_to_le16(CONTROL_PROC_SETTUNE);
-       preq->body.set_tune.req.args.freq = cpu_to_le32(ptune->freq);
+       preq->body.set_tune.req.args.freq = (__force __u32)cpu_to_le32(ptune->freq);
        preq->body.set_tune.req.args.bandwidth = ptune->bandwidth;
        preq->body.set_tune.req.args.hier_select = ptune->hier_select;
        preq->body.set_tune.req.args.modulation = ptune->modulation;
@@ -204,9 +199,9 @@ int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap,
        /* Response OK -> get response data */
        pstatus->tune_state = prsp->body.get_tune_status.rsp.sts.tune_state;
        pstatus->signal_strength  =
-               le16_to_cpu(prsp->body.get_tune_status.rsp.sts.signal_strength);
-       pstatus->PER = le16_to_cpu(prsp->body.get_tune_status.rsp.sts.PER);
-       pstatus->BER = le16_to_cpu(prsp->body.get_tune_status.rsp.sts.BER);
+               le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.signal_strength);
+       pstatus->PER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.PER);
+       pstatus->BER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.BER);
 
 out:
        return error;
@@ -264,7 +259,7 @@ int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps)
        ptps->transmission_mode  = prsp->body.get_tps.rsp.tps.transmission_mode;
        ptps->DVBH_mask_HP = prsp->body.get_tps.rsp.tps.DVBH_mask_HP;
        ptps->DVBH_mask_LP = prsp->body.get_tps.rsp.tps.DVBH_mask_LP;
-       ptps->cell_ID = le16_to_cpu(prsp->body.get_tps.rsp.tps.cell_ID);
+       ptps->cell_ID = le16_to_cpu((__force __le16)prsp->body.get_tps.rsp.tps.cell_ID);
 
 out:
        return error;
@@ -315,13 +310,13 @@ int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap,
 
        /* Response OK -> get response data */
        pdemod_stats->frame_count =
-               le32_to_cpu(prsp->body.get_demod_stats.rsp.stats.frame_count);
+               le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.frame_count);
        pdemod_stats->bad_frame_count =
-               le32_to_cpu(prsp->body.get_demod_stats.rsp.stats.bad_frame_count);
+               le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bad_frame_count);
        pdemod_stats->bytes_fixed_by_rs =
-               le32_to_cpu(prsp->body.get_demod_stats.rsp.stats.bytes_fixed_by_rs);
+               le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bytes_fixed_by_rs);
        pdemod_stats->mer =
-               le16_to_cpu(prsp->body.get_demod_stats.rsp.stats.mer);
+               le16_to_cpu((__force __le16)prsp->body.get_demod_stats.rsp.stats.mer);
        pdemod_stats->has_started =
                prsp->body.get_demod_stats.rsp.stats.has_started;
 
similarity index 89%
rename from drivers/staging/media/as102/as10x_cmd.h
rename to drivers/media/usb/as102/as10x_cmd.h
index e21ec6c702a9c302fa3a641ec32dbc898153bd05..e06b84e2ff79eab459913f55372c74feaf6320fb 100644 (file)
  * 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 _AS10X_CMD_H_
 #define _AS10X_CMD_H_
 
-#ifdef __KERNEL__
 #include <linux/kernel.h>
-#endif
 
-#include "as10x_types.h"
+#include "as102_fe_types.h"
 
 /*********************************/
 /*       MACRO DEFINITIONS       */
@@ -98,12 +92,12 @@ union as10x_turn_on {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
        } __packed rsp;
@@ -113,12 +107,12 @@ union as10x_turn_off {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t err;
        } __packed rsp;
@@ -128,14 +122,14 @@ union as10x_set_tune {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* tune params */
                struct as10x_tune_args args;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* response error */
                uint8_t error;
        } __packed rsp;
@@ -145,12 +139,12 @@ union as10x_get_tune_status {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* response error */
                uint8_t error;
                /* tune status */
@@ -162,12 +156,12 @@ union as10x_get_tps {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* response error */
                uint8_t error;
                /* tps details */
@@ -179,12 +173,12 @@ union as10x_common {
        /* request */
        struct {
                /* request identifier */
-               uint16_t  proc_id;
+               __le16  proc_id;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* response error */
                uint8_t error;
        } __packed rsp;
@@ -194,9 +188,9 @@ union as10x_add_pid_filter {
        /* request */
        struct {
                /* request identifier */
-               uint16_t  proc_id;
+               __le16  proc_id;
                /* PID to filter */
-               uint16_t  pid;
+               __le16  pid;
                /* stream type (MPE, PSI/SI or PES )*/
                uint8_t stream_type;
                /* PID index in filter table */
@@ -205,7 +199,7 @@ union as10x_add_pid_filter {
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* response error */
                uint8_t error;
                /* Filter id */
@@ -217,14 +211,14 @@ union as10x_del_pid_filter {
        /* request */
        struct {
                /* request identifier */
-               uint16_t  proc_id;
+               __le16  proc_id;
                /* PID to remove */
-               uint16_t  pid;
+               __le16  pid;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* response error */
                uint8_t error;
        } __packed rsp;
@@ -234,12 +228,12 @@ union as10x_start_streaming {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
        } __packed rsp;
@@ -249,12 +243,12 @@ union as10x_stop_streaming {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
        } __packed rsp;
@@ -264,12 +258,12 @@ union as10x_get_demod_stats {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
                /* demod stats */
@@ -281,12 +275,12 @@ union as10x_get_impulse_resp {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
                /* impulse response ready */
@@ -298,22 +292,22 @@ union as10x_fw_context {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* value to write (for set context)*/
                struct as10x_register_value reg_val;
                /* context tag */
-               uint16_t tag;
+               __le16 tag;
                /* context request type */
-               uint16_t type;
+               __le16 type;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* value read (for get context) */
                struct as10x_register_value reg_val;
                /* context request type */
-               uint16_t type;
+               __le16 type;
                /* error */
                uint8_t error;
        } __packed rsp;
@@ -323,7 +317,7 @@ union as10x_set_register {
        /* request */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* register description */
                struct as10x_register_addr reg_addr;
                /* register content */
@@ -332,7 +326,7 @@ union as10x_set_register {
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
        } __packed rsp;
@@ -342,14 +336,14 @@ union as10x_get_register {
        /* request */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* register description */
                struct as10x_register_addr reg_addr;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
                /* register content */
@@ -361,24 +355,24 @@ union as10x_cfg_change_mode {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* mode */
                uint8_t mode;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
        } __packed rsp;
 } __packed;
 
 struct as10x_cmd_header_t {
-       uint16_t req_id;
-       uint16_t prog;
-       uint16_t version;
-       uint16_t data_len;
+       __le16 req_id;
+       __le16 prog;
+       __le16 version;
+       __le16 data_len;
 } __packed;
 
 #define DUMP_BLOCK_SIZE 16
@@ -387,18 +381,18 @@ union as10x_dump_memory {
        /* request */
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* dump memory type request */
                uint8_t dump_req;
                /* register description */
                struct as10x_register_addr reg_addr;
                /* nb blocks to read */
-               uint16_t num_blocks;
+               __le16 num_blocks;
        } __packed req;
        /* response */
        struct {
                /* response identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
                /* dump response */
@@ -406,8 +400,8 @@ union as10x_dump_memory {
                /* data */
                union {
                        uint8_t  data8[DUMP_BLOCK_SIZE];
-                       uint16_t data16[DUMP_BLOCK_SIZE / sizeof(uint16_t)];
-                       uint32_t data32[DUMP_BLOCK_SIZE / sizeof(uint32_t)];
+                       __le16 data16[DUMP_BLOCK_SIZE / sizeof(__le16)];
+                       __le32 data32[DUMP_BLOCK_SIZE / sizeof(__le32)];
                } __packed u;
        } __packed rsp;
 } __packed;
@@ -415,13 +409,13 @@ union as10x_dump_memory {
 union as10x_dumplog_memory {
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* dump memory type request */
                uint8_t dump_req;
        } __packed req;
        struct {
                /* request identifier */
-               uint16_t proc_id;
+               __le16 proc_id;
                /* error */
                uint8_t error;
                /* dump response */
@@ -434,13 +428,13 @@ union as10x_dumplog_memory {
 union as10x_raw_data {
        /* request */
        struct {
-               uint16_t proc_id;
+               __le16 proc_id;
                uint8_t data[64 - sizeof(struct as10x_cmd_header_t)
                             - 2 /* proc_id */];
        } __packed req;
        /* response */
        struct {
-               uint16_t proc_id;
+               __le16 proc_id;
                uint8_t error;
                uint8_t data[64 - sizeof(struct as10x_cmd_header_t)
                             - 2 /* proc_id */ - 1 /* rc */];
similarity index 93%
rename from drivers/staging/media/as102/as10x_cmd_cfg.c
rename to drivers/media/usb/as102/as10x_cmd_cfg.c
index b1e300d88753978d517a5f4ae27b0f1dfda229f5..c87f2ca223a255c31972546cc36346a57315efca 100644 (file)
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/kernel.h>
 #include "as102_drv.h"
-#include "as10x_types.h"
 #include "as10x_cmd.h"
 
 /***************************/
@@ -74,7 +69,7 @@ int as10x_cmd_get_context(struct as10x_bus_adapter_t *adap, uint16_t tag,
 
        if (error == 0) {
                /* Response OK -> get response data */
-               *pvalue = le32_to_cpu(prsp->body.context.rsp.reg_val.u.value32);
+               *pvalue = le32_to_cpu((__force __le32)prsp->body.context.rsp.reg_val.u.value32);
                /* value returned is always a 32-bit value */
        }
 
@@ -106,7 +101,7 @@ int as10x_cmd_set_context(struct as10x_bus_adapter_t *adap, uint16_t tag,
        /* fill command */
        pcmd->body.context.req.proc_id = cpu_to_le16(CONTROL_PROC_CONTEXT);
        /* pcmd->body.context.req.reg_val.mode initialization is not required */
-       pcmd->body.context.req.reg_val.u.value32 = cpu_to_le32(value);
+       pcmd->body.context.req.reg_val.u.value32 = (__force u32)cpu_to_le32(value);
        pcmd->body.context.req.tag = cpu_to_le16(tag);
        pcmd->body.context.req.type = cpu_to_le16(SET_CONTEXT_DATA);
 
similarity index 96%
rename from drivers/staging/media/as102/as10x_cmd_stream.c
rename to drivers/media/usb/as102/as10x_cmd_stream.c
index 1088ca1fe92fa616ea06d19d40d0ca4ba55201bb..126aea976639fde850e67f46df00398b4c6ac77e 100644 (file)
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/kernel.h>
similarity index 88%
rename from drivers/staging/media/as102/as10x_handle.h
rename to drivers/media/usb/as102/as10x_handle.h
index 5638b191b780c5054c1f9b0a3159528777370bc6..d6b58c7705003c0c5d3a156e5e1a35fce62fd21a 100644 (file)
  * 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__
+#ifndef _AS10X_HANDLE_H
+#define _AS10X_HANDLE_H
 struct as10x_bus_adapter_t;
 struct as102_dev_t;
 
index 2c6b7da137ed1bc2b20236c1336b1acec097d190..9eb77ac2153b06a60e65c781994901ba12208d6d 100644 (file)
@@ -46,6 +46,8 @@ struct au0828_board au0828_boards[] = {
                .name   = "Hauppauge HVR850",
                .tuner_type = TUNER_XC5000,
                .tuner_addr = 0x61,
+               .has_ir_i2c = 1,
+               .has_analog = 1,
                .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
                .input = {
                        {
@@ -72,12 +74,7 @@ struct au0828_board au0828_boards[] = {
                .tuner_type = TUNER_XC5000,
                .tuner_addr = 0x61,
                .has_ir_i2c = 1,
-               /* The au0828 hardware i2c implementation does not properly
-                  support the xc5000's i2c clock stretching.  So we need to
-                  lower the clock frequency enough where the 15us clock
-                  stretch fits inside of a normal clock cycle, or else the
-                  au0828 fails to set the STOP bit.  A 30 KHz clock puts the
-                  clock pulse width at 18us */
+               .has_analog = 1,
                .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
                .input = {
                        {
@@ -101,20 +98,20 @@ struct au0828_board au0828_boards[] = {
        },
        [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = {
                .name   = "Hauppauge HVR950Q rev xxF8",
-               .tuner_type = UNSET,
-               .tuner_addr = ADDR_UNSET,
+               .tuner_type = TUNER_XC5000,
+               .tuner_addr = 0x61,
                .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
        },
        [AU0828_BOARD_DVICO_FUSIONHDTV7] = {
                .name   = "DViCO FusionHDTV USB",
-               .tuner_type = UNSET,
-               .tuner_addr = ADDR_UNSET,
+               .tuner_type = TUNER_XC5000,
+               .tuner_addr = 0x61,
                .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
        },
        [AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
                .name = "Hauppauge Woodbury",
-               .tuner_type = UNSET,
-               .tuner_addr = ADDR_UNSET,
+               .tuner_type = TUNER_NXP_TDA18271,
+               .tuner_addr = 0x60,
                .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
        },
 };
@@ -142,8 +139,7 @@ int au0828_tuner_callback(void *priv, int component, int command, int arg)
                        mdelay(10);
                        return 0;
                } else {
-                       printk(KERN_ERR
-                               "%s(): Unknown command.\n", __func__);
+                       pr_err("%s(): Unknown command.\n", __func__);
                        return -EINVAL;
                }
                break;
@@ -177,12 +173,12 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data)
        case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */
                break;
        default:
-               printk(KERN_WARNING "%s: warning: "
-                      "unknown hauppauge model #%d\n", __func__, tv.model);
+               pr_warn("%s: warning: unknown hauppauge model #%d\n",
+                       __func__, tv.model);
                break;
        }
 
-       printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
+       pr_info("%s: hauppauge eeprom: model=%d\n",
               __func__, tv.model);
 }
 
@@ -228,16 +224,16 @@ void au0828_card_analog_fe_setup(struct au0828_dev *dev)
                sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
                                "au8522", 0x8e >> 1, NULL);
                if (sd == NULL)
-                       printk(KERN_ERR "analog subdev registration failed\n");
+                       pr_err("analog subdev registration failed\n");
        }
 
        /* Setup tuners */
-       if (dev->board.tuner_type != TUNER_ABSENT) {
+       if (dev->board.tuner_type != TUNER_ABSENT && dev->board.has_analog) {
                /* Load the tuner module, which does the attach */
                sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
                                "tuner", dev->board.tuner_addr, NULL);
                if (sd == NULL)
-                       printk(KERN_ERR "tuner subdev registration fail\n");
+                       pr_err("tuner subdev registration fail\n");
 
                tun_setup.mode_mask      = mode_mask;
                tun_setup.type           = dev->board.tuner_type;
index 56025e6894421e59db3dd2acf8a72b015b12c0ef..bc064803b6c7f6c2c6fb05b93c8fba7efb5cda22 100644 (file)
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include "au0828.h"
+
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
 #include <linux/mutex.h>
 
-#include "au0828.h"
-
 /*
  * 1 = General debug messages
  * 2 = USB handling
@@ -90,7 +90,7 @@ static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
                status = min(status, 0);
 
                if (status < 0) {
-                       printk(KERN_ERR "%s() Failed sending control message, error %d.\n",
+                       pr_err("%s() Failed sending control message, error %d.\n",
                                __func__, status);
                }
 
@@ -115,7 +115,7 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
                status = min(status, 0);
 
                if (status < 0) {
-                       printk(KERN_ERR "%s() Failed receiving control message, error %d.\n",
+                       pr_err("%s() Failed receiving control message, error %d.\n",
                                __func__, status);
                }
 
@@ -153,9 +153,7 @@ static void au0828_usb_disconnect(struct usb_interface *interface)
 
        dprintk(1, "%s()\n", __func__);
 
-#ifdef CONFIG_VIDEO_AU0828_RC
        au0828_rc_unregister(dev);
-#endif
        /* Digital TV */
        au0828_dvb_unregister(dev);
 
@@ -199,15 +197,14 @@ static int au0828_usb_probe(struct usb_interface *interface,
         * not enough even for most Digital TV streams.
         */
        if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
-               printk(KERN_ERR "au0828: Device initialization failed.\n");
-               printk(KERN_ERR "au0828: Device must be connected to a "
-                      "high-speed USB 2.0 port.\n");
+               pr_err("au0828: Device initialization failed.\n");
+               pr_err("au0828: Device must be connected to a high-speed USB 2.0 port.\n");
                return -ENODEV;
        }
 
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (dev == NULL) {
-               printk(KERN_ERR "%s() Unable to allocate memory\n", __func__);
+               pr_err("%s() Unable to allocate memory\n", __func__);
                return -ENOMEM;
        }
 
@@ -266,10 +263,8 @@ static int au0828_usb_probe(struct usb_interface *interface,
                pr_err("%s() au0282_dev_register failed\n",
                       __func__);
 
-#ifdef CONFIG_VIDEO_AU0828_RC
        /* Remote controller */
        au0828_rc_register(dev);
-#endif
 
        /*
         * Store the pointer to the au0828_dev so it can be accessed in
@@ -277,7 +272,7 @@ static int au0828_usb_probe(struct usb_interface *interface,
         */
        usb_set_intfdata(interface, dev);
 
-       printk(KERN_INFO "Registered device AU0828 [%s]\n",
+       pr_info("Registered device AU0828 [%s]\n",
                dev->board.name == NULL ? "Unset" : dev->board.name);
 
        mutex_unlock(&dev->lock);
@@ -285,13 +280,56 @@ static int au0828_usb_probe(struct usb_interface *interface,
        return retval;
 }
 
+static int au0828_suspend(struct usb_interface *interface,
+                               pm_message_t message)
+{
+       struct au0828_dev *dev = usb_get_intfdata(interface);
+
+       if (!dev)
+               return 0;
+
+       pr_info("Suspend\n");
+
+       au0828_rc_suspend(dev);
+       au0828_v4l2_suspend(dev);
+       au0828_dvb_suspend(dev);
+
+       /* FIXME: should suspend also ATV/DTV */
+
+       return 0;
+}
+
+static int au0828_resume(struct usb_interface *interface)
+{
+       struct au0828_dev *dev = usb_get_intfdata(interface);
+       if (!dev)
+               return 0;
+
+       pr_info("Resume\n");
+
+       /* Power Up the bridge */
+       au0828_write(dev, REG_600, 1 << 4);
+
+       /* Bring up the GPIO's and supporting devices */
+       au0828_gpio_setup(dev);
+
+       au0828_rc_resume(dev);
+       au0828_v4l2_resume(dev);
+       au0828_dvb_resume(dev);
+
+       /* FIXME: should resume also ATV/DTV */
+
+       return 0;
+}
+
 static struct usb_driver au0828_usb_driver = {
-       .name           = DRIVER_NAME,
+       .name           = KBUILD_MODNAME,
        .probe          = au0828_usb_probe,
        .disconnect     = au0828_usb_disconnect,
        .id_table       = au0828_usb_id_table,
-
-       /* FIXME: Add suspend and resume functions */
+       .suspend        = au0828_suspend,
+       .resume         = au0828_resume,
+       .reset_resume   = au0828_resume,
 };
 
 static int __init au0828_init(void)
@@ -299,27 +337,27 @@ static int __init au0828_init(void)
        int ret;
 
        if (au0828_debug & 1)
-               printk(KERN_INFO "%s() Debugging is enabled\n", __func__);
+               pr_info("%s() Debugging is enabled\n", __func__);
 
        if (au0828_debug & 2)
-               printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__);
+               pr_info("%s() USB Debugging is enabled\n", __func__);
 
        if (au0828_debug & 4)
-               printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__);
+               pr_info("%s() I2C Debugging is enabled\n", __func__);
 
        if (au0828_debug & 8)
-               printk(KERN_INFO "%s() Bridge Debugging is enabled\n",
+               pr_info("%s() Bridge Debugging is enabled\n",
                       __func__);
 
        if (au0828_debug & 16)
-               printk(KERN_INFO "%s() IR Debugging is enabled\n",
+               pr_info("%s() IR Debugging is enabled\n",
                       __func__);
 
-       printk(KERN_INFO "au0828 driver loaded\n");
+       pr_info("au0828 driver loaded\n");
 
        ret = usb_register(&au0828_usb_driver);
        if (ret)
-               printk(KERN_ERR "usb_register failed, error = %d\n", ret);
+               pr_err("usb_register failed, error = %d\n", ret);
 
        return ret;
 }
index d8b5d9480279a29441f0cadb47a4e36c59936855..00ab1563d142ed682dc91e76a80db44112504595 100644 (file)
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include "au0828.h"
+
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/device.h>
-#include <linux/suspend.h>
 #include <media/v4l2-common.h>
 #include <media/tuner.h>
 
-#include "au0828.h"
 #include "au8522.h"
 #include "xc5000.h"
 #include "mxl5007t.h"
@@ -121,13 +121,13 @@ static void urb_completion(struct urb *purb)
                return;
        }
 
-       if (dev->urb_streaming == 0) {
+       if (!dev->urb_streaming) {
                dprintk(2, "%s: not streaming!\n", __func__);
                return;
        }
 
        if (ptype != PIPE_BULK) {
-               printk(KERN_ERR "%s: Unsupported URB type %d\n",
+               pr_err("%s: Unsupported URB type %d\n",
                       __func__, ptype);
                return;
        }
@@ -159,7 +159,10 @@ static int stop_urb_transfer(struct au0828_dev *dev)
 
        dprintk(2, "%s()\n", __func__);
 
-       dev->urb_streaming = 0;
+       if (!dev->urb_streaming)
+               return 0;
+
+       dev->urb_streaming = false;
        for (i = 0; i < URB_COUNT; i++) {
                if (dev->urbs[i]) {
                        usb_kill_urb(dev->urbs[i]);
@@ -202,8 +205,7 @@ static int start_urb_transfer(struct au0828_dev *dev)
                if (!purb->transfer_buffer) {
                        usb_free_urb(purb);
                        dev->urbs[i] = NULL;
-                       printk(KERN_ERR
-                              "%s: failed big buffer allocation, err = %d\n",
+                       pr_err("%s: failed big buffer allocation, err = %d\n",
                               __func__, ret);
                        goto err;
                }
@@ -224,13 +226,13 @@ static int start_urb_transfer(struct au0828_dev *dev)
                ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC);
                if (ret != 0) {
                        stop_urb_transfer(dev);
-                       printk(KERN_ERR "%s: failed urb submission, "
-                              "err = %d\n", __func__, ret);
+                       pr_err("%s: failed urb submission, err = %d\n",
+                              __func__, ret);
                        return ret;
                }
        }
 
-       dev->urb_streaming = 1;
+       dev->urb_streaming = true;
        ret = 0;
 
 err:
@@ -268,7 +270,7 @@ static int au0828_dvb_start_feed(struct dvb_demux_feed *feed)
        if (!demux->dmx.frontend)
                return -EINVAL;
 
-       if (dvb) {
+       if (dvb->frontend) {
                mutex_lock(&dvb->lock);
                dvb->start_count++;
                dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__,
@@ -297,7 +299,7 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
 
        dprintk(1, "%s()\n", __func__);
 
-       if (dvb) {
+       if (dvb->frontend) {
                cancel_work_sync(&dev->restart_streaming);
 
                mutex_lock(&dvb->lock);
@@ -324,7 +326,7 @@ static void au0828_restart_dvb_streaming(struct work_struct *work)
                                              restart_streaming);
        struct au0828_dvb *dvb = &dev->dvb;
 
-       if (dev->urb_streaming == 0)
+       if (!dev->urb_streaming)
                return;
 
        dprintk(1, "Restarting streaming...!\n");
@@ -393,9 +395,8 @@ static int dvb_register(struct au0828_dev *dev)
                        if (!dev->dig_transfer_buffer[i]) {
                                result = -ENOMEM;
 
-                               printk(KERN_ERR
-                                      "%s: failed buffer allocation (errno = %d)\n",
-                                      DRIVER_NAME, result);
+                               pr_err("failed buffer allocation (errno = %d)\n",
+                                      result);
                                goto fail_adapter;
                        }
                }
@@ -404,11 +405,12 @@ static int dvb_register(struct au0828_dev *dev)
        INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming);
 
        /* register adapter */
-       result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE,
+       result = dvb_register_adapter(&dvb->adapter,
+                                     KBUILD_MODNAME, THIS_MODULE,
                                      &dev->usbdev->dev, adapter_nr);
        if (result < 0) {
-               printk(KERN_ERR "%s: dvb_register_adapter failed "
-                      "(errno = %d)\n", DRIVER_NAME, result);
+               pr_err("dvb_register_adapter failed (errno = %d)\n",
+                      result);
                goto fail_adapter;
        }
        dvb->adapter.priv = dev;
@@ -416,8 +418,8 @@ static int dvb_register(struct au0828_dev *dev)
        /* register frontend */
        result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
        if (result < 0) {
-               printk(KERN_ERR "%s: dvb_register_frontend failed "
-                      "(errno = %d)\n", DRIVER_NAME, result);
+               pr_err("dvb_register_frontend failed (errno = %d)\n",
+                      result);
                goto fail_frontend;
        }
 
@@ -436,8 +438,7 @@ static int dvb_register(struct au0828_dev *dev)
        dvb->demux.stop_feed  = au0828_dvb_stop_feed;
        result = dvb_dmx_init(&dvb->demux);
        if (result < 0) {
-               printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n",
-                      DRIVER_NAME, result);
+               pr_err("dvb_dmx_init failed (errno = %d)\n", result);
                goto fail_dmx;
        }
 
@@ -446,31 +447,29 @@ static int dvb_register(struct au0828_dev *dev)
        dvb->dmxdev.capabilities = 0;
        result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
        if (result < 0) {
-               printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n",
-                      DRIVER_NAME, result);
+               pr_err("dvb_dmxdev_init failed (errno = %d)\n", 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_ERR "%s: add_frontend failed "
-                      "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result);
+               pr_err("add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+                      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_ERR "%s: add_frontend failed "
-                      "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result);
+               pr_err("add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+                      result);
                goto fail_fe_mem;
        }
 
        result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
        if (result < 0) {
-               printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n",
-                      DRIVER_NAME, result);
+               pr_err("connect_frontend failed (errno = %d)\n", result);
                goto fail_fe_conn;
        }
 
@@ -530,8 +529,7 @@ void au0828_dvb_unregister(struct au0828_dev *dev)
                for (i = 0; i < URB_COUNT; i++)
                        kfree(dev->dig_transfer_buffer[i]);
        }
-
-
+       dvb->frontend = NULL;
 }
 
 /* All the DVB attach calls go here, this function get's modified
@@ -596,12 +594,11 @@ int au0828_dvb_register(struct au0828_dev *dev)
                }
                break;
        default:
-               printk(KERN_WARNING "The frontend of your DVB/ATSC card "
-                      "isn't supported yet\n");
+               pr_warn("The frontend of your DVB/ATSC card isn't supported yet\n");
                break;
        }
        if (NULL == dvb->frontend) {
-               printk(KERN_ERR "%s() Frontend initialization failed\n",
+               pr_err("%s() Frontend initialization failed\n",
                       __func__);
                return -1;
        }
@@ -613,8 +610,49 @@ int au0828_dvb_register(struct au0828_dev *dev)
        if (ret < 0) {
                if (dvb->frontend->ops.release)
                        dvb->frontend->ops.release(dvb->frontend);
+               dvb->frontend = NULL;
                return ret;
        }
 
        return 0;
 }
+
+void au0828_dvb_suspend(struct au0828_dev *dev)
+{
+       struct au0828_dvb *dvb = &dev->dvb;
+       int rc;
+
+       if (dvb->frontend) {
+               if (dev->urb_streaming) {
+                       cancel_work_sync(&dev->restart_streaming);
+                       /* Stop transport */
+                       mutex_lock(&dvb->lock);
+                       stop_urb_transfer(dev);
+                       au0828_stop_transport(dev, 1);
+                       mutex_unlock(&dvb->lock);
+                       dev->need_urb_start = true;
+               }
+               /* suspend frontend - does tuner and fe to sleep */
+               rc = dvb_frontend_suspend(dvb->frontend);
+               pr_info("au0828_dvb_suspend(): Suspending DVB fe %d\n", rc);
+       }
+}
+
+void au0828_dvb_resume(struct au0828_dev *dev)
+{
+       struct au0828_dvb *dvb = &dev->dvb;
+       int rc;
+
+       if (dvb->frontend) {
+               /* resume frontend - does fe and tuner init */
+               rc = dvb_frontend_resume(dvb->frontend);
+               pr_info("au0828_dvb_resume(): Resuming DVB fe %d\n", rc);
+               if (dev->need_urb_start) {
+                       /* Start transport */
+                       mutex_lock(&dvb->lock);
+                       au0828_start_transport(dev);
+                       start_urb_transfer(dev);
+                       mutex_unlock(&dvb->lock);
+               }
+       }
+}
index daaeaf1b089cf1976c6204dacd53551af2b6ebb9..ae7ac6669769f3cdcc2d2ba01db5cc0c73ce5039 100644 (file)
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include "au0828.h"
+
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/io.h>
 
-#include "au0828.h"
 #include "media/tuner.h"
 #include <media/v4l2-common.h>
 
@@ -340,7 +341,7 @@ static struct i2c_algorithm au0828_i2c_algo_template = {
 /* ----------------------------------------------------------------------- */
 
 static struct i2c_adapter au0828_i2c_adap_template = {
-       .name              = DRIVER_NAME,
+       .name              = KBUILD_MODNAME,
        .owner             = THIS_MODULE,
        .algo              = &au0828_i2c_algo_template,
 };
@@ -365,7 +366,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
                rc = i2c_master_recv(c, &buf, 0);
                if (rc < 0)
                        continue;
-               printk(KERN_INFO "%s: i2c scan: found device @ 0x%x  [%s]\n",
+               pr_info("%s: i2c scan: found device @ 0x%x  [%s]\n",
                       name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
        }
 }
@@ -381,7 +382,7 @@ int au0828_i2c_register(struct au0828_dev *dev)
 
        dev->i2c_adap.dev.parent = &dev->usbdev->dev;
 
-       strlcpy(dev->i2c_adap.name, DRIVER_NAME,
+       strlcpy(dev->i2c_adap.name, KBUILD_MODNAME,
                sizeof(dev->i2c_adap.name));
 
        dev->i2c_adap.algo = &dev->i2c_algo;
@@ -396,11 +397,11 @@ int au0828_i2c_register(struct au0828_dev *dev)
        dev->i2c_client.adapter = &dev->i2c_adap;
 
        if (0 == dev->i2c_rc) {
-               printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME);
+               pr_info("i2c bus registered\n");
                if (i2c_scan)
-                       do_i2c_scan(DRIVER_NAME, &dev->i2c_client);
+                       do_i2c_scan(KBUILD_MODNAME, &dev->i2c_client);
        } else
-               printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME);
+               pr_info("i2c bus register FAILED\n");
 
        return dev->i2c_rc;
 }
index fd0d3a90ce7d0294cd2f2bc3d0bdc3cdf7d25e21..63995f97dc653276abd5aaaab44e08c3dde0ba69 100644 (file)
@@ -17,6 +17,8 @@
   GNU General Public License for more details.
  */
 
+#include "au0828.h"
+
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/delay.h>
@@ -25,7 +27,9 @@
 #include <linux/slab.h>
 #include <media/rc-core.h>
 
-#include "au0828.h"
+static int disable_ir;
+module_param(disable_ir,        int, 0444);
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
 
 struct au0828_rc {
        struct au0828_dev *dev;
@@ -90,14 +94,19 @@ static int au8522_rc_read(struct au0828_rc *ir, u16 reg, int val,
 static int au8522_rc_andor(struct au0828_rc *ir, u16 reg, u8 mask, u8 value)
 {
        int rc;
-       char buf;
+       char buf, oldbuf;
 
        rc = au8522_rc_read(ir, reg, -1, &buf, 1);
        if (rc < 0)
                return rc;
 
+       oldbuf = buf;
        buf = (buf & ~mask) | (value & mask);
 
+       /* Nothing to do, just return */
+       if (buf == oldbuf)
+               return 0;
+
        return au8522_rc_write(ir, reg, buf);
 }
 
@@ -122,8 +131,11 @@ static int au0828_get_key_au8522(struct au0828_rc *ir)
 
        /* Check IR int */
        rc = au8522_rc_read(ir, 0xe1, -1, buf, 1);
-       if (rc < 0 || !(buf[0] & (1 << 4)))
+       if (rc < 0 || !(buf[0] & (1 << 4))) {
+               /* Be sure that IR is enabled */
+               au8522_rc_set(ir, 0xe0, 1 << 4);
                return 0;
+       }
 
        /* Something arrived. Get the data */
        rc = au8522_rc_read(ir, 0xe3, 0x11, buf, sizeof(buf));
@@ -135,8 +147,6 @@ static int au0828_get_key_au8522(struct au0828_rc *ir)
        /* Disable IR */
        au8522_rc_clear(ir, 0xe0, 1 << 4);
 
-       usleep_range(45000, 46000);
-
        /* Enable IR */
        au8522_rc_set(ir, 0xe0, 1 << 4);
 
@@ -243,10 +253,10 @@ static void au0828_rc_stop(struct rc_dev *rc)
 {
        struct au0828_rc *ir = rc->priv;
 
+       cancel_delayed_work_sync(&ir->work);
+
        /* Disable IR */
        au8522_rc_clear(ir, 0xe0, 1 << 4);
-
-       cancel_delayed_work_sync(&ir->work);
 }
 
 static int au0828_probe_i2c_ir(struct au0828_dev *dev)
@@ -273,7 +283,7 @@ int au0828_rc_register(struct au0828_dev *dev)
        int err = -ENOMEM;
        u16 i2c_rc_dev_addr = 0;
 
-       if (!dev->board.has_ir_i2c)
+       if (!dev->board.has_ir_i2c || disable_ir)
                return 0;
 
        i2c_rc_dev_addr = au0828_probe_i2c_ir(dev);
@@ -368,8 +378,13 @@ int au0828_rc_suspend(struct au0828_dev *dev)
        if (!ir)
                return 0;
 
+       pr_info("Stopping RC\n");
+
        cancel_delayed_work_sync(&ir->work);
 
+       /* Disable IR */
+       au8522_rc_clear(ir, 0xe0, 1 << 4);
+
        return 0;
 }
 
@@ -380,6 +395,11 @@ int au0828_rc_resume(struct au0828_dev *dev)
        if (!ir)
                return 0;
 
+       pr_info("Restarting RC\n");
+
+       /* Enable IR */
+       au8522_rc_set(ir, 0xe0, 1 << 4);
+
        schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
 
        return 0;
index 63f593070ee8b14be8f98e70dc1ab8e8c28c644e..932d24f42b247d7f13ee83534961fcf281aaf1cc 100644 (file)
    02110-1301, USA.
  */
 
+#include "au0828.h"
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 
-#include "au0828.h"
-
 static unsigned int vbibufs = 5;
 module_param(vbibufs, int, 0644);
 MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
index 98f7ea1d6d6382109cfb119ffb62c6789693597b..5f337b118bffe36e9969c3a8963e9871034ffce1 100644 (file)
  *
  */
 
+#include "au0828.h"
+
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/device.h>
-#include <linux/suspend.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-event.h>
 #include <media/tuner.h>
-#include "au0828.h"
 #include "au0828-reg.h"
 
 static DEFINE_MUTEX(au0828_sysfs_lock);
@@ -53,7 +53,7 @@ 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, \
+               pr_info("au0828 %s :"fmt, \
                       __func__ , ##arg);          \
        } \
   } while (0)
@@ -106,12 +106,12 @@ static inline void print_err_status(struct au0828_dev *dev,
 static int check_dev(struct au0828_dev *dev)
 {
        if (dev->dev_state & DEV_DISCONNECTED) {
-               printk(KERN_INFO "v4l2 ioctl: device not present\n");
+               pr_info("v4l2 ioctl: device not present\n");
                return -ENODEV;
        }
 
        if (dev->dev_state & DEV_MISCONFIGURED) {
-               printk(KERN_INFO "v4l2 ioctl: device is misconfigured; "
+               pr_info("v4l2 ioctl: device is misconfigured; "
                       "close and open it again\n");
                return -EIO;
        }
@@ -159,6 +159,7 @@ static void au0828_irq_callback(struct urb *urb)
                au0828_isocdbg("urb resubmit failed (error=%i)\n",
                               urb->status);
        }
+       dev->stream_state = STREAM_ON;
 }
 
 /*
@@ -198,6 +199,8 @@ static void au0828_uninit_isoc(struct au0828_dev *dev)
        dev->isoc_ctl.urb = NULL;
        dev->isoc_ctl.transfer_buffer = NULL;
        dev->isoc_ctl.num_bufs = 0;
+
+       dev->stream_state = STREAM_OFF;
 }
 
 /*
@@ -717,7 +720,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
        if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
                rc = videobuf_iolock(vq, &buf->vb, NULL);
                if (rc < 0) {
-                       printk(KERN_INFO "videobuf_iolock failed\n");
+                       pr_info("videobuf_iolock failed\n");
                        goto fail;
                }
        }
@@ -730,7 +733,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
                                      AU0828_MAX_ISO_BUFS, dev->max_pkt_size,
                                      au0828_isoc_copy);
                if (rc < 0) {
-                       printk(KERN_INFO "au0828_init_isoc failed\n");
+                       pr_info("au0828_init_isoc failed\n");
                        goto fail;
                }
        }
@@ -801,7 +804,7 @@ static int au0828_analog_stream_enable(struct au0828_dev *d)
                /* set au0828 interface0 to AS5 here again */
                ret = usb_set_interface(d->usbdev, 0, 5);
                if (ret < 0) {
-                       printk(KERN_INFO "Au0828 can't set alt setting to 5!\n");
+                       pr_info("Au0828 can't set alt setting to 5!\n");
                        return -EBUSY;
                }
        }
@@ -1090,7 +1093,7 @@ static int au0828_v4l2_close(struct file *filp)
                   USB bandwidth */
                ret = usb_set_interface(dev->usbdev, 0, 0);
                if (ret < 0)
-                       printk(KERN_INFO "Au0828 can't set alternate to 0!\n");
+                       pr_info("Au0828 can't set alternate to 0!\n");
        }
        mutex_unlock(&dev->lock);
 
@@ -1344,7 +1347,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                return rc;
 
        if (videobuf_queue_is_busy(&fh->vb_vidq)) {
-               printk(KERN_INFO "%s queue busy\n", __func__);
+               pr_info("%s queue busy\n", __func__);
                rc = -EBUSY;
                goto out;
        }
@@ -1868,6 +1871,69 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
        return rc;
 }
 
+void au0828_v4l2_suspend(struct au0828_dev *dev)
+{
+       struct urb *urb;
+       int i;
+
+       pr_info("stopping V4L2\n");
+
+       if (dev->stream_state == STREAM_ON) {
+               pr_info("stopping V4L2 active URBs\n");
+               au0828_analog_stream_disable(dev);
+               /* stop urbs */
+               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->vid_timeout_running)
+               del_timer_sync(&dev->vid_timeout);
+       if (dev->vbi_timeout_running)
+               del_timer_sync(&dev->vbi_timeout);
+}
+
+void au0828_v4l2_resume(struct au0828_dev *dev)
+{
+       int i, rc;
+
+       pr_info("restarting V4L2\n");
+
+       if (dev->stream_state == STREAM_ON) {
+               au0828_stream_interrupt(dev);
+               au0828_init_tuner(dev);
+       }
+
+       if (dev->vid_timeout_running)
+               mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
+       if (dev->vbi_timeout_running)
+               mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+
+       /* If we were doing ac97 instead of i2s, it would go here...*/
+       au0828_i2s_init(dev);
+
+       au0828_analog_stream_enable(dev);
+
+       if (!(dev->stream_state == STREAM_ON)) {
+               au0828_analog_stream_reset(dev);
+               /* submit urbs */
+               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);
+                       }
+               }
+       }
+}
+
 static struct v4l2_file_operations au0828_v4l_fops = {
        .owner      = THIS_MODULE,
        .open       = au0828_v4l2_open,
@@ -1939,7 +2005,7 @@ int au0828_analog_register(struct au0828_dev *dev,
        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");
+               pr_info("Failure setting usb interface0 to as5\n");
                return retval;
        }
 
@@ -1963,7 +2029,7 @@ int au0828_analog_register(struct au0828_dev *dev,
                }
        }
        if (!(dev->isoc_in_endpointaddr)) {
-               printk(KERN_INFO "Could not locate isoc endpoint\n");
+               pr_info("Could not locate isoc endpoint\n");
                kfree(dev);
                return -ENODEV;
        }
index 96bec05d7dac1abd153baa53cfa84b25c1d7a27b..36815a369c68f860fc910b9e49e79e3247acfcba 100644 (file)
@@ -19,6 +19,8 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/usb.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
@@ -42,7 +44,6 @@
 #include "au0828-reg.h"
 #include "au0828-cards.h"
 
-#define DRIVER_NAME "au0828"
 #define URB_COUNT   16
 #define URB_BUFSIZE (0xe522)
 
@@ -89,6 +90,7 @@ struct au0828_board {
        unsigned char tuner_addr;
        unsigned char i2c_clk_divider;
        unsigned char has_ir_i2c:1;
+       unsigned char has_analog:1;
        struct au0828_input input[AU0828_MAX_INPUT];
 
 };
@@ -266,8 +268,8 @@ struct au0828_dev {
        char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc
                                                   transfer */
 
-       /* USB / URB Related */
-       int             urb_streaming;
+       /* DVB USB / URB Related */
+       bool            urb_streaming, need_urb_start;
        struct urb      *urbs[URB_COUNT];
 
        /* Preallocated transfer digital transfer buffers */
@@ -311,22 +313,38 @@ 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);
+#ifdef CONFIG_VIDEO_AU0828_V4L2
+void au0828_v4l2_suspend(struct au0828_dev *dev);
+void au0828_v4l2_resume(struct au0828_dev *dev);
+#else
+static inline void au0828_v4l2_suspend(struct au0828_dev *dev) { };
+static inline void au0828_v4l2_resume(struct au0828_dev *dev) { };
+#endif
 
 /* ----------------------------------------------------------- */
 /* au0828-dvb.c */
 extern int au0828_dvb_register(struct au0828_dev *dev);
 extern void au0828_dvb_unregister(struct au0828_dev *dev);
+void au0828_dvb_suspend(struct au0828_dev *dev);
+void au0828_dvb_resume(struct au0828_dev *dev);
 
 /* au0828-vbi.c */
 extern struct videobuf_queue_ops au0828_vbi_qops;
 
 #define dprintk(level, fmt, arg...)\
        do { if (au0828_debug & level)\
-               printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\
+               printk(KERN_DEBUG pr_fmt(fmt), ## arg);\
        } while (0)
 
 /* au0828-input.c */
-int au0828_rc_register(struct au0828_dev *dev);
-void au0828_rc_unregister(struct au0828_dev *dev);
-int au0828_rc_suspend(struct au0828_dev *dev);
-int au0828_rc_resume(struct au0828_dev *dev);
+#ifdef CONFIG_VIDEO_AU0828_RC
+extern int au0828_rc_register(struct au0828_dev *dev);
+extern void au0828_rc_unregister(struct au0828_dev *dev);
+extern int au0828_rc_suspend(struct au0828_dev *dev);
+extern int au0828_rc_resume(struct au0828_dev *dev);
+#else
+static inline int au0828_rc_register(struct au0828_dev *dev) { return 0; }
+static inline void au0828_rc_unregister(struct au0828_dev *dev) { }
+static inline int au0828_rc_suspend(struct au0828_dev *dev) { return 0; }
+static inline int au0828_rc_resume(struct au0828_dev *dev) { return 0; }
+#endif
index a428c10e1a1657dbcb1f767c90993f66fcba1ea4..40a69879fc0ad91993e375637b2e41a6b32e0156 100644 (file)
@@ -1595,7 +1595,7 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq,
                if_freq = 16000000;
        }
 
-       cx231xx_info("Enter IF=%zd\n",
+       cx231xx_info("Enter IF=%zu\n",
                        ARRAY_SIZE(Dif_set_array));
        for (i = 0; i < ARRAY_SIZE(Dif_set_array); i++) {
                if (Dif_set_array[i].if_freq == if_freq) {
@@ -2223,7 +2223,7 @@ int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode)
        if (status < 0)
                return status;
 
-       tmp = le32_to_cpu(*((u32 *) value));
+       tmp = le32_to_cpu(*((__le32 *) value));
 
        switch (mode) {
        case POLARIS_AVMODE_ENXTERNAL_AV:
@@ -2444,7 +2444,7 @@ int cx231xx_power_suspend(struct cx231xx *dev)
        if (status > 0)
                return status;
 
-       tmp = le32_to_cpu(*((u32 *) value));
+       tmp = le32_to_cpu(*((__le32 *) value));
        tmp &= (~PWR_MODE_MASK);
 
        value[0] = (u8) tmp;
@@ -2472,7 +2472,7 @@ int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask)
        if (status < 0)
                return status;
 
-       tmp = le32_to_cpu(*((u32 *) value));
+       tmp = le32_to_cpu(*((__le32 *) value));
        tmp |= ep_mask;
        value[0] = (u8) tmp;
        value[1] = (u8) (tmp >> 8);
@@ -2497,7 +2497,7 @@ int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask)
        if (status < 0)
                return status;
 
-       tmp = le32_to_cpu(*((u32 *) value));
+       tmp = le32_to_cpu(*((__le32 *) value));
        tmp &= (~ep_mask);
        value[0] = (u8) tmp;
        value[1] = (u8) (tmp >> 8);
@@ -2644,7 +2644,7 @@ static int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u32 gpio_val)
 {
        int status = 0;
 
-       gpio_val = cpu_to_le32(gpio_val);
+       gpio_val = (__force u32)cpu_to_le32(gpio_val);
        status = cx231xx_send_gpio_cmd(dev, gpio_bit, (u8 *)&gpio_val, 4, 0, 0);
 
        return status;
@@ -2652,7 +2652,7 @@ static int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u32 gpio_val)
 
 static int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u32 *gpio_val)
 {
-       u32 tmp;
+       __le32 tmp;
        int status = 0;
 
        status = cx231xx_send_gpio_cmd(dev, gpio_bit, (u8 *)&tmp, 4, 0, 1);
index 8039b769f2580fd7409d56b0a4e7cd1078a75ee4..791f00c6276b1e489e38f019d4e7bbe40362a0e2 100644 (file)
@@ -705,7 +705,7 @@ struct cx231xx_board cx231xx_boards[] = {
                },
        },
        [CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx] = {
-               .name = "Hauppauge WinTV 930C-HD (1113xx) / PCTV QuatroStick 521e",
+               .name = "Hauppauge WinTV 930C-HD (1113xx) / HVR-900H (111xxx) / PCTV QuatroStick 521e",
                .tuner_type = TUNER_NXP_TDA18271,
                .tuner_addr = 0x60,
                .tuner_gpio = RDE250_XCV_TUNER,
@@ -744,7 +744,7 @@ struct cx231xx_board cx231xx_boards[] = {
                } },
        },
        [CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx] = {
-               .name = "Hauppauge WinTV 930C-HD (1114xx) / PCTV QuatroStick 522e",
+               .name = "Hauppauge WinTV 930C-HD (1114xx) / HVR-901H (1114xx) / PCTV QuatroStick 522e",
                .tuner_type = TUNER_ABSENT,
                .tuner_addr = 0x60,
                .tuner_gpio = RDE250_XCV_TUNER,
@@ -815,6 +815,12 @@ struct usb_device_id cx231xx_id_table[] = {
         .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx},
        {USB_DEVICE(0x2040, 0xb131),
         .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx},
+       /* Hauppauge WinTV-HVR-900-H */
+       {USB_DEVICE(0x2040, 0xb138),
+        .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx},
+       /* Hauppauge WinTV-HVR-901-H */
+       {USB_DEVICE(0x2040, 0xb139),
+        .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx},
        {USB_DEVICE(0x2040, 0xb140),
         .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
        {USB_DEVICE(0x2040, 0xc200),
index 513194aa6561c76d70f45a7286a6456c52153325..180103e48036727e322399c53437599ced61b8c5 100644 (file)
@@ -1491,7 +1491,7 @@ int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode)
        if (status < 0)
                return status;
 
-       tmp = le32_to_cpu(*((u32 *) value));
+       tmp = le32_to_cpu(*((__le32 *) value));
        tmp |= mode;
 
        value[0] = (u8) tmp;
index 1fa79741d1991f0709cfebfb961d5f11d54fb166..6c7b5e250eed7fc2903eae3f56039ed195fd4d33 100644 (file)
@@ -403,8 +403,6 @@ static int attach_xc5000(u8 addr, struct cx231xx *dev)
 
 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;
@@ -423,7 +421,7 @@ int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
 
        }
 
-       return status;
+       return 0;
 }
 
 int cx231xx_reset_analog_tuner(struct cx231xx *dev)
@@ -740,7 +738,7 @@ static int dvb_init(struct cx231xx *dev)
                        goto out_free;
                }
 
-               dev->dvb->frontend->ops.i2c_gate_ctrl = 0;
+               dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
 
                /* define general-purpose callback pointer */
                dvb->frontend->callback = cx231xx_tuner_callback;
@@ -773,7 +771,7 @@ static int dvb_init(struct cx231xx *dev)
                        goto out_free;
                }
 
-               dev->dvb->frontend->ops.i2c_gate_ctrl = 0;
+               dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
 
                /* define general-purpose callback pointer */
                dvb->frontend->callback = cx231xx_tuner_callback;
index 66645b02c854dd66435f9bfcb2e5f2f8e390506e..5b34323ad207950e3025ee9226b7456f358ddecc 100644 (file)
@@ -141,3 +141,10 @@ config DVB_USB_RTL28XXU
        help
          Say Y here to support the Realtek RTL28xxU DVB USB receiver.
 
+config DVB_USB_DVBSKY
+       tristate "DVBSky USB support"
+       depends on DVB_USB_V2
+       select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
+       help
+         Say Y here to support the USB receivers from DVBSky.
index bc38f03394cda0e1a397b390f354b620fe486a73..f10d4df0eae5c2962e4a20de60de3145a082ea83 100644 (file)
@@ -37,6 +37,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
 dvb-usb-rtl28xxu-objs := rtl28xxu.o
 obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
 
+dvb-usb-dvbsky-objs := dvbsky.o
+obj-$(CONFIG_DVB_USB_DVBSKY) += dvb-usb-dvbsky.o
+
 ccflags-y += -I$(srctree)/drivers/media/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
 ccflags-y += -I$(srctree)/drivers/media/tuners
index 5ca738ab44e000911dec36cecdc1f6818ec6a7c2..16c0b7d4f8e701e58bb23191d66ca74951a0fa1e 100644 (file)
@@ -419,7 +419,7 @@ static int af9015_eeprom_hash(struct dvb_usb_device *d)
        /* calculate checksum */
        for (i = 0; i < AF9015_EEPROM_SIZE / sizeof(u32); i++) {
                state->eeprom_sum *= GOLDEN_RATIO_PRIME_32;
-               state->eeprom_sum += le32_to_cpu(((u32 *)buf)[i]);
+               state->eeprom_sum += le32_to_cpu(((__le32 *)buf)[i]);
        }
 
        for (i = 0; i < AF9015_EEPROM_SIZE; i += 16)
index c82beac0e0cbfd1fa9f3225bf044e55eab7de410..00758c83eec733475be5596818a5347868cde089 100644 (file)
@@ -193,6 +193,92 @@ static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val,
        return af9035_wr_regs(d, reg, &val, 1);
 }
 
+static int af9035_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr,
+               void *platform_data, struct i2c_adapter *adapter)
+{
+       int ret, num;
+       struct state *state = d_to_priv(d);
+       struct i2c_client *client;
+       struct i2c_board_info board_info = {
+               .addr = addr,
+               .platform_data = platform_data,
+       };
+
+       strlcpy(board_info.type, type, I2C_NAME_SIZE);
+
+       /* find first free client */
+       for (num = 0; num < AF9035_I2C_CLIENT_MAX; num++) {
+               if (state->i2c_client[num] == NULL)
+                       break;
+       }
+
+       dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num);
+
+       if (num == AF9035_I2C_CLIENT_MAX) {
+               dev_err(&d->udev->dev, "%s: I2C client out of index\n",
+                               KBUILD_MODNAME);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       request_module(board_info.type);
+
+       /* register I2C device */
+       client = i2c_new_device(adapter, &board_info);
+       if (client == NULL || client->dev.driver == NULL) {
+               ret = -ENODEV;
+               goto err;
+       }
+
+       /* increase I2C driver usage count */
+       if (!try_module_get(client->dev.driver->owner)) {
+               i2c_unregister_device(client);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       state->i2c_client[num] = client;
+       return 0;
+err:
+       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static void af9035_del_i2c_dev(struct dvb_usb_device *d)
+{
+       int num;
+       struct state *state = d_to_priv(d);
+       struct i2c_client *client;
+
+       /* find last used client */
+       num = AF9035_I2C_CLIENT_MAX;
+       while (num--) {
+               if (state->i2c_client[num] != NULL)
+                       break;
+       }
+
+       dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num);
+
+       if (num == -1) {
+               dev_err(&d->udev->dev, "%s: I2C client out of index\n",
+                               KBUILD_MODNAME);
+               goto err;
+       }
+
+       client = state->i2c_client[num];
+
+       /* decrease I2C driver usage count */
+       module_put(client->dev.driver->owner);
+
+       /* unregister I2C device */
+       i2c_unregister_device(client);
+
+       state->i2c_client[num] = NULL;
+       return;
+err:
+       dev_dbg(&d->udev->dev, "%s: failed\n", __func__);
+}
+
 static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
                struct i2c_msg msg[], int num)
 {
@@ -204,7 +290,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
                return -EAGAIN;
 
        /*
-        * I2C sub header is 5 bytes long. Meaning of those bytes are:
+        * AF9035 I2C sub header is 5 bytes long. Meaning of those bytes are:
         * 0: data len
         * 1: I2C addr << 1
         * 2: reg addr len
@@ -218,110 +304,156 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
         * NOTE: As a firmware knows tuner type there is very small possibility
         * there could be some tuner I2C hacks done by firmware and this may
         * lead problems if firmware expects those bytes are used.
+        *
+        * TODO: Here is few hacks. AF9035 chip integrates AF9033 demodulator.
+        * IT9135 chip integrates AF9033 demodulator and RF tuner. For dual
+        * tuner devices, there is also external AF9033 demodulator connected
+        * via external I2C bus. All AF9033 demod I2C traffic, both single and
+        * dual tuner configuration, is covered by firmware - actual USB IO
+        * looks just like a memory access.
+        * In case of IT913x chip, there is own tuner driver. It is implemented
+        * currently as a I2C driver, even tuner IP block is likely build
+        * directly into the demodulator memory space and there is no own I2C
+        * bus. I2C subsystem does not allow register multiple devices to same
+        * bus, having same slave address. Due to that we reuse demod address,
+        * shifted by one bit, on that case.
+        *
+        * For IT930x we use a different command and the sub header is
+        * different as well:
+        * 0: data len
+        * 1: I2C bus (0x03 seems to be only value used)
+        * 2: I2C addr << 1
         */
-       if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
-                       (msg[1].flags & I2C_M_RD)) {
+#define AF9035_IS_I2C_XFER_WRITE_READ(_msg, _num) \
+       (_num == 2 && !(_msg[0].flags & I2C_M_RD) && (_msg[1].flags & I2C_M_RD))
+#define AF9035_IS_I2C_XFER_WRITE(_msg, _num) \
+       (_num == 1 && !(_msg[0].flags & I2C_M_RD))
+#define AF9035_IS_I2C_XFER_READ(_msg, _num) \
+       (_num == 1 && (_msg[0].flags & I2C_M_RD))
+
+       if (AF9035_IS_I2C_XFER_WRITE_READ(msg, num)) {
                if (msg[0].len > 40 || msg[1].len > 40) {
                        /* TODO: correct limits > 40 */
                        ret = -EOPNOTSUPP;
-               } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) ||
-                          (msg[0].addr == state->af9033_config[1].i2c_addr)) {
+               } else if ((msg[0].addr == state->af9033_i2c_addr[0]) ||
+                          (msg[0].addr == state->af9033_i2c_addr[1]) ||
+                          (state->chip_type == 0x9135)) {
                        /* demod access via firmware interface */
                        u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 |
                                        msg[0].buf[2];
 
-                       if (msg[0].addr == state->af9033_config[1].i2c_addr)
+                       if (msg[0].addr == state->af9033_i2c_addr[1] ||
+                           msg[0].addr == (state->af9033_i2c_addr[1] >> 1))
                                reg |= 0x100000;
 
                        ret = af9035_rd_regs(d, reg, &msg[1].buf[0],
                                        msg[1].len);
                } else {
-                       /* I2C */
+                       /* I2C write + read */
                        u8 buf[MAX_XFER_SIZE];
                        struct usb_req req = { CMD_I2C_RD, 0, 5 + msg[0].len,
                                        buf, msg[1].len, msg[1].buf };
 
-                       if (5 + msg[0].len > sizeof(buf)) {
-                               dev_warn(&d->udev->dev,
-                                        "%s: i2c xfer: len=%d is too big!\n",
-                                        KBUILD_MODNAME, msg[0].len);
-                               ret = -EOPNOTSUPP;
-                               goto unlock;
+                       if (state->chip_type == 0x9306) {
+                               req.cmd = CMD_GENERIC_I2C_RD;
+                               req.wlen = 3 + msg[0].len;
                        }
                        req.mbox |= ((msg[0].addr & 0x80)  >>  3);
+
                        buf[0] = msg[1].len;
-                       buf[1] = msg[0].addr << 1;
-                       buf[2] = 0x00; /* reg addr len */
-                       buf[3] = 0x00; /* reg addr MSB */
-                       buf[4] = 0x00; /* reg addr LSB */
-                       memcpy(&buf[5], msg[0].buf, msg[0].len);
+                       if (state->chip_type == 0x9306) {
+                               buf[1] = 0x03; /* I2C bus */
+                               buf[2] = msg[0].addr << 1;
+                               memcpy(&buf[3], msg[0].buf, msg[0].len);
+                       } else {
+                               buf[1] = msg[0].addr << 1;
+                               buf[2] = 0x00; /* reg addr len */
+                               buf[3] = 0x00; /* reg addr MSB */
+                               buf[4] = 0x00; /* reg addr LSB */
+                               memcpy(&buf[5], msg[0].buf, msg[0].len);
+                       }
                        ret = af9035_ctrl_msg(d, &req);
                }
-       } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+       } else if (AF9035_IS_I2C_XFER_WRITE(msg, num)) {
                if (msg[0].len > 40) {
                        /* TODO: correct limits > 40 */
                        ret = -EOPNOTSUPP;
-               } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) ||
-                          (msg[0].addr == state->af9033_config[1].i2c_addr)) {
+               } else if ((msg[0].addr == state->af9033_i2c_addr[0]) ||
+                          (msg[0].addr == state->af9033_i2c_addr[1]) ||
+                          (state->chip_type == 0x9135)) {
                        /* demod access via firmware interface */
                        u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 |
                                        msg[0].buf[2];
 
-                       if (msg[0].addr == state->af9033_config[1].i2c_addr)
+                       if (msg[0].addr == state->af9033_i2c_addr[1] ||
+                           msg[0].addr == (state->af9033_i2c_addr[1] >> 1))
                                reg |= 0x100000;
 
                        ret = af9035_wr_regs(d, reg, &msg[0].buf[3],
                                        msg[0].len - 3);
                } else {
-                       /* I2C */
+                       /* I2C write */
                        u8 buf[MAX_XFER_SIZE];
                        struct usb_req req = { CMD_I2C_WR, 0, 5 + msg[0].len,
                                        buf, 0, NULL };
 
-                       if (5 + msg[0].len > sizeof(buf)) {
-                               dev_warn(&d->udev->dev,
-                                        "%s: i2c xfer: len=%d is too big!\n",
-                                        KBUILD_MODNAME, msg[0].len);
-                               ret = -EOPNOTSUPP;
-                               goto unlock;
+                       if (state->chip_type == 0x9306) {
+                               req.cmd = CMD_GENERIC_I2C_WR;
+                               req.wlen = 3 + msg[0].len;
                        }
+
                        req.mbox |= ((msg[0].addr & 0x80)  >>  3);
                        buf[0] = msg[0].len;
-                       buf[1] = msg[0].addr << 1;
-                       buf[2] = 0x00; /* reg addr len */
-                       buf[3] = 0x00; /* reg addr MSB */
-                       buf[4] = 0x00; /* reg addr LSB */
-                       memcpy(&buf[5], msg[0].buf, msg[0].len);
+                       if (state->chip_type == 0x9306) {
+                               buf[1] = 0x03; /* I2C bus */
+                               buf[2] = msg[0].addr << 1;
+                               memcpy(&buf[3], msg[0].buf, msg[0].len);
+                       } else {
+                               buf[1] = msg[0].addr << 1;
+                               buf[2] = 0x00; /* reg addr len */
+                               buf[3] = 0x00; /* reg addr MSB */
+                               buf[4] = 0x00; /* reg addr LSB */
+                               memcpy(&buf[5], msg[0].buf, msg[0].len);
+                       }
                        ret = af9035_ctrl_msg(d, &req);
                }
-       } else if (num == 1 && (msg[0].flags & I2C_M_RD)) {
+       } else if (AF9035_IS_I2C_XFER_READ(msg, num)) {
                if (msg[0].len > 40) {
                        /* TODO: correct limits > 40 */
                        ret = -EOPNOTSUPP;
                } else {
-                       /* I2C */
+                       /* I2C read */
                        u8 buf[5];
                        struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf),
-                                       buf, msg[0].len, msg[0].buf };
+                                               buf, msg[0].len, msg[0].buf };
+
+                       if (state->chip_type == 0x9306) {
+                               req.cmd = CMD_GENERIC_I2C_RD;
+                               req.wlen = 3;
+                       }
                        req.mbox |= ((msg[0].addr & 0x80)  >>  3);
                        buf[0] = msg[0].len;
-                       buf[1] = msg[0].addr << 1;
-                       buf[2] = 0x00; /* reg addr len */
-                       buf[3] = 0x00; /* reg addr MSB */
-                       buf[4] = 0x00; /* reg addr LSB */
+                       if (state->chip_type == 0x9306) {
+                               buf[1] = 0x03; /* I2C bus */
+                               buf[2] = msg[0].addr << 1;
+                       } else {
+                               buf[1] = msg[0].addr << 1;
+                               buf[2] = 0x00; /* reg addr len */
+                               buf[3] = 0x00; /* reg addr MSB */
+                               buf[4] = 0x00; /* reg addr LSB */
+                       }
                        ret = af9035_ctrl_msg(d, &req);
                }
        } else {
                /*
                 * We support only three kind of I2C transactions:
-                * 1) 1 x read + 1 x write (repeated start)
+                * 1) 1 x write + 1 x read (repeated start)
                 * 2) 1 x write
                 * 3) 1 x read
                 */
                ret = -EOPNOTSUPP;
        }
 
-unlock:
        mutex_unlock(&d->i2c_mutex);
 
        if (ret < 0)
@@ -371,6 +503,9 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name)
                else
                        *name = AF9035_FIRMWARE_IT9135_V1;
                state->eeprom_addr = EEPROM_BASE_IT9135;
+       } else if (state->chip_type == 0x9306) {
+               *name = AF9035_FIRMWARE_IT9303;
+               state->eeprom_addr = EEPROM_BASE_IT9135;
        } else {
                *name = AF9035_FIRMWARE_AF9035;
                state->eeprom_addr = EEPROM_BASE_AF9035;
@@ -536,6 +671,7 @@ static int af9035_download_firmware(struct dvb_usb_device *d,
        u8 tmp;
        struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
        struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf };
+
        dev_dbg(&d->udev->dev, "%s:\n", __func__);
 
        /*
@@ -579,7 +715,8 @@ static int af9035_download_firmware(struct dvb_usb_device *d,
                if (!tmp)
                        tmp = 0x3a;
 
-               if (state->chip_type == 0x9135) {
+               if ((state->chip_type == 0x9135) ||
+                               (state->chip_type == 0x9306)) {
                        ret = af9035_wr_reg(d, 0x004bfb, tmp);
                        if (ret < 0)
                                goto err;
@@ -640,23 +777,26 @@ static int af9035_read_config(struct dvb_usb_device *d)
        u16 tmp16, addr;
 
        /* demod I2C "address" */
-       state->af9033_config[0].i2c_addr = 0x38;
-       state->af9033_config[1].i2c_addr = 0x3a;
+       state->af9033_i2c_addr[0] = 0x38;
+       state->af9033_i2c_addr[1] = 0x3a;
        state->af9033_config[0].adc_multiplier = AF9033_ADC_MULTIPLIER_2X;
        state->af9033_config[1].adc_multiplier = AF9033_ADC_MULTIPLIER_2X;
        state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB;
        state->af9033_config[1].ts_mode = AF9033_TS_MODE_SERIAL;
 
-       /* eeprom memory mapped location */
        if (state->chip_type == 0x9135) {
+               /* feed clock for integrated RF tuner */
+               state->af9033_config[0].dyn0_clk = true;
+               state->af9033_config[1].dyn0_clk = true;
+
                if (state->chip_version == 0x02) {
                        state->af9033_config[0].tuner = AF9033_TUNER_IT9135_60;
                        state->af9033_config[1].tuner = AF9033_TUNER_IT9135_60;
-                       tmp16 = 0x00461d;
+                       tmp16 = 0x00461d; /* eeprom memory mapped location */
                } else {
                        state->af9033_config[0].tuner = AF9033_TUNER_IT9135_38;
                        state->af9033_config[1].tuner = AF9033_TUNER_IT9135_38;
-                       tmp16 = 0x00461b;
+                       tmp16 = 0x00461b; /* eeprom memory mapped location */
                }
 
                /* check if eeprom exists */
@@ -668,8 +808,16 @@ static int af9035_read_config(struct dvb_usb_device *d)
                        dev_dbg(&d->udev->dev, "%s: no eeprom\n", __func__);
                        goto skip_eeprom;
                }
+       } else if (state->chip_type == 0x9306) {
+               /*
+                * IT930x is an USB bridge, only single demod-single tuner
+                * configurations seen so far.
+                */
+               return 0;
        }
 
+
+
        /* check if there is dual tuners */
        ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_TS_MODE, &tmp);
        if (ret < 0)
@@ -690,7 +838,7 @@ static int af9035_read_config(struct dvb_usb_device *d)
                        goto err;
 
                if (tmp)
-                       state->af9033_config[1].i2c_addr = tmp;
+                       state->af9033_i2c_addr[1] = tmp;
 
                dev_dbg(&d->udev->dev, "%s: 2nd demod I2C addr=%02x\n",
                                __func__, tmp);
@@ -799,25 +947,6 @@ static int af9035_read_config(struct dvb_usb_device *d)
                addr += 0x10; /* shift for the 2nd tuner params */
        }
 
-       /*
-        * These AVerMedia devices has a bad EEPROM content :-(
-        * Override some wrong values here.
-        */
-       if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA) {
-               switch (le16_to_cpu(d->udev->descriptor.idProduct)) {
-               case USB_PID_AVERMEDIA_A835B_1835:
-               case USB_PID_AVERMEDIA_A835B_2835:
-               case USB_PID_AVERMEDIA_A835B_3835:
-                       dev_info(&d->udev->dev,
-                                "%s: overriding tuner from %02x to %02x\n",
-                                KBUILD_MODNAME, state->af9033_config[0].tuner,
-                                AF9033_TUNER_IT9135_60);
-
-                       state->af9033_config[0].tuner = AF9033_TUNER_IT9135_60;
-                       break;
-               }
-       }
-
 skip_eeprom:
        /* get demod clock */
        ret = af9035_rd_reg(d, 0x00d800, &tmp);
@@ -990,6 +1119,7 @@ static int af9035_frontend_callback(void *adapter_priv, int component,
 static int af9035_get_adapter_count(struct dvb_usb_device *d)
 {
        struct state *state = d_to_priv(d);
+
        return state->dual_mode + 1;
 }
 
@@ -998,7 +1128,8 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap)
        struct state *state = adap_to_priv(adap);
        struct dvb_usb_device *d = adap_to_d(adap);
        int ret;
-       dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
 
        if (!state->af9033_config[adap->id].tuner) {
                /* unsupported tuner */
@@ -1006,9 +1137,13 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap)
                goto err;
        }
 
-       /* attach demodulator */
-       adap->fe[0] = dvb_attach(af9033_attach, &state->af9033_config[adap->id],
-                       &d->i2c_adap, &state->ops);
+       state->af9033_config[adap->id].fe = &adap->fe[0];
+       state->af9033_config[adap->id].ops = &state->ops;
+       ret = af9035_add_i2c_dev(d, "af9033", state->af9033_i2c_addr[adap->id],
+                       &state->af9033_config[adap->id], &d->i2c_adap);
+       if (ret)
+               goto err;
+
        if (adap->fe[0] == NULL) {
                ret = -ENODEV;
                goto err;
@@ -1026,6 +1161,78 @@ err:
        return ret;
 }
 
+static int it930x_frontend_attach(struct dvb_usb_adapter *adap)
+{
+       struct state *state = adap_to_priv(adap);
+       struct dvb_usb_device *d = adap_to_d(adap);
+       int ret;
+       struct si2168_config si2168_config;
+       struct i2c_adapter *adapter;
+
+       dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id);
+
+       si2168_config.i2c_adapter = &adapter;
+       si2168_config.fe = &adap->fe[0];
+       si2168_config.ts_mode = SI2168_TS_SERIAL;
+
+       state->af9033_config[adap->id].fe = &adap->fe[0];
+       state->af9033_config[adap->id].ops = &state->ops;
+       ret = af9035_add_i2c_dev(d, "si2168", 0x67, &si2168_config,
+                               &d->i2c_adap);
+       if (ret)
+               goto err;
+
+       if (adap->fe[0] == NULL) {
+               ret = -ENODEV;
+               goto err;
+       }
+       state->i2c_adapter_demod = adapter;
+
+       return 0;
+
+err:
+       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+       return ret;
+}
+
+static int af9035_frontend_detach(struct dvb_usb_adapter *adap)
+{
+       struct state *state = adap_to_priv(adap);
+       struct dvb_usb_device *d = adap_to_d(adap);
+       int demod2;
+
+       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
+
+       /*
+        * For dual tuner devices we have to resolve 2nd demod client, as there
+        * is two different kind of tuner drivers; one is using I2C binding
+        * and the other is using DVB attach/detach binding.
+        */
+       switch (state->af9033_config[adap->id].tuner) {
+       case AF9033_TUNER_IT9135_38:
+       case AF9033_TUNER_IT9135_51:
+       case AF9033_TUNER_IT9135_52:
+       case AF9033_TUNER_IT9135_60:
+       case AF9033_TUNER_IT9135_61:
+       case AF9033_TUNER_IT9135_62:
+               demod2 = 2;
+               break;
+       default:
+               demod2 = 1;
+       }
+
+       if (adap->id == 1) {
+               if (state->i2c_client[demod2])
+                       af9035_del_i2c_dev(d);
+       } else if (adap->id == 0) {
+               if (state->i2c_client[0])
+                       af9035_del_i2c_dev(d);
+       }
+
+       return 0;
+}
+
 static struct tua9001_config af9035_tua9001_config = {
        .i2c_addr = 0x60,
 };
@@ -1084,7 +1291,8 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap)
        struct dvb_frontend *fe;
        struct i2c_msg msg[1];
        u8 tuner_addr;
-       dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
 
        /*
         * XXX: Hack used in that function: we abuse unused I2C address bit [7]
@@ -1243,14 +1451,53 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap)
        case AF9033_TUNER_IT9135_38:
        case AF9033_TUNER_IT9135_51:
        case AF9033_TUNER_IT9135_52:
+       {
+               struct it913x_config it913x_config = {
+                       .fe = adap->fe[0],
+                       .chip_ver = 1,
+               };
+
+               if (state->dual_mode) {
+                       if (adap->id == 0)
+                               it913x_config.role = IT913X_ROLE_DUAL_MASTER;
+                       else
+                               it913x_config.role = IT913X_ROLE_DUAL_SLAVE;
+               }
+
+               ret = af9035_add_i2c_dev(d, "it913x",
+                               state->af9033_i2c_addr[adap->id] >> 1,
+                               &it913x_config, &d->i2c_adap);
+               if (ret)
+                       goto err;
+
+               fe = adap->fe[0];
+               break;
+       }
        case AF9033_TUNER_IT9135_60:
        case AF9033_TUNER_IT9135_61:
        case AF9033_TUNER_IT9135_62:
-               /* attach tuner */
-               fe = dvb_attach(it913x_attach, adap->fe[0], &d->i2c_adap,
-                               state->af9033_config[adap->id].i2c_addr,
-                               state->af9033_config[0].tuner);
+       {
+               struct it913x_config it913x_config = {
+                       .fe = adap->fe[0],
+                       .chip_ver = 2,
+               };
+
+               if (state->dual_mode) {
+                       if (adap->id == 0)
+                               it913x_config.role = IT913X_ROLE_DUAL_MASTER;
+                       else
+                               it913x_config.role = IT913X_ROLE_DUAL_SLAVE;
+               }
+
+               ret = af9035_add_i2c_dev(d, "it913x",
+                               state->af9033_i2c_addr[adap->id] >> 1,
+                               &it913x_config, &d->i2c_adap);
+               if (ret)
+                       goto err;
+
+               fe = adap->fe[0];
                break;
+       }
        default:
                fe = NULL;
        }
@@ -1268,6 +1515,119 @@ err:
        return ret;
 }
 
+static int it930x_tuner_attach(struct dvb_usb_adapter *adap)
+{
+       struct state *state = adap_to_priv(adap);
+       struct dvb_usb_device *d = adap_to_d(adap);
+       int ret;
+       struct si2157_config si2157_config;
+
+       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
+
+       /* I2C master bus 2 clock speed 300k */
+       ret = af9035_wr_reg(d, 0x00f6a7, 0x07);
+       if (ret < 0)
+               goto err;
+
+       /* I2C master bus 1,3 clock speed 300k */
+       ret = af9035_wr_reg(d, 0x00f103, 0x07);
+       if (ret < 0)
+               goto err;
+
+       /* set gpio11 low */
+       ret = af9035_wr_reg_mask(d, 0xd8d4, 0x01, 0x01);
+       if (ret < 0)
+               goto err;
+
+       ret = af9035_wr_reg_mask(d, 0xd8d5, 0x01, 0x01);
+       if (ret < 0)
+               goto err;
+
+       ret = af9035_wr_reg_mask(d, 0xd8d3, 0x01, 0x01);
+       if (ret < 0)
+               goto err;
+
+       /* Tuner enable using gpiot2_en, gpiot2_on and gpiot2_o (reset) */
+       ret = af9035_wr_reg_mask(d, 0xd8b8, 0x01, 0x01);
+       if (ret < 0)
+               goto err;
+
+       ret = af9035_wr_reg_mask(d, 0xd8b9, 0x01, 0x01);
+       if (ret < 0)
+               goto err;
+
+       ret = af9035_wr_reg_mask(d, 0xd8b7, 0x00, 0x01);
+       if (ret < 0)
+               goto err;
+
+       msleep(200);
+
+       ret = af9035_wr_reg_mask(d, 0xd8b7, 0x01, 0x01);
+       if (ret < 0)
+               goto err;
+
+       memset(&si2157_config, 0, sizeof(si2157_config));
+       si2157_config.fe = adap->fe[0];
+       ret = af9035_add_i2c_dev(d, "si2157", 0x63,
+                       &si2157_config, state->i2c_adapter_demod);
+
+       if (ret)
+               goto err;
+
+       return 0;
+
+err:
+       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+       return ret;
+}
+
+
+static int it930x_tuner_detach(struct dvb_usb_adapter *adap)
+{
+       struct state *state = adap_to_priv(adap);
+       struct dvb_usb_device *d = adap_to_d(adap);
+
+       dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id);
+
+       if (adap->id == 1) {
+               if (state->i2c_client[3])
+                       af9035_del_i2c_dev(d);
+       } else if (adap->id == 0) {
+               if (state->i2c_client[1])
+                       af9035_del_i2c_dev(d);
+       }
+
+       return 0;
+}
+
+
+static int af9035_tuner_detach(struct dvb_usb_adapter *adap)
+{
+       struct state *state = adap_to_priv(adap);
+       struct dvb_usb_device *d = adap_to_d(adap);
+
+       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
+
+       switch (state->af9033_config[adap->id].tuner) {
+       case AF9033_TUNER_IT9135_38:
+       case AF9033_TUNER_IT9135_51:
+       case AF9033_TUNER_IT9135_52:
+       case AF9033_TUNER_IT9135_60:
+       case AF9033_TUNER_IT9135_61:
+       case AF9033_TUNER_IT9135_62:
+               if (adap->id == 1) {
+                       if (state->i2c_client[3])
+                               af9035_del_i2c_dev(d);
+               } else if (adap->id == 0) {
+                       if (state->i2c_client[1])
+                               af9035_del_i2c_dev(d);
+               }
+       }
+
+       return 0;
+}
+
 static int af9035_init(struct dvb_usb_device *d)
 {
        struct state *state = d_to_priv(d);
@@ -1315,6 +1675,89 @@ err:
        return ret;
 }
 
+static int it930x_init(struct dvb_usb_device *d)
+{
+       struct state *state = d_to_priv(d);
+       int ret, i;
+       u16 frame_size = (d->udev->speed == USB_SPEED_FULL ? 5 : 816) * 188 / 4;
+       u8 packet_size = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4;
+       struct reg_val_mask tab[] = {
+               { 0x00da1a, 0x00, 0x01 }, /* ignore_sync_byte */
+               { 0x00f41f, 0x04, 0x04 }, /* dvbt_inten */
+               { 0x00da10, 0x00, 0x01 }, /* mpeg_full_speed */
+               { 0x00f41a, 0x01, 0x01 }, /* dvbt_en */
+               { 0x00da1d, 0x01, 0x01 }, /* mp2_sw_rst, reset EP4 */
+               { 0x00dd11, 0x00, 0x20 }, /* ep4_tx_en, disable EP4 */
+               { 0x00dd13, 0x00, 0x20 }, /* ep4_tx_nak, disable EP4 NAK */
+               { 0x00dd11, 0x20, 0x20 }, /* ep4_tx_en, enable EP4 */
+               { 0x00dd11, 0x00, 0x40 }, /* ep5_tx_en, disable EP5 */
+               { 0x00dd13, 0x00, 0x40 }, /* ep5_tx_nak, disable EP5 NAK */
+               { 0x00dd11, state->dual_mode << 6, 0x40 }, /* enable EP5 */
+               { 0x00dd88, (frame_size >> 0) & 0xff, 0xff},
+               { 0x00dd89, (frame_size >> 8) & 0xff, 0xff},
+               { 0x00dd0c, packet_size, 0xff},
+               { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff},
+               { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff},
+               { 0x00dd0d, packet_size, 0xff },
+               { 0x00da1d, 0x00, 0x01 }, /* mp2_sw_rst, disable */
+               { 0x00d833, 0x01, 0xff }, /* slew rate ctrl: slew rate boosts */
+               { 0x00d830, 0x00, 0xff }, /* Bit 0 of output driving control */
+               { 0x00d831, 0x01, 0xff }, /* Bit 1 of output driving control */
+               { 0x00d832, 0x00, 0xff }, /* Bit 2 of output driving control */
+
+               /* suspend gpio1 for TS-C */
+               { 0x00d8b0, 0x01, 0xff }, /* gpio1 */
+               { 0x00d8b1, 0x01, 0xff }, /* gpio1 */
+               { 0x00d8af, 0x00, 0xff }, /* gpio1 */
+
+               /* suspend gpio7 for TS-D */
+               { 0x00d8c4, 0x01, 0xff }, /* gpio7 */
+               { 0x00d8c5, 0x01, 0xff }, /* gpio7 */
+               { 0x00d8c3, 0x00, 0xff }, /* gpio7 */
+
+               /* suspend gpio13 for TS-B */
+               { 0x00d8dc, 0x01, 0xff }, /* gpio13 */
+               { 0x00d8dd, 0x01, 0xff }, /* gpio13 */
+               { 0x00d8db, 0x00, 0xff }, /* gpio13 */
+
+               /* suspend gpio14 for TS-E */
+               { 0x00d8e4, 0x01, 0xff }, /* gpio14 */
+               { 0x00d8e5, 0x01, 0xff }, /* gpio14 */
+               { 0x00d8e3, 0x00, 0xff }, /* gpio14 */
+
+               /* suspend gpio15 for TS-A */
+               { 0x00d8e8, 0x01, 0xff }, /* gpio15 */
+               { 0x00d8e9, 0x01, 0xff }, /* gpio15 */
+               { 0x00d8e7, 0x00, 0xff }, /* gpio15 */
+
+               { 0x00da58, 0x00, 0x01 }, /* ts_in_src, serial */
+               { 0x00da73, 0x01, 0xff }, /* ts0_aggre_mode */
+               { 0x00da78, 0x47, 0xff }, /* ts0_sync_byte */
+               { 0x00da4c, 0x01, 0xff }, /* ts0_en */
+               { 0x00da5a, 0x1f, 0xff }, /* ts_fail_ignore */
+       };
+
+       dev_dbg(&d->udev->dev,
+                       "%s: USB speed=%d frame_size=%04x packet_size=%02x\n",
+                       __func__, d->udev->speed, frame_size, packet_size);
+
+       /* init endpoints */
+       for (i = 0; i < ARRAY_SIZE(tab); i++) {
+               ret = af9035_wr_reg_mask(d, tab[i].reg,
+                               tab[i].val, tab[i].mask);
+
+               if (ret < 0)
+                       goto err;
+       }
+
+       return 0;
+err:
+       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+       return ret;
+}
+
+
 #if IS_ENABLED(CONFIG_RC_CORE)
 static int af9035_rc_query(struct dvb_usb_device *d)
 {
@@ -1409,6 +1852,7 @@ static int af9035_get_stream_config(struct dvb_frontend *fe, u8 *ts_type,
                struct usb_data_stream_properties *stream)
 {
        struct dvb_usb_device *d = fe_to_d(fe);
+
        dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, fe_to_adap(fe)->id);
 
        if (d->udev->speed == USB_SPEED_FULL)
@@ -1486,7 +1930,9 @@ static const struct dvb_usb_device_properties af9035_props = {
        .i2c_algo = &af9035_i2c_algo,
        .read_config = af9035_read_config,
        .frontend_attach = af9035_frontend_attach,
+       .frontend_detach = af9035_frontend_detach,
        .tuner_attach = af9035_tuner_attach,
+       .tuner_detach = af9035_tuner_detach,
        .init = af9035_init,
        .get_rc_config = af9035_get_rc_config,
        .get_stream_config = af9035_get_stream_config,
@@ -1515,6 +1961,37 @@ static const struct dvb_usb_device_properties af9035_props = {
        },
 };
 
+static const struct dvb_usb_device_properties it930x_props = {
+       .driver_name = KBUILD_MODNAME,
+       .owner = THIS_MODULE,
+       .adapter_nr = adapter_nr,
+       .size_of_priv = sizeof(struct state),
+
+       .generic_bulk_ctrl_endpoint = 0x02,
+       .generic_bulk_ctrl_endpoint_response = 0x81,
+
+       .identify_state = af9035_identify_state,
+       .download_firmware = af9035_download_firmware,
+
+       .i2c_algo = &af9035_i2c_algo,
+       .read_config = af9035_read_config,
+       .frontend_attach = it930x_frontend_attach,
+       .frontend_detach = af9035_frontend_detach,
+       .tuner_attach = it930x_tuner_attach,
+       .tuner_detach = it930x_tuner_detach,
+       .init = it930x_init,
+       .get_stream_config = af9035_get_stream_config,
+
+       .get_adapter_count = af9035_get_adapter_count,
+       .adapter = {
+               {
+                       .stream = DVB_USB_STREAM_BULK(0x84, 4, 816 * 188),
+               }, {
+                       .stream = DVB_USB_STREAM_BULK(0x85, 4, 816 * 188),
+               },
+       },
+};
+
 static const struct usb_device_id af9035_id_table[] = {
        /* AF9035 devices */
        { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_9035,
@@ -1568,17 +2045,21 @@ static const struct usb_device_id af9035_id_table[] = {
        { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CTVDIGDUAL_V2,
                &af9035_props, "Digital Dual TV Receiver CTVDIGDUAL_V2",
                                                        RC_MAP_IT913X_V1) },
+       /* IT930x devices */
+       { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9303,
+               &it930x_props, "ITE 9303 Generic", NULL) },
        /* XXX: that same ID [0ccd:0099] is used by af9015 driver too */
        { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x0099,
-               &af9035_props, "TerraTec Cinergy T Stick Dual RC (rev. 2)", NULL) },
+               &af9035_props, "TerraTec Cinergy T Stick Dual RC (rev. 2)",
+               NULL) },
        { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a05,
                &af9035_props, "Leadtek WinFast DTV Dongle Dual", NULL) },
        { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xf900,
                &af9035_props, "Hauppauge WinTV-MiniStick 2", NULL) },
        { DVB_USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_78E,
-               &af9035_props, "PCTV 78e", RC_MAP_IT913X_V1) },
+               &af9035_props, "PCTV AndroiDTV (78e)", RC_MAP_IT913X_V1) },
        { DVB_USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_79E,
-               &af9035_props, "PCTV 79e", RC_MAP_IT913X_V2) },
+               &af9035_props, "PCTV microStick (79e)", RC_MAP_IT913X_V2) },
        { }
 };
 MODULE_DEVICE_TABLE(usb, af9035_id_table);
@@ -1603,3 +2084,4 @@ MODULE_LICENSE("GPL");
 MODULE_FIRMWARE(AF9035_FIRMWARE_AF9035);
 MODULE_FIRMWARE(AF9035_FIRMWARE_IT9135_V1);
 MODULE_FIRMWARE(AF9035_FIRMWARE_IT9135_V2);
+MODULE_FIRMWARE(AF9035_FIRMWARE_IT9303);
index c21902fdd4c44e8d04ecce5962487921f4a92c16..416a97f05ec8539fbc2c11dad23e1261a819650c 100644 (file)
@@ -30,7 +30,9 @@
 #include "mxl5007t.h"
 #include "tda18218.h"
 #include "fc2580.h"
-#include "tuner_it913x.h"
+#include "it913x.h"
+#include "si2168.h"
+#include "si2157.h"
 
 struct reg_val {
        u32 reg;
@@ -61,9 +63,12 @@ struct state {
        u16 chip_type;
        u8 dual_mode:1;
        u16 eeprom_addr;
+       u8 af9033_i2c_addr[2];
        struct af9033_config af9033_config[2];
-
        struct af9033_ops ops;
+       #define AF9035_I2C_CLIENT_MAX 4
+       struct i2c_client *i2c_client[AF9035_I2C_CLIENT_MAX];
+       struct i2c_adapter *i2c_adapter_demod;
 };
 
 static const u32 clock_lut_af9035[] = {
@@ -97,6 +102,7 @@ static const u32 clock_lut_it9135[] = {
 #define AF9035_FIRMWARE_AF9035 "dvb-usb-af9035-02.fw"
 #define AF9035_FIRMWARE_IT9135_V1 "dvb-usb-it9135-01.fw"
 #define AF9035_FIRMWARE_IT9135_V2 "dvb-usb-it9135-02.fw"
+#define AF9035_FIRMWARE_IT9303 "dvb-usb-it9303-01.fw"
 
 /*
  * eeprom is memory mapped as read only. Writing that memory mapped address
@@ -138,5 +144,7 @@ static const u32 clock_lut_it9135[] = {
 #define CMD_FW_DL_BEGIN             0x24
 #define CMD_FW_DL_END               0x25
 #define CMD_FW_SCATTER_WR           0x29
+#define CMD_GENERIC_I2C_RD          0x2a
+#define CMD_GENERIC_I2C_WR          0x2b
 
 #endif
index e4a2382196f0b98dba2cf82cba61879357ad397b..d3c5f230e97a7272f69e7cbba367c3a18c9cbe8b 100644 (file)
@@ -332,7 +332,6 @@ static struct tda10023_config anysee_tda10023_tda18212_config = {
 };
 
 static struct tda18212_config anysee_tda18212_config = {
-       .i2c_address = (0xc0 >> 1),
        .if_dvbt_6 = 4150,
        .if_dvbt_7 = 4150,
        .if_dvbt_8 = 4150,
@@ -340,7 +339,6 @@ static struct tda18212_config anysee_tda18212_config = {
 };
 
 static struct tda18212_config anysee_tda18212_config2 = {
-       .i2c_address = 0x60 /* (0xc0 >> 1) */,
        .if_dvbt_6 = 3550,
        .if_dvbt_7 = 3700,
        .if_dvbt_8 = 4150,
@@ -632,6 +630,92 @@ error:
        return ret;
 }
 
+static int anysee_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr,
+               void *platform_data)
+{
+       int ret, num;
+       struct anysee_state *state = d_to_priv(d);
+       struct i2c_client *client;
+       struct i2c_adapter *adapter = &d->i2c_adap;
+       struct i2c_board_info board_info = {
+               .addr = addr,
+               .platform_data = platform_data,
+       };
+
+       strlcpy(board_info.type, type, I2C_NAME_SIZE);
+
+       /* find first free client */
+       for (num = 0; num < ANYSEE_I2C_CLIENT_MAX; num++) {
+               if (state->i2c_client[num] == NULL)
+                       break;
+       }
+
+       dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num);
+
+       if (num == ANYSEE_I2C_CLIENT_MAX) {
+               dev_err(&d->udev->dev, "%s: I2C client out of index\n",
+                               KBUILD_MODNAME);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       request_module(board_info.type);
+
+       /* register I2C device */
+       client = i2c_new_device(adapter, &board_info);
+       if (client == NULL || client->dev.driver == NULL) {
+               ret = -ENODEV;
+               goto err;
+       }
+
+       /* increase I2C driver usage count */
+       if (!try_module_get(client->dev.driver->owner)) {
+               i2c_unregister_device(client);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       state->i2c_client[num] = client;
+       return 0;
+err:
+       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static void anysee_del_i2c_dev(struct dvb_usb_device *d)
+{
+       int num;
+       struct anysee_state *state = d_to_priv(d);
+       struct i2c_client *client;
+
+       /* find last used client */
+       num = ANYSEE_I2C_CLIENT_MAX;
+       while (num--) {
+               if (state->i2c_client[num] != NULL)
+                       break;
+       }
+
+       dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num);
+
+       if (num == -1) {
+               dev_err(&d->udev->dev, "%s: I2C client out of index\n",
+                               KBUILD_MODNAME);
+               goto err;
+       }
+
+       client = state->i2c_client[num];
+
+       /* decrease I2C driver usage count */
+       module_put(client->dev.driver->owner);
+
+       /* unregister I2C device */
+       i2c_unregister_device(client);
+
+       state->i2c_client[num] = NULL;
+err:
+       dev_dbg(&d->udev->dev, "%s: failed\n", __func__);
+}
+
 static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
 {
        struct anysee_state *state = adap_to_priv(adap);
@@ -640,12 +724,12 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
        u8 tmp;
        struct i2c_msg msg[2] = {
                {
-                       .addr = anysee_tda18212_config.i2c_address,
+                       .addr = 0x60,
                        .flags = 0,
                        .len = 1,
                        .buf = "\x00",
                }, {
-                       .addr = anysee_tda18212_config.i2c_address,
+                       .addr = 0x60,
                        .flags = I2C_M_RD,
                        .len = 1,
                        .buf = &tmp,
@@ -723,9 +807,11 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
                /* probe TDA18212 */
                tmp = 0;
                ret = i2c_transfer(&d->i2c_adap, msg, 2);
-               if (ret == 2 && tmp == 0xc7)
+               if (ret == 2 && tmp == 0xc7) {
                        dev_dbg(&d->udev->dev, "%s: TDA18212 found\n",
                                        __func__);
+                       state->has_tda18212 = true;
+               }
                else
                        tmp = 0;
 
@@ -939,46 +1025,63 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
                 * fails attach old simple PLL. */
 
                /* attach tuner */
-               fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap,
-                               &anysee_tda18212_config);
+               if (state->has_tda18212) {
+                       struct tda18212_config tda18212_config =
+                                       anysee_tda18212_config;
 
-               if (fe && adap->fe[1]) {
-                       /* attach tuner for 2nd FE */
-                       fe = dvb_attach(tda18212_attach, adap->fe[1],
-                                       &d->i2c_adap, &anysee_tda18212_config);
-                       break;
-               } else if (fe) {
-                       break;
-               }
-
-               /* attach tuner */
-               fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc0 >> 1),
-                               &d->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
+                       tda18212_config.fe = adap->fe[0];
+                       ret = anysee_add_i2c_dev(d, "tda18212", 0x60,
+                                       &tda18212_config);
+                       if (ret)
+                               goto err;
+
+                       /* copy tuner ops for 2nd FE as tuner is shared */
+                       if (adap->fe[1]) {
+                               adap->fe[1]->tuner_priv =
+                                               adap->fe[0]->tuner_priv;
+                               memcpy(&adap->fe[1]->ops.tuner_ops,
+                                               &adap->fe[0]->ops.tuner_ops,
+                                               sizeof(struct dvb_tuner_ops));
+                       }
 
-               if (fe && adap->fe[1]) {
-                       /* attach tuner for 2nd FE */
-                       fe = dvb_attach(dvb_pll_attach, adap->fe[1],
+                       return 0;
+               } else {
+                       /* attach tuner */
+                       fe = dvb_attach(dvb_pll_attach, adap->fe[0],
                                        (0xc0 >> 1), &d->i2c_adap,
                                        DVB_PLL_SAMSUNG_DTOS403IH102A);
+
+                       if (fe && adap->fe[1]) {
+                               /* attach tuner for 2nd FE */
+                               fe = dvb_attach(dvb_pll_attach, adap->fe[1],
+                                               (0xc0 >> 1), &d->i2c_adap,
+                                               DVB_PLL_SAMSUNG_DTOS403IH102A);
+                       }
                }
 
                break;
        case ANYSEE_HW_508TC: /* 18 */
        case ANYSEE_HW_508PTC: /* 21 */
+       {
                /* E7 TC */
                /* E7 PTC */
+               struct tda18212_config tda18212_config = anysee_tda18212_config;
 
-               /* attach tuner */
-               fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap,
-                               &anysee_tda18212_config);
-
-               if (fe) {
-                       /* attach tuner for 2nd FE */
-                       fe = dvb_attach(tda18212_attach, adap->fe[1],
-                                       &d->i2c_adap, &anysee_tda18212_config);
+               tda18212_config.fe = adap->fe[0];
+               ret = anysee_add_i2c_dev(d, "tda18212", 0x60, &tda18212_config);
+               if (ret)
+                       goto err;
+
+               /* copy tuner ops for 2nd FE as tuner is shared */
+               if (adap->fe[1]) {
+                       adap->fe[1]->tuner_priv = adap->fe[0]->tuner_priv;
+                       memcpy(&adap->fe[1]->ops.tuner_ops,
+                                       &adap->fe[0]->ops.tuner_ops,
+                                       sizeof(struct dvb_tuner_ops));
                }
 
-               break;
+               return 0;
+       }
        case ANYSEE_HW_508S2: /* 19 */
        case ANYSEE_HW_508PS2: /* 22 */
                /* E7 S2 */
@@ -997,13 +1100,18 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
                break;
 
        case ANYSEE_HW_508T2C: /* 20 */
+       {
                /* E7 T2C */
+               struct tda18212_config tda18212_config =
+                               anysee_tda18212_config2;
 
-               /* attach tuner */
-               fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap,
-                               &anysee_tda18212_config2);
+               tda18212_config.fe = adap->fe[0];
+               ret = anysee_add_i2c_dev(d, "tda18212", 0x60, &tda18212_config);
+               if (ret)
+                       goto err;
 
-               break;
+               return 0;
+       }
        default:
                fe = NULL;
        }
@@ -1012,7 +1120,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
                ret = 0;
        else
                ret = -ENODEV;
-
+err:
        return ret;
 }
 
@@ -1270,6 +1378,11 @@ static int anysee_init(struct dvb_usb_device *d)
 
 static void anysee_exit(struct dvb_usb_device *d)
 {
+       struct anysee_state *state = d_to_priv(d);
+
+       if (state->i2c_client[0])
+               anysee_del_i2c_dev(d);
+
        return anysee_ci_release(d);
 }
 
index 8f426d9fc6e1110b14c521deb770e35bbc837843..3ca2bca4ebafacc581bfd3cc3aa17c3d087c209b 100644 (file)
@@ -55,8 +55,11 @@ struct anysee_state {
        u8 buf[64];
        u8 seq;
        u8 hw; /* PCB ID */
+       #define ANYSEE_I2C_CLIENT_MAX 1
+       struct i2c_client *i2c_client[ANYSEE_I2C_CLIENT_MAX];
        u8 fe_id:1; /* frondend ID */
        u8 has_ci:1;
+       u8 has_tda18212:1;
        u8 ci_attached:1;
        struct dvb_ca_en50221 ci;
        unsigned long ci_cam_ready; /* jiffies */
index 124b4baa7e975d9ec63ac8c243d3e2741fd8d4c7..14e111e13e54c31eae02814b9602e1cf06564e4d 100644 (file)
@@ -214,6 +214,7 @@ struct dvb_usb_adapter_properties {
  * @read_config: called to resolve device configuration
  * @read_mac_address: called to resolve adapter mac-address
  * @frontend_attach: called to attach the possible frontends
+ * @frontend_detach: called to detach the possible frontends
  * @tuner_attach: called to attach the possible tuners
  * @frontend_ctrl: called to power on/off active frontend
  * @streaming_ctrl: called to start/stop the usb streaming of adapter
@@ -254,7 +255,9 @@ struct dvb_usb_device_properties {
        int (*read_config) (struct dvb_usb_device *d);
        int (*read_mac_address) (struct dvb_usb_adapter *, u8 []);
        int (*frontend_attach) (struct dvb_usb_adapter *);
+       int (*frontend_detach)(struct dvb_usb_adapter *);
        int (*tuner_attach) (struct dvb_usb_adapter *);
+       int (*tuner_detach)(struct dvb_usb_adapter *);
        int (*frontend_ctrl) (struct dvb_frontend *, int);
        int (*streaming_ctrl) (struct dvb_frontend *, int);
        int (*init) (struct dvb_usb_device *);
index 2e90310be2afd7d9ae954b63c55301ae5d25bac0..1950f37df835187c0ac0e153231b316adcaea106 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "dvb_usb_common.h"
 
-int dvb_usbv2_disable_rc_polling;
+static int dvb_usbv2_disable_rc_polling;
 module_param_named(disable_rc_polling, dvb_usbv2_disable_rc_polling, int, 0644);
 MODULE_PARM_DESC(disable_rc_polling,
                "disable remote control polling (default: 0)");
@@ -664,9 +664,10 @@ err:
 
 static int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap)
 {
-       int i;
-       dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__,
-                       adap->id);
+       int ret, i;
+       struct dvb_usb_device *d = adap_to_d(adap);
+
+       dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, adap->id);
 
        for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) {
                if (adap->fe[i]) {
@@ -675,6 +676,23 @@ static int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap)
                }
        }
 
+       if (d->props->tuner_detach) {
+               ret = d->props->tuner_detach(adap);
+               if (ret < 0) {
+                       dev_dbg(&d->udev->dev, "%s: tuner_detach() failed=%d\n",
+                                       __func__, ret);
+               }
+       }
+
+       if (d->props->frontend_detach) {
+               ret = d->props->frontend_detach(adap);
+               if (ret < 0) {
+                       dev_dbg(&d->udev->dev,
+                                       "%s: frontend_detach() failed=%d\n",
+                                       __func__, ret);
+               }
+       }
+
        return 0;
 }
 
@@ -762,9 +780,9 @@ static int dvb_usbv2_adapter_exit(struct dvb_usb_device *d)
 
        for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) {
                if (d->adapter[i].props) {
-                       dvb_usbv2_adapter_frontend_exit(&d->adapter[i]);
                        dvb_usbv2_adapter_dvb_exit(&d->adapter[i]);
                        dvb_usbv2_adapter_stream_exit(&d->adapter[i]);
+                       dvb_usbv2_adapter_frontend_exit(&d->adapter[i]);
                }
        }
 
index 33ff97e708e3a8c2defa22491ab56cd1619fac8a..22bdce15ecf31fcd351309ca7fa5794cb228869d 100644 (file)
@@ -26,7 +26,7 @@ static int dvb_usb_v2_generic_io(struct dvb_usb_device *d,
 {
        int ret, actual_length;
 
-       if (!d || !wbuf || !wlen || !d->props->generic_bulk_ctrl_endpoint ||
+       if (!wbuf || !wlen || !d->props->generic_bulk_ctrl_endpoint ||
                        !d->props->generic_bulk_ctrl_endpoint_response) {
                dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, -EINVAL);
                return -EINVAL;
diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c
new file mode 100644 (file)
index 0000000..34688c8
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * Driver for DVBSky USB2.0 receiver
+ *
+ * Copyright (C) 2013 Max nibble <nibble.max@gmail.com>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dvb_usb.h"
+#include "m88ds3103.h"
+#include "m88ts2022.h"
+
+#define DVBSKY_MSG_DELAY       0/*2000*/
+#define DVBSKY_BUF_LEN 64
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct dvbsky_state {
+       struct mutex stream_mutex;
+       u8 ibuf[DVBSKY_BUF_LEN];
+       u8 obuf[DVBSKY_BUF_LEN];
+       u8 last_lock;
+       struct i2c_client *i2c_client_tuner;
+
+       /* fe hook functions*/
+       int (*fe_set_voltage)(struct dvb_frontend *fe,
+               fe_sec_voltage_t voltage);
+       int (*fe_read_status)(struct dvb_frontend *fe,
+               fe_status_t *status);
+};
+
+static int dvbsky_usb_generic_rw(struct dvb_usb_device *d,
+               u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
+{
+       int ret;
+       struct dvbsky_state *state = d_to_priv(d);
+
+       mutex_lock(&d->usb_mutex);
+       if (wlen != 0)
+               memcpy(state->obuf, wbuf, wlen);
+
+       ret = dvb_usbv2_generic_rw_locked(d, state->obuf, wlen,
+                       state->ibuf, rlen);
+
+       if (!ret && (rlen != 0))
+               memcpy(rbuf, state->ibuf, rlen);
+
+       mutex_unlock(&d->usb_mutex);
+       return ret;
+}
+
+static int dvbsky_stream_ctrl(struct dvb_usb_device *d, u8 onoff)
+{
+       struct dvbsky_state *state = d_to_priv(d);
+       int ret;
+       u8 obuf_pre[3] = { 0x37, 0, 0 };
+       u8 obuf_post[3] = { 0x36, 3, 0 };
+
+       mutex_lock(&state->stream_mutex);
+       ret = dvbsky_usb_generic_rw(d, obuf_pre, 3, NULL, 0);
+       if (!ret && onoff) {
+               msleep(20);
+               ret = dvbsky_usb_generic_rw(d, obuf_post, 3, NULL, 0);
+       }
+       mutex_unlock(&state->stream_mutex);
+       return ret;
+}
+
+static int dvbsky_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+       struct dvb_usb_device *d = fe_to_d(fe);
+
+       return dvbsky_stream_ctrl(d, (onoff == 0) ? 0 : 1);
+}
+
+/* GPIO */
+static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value)
+{
+       int ret;
+       u8 obuf[3], ibuf[2];
+
+       obuf[0] = 0x0e;
+       obuf[1] = gport;
+       obuf[2] = value;
+       ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1);
+       if (ret)
+               dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
+                       KBUILD_MODNAME, __func__, ret);
+       return ret;
+}
+
+/* I2C */
+static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+       int num)
+{
+       struct dvb_usb_device *d = i2c_get_adapdata(adap);
+       int ret = 0;
+       u8 ibuf[64], obuf[64];
+
+       if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+               return -EAGAIN;
+
+       if (num > 2) {
+               dev_err(&d->udev->dev,
+               "dvbsky_usb: too many i2c messages[%d] than 2.", num);
+               ret = -EOPNOTSUPP;
+               goto i2c_error;
+       }
+
+       if (num == 1) {
+               if (msg[0].len > 60) {
+                       dev_err(&d->udev->dev,
+                       "dvbsky_usb: too many i2c bytes[%d] than 60.",
+                       msg[0].len);
+                       ret = -EOPNOTSUPP;
+                       goto i2c_error;
+               }
+               if (msg[0].flags & I2C_M_RD) {
+                       /* single read */
+                       obuf[0] = 0x09;
+                       obuf[1] = 0;
+                       obuf[2] = msg[0].len;
+                       obuf[3] = msg[0].addr;
+                       ret = dvbsky_usb_generic_rw(d, obuf, 4,
+                                       ibuf, msg[0].len + 1);
+                       if (ret)
+                               dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
+                                       KBUILD_MODNAME, __func__, ret);
+                       if (!ret)
+                               memcpy(msg[0].buf, &ibuf[1], msg[0].len);
+               } else {
+                       /* write */
+                       obuf[0] = 0x08;
+                       obuf[1] = msg[0].addr;
+                       obuf[2] = msg[0].len;
+                       memcpy(&obuf[3], msg[0].buf, msg[0].len);
+                       ret = dvbsky_usb_generic_rw(d, obuf,
+                                       msg[0].len + 3, ibuf, 1);
+                       if (ret)
+                               dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
+                                       KBUILD_MODNAME, __func__, ret);
+               }
+       } else {
+               if ((msg[0].len > 60) || (msg[1].len > 60)) {
+                       dev_err(&d->udev->dev,
+                       "dvbsky_usb: too many i2c bytes[w-%d][r-%d] than 60.",
+                       msg[0].len, msg[1].len);
+                       ret = -EOPNOTSUPP;
+                       goto i2c_error;
+               }
+               /* write then read */
+               obuf[0] = 0x09;
+               obuf[1] = msg[0].len;
+               obuf[2] = msg[1].len;
+               obuf[3] = msg[0].addr;
+               memcpy(&obuf[4], msg[0].buf, msg[0].len);
+               ret = dvbsky_usb_generic_rw(d, obuf,
+                       msg[0].len + 4, ibuf, msg[1].len + 1);
+               if (ret)
+                       dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
+                               KBUILD_MODNAME, __func__, ret);
+
+               if (!ret)
+                       memcpy(msg[1].buf, &ibuf[1], msg[1].len);
+       }
+i2c_error:
+       mutex_unlock(&d->i2c_mutex);
+       return (ret) ? ret : num;
+}
+
+static u32 dvbsky_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dvbsky_i2c_algo = {
+       .master_xfer   = dvbsky_i2c_xfer,
+       .functionality = dvbsky_i2c_func,
+};
+
+#if IS_ENABLED(CONFIG_RC_CORE)
+static int dvbsky_rc_query(struct dvb_usb_device *d)
+{
+       u32 code = 0xffff, scancode;
+       u8 rc5_command, rc5_system;
+       u8 obuf[2], ibuf[2], toggle;
+       int ret;
+
+       obuf[0] = 0x10;
+       ret = dvbsky_usb_generic_rw(d, obuf, 1, ibuf, 2);
+       if (ret)
+               dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
+                       KBUILD_MODNAME, __func__, ret);
+       if (ret == 0)
+               code = (ibuf[0] << 8) | ibuf[1];
+       if (code != 0xffff) {
+               dev_dbg(&d->udev->dev, "rc code: %x\n", code);
+               rc5_command = code & 0x3F;
+               rc5_system = (code & 0x7C0) >> 6;
+               toggle = (code & 0x800) ? 1 : 0;
+               scancode = rc5_system << 8 | rc5_command;
+               rc_keydown(d->rc_dev, RC_TYPE_RC5, scancode, toggle);
+       }
+       return 0;
+}
+
+static int dvbsky_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
+{
+       rc->allowed_protos = RC_BIT_RC5;
+       rc->query          = dvbsky_rc_query;
+       rc->interval       = 300;
+       return 0;
+}
+#else
+       #define dvbsky_get_rc_config NULL
+#endif
+
+static int dvbsky_usb_set_voltage(struct dvb_frontend *fe,
+       fe_sec_voltage_t voltage)
+{
+       struct dvb_usb_device *d = fe_to_d(fe);
+       struct dvbsky_state *state = d_to_priv(d);
+       u8 value;
+
+       if (voltage == SEC_VOLTAGE_OFF)
+               value = 0;
+       else
+               value = 1;
+       dvbsky_gpio_ctrl(d, 0x80, value);
+
+       return state->fe_set_voltage(fe, voltage);
+}
+
+static int dvbsky_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6])
+{
+       struct dvb_usb_device *d = adap_to_d(adap);
+       u8 obuf[] = { 0x1e, 0x00 };
+       u8 ibuf[6] = { 0 };
+       struct i2c_msg msg[] = {
+               {
+                       .addr = 0x51,
+                       .flags = 0,
+                       .buf = obuf,
+                       .len = 2,
+               }, {
+                       .addr = 0x51,
+                       .flags = I2C_M_RD,
+                       .buf = ibuf,
+                       .len = 6,
+               }
+       };
+
+       if (i2c_transfer(&d->i2c_adap, msg, 2) == 2)
+               memcpy(mac, ibuf, 6);
+
+       dev_info(&d->udev->dev, "dvbsky_usb MAC address=%pM\n", mac);
+
+       return 0;
+}
+
+static int dvbsky_usb_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+       struct dvb_usb_device *d = fe_to_d(fe);
+       struct dvbsky_state *state = d_to_priv(d);
+       int ret;
+
+       ret = state->fe_read_status(fe, status);
+
+       /* it need resync slave fifo when signal change from unlock to lock.*/
+       if ((*status & FE_HAS_LOCK) && (!state->last_lock))
+               dvbsky_stream_ctrl(d, 1);
+
+       state->last_lock = (*status & FE_HAS_LOCK) ? 1 : 0;
+       return ret;
+}
+
+static const struct m88ds3103_config dvbsky_s960_m88ds3103_config = {
+       .i2c_addr = 0x68,
+       .clock = 27000000,
+       .i2c_wr_max = 33,
+       .clock_out = 0,
+       .ts_mode = M88DS3103_TS_CI,
+       .ts_clk = 16000,
+       .ts_clk_pol = 0,
+       .agc = 0x99,
+       .lnb_hv_pol = 1,
+       .lnb_en_pol = 1,
+};
+
+static int dvbsky_s960_attach(struct dvb_usb_adapter *adap)
+{
+       struct dvbsky_state *state = adap_to_priv(adap);
+       struct dvb_usb_device *d = adap_to_d(adap);
+       int ret = 0;
+       /* demod I2C adapter */
+       struct i2c_adapter *i2c_adapter;
+       struct i2c_client *client;
+       struct i2c_board_info info;
+       struct m88ts2022_config m88ts2022_config = {
+                       .clock = 27000000,
+               };
+       memset(&info, 0, sizeof(struct i2c_board_info));
+
+       /* attach demod */
+       adap->fe[0] = dvb_attach(m88ds3103_attach,
+                       &dvbsky_s960_m88ds3103_config,
+                       &d->i2c_adap,
+                       &i2c_adapter);
+       if (!adap->fe[0]) {
+               dev_err(&d->udev->dev, "dvbsky_s960_attach fail.\n");
+               ret = -ENODEV;
+               goto fail_attach;
+       }
+
+       /* attach tuner */
+       m88ts2022_config.fe = adap->fe[0];
+       strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+       info.addr = 0x60;
+       info.platform_data = &m88ts2022_config;
+       request_module("m88ts2022");
+       client = i2c_new_device(i2c_adapter, &info);
+       if (client == NULL || client->dev.driver == NULL) {
+               dvb_frontend_detach(adap->fe[0]);
+               ret = -ENODEV;
+               goto fail_attach;
+       }
+
+       if (!try_module_get(client->dev.driver->owner)) {
+               i2c_unregister_device(client);
+               dvb_frontend_detach(adap->fe[0]);
+               ret = -ENODEV;
+               goto fail_attach;
+       }
+
+       /* delegate signal strength measurement to tuner */
+       adap->fe[0]->ops.read_signal_strength =
+                       adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+       /* hook fe: need to resync the slave fifo when signal locks. */
+       state->fe_read_status = adap->fe[0]->ops.read_status;
+       adap->fe[0]->ops.read_status = dvbsky_usb_read_status;
+
+       /* hook fe: LNB off/on is control by Cypress usb chip. */
+       state->fe_set_voltage = adap->fe[0]->ops.set_voltage;
+       adap->fe[0]->ops.set_voltage = dvbsky_usb_set_voltage;
+
+       state->i2c_client_tuner = client;
+
+fail_attach:
+       return ret;
+}
+
+static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name)
+{
+       dvbsky_gpio_ctrl(d, 0x04, 1);
+       msleep(20);
+       dvbsky_gpio_ctrl(d, 0x83, 0);
+       dvbsky_gpio_ctrl(d, 0xc0, 1);
+       msleep(100);
+       dvbsky_gpio_ctrl(d, 0x83, 1);
+       dvbsky_gpio_ctrl(d, 0xc0, 0);
+       msleep(50);
+
+       return WARM;
+}
+
+static int dvbsky_init(struct dvb_usb_device *d)
+{
+       struct dvbsky_state *state = d_to_priv(d);
+
+       /* use default interface */
+       /*
+       ret = usb_set_interface(d->udev, 0, 0);
+       if (ret)
+               return ret;
+       */
+       mutex_init(&state->stream_mutex);
+
+       state->last_lock = 0;
+
+       return 0;
+}
+
+static void dvbsky_exit(struct dvb_usb_device *d)
+{
+       struct dvbsky_state *state = d_to_priv(d);
+       struct i2c_client *client;
+
+       client = state->i2c_client_tuner;
+       /* remove I2C tuner */
+       if (client) {
+               module_put(client->dev.driver->owner);
+               i2c_unregister_device(client);
+       }
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties dvbsky_s960_props = {
+       .driver_name = KBUILD_MODNAME,
+       .owner = THIS_MODULE,
+       .adapter_nr = adapter_nr,
+       .size_of_priv = sizeof(struct dvbsky_state),
+
+       .generic_bulk_ctrl_endpoint = 0x01,
+       .generic_bulk_ctrl_endpoint_response = 0x81,
+       .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
+
+       .i2c_algo         = &dvbsky_i2c_algo,
+       .frontend_attach  = dvbsky_s960_attach,
+       .init             = dvbsky_init,
+       .get_rc_config    = dvbsky_get_rc_config,
+       .streaming_ctrl   = dvbsky_streaming_ctrl,
+       .identify_state   = dvbsky_identify_state,
+       .exit             = dvbsky_exit,
+       .read_mac_address = dvbsky_read_mac_addr,
+
+       .num_adapters = 1,
+       .adapter = {
+               {
+                       .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
+               }
+       }
+};
+
+static const struct usb_device_id dvbsky_id_table[] = {
+       { DVB_USB_DEVICE(0x0572, 0x6831,
+               &dvbsky_s960_props, "DVBSky S960/S860", RC_MAP_DVBSKY) },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, dvbsky_id_table);
+
+static struct usb_driver dvbsky_usb_driver = {
+       .name = KBUILD_MODNAME,
+       .id_table = dvbsky_id_table,
+       .probe = dvb_usbv2_probe,
+       .disconnect = dvb_usbv2_disconnect,
+       .suspend = dvb_usbv2_suspend,
+       .resume = dvb_usbv2_resume,
+       .reset_resume = dvb_usbv2_reset_resume,
+       .no_dynamic_id = 1,
+       .soft_unbind = 1,
+};
+
+module_usb_driver(dvbsky_usb_driver);
+
+MODULE_AUTHOR("Max nibble <nibble.max@gmail.com>");
+MODULE_DESCRIPTION("Driver for DVBSky USB");
+MODULE_LICENSE("GPL");
index e332af7311872c162333a386021cd89be3e2b1ca..9f2c5459b73a672485e118e4e17df4f671c6b776 100644 (file)
@@ -1252,7 +1252,7 @@ static int lme2510_get_stream_config(struct dvb_frontend *fe, u8 *ts_type,
 
        /* Turn PID filter on the fly by module option */
        if (pid_filter == 2) {
-               adap->pid_filtering  = 1;
+               adap->pid_filtering  = true;
                adap->max_feed_count = 15;
        }
 
index b8a707e57b994c6d7863941402d91350b990ea0e..c3447eaf11047339eafc14edc8ce2d573b7d96be 100644 (file)
@@ -31,11 +31,11 @@ module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debugging level "
                 "(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able)).");
 
-int dvb_usb_mxl111sf_isoc;
+static int dvb_usb_mxl111sf_isoc;
 module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644);
 MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc).");
 
-int dvb_usb_mxl111sf_spi;
+static int dvb_usb_mxl111sf_spi;
 module_param_named(spi, dvb_usb_mxl111sf_spi, int, 0644);
 MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi).");
 
@@ -43,7 +43,7 @@ MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi).");
 #define ANT_PATH_EXTERNAL 1
 #define ANT_PATH_INTERNAL 2
 
-int dvb_usb_mxl111sf_rfswitch =
+static int dvb_usb_mxl111sf_rfswitch =
 #if 0
                ANT_PATH_AUTO;
 #else
@@ -887,7 +887,7 @@ static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter)
        return I2C_FUNC_I2C;
 }
 
-struct i2c_algorithm mxl111sf_i2c_algo = {
+static struct i2c_algorithm mxl111sf_i2c_algo = {
        .master_xfer   = mxl111sf_i2c_xfer,
        .functionality = mxl111sf_i2c_func,
 #ifdef NEED_ALGO_CONTROL
index 10aef2188fbe764deb206320d1db71beb386f2aa..41d3eb922a006d5f4649adb34b64707eaac02759 100644 (file)
@@ -130,7 +130,7 @@ config DVB_USB_CXUSB
 
          Medion MD95700 hybrid USB2.0 device.
          DViCO FusionHDTV (Bluebird) USB2.0 devices
-         TechnoTrend TVStick CT2-4400
+         TechnoTrend TVStick CT2-4400 and CT2-4650 CI devices
 
 config DVB_USB_M920X
        tristate "Uli m920x DVB-T USB2.0 support"
index af176b6ce738e78d4dc366ae0d56597f833f4e59..3f4361e48a32ef98dd3faf6970a32127df549d4e 100644 (file)
@@ -30,7 +30,7 @@ MODULE_PARM_DESC(debug,
                 "set debugging level (1=info,xfer=2,rc=4,reg=8,i2c=16,fw=32 (or-able))."
                 DVB_USB_DEBUG_STATUS);
 /* enable obnoxious led */
-bool dvb_usb_af9005_led = 1;
+bool dvb_usb_af9005_led = true;
 module_param_named(led, dvb_usb_af9005_led, bool, 0644);
 MODULE_PARM_DESC(led, "enable led (default: 1).");
 
index 16bc579d1404e836e7bb287a1744c1e52fec48d7..356abb369c20b6cd07649fd1f75252f939d0a6e0 100644 (file)
@@ -44,6 +44,7 @@
 #include "atbm8830.h"
 #include "si2168.h"
 #include "si2157.h"
+#include "sp2.h"
 
 /* Max transfer size done by I2C transfer functions */
 #define MAX_XFER_SIZE  80
@@ -175,7 +176,7 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
 
        for (i = 0; i < num; i++) {
 
-               if (d->udev->descriptor.idVendor == USB_VID_MEDION)
+               if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_MEDION)
                        switch (msg[i].addr) {
                        case 0x63:
                                cxusb_gpio_tuner(d, 0);
@@ -672,6 +673,70 @@ static struct rc_map_table rc_map_d680_dmb_table[] = {
        { 0x0025, KEY_POWER },
 };
 
+static int cxusb_tt_ct2_4400_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+       u8 wbuf[2];
+       u8 rbuf[6];
+       int ret;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = 0x51,
+                       .flags = 0,
+                       .buf = wbuf,
+                       .len = 2,
+               }, {
+                       .addr = 0x51,
+                       .flags = I2C_M_RD,
+                       .buf = rbuf,
+                       .len = 6,
+               }
+       };
+
+       wbuf[0] = 0x1e;
+       wbuf[1] = 0x00;
+       ret = cxusb_i2c_xfer(&d->i2c_adap, msg, 2);
+
+       if (ret == 2) {
+               memcpy(mac, rbuf, 6);
+               return 0;
+       } else {
+               if (ret < 0)
+                       return ret;
+               return -EIO;
+       }
+}
+
+static int cxusb_tt_ct2_4650_ci_ctrl(void *priv, u8 read, int addr,
+                                       u8 data, int *mem)
+{
+       struct dvb_usb_device *d = priv;
+       u8 wbuf[3];
+       u8 rbuf[2];
+       int ret;
+
+       wbuf[0] = (addr >> 8) & 0xff;
+       wbuf[1] = addr & 0xff;
+
+       if (read) {
+               ret = cxusb_ctrl_msg(d, CMD_SP2_CI_READ, wbuf, 2, rbuf, 2);
+       } else {
+               wbuf[2] = data;
+               ret = cxusb_ctrl_msg(d, CMD_SP2_CI_WRITE, wbuf, 3, rbuf, 1);
+       }
+
+       if (ret)
+               goto err;
+
+       if (read)
+               *mem = rbuf[1];
+
+       return 0;
+err:
+       deb_info("%s: ci usb write returned %d\n", __func__, ret);
+       return ret;
+
+}
+
 static int cxusb_dee1601_demod_init(struct dvb_frontend* fe)
 {
        static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x28 };
@@ -1350,9 +1415,12 @@ static int cxusb_tt_ct2_4400_attach(struct dvb_usb_adapter *adap)
        struct i2c_adapter *adapter;
        struct i2c_client *client_demod;
        struct i2c_client *client_tuner;
+       struct i2c_client *client_ci;
        struct i2c_board_info info;
        struct si2168_config si2168_config;
        struct si2157_config si2157_config;
+       struct sp2_config sp2_config;
+       u8 o[2], i;
 
        /* reset the tuner */
        if (cxusb_tt_ct2_4400_gpio_tuner(d, 0) < 0) {
@@ -1369,6 +1437,7 @@ static int cxusb_tt_ct2_4400_attach(struct dvb_usb_adapter *adap)
        /* attach frontend */
        si2168_config.i2c_adapter = &adapter;
        si2168_config.fe = &adap->fe_adap[0].fe;
+       si2168_config.ts_mode = SI2168_TS_PARALLEL;
        memset(&info, 0, sizeof(struct i2c_board_info));
        strlcpy(info.type, "si2168", I2C_NAME_SIZE);
        info.addr = 0x64;
@@ -1408,6 +1477,48 @@ static int cxusb_tt_ct2_4400_attach(struct dvb_usb_adapter *adap)
 
        st->i2c_client_tuner = client_tuner;
 
+       /* initialize CI */
+       if (d->udev->descriptor.idProduct ==
+               USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI) {
+
+               memcpy(o, "\xc0\x01", 2);
+               cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
+               msleep(100);
+
+               memcpy(o, "\xc0\x00", 2);
+               cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
+               msleep(100);
+
+               memset(&sp2_config, 0, sizeof(sp2_config));
+               sp2_config.dvb_adap = &adap->dvb_adap;
+               sp2_config.priv = d;
+               sp2_config.ci_control = cxusb_tt_ct2_4650_ci_ctrl;
+               memset(&info, 0, sizeof(struct i2c_board_info));
+               strlcpy(info.type, "sp2", I2C_NAME_SIZE);
+               info.addr = 0x40;
+               info.platform_data = &sp2_config;
+               request_module(info.type);
+               client_ci = i2c_new_device(&d->i2c_adap, &info);
+               if (client_ci == NULL || client_ci->dev.driver == NULL) {
+                       module_put(client_tuner->dev.driver->owner);
+                       i2c_unregister_device(client_tuner);
+                       module_put(client_demod->dev.driver->owner);
+                       i2c_unregister_device(client_demod);
+                       return -ENODEV;
+               }
+               if (!try_module_get(client_ci->dev.driver->owner)) {
+                       i2c_unregister_device(client_ci);
+                       module_put(client_tuner->dev.driver->owner);
+                       i2c_unregister_device(client_tuner);
+                       module_put(client_demod->dev.driver->owner);
+                       i2c_unregister_device(client_demod);
+                       return -ENODEV;
+               }
+
+               st->i2c_client_ci = client_ci;
+
+       }
+
        return 0;
 }
 
@@ -1537,6 +1648,13 @@ static void cxusb_disconnect(struct usb_interface *intf)
        struct cxusb_state *st = d->priv;
        struct i2c_client *client;
 
+       /* remove I2C client for CI */
+       client = st->i2c_client_ci;
+       if (client) {
+               module_put(client->dev.driver->owner);
+               i2c_unregister_device(client);
+       }
+
        /* remove I2C client for tuner */
        client = st->i2c_client_tuner;
        if (client) {
@@ -1576,6 +1694,7 @@ static struct usb_device_id cxusb_table [] = {
        { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) },
        { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) },
        { USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_TVSTICK_CT2_4400) },
+       { USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI) },
        {}              /* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -2230,6 +2349,8 @@ static struct dvb_usb_device_properties cxusb_tt_ct2_4400_properties = {
        .size_of_priv     = sizeof(struct cxusb_state),
 
        .num_adapters = 1,
+       .read_mac_address = cxusb_tt_ct2_4400_read_mac_address,
+
        .adapter = {
                {
                .num_frontends = 1,
@@ -2265,13 +2386,18 @@ static struct dvb_usb_device_properties cxusb_tt_ct2_4400_properties = {
                .rc_interval    = 150,
        },
 
-       .num_device_descs = 1,
+       .num_device_descs = 2,
        .devices = {
                {
                        "TechnoTrend TVStick CT2-4400",
                        { NULL },
                        { &cxusb_table[20], NULL },
                },
+               {
+                       "TechnoTrend TT-connect CT2-4650 CI",
+                       { NULL },
+                       { &cxusb_table[21], NULL },
+               },
        }
 };
 
index 527ff7905e1590961b64b8edd3b62b859a9f903a..29f3e2ea2476a21394dcc60e7ba2cc70b9d2cdad 100644 (file)
 #define CMD_ANALOG        0x50
 #define CMD_DIGITAL       0x51
 
+#define CMD_SP2_CI_WRITE  0x70
+#define CMD_SP2_CI_READ   0x71
+
 struct cxusb_state {
        u8 gpio_write_state[3];
        struct i2c_client *i2c_client_demod;
        struct i2c_client *i2c_client_tuner;
+       struct i2c_client *i2c_client_ci;
 };
 
 #endif
index ce47d3f1c8502a1774a3eee1b518d0cd5821247d..e1757b8f5f5d0e7775abbc91c40bba817fec9d3d 100644 (file)
@@ -220,12 +220,21 @@ static struct dibx000_agc_config stk7700d_7000p_mt2266_agc_config[2] = {
 };
 
 static struct dibx000_bandwidth_config stk7700d_mt2266_pll_config = {
-       60000, 30000,
-       1, 8, 3, 1, 0,
-       0, 0, 1, 1, 2,
-       (3 << 14) | (1 << 12) | (524 << 0),
-       0,
-       20452225,
+       .internal = 60000,
+       .sampling = 30000,
+       .pll_prediv = 1,
+       .pll_ratio = 8,
+       .pll_range = 3,
+       .pll_reset = 1,
+       .pll_bypass = 0,
+       .enable_refdiv = 0,
+       .bypclk_div = 0,
+       .IO_CLK_en_core = 1,
+       .ADClkSrc = 1,
+       .modulo = 2,
+       .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0),
+       .ifreq = 0,
+       .timf = 20452225,
 };
 
 static struct dib7000p_config stk7700d_dib7000p_mt2266_config[] = {
@@ -342,57 +351,57 @@ static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap)
 
 /* STK7700-PH: Digital/Analog Hybrid Tuner, e.h. Cinergy HT USB HE */
 static struct dibx000_agc_config xc3028_agc_config = {
-       BAND_VHF | BAND_UHF,       /* band_caps */
-
+       .band_caps = BAND_VHF | BAND_UHF,
        /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0,
         * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0,
         * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
-       (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) |
-       (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */
-
-       712,    /* inv_gain */
-       21,     /* time_stabiliz */
-
-       0,      /* alpha_level */
-       118,    /* thlock */
-
-       0,      /* wbd_inv */
-       2867,   /* wbd_ref */
-       0,      /* wbd_sel */
-       2,      /* wbd_alpha */
-
-       0,      /* agc1_max */
-       0,      /* agc1_min */
-       39718,  /* agc2_max */
-       9930,   /* agc2_min */
-       0,      /* agc1_pt1 */
-       0,      /* agc1_pt2 */
-       0,      /* agc1_pt3 */
-       0,      /* agc1_slope1 */
-       0,      /* agc1_slope2 */
-       0,      /* agc2_pt1 */
-       128,    /* agc2_pt2 */
-       29,     /* agc2_slope1 */
-       29,     /* agc2_slope2 */
-
-       17,     /* alpha_mant */
-       27,     /* alpha_exp */
-       23,     /* beta_mant */
-       51,     /* beta_exp */
-
-       1,      /* perform_agc_softsplit */
+       .setup = (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0),
+       .inv_gain = 712,
+       .time_stabiliz = 21,
+       .alpha_level = 0,
+       .thlock = 118,
+       .wbd_inv = 0,
+       .wbd_ref = 2867,
+       .wbd_sel = 0,
+       .wbd_alpha = 2,
+       .agc1_max = 0,
+       .agc1_min = 0,
+       .agc2_max = 39718,
+       .agc2_min = 9930,
+       .agc1_pt1 = 0,
+       .agc1_pt2 = 0,
+       .agc1_pt3 = 0,
+       .agc1_slope1 = 0,
+       .agc1_slope2 = 0,
+       .agc2_pt1 = 0,
+       .agc2_pt2 = 128,
+       .agc2_slope1 = 29,
+       .agc2_slope2 = 29,
+       .alpha_mant = 17,
+       .alpha_exp = 27,
+       .beta_mant = 23,
+       .beta_exp = 51,
+       .perform_agc_softsplit = 1,
 };
 
 /* PLL Configuration for COFDM BW_MHz = 8.00 with external clock = 30.00 */
 static struct dibx000_bandwidth_config xc3028_bw_config = {
-       60000, 30000, /* internal, sampling */
-       1, 8, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass */
-       0, 0, 1, 1, 0, /* misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc,
-                         modulo */
-       (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */
-       (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */
-       20452225, /* timf */
-       30000000, /* xtal_hz */
+       .internal = 60000,
+       .sampling = 30000,
+       .pll_prediv = 1,
+       .pll_ratio = 8,
+       .pll_range = 3,
+       .pll_reset = 1,
+       .pll_bypass = 0,
+       .enable_refdiv = 0,
+       .bypclk_div = 0,
+       .IO_CLK_en_core = 1,
+       .ADClkSrc = 1,
+       .modulo = 0,
+       .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */
+       .ifreq = (1 << 25) | 5816102,  /* ifreq = 5.200000 MHz */
+       .timf = 20452225,
+       .xtal_hz = 30000000,
 };
 
 static struct dib7000p_config stk7700ph_dib7700_xc3028_config = {
@@ -614,59 +623,55 @@ static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = {
 };
 
 static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = {
-       BAND_UHF | BAND_VHF,
-
+       .band_caps = BAND_UHF | BAND_VHF,
        /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
         * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
-       (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8)
-       | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0),
-
-       712,
-       41,
-
-       0,
-       118,
-
-       0,
-       4095,
-       0,
-       0,
-
-       42598,
-       16384,
-       42598,
-           0,
-
-         0,
-       137,
-       255,
-
-         0,
-       255,
-
-       0,
-       0,
-
-        0,
-       41,
-
-       15,
-       25,
-
-       28,
-       48,
-
-       0,
+       .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0),
+       .inv_gain = 712,
+       .time_stabiliz = 41,
+       .alpha_level = 0,
+       .thlock = 118,
+       .wbd_inv = 0,
+       .wbd_ref = 4095,
+       .wbd_sel = 0,
+       .wbd_alpha = 0,
+       .agc1_max = 42598,
+       .agc1_min = 16384,
+       .agc2_max = 42598,
+       .agc2_min = 0,
+       .agc1_pt1 = 0,
+       .agc1_pt2 = 137,
+       .agc1_pt3 = 255,
+       .agc1_slope1 = 0,
+       .agc1_slope2 = 255,
+       .agc2_pt1 = 0,
+       .agc2_pt2 = 0,
+       .agc2_slope1 = 0,
+       .agc2_slope2 = 41,
+       .alpha_mant = 15,
+       .alpha_exp = 25,
+       .beta_mant = 28,
+       .beta_exp = 48,
+       .perform_agc_softsplit = 0,
 };
 
 static struct dibx000_bandwidth_config stk7700p_pll_config = {
-       60000, 30000,
-       1, 8, 3, 1, 0,
-       0, 0, 1, 1, 0,
-       (3 << 14) | (1 << 12) | (524 << 0),
-       60258167,
-       20452225,
-       30000000,
+       .internal = 60000,
+       .sampling = 30000,
+       .pll_prediv = 1,
+       .pll_ratio = 8,
+       .pll_range = 3,
+       .pll_reset = 1,
+       .pll_bypass = 0,
+       .enable_refdiv = 0,
+       .bypclk_div = 0,
+       .IO_CLK_en_core = 1,
+       .ADClkSrc = 1,
+       .modulo = 0,
+       .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0),
+       .ifreq = 60258167,
+       .timf = 20452225,
+       .xtal_hz = 30000000,
 };
 
 static struct dib7000m_config stk7700p_dib7000m_config = {
@@ -758,45 +763,36 @@ static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap)
 
 /* DIB7070 generic */
 static struct dibx000_agc_config dib7070_agc_config = {
-       BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND,
+       .band_caps = BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND,
        /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
         * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
-       (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8)
-       | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
-
-       600,
-       10,
-
-       0,
-       118,
-
-       0,
-       3530,
-       1,
-       5,
-
-       65535,
-               0,
-
-       65535,
-       0,
-
-       0,
-       40,
-       183,
-       206,
-       255,
-       72,
-       152,
-       88,
-       90,
-
-       17,
-       27,
-       23,
-       51,
-
-       0,
+       .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
+       .inv_gain = 600,
+       .time_stabiliz = 10,
+       .alpha_level = 0,
+       .thlock = 118,
+       .wbd_inv = 0,
+       .wbd_ref = 3530,
+       .wbd_sel = 1,
+       .wbd_alpha = 5,
+       .agc1_max = 65535,
+       .agc1_min = 0,
+       .agc2_max = 65535,
+       .agc2_min = 0,
+       .agc1_pt1 = 0,
+       .agc1_pt2 = 40,
+       .agc1_pt3 = 183,
+       .agc1_slope1 = 206,
+       .agc1_slope2 = 255,
+       .agc2_pt1 = 72,
+       .agc2_pt2 = 152,
+       .agc2_slope1 = 88,
+       .agc2_slope2 = 90,
+       .alpha_mant = 17,
+       .alpha_exp = 27,
+       .beta_mant = 23,
+       .beta_exp = 51,
+       .perform_agc_softsplit = 0,
 };
 
 static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff)
@@ -952,13 +948,22 @@ static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
 }
 
 static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = {
-       60000, 15000,
-       1, 20, 3, 1, 0,
-       0, 0, 1, 1, 2,
-       (3 << 14) | (1 << 12) | (524 << 0),
-       (0 << 25) | 0,
-       20452225,
-       12000000,
+       .internal = 60000,
+       .sampling = 15000,
+       .pll_prediv = 1,
+       .pll_ratio = 20,
+       .pll_range = 3,
+       .pll_reset = 1,
+       .pll_bypass = 0,
+       .enable_refdiv = 0,
+       .bypclk_div = 0,
+       .IO_CLK_en_core = 1,
+       .ADClkSrc = 1,
+       .modulo = 2,
+       .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0),
+       .ifreq = (0 << 25) | 0,
+       .timf = 20452225,
+       .xtal_hz = 12000000,
 };
 
 static struct dib7000p_config dib7070p_dib7000p_config = {
@@ -1169,14 +1174,22 @@ static struct dibx000_agc_config dib807x_agc_config[2] = {
 };
 
 static struct dibx000_bandwidth_config dib807x_bw_config_12_mhz = {
-       60000, 15000, /* internal, sampling*/
-       1, 20, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass*/
-       0, 0, 1, 1, 2, /* misc: refdiv, bypclk_div, IO_CLK_en_core,
-                         ADClkSrc, modulo */
-       (3 << 14) | (1 << 12) | (599 << 0), /* sad_cfg: refsel, sel, freq_15k*/
-       (0 << 25) | 0, /* ifreq = 0.000000 MHz*/
-       18179755, /* timf*/
-       12000000, /* xtal_hz*/
+       .internal = 60000,
+       .sampling = 15000,
+       .pll_prediv = 1,
+       .pll_ratio = 20,
+       .pll_range = 3,
+       .pll_reset = 1,
+       .pll_bypass = 0,
+       .enable_refdiv = 0,
+       .bypclk_div = 0,
+       .IO_CLK_en_core = 1,
+       .ADClkSrc = 1,
+       .modulo = 2,
+       .sad_cfg = (3 << 14) | (1 << 12) | (599 << 0),  /* sad_cfg: refsel, sel, freq_15k*/
+       .ifreq = (0 << 25) | 0,                         /* ifreq = 0.000000 MHz*/
+       .timf = 18179755,
+       .xtal_hz = 12000000,
 };
 
 static struct dib8000_config dib807x_dib8000_config[2] = {
@@ -1921,13 +1934,22 @@ static struct dibx000_agc_config dib8096p_agc_config[2] = {
 };
 
 static struct dibx000_bandwidth_config dib8096p_clock_config_12_mhz = {
-       108000, 13500,
-       1, 9, 1, 0, 0,
-       0, 0, 0, 0, 2,
-       (3 << 14) | (1 << 12) | (524 << 0),
-       (0 << 25) | 0,
-       20199729,
-       12000000,
+       .internal = 108000,
+       .sampling = 13500,
+       .pll_prediv = 1,
+       .pll_ratio = 9,
+       .pll_range = 1,
+       .pll_reset = 0,
+       .pll_bypass = 0,
+       .enable_refdiv = 0,
+       .bypclk_div = 0,
+       .IO_CLK_en_core = 0,
+       .ADClkSrc = 0,
+       .modulo = 2,
+       .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0),
+       .ifreq = (0 << 25) | 0,
+       .timf = 20199729,
+       .xtal_hz = 12000000,
 };
 
 static struct dib8000_config tfe8096p_dib8000_config = {
@@ -2724,13 +2746,22 @@ static struct dibx000_agc_config dib7090_agc_config[2] = {
 };
 
 static struct dibx000_bandwidth_config dib7090_clock_config_12_mhz = {
-       60000, 15000,
-       1, 5, 0, 0, 0,
-       0, 0, 1, 1, 2,
-       (3 << 14) | (1 << 12) | (524 << 0),
-       (0 << 25) | 0,
-       20452225,
-       15000000,
+       .internal = 60000,
+       .sampling = 15000,
+       .pll_prediv = 1,
+       .pll_ratio = 5,
+       .pll_range = 0,
+       .pll_reset = 0,
+       .pll_bypass = 0,
+       .enable_refdiv = 0,
+       .bypclk_div = 0,
+       .IO_CLK_en_core = 1,
+       .ADClkSrc = 1,
+       .modulo = 2,
+       .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0),
+       .ifreq = (0 << 25) | 0,
+       .timf = 20452225,
+       .xtal_hz = 15000000,
 };
 
 static struct dib7000p_config nim7090_dib7000p_config = {
@@ -3498,14 +3529,22 @@ static struct dibx000_agc_config stk7700p_7000p_xc4000_agc_config = {
 };
 
 static struct dibx000_bandwidth_config stk7700p_xc4000_pll_config = {
-       60000, 30000,   /* internal, sampling */
-       1, 8, 3, 1, 0,  /* pll_cfg: prediv, ratio, range, reset, bypass */
-       0, 0, 1, 1, 0,  /* misc: refdiv, bypclk_div, IO_CLK_en_core, */
-                       /* ADClkSrc, modulo */
-       (3 << 14) | (1 << 12) | 524,    /* sad_cfg: refsel, sel, freq_15k */
-       39370534,       /* ifreq */
-       20452225,       /* timf */
-       30000000        /* xtal */
+       .internal = 60000,
+       .sampling = 30000,
+       .pll_prediv = 1,
+       .pll_ratio = 8,
+       .pll_range = 3,
+       .pll_reset = 1,
+       .pll_bypass = 0,
+       .enable_refdiv = 0,
+       .bypclk_div = 0,
+       .IO_CLK_en_core = 1,
+       .ADClkSrc = 1,
+       .modulo = 0,
+       .sad_cfg = (3 << 14) | (1 << 12) | 524, /* sad_cfg: refsel, sel, freq_15k */
+       .ifreq = 39370534,
+       .timf = 20452225,
+       .xtal_hz = 30000000
 };
 
 /* FIXME: none of these inputs are validated yet */
index 6d68af0c49c83ecab8d542eb0a3942902e70e7e8..ef3a8f75f82ebe21b4ee5b7963aed9bbf8449c32 100644 (file)
@@ -258,8 +258,8 @@ static struct dib3000mc_config mod3000p_dib3000p_config = {
 
 int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap)
 {
-       if (adap->dev->udev->descriptor.idVendor  == USB_VID_LITEON &&
-                       adap->dev->udev->descriptor.idProduct ==
+       if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON &&
+           le16_to_cpu(adap->dev->udev->descriptor.idProduct) ==
                        USB_PID_LITEON_DVB_T_WARM) {
                msleep(1000);
        }
@@ -297,8 +297,8 @@ int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap)
        struct i2c_adapter *tun_i2c;
 
        // First IF calibration for Liteon Sticks
-       if (adap->dev->udev->descriptor.idVendor  == USB_VID_LITEON &&
-               adap->dev->udev->descriptor.idProduct == USB_PID_LITEON_DVB_T_WARM) {
+       if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON &&
+           le16_to_cpu(adap->dev->udev->descriptor.idProduct) == USB_PID_LITEON_DVB_T_WARM) {
 
                dibusb_read_eeprom_byte(adap->dev,0x7E,&a);
                dibusb_read_eeprom_byte(adap->dev,0x7F,&b);
@@ -310,8 +310,8 @@ int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap)
                else
                        warn("LITE-ON DVB-T: Strange IF1 calibration :%2X %2X\n", a, b);
 
-       } else if (adap->dev->udev->descriptor.idVendor  == USB_VID_DIBCOM &&
-                  adap->dev->udev->descriptor.idProduct == USB_PID_DIBCOM_MOD3001_WARM) {
+       } else if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_DIBCOM &&
+                  le16_to_cpu(adap->dev->udev->descriptor.idProduct) == USB_PID_DIBCOM_MOD3001_WARM) {
                u8 desc;
                dibusb_read_eeprom_byte(adap->dev, 7, &desc);
                if (desc == 2) {
index 2add8c507ec9ecd6cf2d27b7097a9c7e98fe9d2d..1a3df10d6bad5e1c1354874c8d715a7f6e5629c0 100644 (file)
@@ -667,7 +667,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                                obuf[1] = (msg[j].addr << 1);
                                memcpy(obuf + 2, msg[j].buf, msg[j].len);
                                dw210x_op_rw(d->udev,
-                                               udev->descriptor.idProduct ==
+                                               le16_to_cpu(udev->descriptor.idProduct) ==
                                                0x7500 ? 0x92 : 0x90, 0, 0,
                                                obuf, msg[j].len + 2,
                                                DW210X_WRITE_MSG);
@@ -1598,7 +1598,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
        u8 reset16[] = {0, 0, 0, 0, 0, 0, 0};
        const struct firmware *fw;
 
-       switch (dev->descriptor.idProduct) {
+       switch (le16_to_cpu(dev->descriptor.idProduct)) {
        case 0x2101:
                ret = request_firmware(&fw, DW2101_FIRMWARE, &dev->dev);
                if (ret != 0) {
@@ -1641,7 +1641,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
                        ret = -EINVAL;
                }
                /* init registers */
-               switch (dev->descriptor.idProduct) {
+               switch (le16_to_cpu(dev->descriptor.idProduct)) {
                case USB_PID_TEVII_S650:
                        dw2104_properties.rc.core.rc_codes = RC_MAP_TEVII_NEC;
                case USB_PID_DW2104:
@@ -1901,14 +1901,14 @@ static struct dvb_usb_device_properties s6x0_properties = {
        }
 };
 
-struct dvb_usb_device_properties *p1100;
+static struct dvb_usb_device_properties *p1100;
 static struct dvb_usb_device_description d1100 = {
        "Prof 1100 USB ",
        {&dw2102_table[PROF_1100], NULL},
        {NULL},
 };
 
-struct dvb_usb_device_properties *s660;
+static struct dvb_usb_device_properties *s660;
 static struct dvb_usb_device_description d660 = {
        "TeVii S660 USB",
        {&dw2102_table[TEVII_S660], NULL},
@@ -1927,14 +1927,14 @@ static struct dvb_usb_device_description d480_2 = {
        {NULL},
 };
 
-struct dvb_usb_device_properties *p7500;
+static struct dvb_usb_device_properties *p7500;
 static struct dvb_usb_device_description d7500 = {
        "Prof 7500 USB DVB-S2",
        {&dw2102_table[PROF_7500], NULL},
        {NULL},
 };
 
-struct dvb_usb_device_properties *s421;
+static struct dvb_usb_device_properties *s421;
 static struct dvb_usb_device_description d421 = {
        "TeVii S421 PCI",
        {&dw2102_table[TEVII_S421], NULL},
index 16ba90acf5396ff54395497e9180b5f883adfc37..14a2119912ba5246474c6b249a855f5815c34331 100644 (file)
@@ -554,8 +554,8 @@ static int opera1_probe(struct usb_interface *intf,
 {
        struct usb_device *udev = interface_to_usbdev(intf);
 
-       if (udev->descriptor.idProduct == USB_PID_OPERA1_WARM &&
-               udev->descriptor.idVendor == USB_VID_OPERA1 &&
+       if (le16_to_cpu(udev->descriptor.idProduct) == USB_PID_OPERA1_WARM &&
+           le16_to_cpu(udev->descriptor.idVendor) == USB_VID_OPERA1 &&
                opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga-01.fw") != 0
            ) {
                return -EINVAL;
index bdfe8963591cbac6dfe69028ead20993421b118e..d17618fe8f5c80528bb8f6febc202449b5ad2999 100644 (file)
@@ -883,7 +883,7 @@ static int pctv452e_frontend_attach(struct dvb_usb_adapter *a)
        if (!a->fe_adap[0].fe)
                return -ENODEV;
        if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe,
-                                       &a->dev->i2c_adap)) == 0)
+                                       &a->dev->i2c_adap)) == NULL)
                err("Cannot attach lnbp22\n");
 
        id = a->dev->desc->warm_ids[0];
@@ -900,7 +900,7 @@ static int pctv452e_tuner_attach(struct dvb_usb_adapter *a)
        if (!a->fe_adap[0].fe)
                return -ENODEV;
        if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config,
-                                       &a->dev->i2c_adap) == 0) {
+                                       &a->dev->i2c_adap) == NULL) {
                err("%s failed\n", __func__);
                return -ENODEV;
        }
@@ -965,7 +965,7 @@ static struct dvb_usb_device_properties pctv452e_properties = {
                  .cold_ids = { NULL, NULL }, /* this is a warm only device */
                  .warm_ids = { &pctv452e_usb_table[0], NULL }
                },
-               { 0 },
+               { NULL },
        }
 };
 
@@ -1023,7 +1023,7 @@ static struct dvb_usb_device_properties tt_connect_s2_3600_properties = {
                  .cold_ids = { NULL, NULL },
                  .warm_ids = { &pctv452e_usb_table[2], NULL }
                },
-               { 0 },
+               { NULL },
        }
 };
 
index e881ef7b64451060f39627ece2cb79c0f49330f7..957c7ae30efe5f5d724908f9b231e8a0b780e406 100644 (file)
@@ -268,7 +268,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
        nonblock = !!(substream->f_flags & O_NONBLOCK);
        if (nonblock) {
                if (!mutex_trylock(&dev->lock))
-               return -EAGAIN;
+                       return -EAGAIN;
        } else
                mutex_lock(&dev->lock);
 
@@ -893,7 +893,7 @@ static int em28xx_audio_init(struct em28xx *dev)
        static int          devnr;
        int                 err;
 
-       if (!dev->has_alsa_audio) {
+       if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) {
                /* 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) */
@@ -975,7 +975,7 @@ static int em28xx_audio_fini(struct em28xx *dev)
        if (dev == NULL)
                return 0;
 
-       if (!dev->has_alsa_audio) {
+       if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) {
                /* 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) */
@@ -1003,7 +1003,7 @@ static int em28xx_audio_suspend(struct em28xx *dev)
        if (dev == NULL)
                return 0;
 
-       if (!dev->has_alsa_audio)
+       if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR)
                return 0;
 
        em28xx_info("Suspending audio extension");
@@ -1017,7 +1017,7 @@ static int em28xx_audio_resume(struct em28xx *dev)
        if (dev == NULL)
                return 0;
 
-       if (!dev->has_alsa_audio)
+       if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR)
                return 0;
 
        em28xx_info("Resuming audio extension");
index 9da812b8a7861de75722cf5fcfdbe29e3baf39eb..71fa51e7984e462115d0bb7564ea6871a9c012b9 100644 (file)
@@ -2246,7 +2246,7 @@ struct em28xx_board em28xx_boards[] = {
 };
 EXPORT_SYMBOL_GPL(em28xx_boards);
 
-const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
+static const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
 
 /* table of devices that work with this driver */
 struct usb_device_id em28xx_id_table[] = {
@@ -2931,9 +2931,9 @@ static void request_module_async(struct work_struct *work)
 #if defined(CONFIG_MODULES) && defined(MODULE)
        if (dev->has_video)
                request_module("em28xx-v4l");
-       if (dev->has_audio_class)
+       if (dev->usb_audio_type == EM28XX_USB_AUDIO_CLASS)
                request_module("snd-usb-audio");
-       else if (dev->has_alsa_audio)
+       else if (dev->usb_audio_type == EM28XX_USB_AUDIO_VENDOR)
                request_module("em28xx-alsa");
        if (dev->board.has_dvb)
                request_module("em28xx-dvb");
@@ -3098,16 +3098,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
                }
        }
 
-       if (dev->chip_id == CHIP_ID_EM2870 ||
-           dev->chip_id == CHIP_ID_EM2874 ||
-           dev->chip_id == CHIP_ID_EM28174 ||
-           dev->chip_id == CHIP_ID_EM28178) {
-               /* Digital only device - don't load any alsa module */
-               dev->audio_mode.has_audio = false;
-               dev->has_audio_class = false;
-               dev->has_alsa_audio = false;
-       }
-
        if (chip_name != default_chip_name)
                printk(KERN_INFO DRIVER_NAME
                       ": chip ID is %s\n", chip_name);
@@ -3190,7 +3180,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
        struct usb_device *udev;
        struct em28xx *dev = NULL;
        int retval;
-       bool has_audio = false, has_video = false, has_dvb = false;
+       bool has_vendor_audio = false, has_video = false, has_dvb = false;
        int i, nr, try_bulk;
        const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
        char *speed;
@@ -3272,7 +3262,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
                                        break;
                                case 0x83:
                                        if (usb_endpoint_xfer_isoc(e)) {
-                                               has_audio = true;
+                                               has_vendor_audio = true;
                                        } else {
                                                printk(KERN_INFO DRIVER_NAME
                                                ": error: skipping audio endpoint 0x83, because it uses bulk transfers !\n");
@@ -3328,7 +3318,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
                }
        }
 
-       if (!(has_audio || has_video || has_dvb)) {
+       if (!(has_vendor_audio || has_video || has_dvb)) {
                retval = -ENODEV;
                goto err_free;
        }
@@ -3375,26 +3365,27 @@ static int em28xx_usb_probe(struct usb_interface *interface,
        dev->devno = nr;
        dev->model = id->driver_info;
        dev->alt   = -1;
-       dev->is_audio_only = has_audio && !(has_video || has_dvb);
-       dev->has_alsa_audio = has_audio;
-       dev->audio_mode.has_audio = has_audio;
+       dev->is_audio_only = has_vendor_audio && !(has_video || has_dvb);
        dev->has_video = has_video;
        dev->ifnum = ifnum;
 
-       /* Checks if audio is provided by some interface */
+       if (has_vendor_audio) {
+               printk(KERN_INFO DRIVER_NAME ": Audio interface %i found %s\n",
+                      ifnum, "(Vendor Class)");
+               dev->usb_audio_type = EM28XX_USB_AUDIO_VENDOR;
+       }
+       /* Checks if audio is provided by a USB Audio Class interface */
        for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
                struct usb_interface *uif = udev->config->interface[i];
                if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
-                       dev->has_audio_class = 1;
+                       if (has_vendor_audio)
+                               em28xx_err("em28xx: device seems to have vendor AND usb audio class interfaces !\n"
+                                          "\t\tThe vendor interface will be ignored. Please contact the developers <linux-media@vger.kernel.org>\n");
+                       dev->usb_audio_type = EM28XX_USB_AUDIO_CLASS;
                        break;
                }
        }
 
-       if (has_audio)
-               printk(KERN_INFO DRIVER_NAME
-                      ": Audio interface %i found %s\n",
-                      ifnum,
-                      dev->has_audio_class ? "(USB Audio Class)" : "(Vendor Class)");
        if (has_video)
                printk(KERN_INFO DRIVER_NAME
                       ": Video interface %i found:%s%s\n",
index 523d7e92bf47b7974515a1d9acd2fc19ab131b51..b5e52fe7957aff0d047abb1f89caab275fcb1afd 100644 (file)
@@ -279,7 +279,7 @@ int em28xx_read_ac97(struct em28xx *dev, u8 reg)
 {
        int ret;
        u8 addr = (reg & 0x7f) | 0x80;
-       u16 val;
+       __le16 val;
 
        ret = em28xx_is_ac97_ready(dev);
        if (ret < 0)
@@ -433,7 +433,7 @@ int em28xx_audio_analog_set(struct em28xx *dev)
        int ret, i;
        u8 xclk;
 
-       if (!dev->audio_mode.has_audio)
+       if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE)
                return 0;
 
        /* It is assumed that all devices use master volume for output.
@@ -505,37 +505,48 @@ int em28xx_audio_setup(struct em28xx *dev)
 {
        int vid1, vid2, feat, cfg;
        u32 vid;
+       u8 i2s_samplerates;
 
-       if (!dev->audio_mode.has_audio)
+       if (dev->chip_id == CHIP_ID_EM2870 ||
+           dev->chip_id == CHIP_ID_EM2874 ||
+           dev->chip_id == CHIP_ID_EM28174 ||
+           dev->chip_id == CHIP_ID_EM28178) {
+               /* Digital only device - don't load any alsa module */
+               dev->int_audio_type = EM28XX_INT_AUDIO_NONE;
+               dev->usb_audio_type = EM28XX_USB_AUDIO_NONE;
                return 0;
+       }
 
        /* See how this device is configured */
        cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
        em28xx_info("Config register raw data: 0x%02x\n", cfg);
-       if (cfg < 0) {
-               /* Register read error?  */
-               cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */
+       if (cfg < 0) { /* Register read error */
+               /* Be conservative */
+               dev->int_audio_type = EM28XX_INT_AUDIO_AC97;
        } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) {
                /* The device doesn't have vendor audio at all */
-               dev->has_alsa_audio = false;
-               dev->audio_mode.has_audio = false;
+               dev->int_audio_type = EM28XX_INT_AUDIO_NONE;
+               dev->usb_audio_type = EM28XX_USB_AUDIO_NONE;
                return 0;
        } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) {
+               dev->int_audio_type = EM28XX_INT_AUDIO_I2S;
                if (dev->chip_id < CHIP_ID_EM2860 &&
                    (cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
                    EM2820_CHIPCFG_I2S_1_SAMPRATE)
-                       dev->audio_mode.i2s_samplerates = 1;
+                       i2s_samplerates = 1;
                else if (dev->chip_id >= CHIP_ID_EM2860 &&
                         (cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
                         EM2860_CHIPCFG_I2S_5_SAMPRATES)
-                       dev->audio_mode.i2s_samplerates = 5;
+                       i2s_samplerates = 5;
                else
-                       dev->audio_mode.i2s_samplerates = 3;
+                       i2s_samplerates = 3;
                em28xx_info("I2S Audio (%d sample rate(s))\n",
-                                              dev->audio_mode.i2s_samplerates);
+                                              i2s_samplerates);
                /* Skip the code that does AC97 vendor detection */
                dev->audio_mode.ac97 = EM28XX_NO_AC97;
                goto init_audio;
+       } else {
+               dev->int_audio_type = EM28XX_INT_AUDIO_AC97;
        }
 
        dev->audio_mode.ac97 = EM28XX_AC97_OTHER;
@@ -549,8 +560,9 @@ int em28xx_audio_setup(struct em28xx *dev)
                 */
                em28xx_warn("AC97 chip type couldn't be determined\n");
                dev->audio_mode.ac97 = EM28XX_NO_AC97;
-               dev->has_alsa_audio = false;
-               dev->audio_mode.has_audio = false;
+               if (dev->usb_audio_type == EM28XX_USB_AUDIO_VENDOR)
+                       dev->usb_audio_type = EM28XX_USB_AUDIO_NONE;
+               dev->int_audio_type = EM28XX_INT_AUDIO_NONE;
                goto init_audio;
        }
 
@@ -559,15 +571,12 @@ int em28xx_audio_setup(struct em28xx *dev)
                goto init_audio;
 
        vid = vid1 << 16 | vid2;
-
-       dev->audio_mode.ac97_vendor_id = vid;
        em28xx_warn("AC97 vendor ID = 0x%08x\n", vid);
 
        feat = em28xx_read_ac97(dev, AC97_RESET);
        if (feat < 0)
                goto init_audio;
 
-       dev->audio_mode.ac97_feat = feat;
        em28xx_warn("AC97 features = 0x%04x\n", feat);
 
        /* Try to identify what audio processor we have */
@@ -586,8 +595,8 @@ init_audio:
                em28xx_info("Empia 202 AC97 audio processor detected\n");
                break;
        case EM28XX_AC97_SIGMATEL:
-               em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n",
-                           dev->audio_mode.ac97_vendor_id & 0xff);
+               em28xx_info("Sigmatel audio processor detected (stac 97%02x)\n",
+                           vid & 0xff);
                break;
        case EM28XX_AC97_OTHER:
                em28xx_warn("Unknown AC97 audio processor detected!\n");
index 3a3e243edf89df29982f632cb06e0f53af6ad515..9682c52d67d17b3be3d52e9fc9f878f73d2aed9d 100644 (file)
@@ -373,7 +373,6 @@ static struct tda18271_config kworld_ub435q_v2_config = {
 };
 
 static struct tda18212_config kworld_ub435q_v3_config = {
-       .i2c_address    = 0x60,
        .if_atsc_vsb    = 3600,
        .if_atsc_qam    = 3600,
 };
@@ -856,7 +855,9 @@ static const struct m88ds3103_config pctv_461e_m88ds3103_config = {
        .clock = 27000000,
        .i2c_wr_max = 33,
        .clock_out = 0,
-       .ts_mode = M88DS3103_TS_PARALLEL_16,
+       .ts_mode = M88DS3103_TS_PARALLEL,
+       .ts_clk = 16000,
+       .ts_clk_pol = 1,
        .agc = 0x99,
 };
 
@@ -1435,6 +1436,15 @@ static int em28xx_dvb_init(struct em28xx *dev)
                }
                break;
        case EM2874_BOARD_KWORLD_UB435Q_V3:
+       {
+               struct i2c_client *client;
+               struct i2c_adapter *adapter = &dev->i2c_adap[dev->def_i2c_bus];
+               struct i2c_board_info board_info = {
+                       .type = "tda18212",
+                       .addr = 0x60,
+                       .platform_data = &kworld_ub435q_v3_config,
+               };
+
                dvb->fe[0] = dvb_attach(lgdt3305_attach,
                                        &em2874_lgdt3305_nogate_dev,
                                        &dev->i2c_adap[dev->def_i2c_bus]);
@@ -1443,14 +1453,26 @@ static int em28xx_dvb_init(struct em28xx *dev)
                        goto out_free;
                }
 
-               /* Attach the demodulator. */
-               if (!dvb_attach(tda18212_attach, dvb->fe[0],
-                               &dev->i2c_adap[dev->def_i2c_bus],
-                               &kworld_ub435q_v3_config)) {
-                       result = -EINVAL;
+               /* attach tuner */
+               kworld_ub435q_v3_config.fe = dvb->fe[0];
+               request_module("tda18212");
+               client = i2c_new_device(adapter, &board_info);
+               if (client == NULL || client->dev.driver == NULL) {
+                       dvb_frontend_detach(dvb->fe[0]);
+                       result = -ENODEV;
                        goto out_free;
                }
+
+               if (!try_module_get(client->dev.driver->owner)) {
+                       i2c_unregister_device(client);
+                       dvb_frontend_detach(dvb->fe[0]);
+                       result = -ENODEV;
+                       goto out_free;
+               }
+
+               dvb->i2c_client_tuner = client;
                break;
+       }
        case EM2874_BOARD_PCTV_HD_MINI_80E:
                dvb->fe[0] = dvb_attach(drx39xxj_attach, &dev->i2c_adap[dev->def_i2c_bus]);
                if (dvb->fe[0] != NULL) {
@@ -1533,6 +1555,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
                        /* attach demod */
                        si2168_config.i2c_adapter = &adapter;
                        si2168_config.fe = &dvb->fe[0];
+                       si2168_config.ts_mode = SI2168_TS_PARALLEL;
                        memset(&info, 0, sizeof(struct i2c_board_info));
                        strlcpy(info.type, "si2168", I2C_NAME_SIZE);
                        info.addr = 0x64;
index ed843bd221eaec8a9bb3307d965a0b49275e50f1..581f6dad4ca9deef0fa5a0820372238b8a4e3bbd 100644 (file)
@@ -71,8 +71,7 @@ struct em28xx_IR {
        unsigned int last_readcount;
        u64 rc_type;
 
-       /* i2c slave address of external device (if used) */
-       u16 i2c_dev_addr;
+       struct i2c_client *i2c_client;
 
        int  (*get_key_i2c)(struct i2c_client *ir, enum rc_type *protocol, u32 *scancode);
        int  (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *);
@@ -294,16 +293,11 @@ static int em2874_polling_getkey(struct em28xx_IR *ir,
 
 static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir)
 {
-       struct em28xx *dev = ir->dev;
        static u32 scancode;
        enum rc_type protocol;
        int rc;
-       struct i2c_client client;
-
-       client.adapter = &ir->dev->i2c_adap[dev->def_i2c_bus];
-       client.addr = ir->i2c_dev_addr;
 
-       rc = ir->get_key_i2c(&client, &protocol, &scancode);
+       rc = ir->get_key_i2c(ir->i2c_client, &protocol, &scancode);
        if (rc < 0) {
                dprintk("ir->get_key_i2c() failed: %d\n", rc);
                return rc;
@@ -361,7 +355,7 @@ static void em28xx_ir_work(struct work_struct *work)
 {
        struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work);
 
-       if (ir->i2c_dev_addr) /* external i2c device */
+       if (ir->i2c_client) /* external i2c device */
                em28xx_i2c_ir_handle_key(ir);
        else /* internal device */
                em28xx_ir_handle_key(ir);
@@ -609,17 +603,17 @@ static int em28xx_register_snapshot_button(struct em28xx *dev)
 static void em28xx_init_buttons(struct em28xx *dev)
 {
        u8  i = 0, j = 0;
-       bool addr_new = 0;
+       bool addr_new = false;
 
        dev->button_polling_interval = EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL;
        while (dev->board.buttons[i].role >= 0 &&
                         dev->board.buttons[i].role < EM28XX_NUM_BUTTON_ROLES) {
                struct em28xx_button *button = &dev->board.buttons[i];
                /* Check if polling address is already on the list */
-               addr_new = 1;
+               addr_new = true;
                for (j = 0; j < dev->num_button_polling_addresses; j++) {
                        if (button->reg_r == dev->button_polling_addresses[j]) {
-                               addr_new = 0;
+                               addr_new = false;
                                break;
                        }
                }
@@ -756,7 +750,13 @@ static int em28xx_ir_init(struct em28xx *dev)
                        goto error;
                }
 
-               ir->i2c_dev_addr = i2c_rc_dev_addr;
+               ir->i2c_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+               if (!ir->i2c_client)
+                       goto error;
+               ir->i2c_client->adapter = &ir->dev->i2c_adap[dev->def_i2c_bus];
+               ir->i2c_client->addr = i2c_rc_dev_addr;
+               ir->i2c_client->flags = 0;
+               /* NOTE: all other fields of i2c_client are unused */
        } else {        /* internal device */
                switch (dev->chip_id) {
                case CHIP_ID_EM2860:
@@ -815,6 +815,7 @@ static int em28xx_ir_init(struct em28xx *dev)
        return 0;
 
 error:
+       kfree(ir->i2c_client);
        dev->ir = NULL;
        rc_free_device(rc);
        kfree(ir);
@@ -841,6 +842,8 @@ static int em28xx_ir_fini(struct em28xx *dev)
        if (ir->rc)
                rc_unregister_device(ir->rc);
 
+       kfree(ir->i2c_client);
+
        /* done */
        kfree(ir);
        dev->ir = NULL;
index 6d7f657f6f55cad6e19a72a1b0ff0477751a909c..34ee1e03a73283b3cc1e4100140ee343984a7b0d 100644 (file)
 #include "em28xx.h"
 #include "em28xx-v4l.h"
 
-static unsigned int vbibufs = 5;
-module_param(vbibufs, int, 0644);
-MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
-
-static unsigned int vbi_debug;
-module_param(vbi_debug, int, 0644);
-MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
-
-#define dprintk(level, fmt, arg...)    if (vbi_debug >= level) \
-       printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg)
-
 /* ------------------------------------------------------------------ */
 
 static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
index 29abc379551e5d6f79483520a892006dca848ed7..03d5ece0319c63e748f669635889769fc64ea210 100644 (file)
@@ -435,7 +435,10 @@ static inline void finish_buffer(struct em28xx *dev,
        em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field);
 
        buf->vb.v4l2_buf.sequence = dev->v4l2->field_count++;
-       buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
+       if (dev->v4l2->progressive)
+               buf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
+       else
+               buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
        v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
 
        vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
@@ -478,7 +481,7 @@ static void em28xx_copy_video(struct em28xx *dev,
        lencopy = lencopy > remain ? remain : lencopy;
 
        if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) {
-               em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
+               em28xx_isocdbg("Overflow of %zu bytes past buffer end (1)\n",
                              ((char *)startwrite + lencopy) -
                              ((char *)buf->vb_buf + buf->length));
                remain = (char *)buf->vb_buf + buf->length -
@@ -504,7 +507,7 @@ static void em28xx_copy_video(struct em28xx *dev,
 
                if ((char *)startwrite + lencopy > (char *)buf->vb_buf +
                    buf->length) {
-                       em28xx_isocdbg("Overflow of %zi bytes past buffer end"
+                       em28xx_isocdbg("Overflow of %zu bytes past buffer end"
                                       "(2)\n",
                                       ((char *)startwrite + lencopy) -
                                       ((char *)buf->vb_buf + buf->length));
@@ -718,7 +721,7 @@ static inline void process_frame_data_em25xx(struct em28xx *dev,
        struct em28xx_buffer    *buf = dev->usb_ctl.vid_buf;
        struct em28xx_dmaqueue  *dmaq = &dev->vidq;
        struct em28xx_v4l2      *v4l2 = dev->v4l2;
-       bool frame_end = 0;
+       bool frame_end = false;
 
        /* Check for header */
        /* NOTE: at least with bulk transfers, only the first packet
@@ -994,13 +997,16 @@ static void em28xx_stop_streaming(struct vb2_queue *vq)
        }
 
        spin_lock_irqsave(&dev->slock, flags);
+       if (dev->usb_ctl.vid_buf != NULL) {
+               vb2_buffer_done(&dev->usb_ctl.vid_buf->vb, VB2_BUF_STATE_ERROR);
+               dev->usb_ctl.vid_buf = NULL;
+       }
        while (!list_empty(&vidq->active)) {
                struct em28xx_buffer *buf;
                buf = list_entry(vidq->active.next, struct em28xx_buffer, list);
                list_del(&buf->list);
                vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
        }
-       dev->usb_ctl.vid_buf = NULL;
        spin_unlock_irqrestore(&dev->slock, flags);
 }
 
@@ -1021,13 +1027,16 @@ void em28xx_stop_vbi_streaming(struct vb2_queue *vq)
        }
 
        spin_lock_irqsave(&dev->slock, flags);
+       if (dev->usb_ctl.vbi_buf != NULL) {
+               vb2_buffer_done(&dev->usb_ctl.vbi_buf->vb, VB2_BUF_STATE_ERROR);
+               dev->usb_ctl.vbi_buf = NULL;
+       }
        while (!list_empty(&vbiq->active)) {
                struct em28xx_buffer *buf;
                buf = list_entry(vbiq->active.next, struct em28xx_buffer, list);
                list_del(&buf->list);
                vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
        }
-       dev->usb_ctl.vbi_buf = NULL;
        spin_unlock_irqrestore(&dev->slock, flags);
 }
 
@@ -1711,7 +1720,7 @@ static int vidioc_querycap(struct file *file, void  *priv,
        else
                cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE;
 
-       if (dev->audio_mode.has_audio)
+       if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE)
                cap->device_caps |= V4L2_CAP_AUDIO;
 
        if (dev->tuner_type != TUNER_ABSENT)
@@ -2296,7 +2305,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
        v4l2->v4l2_dev.ctrl_handler = hdl;
 
        if (dev->board.is_webcam)
-               v4l2->progressive = 1;
+               v4l2->progressive = true;
 
        /*
         * Default format, used for tvp5150 or saa711x output formats
@@ -2502,7 +2511,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
                v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_FREQUENCY);
                v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_FREQUENCY);
        }
-       if (!dev->audio_mode.has_audio) {
+       if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) {
                v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_AUDIO);
                v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_AUDIO);
        }
@@ -2532,7 +2541,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
                        v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_FREQUENCY);
                        v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_FREQUENCY);
                }
-               if (!dev->audio_mode.has_audio) {
+               if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) {
                        v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_AUDIO);
                        v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_AUDIO);
                }
index 4360338e7b310a2ab985eca427ef34124ddf15a5..a21a7463b557913ff0c97eb41f203a1f1d95c930 100644 (file)
@@ -309,13 +309,18 @@ enum em28xx_ac97_mode {
 
 struct em28xx_audio_mode {
        enum em28xx_ac97_mode ac97;
+};
 
-       u16 ac97_feat;
-       u32 ac97_vendor_id;
-
-       unsigned int has_audio:1;
+enum em28xx_int_audio_type {
+       EM28XX_INT_AUDIO_NONE = 0,
+       EM28XX_INT_AUDIO_AC97,
+       EM28XX_INT_AUDIO_I2S,
+};
 
-       u8 i2s_samplerates;
+enum em28xx_usb_audio_type {
+       EM28XX_USB_AUDIO_NONE = 0,
+       EM28XX_USB_AUDIO_CLASS,
+       EM28XX_USB_AUDIO_VENDOR,
 };
 
 /* em28xx has two audio inputs: tuner and line in.
@@ -608,9 +613,9 @@ struct em28xx {
        unsigned int is_em25xx:1;       /* em25xx/em276x/7x/8x family bridge */
        unsigned char disconnected:1;   /* device has been diconnected */
        unsigned int has_video:1;
-       unsigned int has_audio_class:1;
-       unsigned int has_alsa_audio:1;
        unsigned int is_audio_only:1;
+       enum em28xx_int_audio_type int_audio_type;
+       enum em28xx_usb_audio_type usb_audio_type;
 
        struct em28xx_board board;
 
index ece27ece81158f0c17b1125f3a8e27616082a45b..3f986e1178ce4fa5f821eadf489b3e5f15ff2e33 100644 (file)
@@ -696,7 +696,7 @@ static int go7007_usb_ezusb_write_interrupt(struct go7007 *go,
                                sizeof(status_reg), timeout);
                if (r < 0)
                        break;
-               status_reg = le16_to_cpu(*((u16 *)go->usb_buf));
+               status_reg = le16_to_cpu(*((__le16 *)go->usb_buf));
                if (!(status_reg & 0x0010))
                        break;
                msleep(10);
@@ -751,7 +751,7 @@ static int go7007_usb_onboard_write_interrupt(struct go7007 *go,
 static void go7007_usb_readinterrupt_complete(struct urb *urb)
 {
        struct go7007 *go = (struct go7007 *)urb->context;
-       u16 *regs = (u16 *)urb->transfer_buffer;
+       __le16 *regs = (__le16 *)urb->transfer_buffer;
        int status = urb->status;
 
        if (status) {
index e8cf23c91cefc0b570391f5146b831733f5d14be..43d65057a5fe657464d5e7709c769e9fa4b93115 100644 (file)
@@ -876,9 +876,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
                ep_tb[0].alt = gspca_dev->alt;
                alt_idx = 1;
        } else {
-
-       /* else, compute the minimum bandwidth
-        * and build the endpoint table */
+               /* else, compute the minimum bandwidth
+                * and build the endpoint table */
                alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb);
                if (alt_idx <= 0) {
                        pr_err("no transfer endpoint found\n");
index f06253cd746901bfbeca0521e42c465db4241fa9..d39adf90303b9a4371fd3ed878705ae7754535d8 100644 (file)
@@ -235,6 +235,6 @@ int gspca_resume(struct usb_interface *intf);
 int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum,
        int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
 int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev,
-        int avg_lum, int desired_avg_lum, int deadzone);
+       int avg_lum, int desired_avg_lum, int deadzone);
 
 #endif /* GSPCAV2_H */
index 45bc1f51c5d8c14ed0fe6d6dd71690d7b990c530..3cb30a37d6ac90818521dc646b48b9f9bfcabb95 100644 (file)
@@ -51,9 +51,9 @@ struct pkt_hdr {
 
 struct cam_hdr {
        uint8_t magic[2];
-       uint16_t len;
-       uint16_t cmd;
-       uint16_t tag;
+       __le16 len;
+       __le16 cmd;
+       __le16 tag;
 };
 
 /* specific webcam descriptor */
@@ -188,9 +188,9 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
                       rhdr->tag, chdr->tag);
                return -1;
        }
-       if (cpu_to_le16(rhdr->len) != (actual_len/2)) {
+       if (le16_to_cpu(rhdr->len) != (actual_len/2)) {
                pr_err("send_cmd: Bad len %04x != %04x\n",
-                      cpu_to_le16(rhdr->len), (int)(actual_len/2));
+                      le16_to_cpu(rhdr->len), (int)(actual_len/2));
                return -1;
        }
 
@@ -211,7 +211,7 @@ static int write_register(struct gspca_dev *gspca_dev, uint16_t reg,
                        uint16_t data)
 {
        uint16_t reply[2];
-       uint16_t cmd[2];
+       __le16 cmd[2];
        int res;
 
        cmd[0] = cpu_to_le16(reg);
index 41a9a892f79c546c4a6b54edbee1ac09408897f6..d0ee899584a9f1188d44613ec6f21228a0e94e7a 100644 (file)
@@ -1297,7 +1297,7 @@ static void set_cmatrix(struct gspca_dev *gspca_dev,
        s32 hue_coord, hue_index = 180 + hue;
        u8 cmatrix[21];
 
-       memset(cmatrix, 0, sizeof cmatrix);
+       memset(cmatrix, 0, sizeof(cmatrix));
        cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26;
        cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
        cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
@@ -1787,8 +1787,9 @@ static int sd_init(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
        int i;
        u8 value;
-       u8 i2c_init[9] =
-               {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03};
+       u8 i2c_init[9] = {
+               0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
+       };
 
        for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
                value = bridge_init[i][1];
@@ -2242,8 +2243,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int avg_lum, is_jpeg;
-       static const u8 frame_header[] =
-               {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
+       static const u8 frame_header[] = {
+               0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96
+       };
 
        is_jpeg = (sd->fmt & 0x03) == 0;
        if (len >= 64 && memcmp(data, frame_header, 6) == 0) {
diff --git a/drivers/media/usb/hackrf/Kconfig b/drivers/media/usb/hackrf/Kconfig
new file mode 100644 (file)
index 0000000..937e6f5
--- /dev/null
@@ -0,0 +1,10 @@
+config USB_HACKRF
+       tristate "HackRF"
+       depends on VIDEO_V4L2
+       select VIDEOBUF2_VMALLOC
+       ---help---
+         This is a video4linux2 driver for HackRF SDR device.
+
+         To compile this driver as a module, choose M here: the
+         module will be called hackrf
+
diff --git a/drivers/media/usb/hackrf/Makefile b/drivers/media/usb/hackrf/Makefile
new file mode 100644 (file)
index 0000000..73064a2
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_USB_HACKRF)              += hackrf.o
diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c
new file mode 100644 (file)
index 0000000..328b5ba
--- /dev/null
@@ -0,0 +1,1142 @@
+/*
+ * HackRF driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* HackRF USB API commands (from HackRF Library) */
+enum {
+       CMD_SET_TRANSCEIVER_MODE           = 0x01,
+       CMD_SAMPLE_RATE_SET                = 0x06,
+       CMD_BASEBAND_FILTER_BANDWIDTH_SET  = 0x07,
+       CMD_BOARD_ID_READ                  = 0x0e,
+       CMD_VERSION_STRING_READ            = 0x0f,
+       CMD_SET_FREQ                       = 0x10,
+       CMD_SET_LNA_GAIN                   = 0x13,
+       CMD_SET_VGA_GAIN                   = 0x14,
+};
+
+/*
+ *       bEndpointAddress     0x81  EP 1 IN
+ *         Transfer Type            Bulk
+ *       wMaxPacketSize     0x0200  1x 512 bytes
+ */
+#define MAX_BULK_BUFS            (6)
+#define BULK_BUFFER_SIZE         (128 * 512)
+
+static const struct v4l2_frequency_band bands_adc[] = {
+       {
+               .tuner = 0,
+               .type = V4L2_TUNER_ADC,
+               .index = 0,
+               .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow   =   200000,
+               .rangehigh  = 24000000,
+       },
+};
+
+static const struct v4l2_frequency_band bands_rf[] = {
+       {
+               .tuner = 1,
+               .type = V4L2_TUNER_RF,
+               .index = 0,
+               .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow   =          1,
+               .rangehigh  = 4294967294LL, /* max u32, hw goes over 7GHz */
+       },
+};
+
+/* stream formats */
+struct hackrf_format {
+       char    *name;
+       u32     pixelformat;
+       u32     buffersize;
+};
+
+/* format descriptions for capture and preview */
+static struct hackrf_format formats[] = {
+       {
+               .name           = "Complex S8",
+               .pixelformat    = V4L2_SDR_FMT_CS8,
+               .buffersize     = BULK_BUFFER_SIZE,
+       },
+};
+
+static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
+
+/* intermediate buffers with raw data from the USB device */
+struct hackrf_frame_buf {
+       struct vb2_buffer vb;   /* common v4l buffer stuff -- must be first */
+       struct list_head list;
+};
+
+struct hackrf_dev {
+#define POWER_ON           (1 << 1)
+#define URB_BUF            (1 << 2)
+#define USB_STATE_URB_BUF  (1 << 3)
+       unsigned long flags;
+
+       struct device *dev;
+       struct usb_device *udev;
+       struct video_device vdev;
+       struct v4l2_device v4l2_dev;
+
+       /* videobuf2 queue and queued buffers list */
+       struct vb2_queue vb_queue;
+       struct list_head queued_bufs;
+       spinlock_t queued_bufs_lock; /* Protects queued_bufs */
+       unsigned sequence;           /* Buffer sequence counter */
+       unsigned int vb_full;        /* vb is full and packets dropped */
+
+       /* Note if taking both locks v4l2_lock must always be locked first! */
+       struct mutex v4l2_lock;      /* Protects everything else */
+       struct mutex vb_queue_lock;  /* Protects vb_queue */
+
+       struct urb     *urb_list[MAX_BULK_BUFS];
+       int            buf_num;
+       unsigned long  buf_size;
+       u8             *buf_list[MAX_BULK_BUFS];
+       dma_addr_t     dma_addr[MAX_BULK_BUFS];
+       int            urbs_initialized;
+       int            urbs_submitted;
+
+       /* USB control message buffer */
+       #define BUF_SIZE 24
+       u8 buf[BUF_SIZE];
+
+       /* Current configuration */
+       unsigned int f_adc;
+       unsigned int f_rf;
+       u32 pixelformat;
+       u32 buffersize;
+
+       /* Controls */
+       struct v4l2_ctrl_handler hdl;
+       struct v4l2_ctrl *bandwidth_auto;
+       struct v4l2_ctrl *bandwidth;
+       struct v4l2_ctrl *lna_gain;
+       struct v4l2_ctrl *if_gain;
+
+       /* Sample rate calc */
+       unsigned long jiffies_next;
+       unsigned int sample;
+       unsigned int sample_measured;
+};
+
+#define hackrf_dbg_usb_control_msg(_dev, _r, _t, _v, _i, _b, _l) { \
+       char *_direction; \
+       if (_t & USB_DIR_IN) \
+               _direction = "<<<"; \
+       else \
+               _direction = ">>>"; \
+       dev_dbg(_dev, "%02x %02x %02x %02x %02x %02x %02x %02x %s %*ph\n", \
+                       _t, _r, _v & 0xff, _v >> 8, _i & 0xff, \
+                       _i >> 8, _l & 0xff, _l >> 8, _direction, _l, _b); \
+}
+
+/* execute firmware command */
+static int hackrf_ctrl_msg(struct hackrf_dev *dev, u8 request, u16 value,
+               u16 index, u8 *data, u16 size)
+{
+       int ret;
+       unsigned int pipe;
+       u8 requesttype;
+
+       switch (request) {
+       case CMD_SET_TRANSCEIVER_MODE:
+       case CMD_SET_FREQ:
+       case CMD_SAMPLE_RATE_SET:
+       case CMD_BASEBAND_FILTER_BANDWIDTH_SET:
+               pipe = usb_sndctrlpipe(dev->udev, 0);
+               requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+               break;
+       case CMD_BOARD_ID_READ:
+       case CMD_VERSION_STRING_READ:
+       case CMD_SET_LNA_GAIN:
+       case CMD_SET_VGA_GAIN:
+               pipe = usb_rcvctrlpipe(dev->udev, 0);
+               requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+               break;
+       default:
+               dev_err(dev->dev, "Unknown command %02x\n", request);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* write request */
+       if (!(requesttype & USB_DIR_IN))
+               memcpy(dev->buf, data, size);
+
+       ret = usb_control_msg(dev->udev, pipe, request, requesttype, value,
+                       index, dev->buf, size, 1000);
+       hackrf_dbg_usb_control_msg(dev->dev, request, requesttype, value,
+                       index, dev->buf, size);
+       if (ret < 0) {
+               dev_err(dev->dev, "usb_control_msg() failed %d request %02x\n",
+                               ret, request);
+               goto err;
+       }
+
+       /* read request */
+       if (requesttype & USB_DIR_IN)
+               memcpy(data, dev->buf, size);
+
+       return 0;
+err:
+       return ret;
+}
+
+/* Private functions */
+static struct hackrf_frame_buf *hackrf_get_next_fill_buf(struct hackrf_dev *dev)
+{
+       unsigned long flags;
+       struct hackrf_frame_buf *buf = NULL;
+
+       spin_lock_irqsave(&dev->queued_bufs_lock, flags);
+       if (list_empty(&dev->queued_bufs))
+               goto leave;
+
+       buf = list_entry(dev->queued_bufs.next, struct hackrf_frame_buf, list);
+       list_del(&buf->list);
+leave:
+       spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
+       return buf;
+}
+
+static unsigned int hackrf_convert_stream(struct hackrf_dev *dev,
+               void *dst, void *src, unsigned int src_len)
+{
+       memcpy(dst, src, src_len);
+
+       /* calculate sample rate and output it in 10 seconds intervals */
+       if (unlikely(time_is_before_jiffies(dev->jiffies_next))) {
+               #define MSECS 10000UL
+               unsigned int msecs = jiffies_to_msecs(jiffies -
+                               dev->jiffies_next + msecs_to_jiffies(MSECS));
+               unsigned int samples = dev->sample - dev->sample_measured;
+
+               dev->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
+               dev->sample_measured = dev->sample;
+               dev_dbg(dev->dev, "slen=%u samples=%u msecs=%u sample rate=%lu\n",
+                               src_len, samples, msecs,
+                               samples * 1000UL / msecs);
+       }
+
+       /* total number of samples */
+       dev->sample += src_len / 2;
+
+       return src_len;
+}
+
+/*
+ * This gets called for the bulk stream pipe. This is done in interrupt
+ * time, so it has to be fast, not crash, and not stall. Neat.
+ */
+static void hackrf_urb_complete(struct urb *urb)
+{
+       struct hackrf_dev *dev = urb->context;
+       struct hackrf_frame_buf *fbuf;
+
+       dev_dbg_ratelimited(dev->dev, "status=%d length=%d/%d errors=%d\n",
+                       urb->status, urb->actual_length,
+                       urb->transfer_buffer_length, urb->error_count);
+
+       switch (urb->status) {
+       case 0:             /* success */
+       case -ETIMEDOUT:    /* NAK */
+               break;
+       case -ECONNRESET:   /* kill */
+       case -ENOENT:
+       case -ESHUTDOWN:
+               return;
+       default:            /* error */
+               dev_err_ratelimited(dev->dev, "URB failed %d\n", urb->status);
+               break;
+       }
+
+       if (likely(urb->actual_length > 0)) {
+               void *ptr;
+               unsigned int len;
+               /* get free framebuffer */
+               fbuf = hackrf_get_next_fill_buf(dev);
+               if (unlikely(fbuf == NULL)) {
+                       dev->vb_full++;
+                       dev_notice_ratelimited(dev->dev,
+                                       "videobuf is full, %d packets dropped\n",
+                                       dev->vb_full);
+                       goto skip;
+               }
+
+               /* fill framebuffer */
+               ptr = vb2_plane_vaddr(&fbuf->vb, 0);
+               len = hackrf_convert_stream(dev, ptr, urb->transfer_buffer,
+                               urb->actual_length);
+               vb2_set_plane_payload(&fbuf->vb, 0, len);
+               v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp);
+               fbuf->vb.v4l2_buf.sequence = dev->sequence++;
+               vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
+       }
+skip:
+       usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int hackrf_kill_urbs(struct hackrf_dev *dev)
+{
+       int i;
+
+       for (i = dev->urbs_submitted - 1; i >= 0; i--) {
+               dev_dbg(dev->dev, "kill urb=%d\n", i);
+               /* stop the URB */
+               usb_kill_urb(dev->urb_list[i]);
+       }
+       dev->urbs_submitted = 0;
+
+       return 0;
+}
+
+static int hackrf_submit_urbs(struct hackrf_dev *dev)
+{
+       int i, ret;
+
+       for (i = 0; i < dev->urbs_initialized; i++) {
+               dev_dbg(dev->dev, "submit urb=%d\n", i);
+               ret = usb_submit_urb(dev->urb_list[i], GFP_ATOMIC);
+               if (ret) {
+                       dev_err(dev->dev, "Could not submit URB no. %d - get them all back\n",
+                                       i);
+                       hackrf_kill_urbs(dev);
+                       return ret;
+               }
+               dev->urbs_submitted++;
+       }
+
+       return 0;
+}
+
+static int hackrf_free_stream_bufs(struct hackrf_dev *dev)
+{
+       if (dev->flags & USB_STATE_URB_BUF) {
+               while (dev->buf_num) {
+                       dev->buf_num--;
+                       dev_dbg(dev->dev, "free buf=%d\n", dev->buf_num);
+                       usb_free_coherent(dev->udev, dev->buf_size,
+                                         dev->buf_list[dev->buf_num],
+                                         dev->dma_addr[dev->buf_num]);
+               }
+       }
+       dev->flags &= ~USB_STATE_URB_BUF;
+
+       return 0;
+}
+
+static int hackrf_alloc_stream_bufs(struct hackrf_dev *dev)
+{
+       dev->buf_num = 0;
+       dev->buf_size = BULK_BUFFER_SIZE;
+
+       dev_dbg(dev->dev, "all in all I will use %u bytes for streaming\n",
+                       MAX_BULK_BUFS * BULK_BUFFER_SIZE);
+
+       for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) {
+               dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev,
+                               BULK_BUFFER_SIZE, GFP_ATOMIC,
+                               &dev->dma_addr[dev->buf_num]);
+               if (!dev->buf_list[dev->buf_num]) {
+                       dev_dbg(dev->dev, "alloc buf=%d failed\n",
+                                       dev->buf_num);
+                       hackrf_free_stream_bufs(dev);
+                       return -ENOMEM;
+               }
+
+               dev_dbg(dev->dev, "alloc buf=%d %p (dma %llu)\n", dev->buf_num,
+                               dev->buf_list[dev->buf_num],
+                               (long long)dev->dma_addr[dev->buf_num]);
+               dev->flags |= USB_STATE_URB_BUF;
+       }
+
+       return 0;
+}
+
+static int hackrf_free_urbs(struct hackrf_dev *dev)
+{
+       int i;
+
+       hackrf_kill_urbs(dev);
+
+       for (i = dev->urbs_initialized - 1; i >= 0; i--) {
+               if (dev->urb_list[i]) {
+                       dev_dbg(dev->dev, "free urb=%d\n", i);
+                       /* free the URBs */
+                       usb_free_urb(dev->urb_list[i]);
+               }
+       }
+       dev->urbs_initialized = 0;
+
+       return 0;
+}
+
+static int hackrf_alloc_urbs(struct hackrf_dev *dev)
+{
+       int i, j;
+
+       /* allocate the URBs */
+       for (i = 0; i < MAX_BULK_BUFS; i++) {
+               dev_dbg(dev->dev, "alloc urb=%d\n", i);
+               dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
+               if (!dev->urb_list[i]) {
+                       dev_dbg(dev->dev, "failed\n");
+                       for (j = 0; j < i; j++)
+                               usb_free_urb(dev->urb_list[j]);
+                       return -ENOMEM;
+               }
+               usb_fill_bulk_urb(dev->urb_list[i],
+                               dev->udev,
+                               usb_rcvbulkpipe(dev->udev, 0x81),
+                               dev->buf_list[i],
+                               BULK_BUFFER_SIZE,
+                               hackrf_urb_complete, dev);
+
+               dev->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+               dev->urb_list[i]->transfer_dma = dev->dma_addr[i];
+               dev->urbs_initialized++;
+       }
+
+       return 0;
+}
+
+/* Must be called with vb_queue_lock hold */
+static void hackrf_cleanup_queued_bufs(struct hackrf_dev *dev)
+{
+       unsigned long flags;
+
+       dev_dbg(dev->dev, "\n");
+
+       spin_lock_irqsave(&dev->queued_bufs_lock, flags);
+       while (!list_empty(&dev->queued_bufs)) {
+               struct hackrf_frame_buf *buf;
+
+               buf = list_entry(dev->queued_bufs.next,
+                               struct hackrf_frame_buf, list);
+               list_del(&buf->list);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+       }
+       spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
+}
+
+/* The user yanked out the cable... */
+static void hackrf_disconnect(struct usb_interface *intf)
+{
+       struct v4l2_device *v = usb_get_intfdata(intf);
+       struct hackrf_dev *dev = container_of(v, struct hackrf_dev, v4l2_dev);
+
+       dev_dbg(dev->dev, "\n");
+
+       mutex_lock(&dev->vb_queue_lock);
+       mutex_lock(&dev->v4l2_lock);
+       /* No need to keep the urbs around after disconnection */
+       dev->udev = NULL;
+       v4l2_device_disconnect(&dev->v4l2_dev);
+       video_unregister_device(&dev->vdev);
+       mutex_unlock(&dev->v4l2_lock);
+       mutex_unlock(&dev->vb_queue_lock);
+
+       v4l2_device_put(&dev->v4l2_dev);
+}
+
+/* Videobuf2 operations */
+static int hackrf_queue_setup(struct vb2_queue *vq,
+               const struct v4l2_format *fmt, unsigned int *nbuffers,
+               unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
+{
+       struct hackrf_dev *dev = vb2_get_drv_priv(vq);
+
+       dev_dbg(dev->dev, "nbuffers=%d\n", *nbuffers);
+
+       /* Need at least 8 buffers */
+       if (vq->num_buffers + *nbuffers < 8)
+               *nbuffers = 8 - vq->num_buffers;
+       *nplanes = 1;
+       sizes[0] = PAGE_ALIGN(dev->buffersize);
+
+       dev_dbg(dev->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]);
+       return 0;
+}
+
+static void hackrf_buf_queue(struct vb2_buffer *vb)
+{
+       struct hackrf_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct hackrf_frame_buf *buf =
+                       container_of(vb, struct hackrf_frame_buf, vb);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->queued_bufs_lock, flags);
+       list_add_tail(&buf->list, &dev->queued_bufs);
+       spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
+}
+
+static int hackrf_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+       struct hackrf_dev *dev = vb2_get_drv_priv(vq);
+       int ret;
+
+       dev_dbg(dev->dev, "\n");
+
+       if (!dev->udev)
+               return -ENODEV;
+
+       mutex_lock(&dev->v4l2_lock);
+
+       dev->sequence = 0;
+
+       set_bit(POWER_ON, &dev->flags);
+
+       ret = hackrf_alloc_stream_bufs(dev);
+       if (ret)
+               goto err;
+
+       ret = hackrf_alloc_urbs(dev);
+       if (ret)
+               goto err;
+
+       ret = hackrf_submit_urbs(dev);
+       if (ret)
+               goto err;
+
+       /* start hardware streaming */
+       ret = hackrf_ctrl_msg(dev, CMD_SET_TRANSCEIVER_MODE, 1, 0, NULL, 0);
+       if (ret)
+               goto err;
+
+       goto exit_mutex_unlock;
+err:
+       hackrf_kill_urbs(dev);
+       hackrf_free_urbs(dev);
+       hackrf_free_stream_bufs(dev);
+       clear_bit(POWER_ON, &dev->flags);
+
+       /* return all queued buffers to vb2 */
+       {
+               struct hackrf_frame_buf *buf, *tmp;
+
+               list_for_each_entry_safe(buf, tmp, &dev->queued_bufs, list) {
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+               }
+       }
+
+exit_mutex_unlock:
+       mutex_unlock(&dev->v4l2_lock);
+
+       return ret;
+}
+
+static void hackrf_stop_streaming(struct vb2_queue *vq)
+{
+       struct hackrf_dev *dev = vb2_get_drv_priv(vq);
+
+       dev_dbg(dev->dev, "\n");
+
+       mutex_lock(&dev->v4l2_lock);
+
+       /* stop hardware streaming */
+       hackrf_ctrl_msg(dev, CMD_SET_TRANSCEIVER_MODE, 0, 0, NULL, 0);
+
+       hackrf_kill_urbs(dev);
+       hackrf_free_urbs(dev);
+       hackrf_free_stream_bufs(dev);
+
+       hackrf_cleanup_queued_bufs(dev);
+
+       clear_bit(POWER_ON, &dev->flags);
+
+       mutex_unlock(&dev->v4l2_lock);
+}
+
+static struct vb2_ops hackrf_vb2_ops = {
+       .queue_setup            = hackrf_queue_setup,
+       .buf_queue              = hackrf_buf_queue,
+       .start_streaming        = hackrf_start_streaming,
+       .stop_streaming         = hackrf_stop_streaming,
+       .wait_prepare           = vb2_ops_wait_prepare,
+       .wait_finish            = vb2_ops_wait_finish,
+};
+
+static int hackrf_querycap(struct file *file, void *fh,
+               struct v4l2_capability *cap)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+
+       dev_dbg(dev->dev, "\n");
+
+       strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+       strlcpy(cap->card, dev->vdev.name, sizeof(cap->card));
+       usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+       cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING |
+                       V4L2_CAP_READWRITE | V4L2_CAP_TUNER;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+       return 0;
+}
+
+static int hackrf_s_fmt_sdr_cap(struct file *file, void *priv,
+               struct v4l2_format *f)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+       struct vb2_queue *q = &dev->vb_queue;
+       int i;
+
+       dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n",
+                       (char *)&f->fmt.sdr.pixelformat);
+
+       if (vb2_is_busy(q))
+               return -EBUSY;
+
+       memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+       for (i = 0; i < NUM_FORMATS; i++) {
+               if (f->fmt.sdr.pixelformat == formats[i].pixelformat) {
+                       dev->pixelformat = formats[i].pixelformat;
+                       dev->buffersize = formats[i].buffersize;
+                       f->fmt.sdr.buffersize = formats[i].buffersize;
+                       return 0;
+               }
+       }
+
+       dev->pixelformat = formats[0].pixelformat;
+       dev->buffersize = formats[0].buffersize;
+       f->fmt.sdr.pixelformat = formats[0].pixelformat;
+       f->fmt.sdr.buffersize = formats[0].buffersize;
+
+       return 0;
+}
+
+static int hackrf_g_fmt_sdr_cap(struct file *file, void *priv,
+               struct v4l2_format *f)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+
+       dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n",
+                       (char *)&dev->pixelformat);
+
+       memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+       f->fmt.sdr.pixelformat = dev->pixelformat;
+       f->fmt.sdr.buffersize = dev->buffersize;
+
+       return 0;
+}
+
+static int hackrf_try_fmt_sdr_cap(struct file *file, void *priv,
+               struct v4l2_format *f)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+       int i;
+
+       dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n",
+                       (char *)&f->fmt.sdr.pixelformat);
+
+       memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+       for (i = 0; i < NUM_FORMATS; i++) {
+               if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+                       f->fmt.sdr.buffersize = formats[i].buffersize;
+                       return 0;
+               }
+       }
+
+       f->fmt.sdr.pixelformat = formats[0].pixelformat;
+       f->fmt.sdr.buffersize = formats[0].buffersize;
+
+       return 0;
+}
+
+static int hackrf_enum_fmt_sdr_cap(struct file *file, void *priv,
+               struct v4l2_fmtdesc *f)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+
+       dev_dbg(dev->dev, "index=%d\n", f->index);
+
+       if (f->index >= NUM_FORMATS)
+               return -EINVAL;
+
+       strlcpy(f->description, formats[f->index].name, sizeof(f->description));
+       f->pixelformat = formats[f->index].pixelformat;
+
+       return 0;
+}
+
+static int hackrf_s_tuner(struct file *file, void *priv,
+               const struct v4l2_tuner *v)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+       int ret;
+
+       dev_dbg(dev->dev, "index=%d\n", v->index);
+
+       if (v->index == 0)
+               ret = 0;
+       else if (v->index == 1)
+               ret = 0;
+       else
+               ret = -EINVAL;
+
+       return ret;
+}
+
+static int hackrf_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+       int ret;
+
+       dev_dbg(dev->dev, "index=%d\n", v->index);
+
+       if (v->index == 0) {
+               strlcpy(v->name, "HackRF ADC", sizeof(v->name));
+               v->type = V4L2_TUNER_ADC;
+               v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+               v->rangelow  = bands_adc[0].rangelow;
+               v->rangehigh = bands_adc[0].rangehigh;
+               ret = 0;
+       } else if (v->index == 1) {
+               strlcpy(v->name, "HackRF RF", sizeof(v->name));
+               v->type = V4L2_TUNER_RF;
+               v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+               v->rangelow  = bands_rf[0].rangelow;
+               v->rangehigh = bands_rf[0].rangehigh;
+               ret = 0;
+       } else {
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int hackrf_s_frequency(struct file *file, void *priv,
+               const struct v4l2_frequency *f)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+       int ret;
+       unsigned int upper, lower;
+       u8 buf[8];
+
+       dev_dbg(dev->dev, "tuner=%d type=%d frequency=%u\n",
+                       f->tuner, f->type, f->frequency);
+
+       if (f->tuner == 0) {
+               dev->f_adc = clamp_t(unsigned int, f->frequency,
+                               bands_adc[0].rangelow, bands_adc[0].rangehigh);
+               dev_dbg(dev->dev, "ADC frequency=%u Hz\n", dev->f_adc);
+               upper = dev->f_adc;
+               lower = 1;
+               buf[0] = (upper >>  0) & 0xff;
+               buf[1] = (upper >>  8) & 0xff;
+               buf[2] = (upper >> 16) & 0xff;
+               buf[3] = (upper >> 24) & 0xff;
+               buf[4] = (lower >>  0) & 0xff;
+               buf[5] = (lower >>  8) & 0xff;
+               buf[6] = (lower >> 16) & 0xff;
+               buf[7] = (lower >> 24) & 0xff;
+               ret = hackrf_ctrl_msg(dev, CMD_SAMPLE_RATE_SET, 0, 0, buf, 8);
+       } else if (f->tuner == 1) {
+               dev->f_rf = clamp_t(unsigned int, f->frequency,
+                               bands_rf[0].rangelow, bands_rf[0].rangehigh);
+               dev_dbg(dev->dev, "RF frequency=%u Hz\n", dev->f_rf);
+               upper = dev->f_rf / 1000000;
+               lower = dev->f_rf % 1000000;
+               buf[0] = (upper >>  0) & 0xff;
+               buf[1] = (upper >>  8) & 0xff;
+               buf[2] = (upper >> 16) & 0xff;
+               buf[3] = (upper >> 24) & 0xff;
+               buf[4] = (lower >>  0) & 0xff;
+               buf[5] = (lower >>  8) & 0xff;
+               buf[6] = (lower >> 16) & 0xff;
+               buf[7] = (lower >> 24) & 0xff;
+               ret = hackrf_ctrl_msg(dev, CMD_SET_FREQ, 0, 0, buf, 8);
+       } else {
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int hackrf_g_frequency(struct file *file, void *priv,
+               struct v4l2_frequency *f)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+       int ret;
+
+       dev_dbg(dev->dev, "tuner=%d type=%d\n", f->tuner, f->type);
+
+       if (f->tuner == 0) {
+               f->type = V4L2_TUNER_ADC;
+               f->frequency = dev->f_adc;
+               ret = 0;
+       } else if (f->tuner == 1) {
+               f->type = V4L2_TUNER_RF;
+               f->frequency = dev->f_rf;
+               ret = 0;
+       } else {
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int hackrf_enum_freq_bands(struct file *file, void *priv,
+               struct v4l2_frequency_band *band)
+{
+       struct hackrf_dev *dev = video_drvdata(file);
+       int ret;
+
+       dev_dbg(dev->dev, "tuner=%d type=%d index=%d\n",
+                       band->tuner, band->type, band->index);
+
+       if (band->tuner == 0) {
+               if (band->index >= ARRAY_SIZE(bands_adc)) {
+                       ret = -EINVAL;
+               } else {
+                       *band = bands_adc[band->index];
+                       ret = 0;
+               }
+       } else if (band->tuner == 1) {
+               if (band->index >= ARRAY_SIZE(bands_rf)) {
+                       ret = -EINVAL;
+               } else {
+                       *band = bands_rf[band->index];
+                       ret = 0;
+               }
+       } else {
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static const struct v4l2_ioctl_ops hackrf_ioctl_ops = {
+       .vidioc_querycap          = hackrf_querycap,
+
+       .vidioc_s_fmt_sdr_cap     = hackrf_s_fmt_sdr_cap,
+       .vidioc_g_fmt_sdr_cap     = hackrf_g_fmt_sdr_cap,
+       .vidioc_enum_fmt_sdr_cap  = hackrf_enum_fmt_sdr_cap,
+       .vidioc_try_fmt_sdr_cap   = hackrf_try_fmt_sdr_cap,
+
+       .vidioc_reqbufs           = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs       = vb2_ioctl_create_bufs,
+       .vidioc_prepare_buf       = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf          = vb2_ioctl_querybuf,
+       .vidioc_qbuf              = vb2_ioctl_qbuf,
+       .vidioc_dqbuf             = vb2_ioctl_dqbuf,
+
+       .vidioc_streamon          = vb2_ioctl_streamon,
+       .vidioc_streamoff         = vb2_ioctl_streamoff,
+
+       .vidioc_s_tuner           = hackrf_s_tuner,
+       .vidioc_g_tuner           = hackrf_g_tuner,
+
+       .vidioc_s_frequency       = hackrf_s_frequency,
+       .vidioc_g_frequency       = hackrf_g_frequency,
+       .vidioc_enum_freq_bands   = hackrf_enum_freq_bands,
+
+       .vidioc_subscribe_event   = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+       .vidioc_log_status        = v4l2_ctrl_log_status,
+};
+
+static const struct v4l2_file_operations hackrf_fops = {
+       .owner                    = THIS_MODULE,
+       .open                     = v4l2_fh_open,
+       .release                  = vb2_fop_release,
+       .read                     = vb2_fop_read,
+       .poll                     = vb2_fop_poll,
+       .mmap                     = vb2_fop_mmap,
+       .unlocked_ioctl           = video_ioctl2,
+};
+
+static struct video_device hackrf_template = {
+       .name                     = "HackRF One",
+       .release                  = video_device_release_empty,
+       .fops                     = &hackrf_fops,
+       .ioctl_ops                = &hackrf_ioctl_ops,
+};
+
+static void hackrf_video_release(struct v4l2_device *v)
+{
+       struct hackrf_dev *dev = container_of(v, struct hackrf_dev, v4l2_dev);
+
+       v4l2_ctrl_handler_free(&dev->hdl);
+       v4l2_device_unregister(&dev->v4l2_dev);
+       kfree(dev);
+}
+
+static int hackrf_set_bandwidth(struct hackrf_dev *dev)
+{
+       int ret, i;
+       u16 u16tmp, u16tmp2;
+       unsigned int bandwidth;
+
+       static const struct {
+               u32 freq;
+       } bandwidth_lut[] = {
+               { 1750000}, /*  1.75 MHz */
+               { 2500000}, /*  2.5  MHz */
+               { 3500000}, /*  3.5  MHz */
+               { 5000000}, /*  5    MHz */
+               { 5500000}, /*  5.5  MHz */
+               { 6000000}, /*  6    MHz */
+               { 7000000}, /*  7    MHz */
+               { 8000000}, /*  8    MHz */
+               { 9000000}, /*  9    MHz */
+               {10000000}, /* 10    MHz */
+               {12000000}, /* 12    MHz */
+               {14000000}, /* 14    MHz */
+               {15000000}, /* 15    MHz */
+               {20000000}, /* 20    MHz */
+               {24000000}, /* 24    MHz */
+               {28000000}, /* 28    MHz */
+       };
+
+       dev_dbg(dev->dev, "bandwidth auto=%d->%d val=%d->%d f_adc=%u\n",
+                       dev->bandwidth_auto->cur.val,
+                       dev->bandwidth_auto->val, dev->bandwidth->cur.val,
+                       dev->bandwidth->val, dev->f_adc);
+
+       if (dev->bandwidth_auto->val == true)
+               bandwidth = dev->f_adc;
+       else
+               bandwidth = dev->bandwidth->val;
+
+       for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) {
+               if (bandwidth <= bandwidth_lut[i].freq) {
+                       bandwidth = bandwidth_lut[i].freq;
+                       break;
+               }
+       }
+
+       dev->bandwidth->val = bandwidth;
+       dev->bandwidth->cur.val = bandwidth;
+
+       dev_dbg(dev->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq);
+
+       u16tmp = 0;
+       u16tmp |= ((bandwidth >> 0) & 0xff) << 0;
+       u16tmp |= ((bandwidth >> 8) & 0xff) << 8;
+       u16tmp2 = 0;
+       u16tmp2 |= ((bandwidth >> 16) & 0xff) << 0;
+       u16tmp2 |= ((bandwidth >> 24) & 0xff) << 8;
+
+       ret = hackrf_ctrl_msg(dev, CMD_BASEBAND_FILTER_BANDWIDTH_SET,
+                               u16tmp, u16tmp2, NULL, 0);
+       if (ret)
+               dev_dbg(dev->dev, "failed=%d\n", ret);
+
+       return ret;
+}
+
+static int hackrf_set_lna_gain(struct hackrf_dev *dev)
+{
+       int ret;
+       u8 u8tmp;
+
+       dev_dbg(dev->dev, "lna val=%d->%d\n",
+                       dev->lna_gain->cur.val, dev->lna_gain->val);
+
+       ret = hackrf_ctrl_msg(dev, CMD_SET_LNA_GAIN, 0, dev->lna_gain->val,
+                       &u8tmp, 1);
+       if (ret)
+               dev_dbg(dev->dev, "failed=%d\n", ret);
+
+       return ret;
+}
+
+static int hackrf_set_if_gain(struct hackrf_dev *dev)
+{
+       int ret;
+       u8 u8tmp;
+
+       dev_dbg(dev->dev, "val=%d->%d\n",
+                       dev->if_gain->cur.val, dev->if_gain->val);
+
+       ret = hackrf_ctrl_msg(dev, CMD_SET_VGA_GAIN, 0, dev->if_gain->val,
+                       &u8tmp, 1);
+       if (ret)
+               dev_dbg(dev->dev, "failed=%d\n", ret);
+
+       return ret;
+}
+
+static int hackrf_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct hackrf_dev *dev = container_of(ctrl->handler,
+                       struct hackrf_dev, hdl);
+       int ret;
+
+       switch (ctrl->id) {
+       case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
+       case V4L2_CID_RF_TUNER_BANDWIDTH:
+               ret = hackrf_set_bandwidth(dev);
+               break;
+       case  V4L2_CID_RF_TUNER_LNA_GAIN:
+               ret = hackrf_set_lna_gain(dev);
+               break;
+       case  V4L2_CID_RF_TUNER_IF_GAIN:
+               ret = hackrf_set_if_gain(dev);
+               break;
+       default:
+               dev_dbg(dev->dev, "unknown ctrl: id=%d name=%s\n",
+                               ctrl->id, ctrl->name);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static const struct v4l2_ctrl_ops hackrf_ctrl_ops = {
+       .s_ctrl = hackrf_s_ctrl,
+};
+
+static int hackrf_probe(struct usb_interface *intf,
+               const struct usb_device_id *id)
+{
+       struct hackrf_dev *dev;
+       int ret;
+       u8 u8tmp, buf[BUF_SIZE];
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL)
+               return -ENOMEM;
+
+       mutex_init(&dev->v4l2_lock);
+       mutex_init(&dev->vb_queue_lock);
+       spin_lock_init(&dev->queued_bufs_lock);
+       INIT_LIST_HEAD(&dev->queued_bufs);
+       dev->dev = &intf->dev;
+       dev->udev = interface_to_usbdev(intf);
+       dev->f_adc = bands_adc[0].rangelow;
+       dev->f_rf = bands_rf[0].rangelow;
+       dev->pixelformat = formats[0].pixelformat;
+       dev->buffersize = formats[0].buffersize;
+
+       /* Detect device */
+       ret = hackrf_ctrl_msg(dev, CMD_BOARD_ID_READ, 0, 0, &u8tmp, 1);
+       if (ret == 0)
+               ret = hackrf_ctrl_msg(dev, CMD_VERSION_STRING_READ, 0, 0,
+                               buf, BUF_SIZE);
+       if (ret) {
+               dev_err(dev->dev, "Could not detect board\n");
+               goto err_free_mem;
+       }
+
+       buf[BUF_SIZE - 1] = '\0';
+
+       dev_info(dev->dev, "Board ID: %02x\n", u8tmp);
+       dev_info(dev->dev, "Firmware version: %s\n", buf);
+
+       /* Init videobuf2 queue structure */
+       dev->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+       dev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+       dev->vb_queue.drv_priv = dev;
+       dev->vb_queue.buf_struct_size = sizeof(struct hackrf_frame_buf);
+       dev->vb_queue.ops = &hackrf_vb2_ops;
+       dev->vb_queue.mem_ops = &vb2_vmalloc_memops;
+       dev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       ret = vb2_queue_init(&dev->vb_queue);
+       if (ret) {
+               dev_err(dev->dev, "Could not initialize vb2 queue\n");
+               goto err_free_mem;
+       }
+
+       /* Init video_device structure */
+       dev->vdev = hackrf_template;
+       dev->vdev.queue = &dev->vb_queue;
+       dev->vdev.queue->lock = &dev->vb_queue_lock;
+       video_set_drvdata(&dev->vdev, dev);
+
+       /* Register the v4l2_device structure */
+       dev->v4l2_dev.release = hackrf_video_release;
+       ret = v4l2_device_register(&intf->dev, &dev->v4l2_dev);
+       if (ret) {
+               dev_err(dev->dev, "Failed to register v4l2-device (%d)\n", ret);
+               goto err_free_mem;
+       }
+
+       /* Register controls */
+       v4l2_ctrl_handler_init(&dev->hdl, 4);
+       dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops,
+                       V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
+       dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops,
+                       V4L2_CID_RF_TUNER_BANDWIDTH,
+                       1750000, 28000000, 50000, 1750000);
+       v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false);
+       dev->lna_gain = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops,
+                       V4L2_CID_RF_TUNER_LNA_GAIN, 0, 40, 8, 0);
+       dev->if_gain = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops,
+                       V4L2_CID_RF_TUNER_IF_GAIN, 0, 62, 2, 0);
+       if (dev->hdl.error) {
+               ret = dev->hdl.error;
+               dev_err(dev->dev, "Could not initialize controls\n");
+               goto err_free_controls;
+       }
+
+       v4l2_ctrl_handler_setup(&dev->hdl);
+
+       dev->v4l2_dev.ctrl_handler = &dev->hdl;
+       dev->vdev.v4l2_dev = &dev->v4l2_dev;
+       dev->vdev.lock = &dev->v4l2_lock;
+
+       ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1);
+       if (ret) {
+               dev_err(dev->dev, "Failed to register as video device (%d)\n",
+                               ret);
+               goto err_unregister_v4l2_dev;
+       }
+       dev_info(dev->dev, "Registered as %s\n",
+                       video_device_node_name(&dev->vdev));
+       dev_notice(dev->dev, "SDR API is still slightly experimental and functionality changes may follow\n");
+       return 0;
+
+err_free_controls:
+       v4l2_ctrl_handler_free(&dev->hdl);
+err_unregister_v4l2_dev:
+       v4l2_device_unregister(&dev->v4l2_dev);
+err_free_mem:
+       kfree(dev);
+       return ret;
+}
+
+/* USB device ID list */
+static struct usb_device_id hackrf_id_table[] = {
+       { USB_DEVICE(0x1d50, 0x6089) }, /* HackRF One */
+       { }
+};
+MODULE_DEVICE_TABLE(usb, hackrf_id_table);
+
+/* USB subsystem interface */
+static struct usb_driver hackrf_driver = {
+       .name                     = KBUILD_MODNAME,
+       .probe                    = hackrf_probe,
+       .disconnect               = hackrf_disconnect,
+       .id_table                 = hackrf_id_table,
+};
+
+module_usb_driver(hackrf_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("HackRF");
+MODULE_LICENSE("GPL");
index 6053661dc04bd39727021c311f7d80f1d37ca60a..6e86032ea5dba481e41fa1713852ce05cb120f67 100644 (file)
@@ -59,13 +59,10 @@ int get_video_info(struct hdpvr_device *dev, struct hdpvr_video_info *vidinf)
                              1000);
 
 #ifdef HDPVR_DEBUG
-       if (hdpvr_debug & MSG_INFO) {
-               char print_buf[15];
-               hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf,
-                                  sizeof(print_buf), 0);
+       if (hdpvr_debug & MSG_INFO)
                v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
-                        "get video info returned: %d, %s\n", ret, print_buf);
-       }
+                        "get video info returned: %d, %5ph\n", ret,
+                        dev->usbc_buf);
 #endif
        mutex_unlock(&dev->usbc_mutex);
 
@@ -82,9 +79,6 @@ int get_video_info(struct hdpvr_device *dev, struct hdpvr_video_info *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);
@@ -96,13 +90,10 @@ int get_input_lines_info(struct hdpvr_device *dev)
                              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);
+       if (hdpvr_debug & MSG_INFO)
                v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
-                        "get input lines info returned: %d, %s\n", ret,
-                        print_buf);
-       }
+                        "get input lines info returned: %d, %3ph\n", ret,
+                        dev->usbc_buf);
 #else
        (void)ret;      /* suppress compiler warning */
 #endif
index c5638964c3f286665e0cd57ae1e713be0506e881..42b4cdf28cfd9d24e89e38a7ca3eb84a0354b57e 100644 (file)
@@ -124,14 +124,6 @@ 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");
-               return retval;
-       }
-#endif
 
        mutex_lock(&dev->usbc_mutex);
        ret = usb_control_msg(dev->udev,
@@ -147,11 +139,9 @@ static int device_authorization(struct hdpvr_device *dev)
        }
 #ifdef HDPVR_DEBUG
        else {
-               hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf,
-                                  5*buf_size+1, 0);
                v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
-                        "Status request returned, len %d: %s\n",
-                        ret, print_buf);
+                        "Status request returned, len %d: %46ph\n",
+                        ret, dev->usbc_buf);
        }
 #endif
 
@@ -189,15 +179,13 @@ static int device_authorization(struct hdpvr_device *dev)
 
        response = dev->usbc_buf+38;
 #ifdef HDPVR_DEBUG
-       hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0);
-       v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n",
-                print_buf);
+       v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %8ph\n",
+                response);
 #endif
        challenge(response);
 #ifdef HDPVR_DEBUG
-       hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0);
-       v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %s\n",
-                print_buf);
+       v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %8ph\n",
+                response);
 #endif
 
        msleep(100);
@@ -213,9 +201,6 @@ static int device_authorization(struct hdpvr_device *dev)
        retval = ret != 8;
 unlock:
        mutex_unlock(&dev->usbc_mutex);
-#ifdef HDPVR_DEBUG
-       kfree(print_buf);
-#endif
        return retval;
 }
 
index 26b133414032bcaf3f6b48a4da78f1809f9d4872..efc761c78f7250e5b27a93b0fcb18bbc3fb142a5 100644 (file)
@@ -120,6 +120,7 @@ struct msi2500_frame_buf {
 };
 
 struct msi2500_state {
+       struct device *dev;
        struct video_device vdev;
        struct v4l2_device v4l2_dev;
        struct v4l2_subdev *v4l2_subdev;
@@ -153,14 +154,13 @@ struct msi2500_state {
        u32 next_sample; /* for track lost packets */
        u32 sample; /* for sample rate calc */
        unsigned long jiffies_next;
-       unsigned int sample_ctrl_bit[4];
 };
 
 /* Private functions */
 static struct msi2500_frame_buf *msi2500_get_next_fill_buf(
                struct msi2500_state *s)
 {
-       unsigned long flags = 0;
+       unsigned long flags;
        struct msi2500_frame_buf *buf = NULL;
 
        spin_lock_irqsave(&s->queued_bufs_lock, flags);
@@ -269,7 +269,7 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src,
                sample[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 |
                                src[0] << 0;
                if (i == 0 && s->next_sample != sample[0]) {
-                       dev_dbg_ratelimited(&s->udev->dev,
+                       dev_dbg_ratelimited(s->dev,
                                        "%d samples lost, %d %08x:%08x\n",
                                        sample[0] - s->next_sample,
                                        src_len, s->next_sample, sample[0]);
@@ -279,7 +279,7 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src,
                 * Dump all unknown 'garbage' data - maybe we will discover
                 * someday if there is something rational...
                 */
-               dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]);
+               dev_dbg_ratelimited(s->dev, "%*ph\n", 12, &src[4]);
 
                src += 16; /* skip header */
 
@@ -322,8 +322,7 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src,
                }
                case MSI2500_PIX_FMT_SDR_MSI2500_384: /* 384 x IQ samples */
                        /* Dump unknown 'garbage' data */
-                       dev_dbg_ratelimited(&s->udev->dev,
-                                       "%*ph\n", 24, &src[1000]);
+                       dev_dbg_ratelimited(s->dev, "%*ph\n", 24, &src[1000]);
                        memcpy(dst, src, 984);
                        src += 984 + 24;
                        dst += 984;
@@ -365,8 +364,7 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src,
 
                s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
                s->sample = s->next_sample;
-               dev_dbg(&s->udev->dev,
-                               "size=%u samples=%u msecs=%u sample rate=%lu\n",
+               dev_dbg(s->dev, "size=%u samples=%u msecs=%u sample rate=%lu\n",
                                src_len, samples, msecs,
                                samples * 1000UL / msecs);
        }
@@ -387,19 +385,16 @@ static void msi2500_isoc_handler(struct urb *urb)
 
        if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET ||
                        urb->status == -ESHUTDOWN)) {
-               dev_dbg(&s->udev->dev, "URB (%p) unlinked %ssynchronuously\n",
+               dev_dbg(s->dev, "URB (%p) unlinked %ssynchronuously\n",
                                urb, urb->status == -ENOENT ? "" : "a");
                return;
        }
 
        if (unlikely(urb->status != 0)) {
-               dev_dbg(&s->udev->dev,
-                               "msi2500_isoc_handler() called with status %d\n",
-                               urb->status);
+               dev_dbg(s->dev, "called with status %d\n", urb->status);
                /* Give up after a number of contiguous errors */
                if (++s->isoc_errors > MAX_ISOC_ERRORS)
-                       dev_dbg(&s->udev->dev,
-                                       "Too many ISOC errors, bailing out\n");
+                       dev_dbg(s->dev, "Too many ISOC errors, bailing out\n");
                goto handler_end;
        } else {
                /* Reset ISOC error counter. We did get here, after all. */
@@ -413,7 +408,7 @@ static void msi2500_isoc_handler(struct urb *urb)
                /* Check frame error */
                fstatus = urb->iso_frame_desc[i].status;
                if (unlikely(fstatus)) {
-                       dev_dbg_ratelimited(&s->udev->dev,
+                       dev_dbg_ratelimited(s->dev,
                                        "frame=%d/%d has error %d skipping\n",
                                        i, urb->number_of_packets, fstatus);
                        continue;
@@ -430,7 +425,7 @@ static void msi2500_isoc_handler(struct urb *urb)
                fbuf = msi2500_get_next_fill_buf(s);
                if (unlikely(fbuf == NULL)) {
                        s->vb_full++;
-                       dev_dbg_ratelimited(&s->udev->dev,
+                       dev_dbg_ratelimited(s->dev,
                                        "videobuf is full, %d packets dropped\n",
                                        s->vb_full);
                        continue;
@@ -446,22 +441,19 @@ static void msi2500_isoc_handler(struct urb *urb)
 handler_end:
        i = usb_submit_urb(urb, GFP_ATOMIC);
        if (unlikely(i != 0))
-               dev_dbg(&s->udev->dev,
-                               "Error (%d) re-submitting urb in msi2500_isoc_handler\n",
-                               i);
+               dev_dbg(s->dev, "Error (%d) re-submitting urb\n", i);
 }
 
 static void msi2500_iso_stop(struct msi2500_state *s)
 {
        int i;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        /* Unlinking ISOC buffers one by one */
        for (i = 0; i < MAX_ISO_BUFS; i++) {
                if (s->urbs[i]) {
-                       dev_dbg(&s->udev->dev, "Unlinking URB %p\n",
-                                       s->urbs[i]);
+                       dev_dbg(s->dev, "Unlinking URB %p\n", s->urbs[i]);
                        usb_kill_urb(s->urbs[i]);
                }
        }
@@ -471,12 +463,12 @@ static void msi2500_iso_free(struct msi2500_state *s)
 {
        int i;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        /* Freeing ISOC buffers one by one */
        for (i = 0; i < MAX_ISO_BUFS; i++) {
                if (s->urbs[i]) {
-                       dev_dbg(&s->udev->dev, "Freeing URB\n");
+                       dev_dbg(s->dev, "Freeing URB\n");
                        if (s->urbs[i]->transfer_buffer) {
                                usb_free_coherent(s->udev,
                                        s->urbs[i]->transfer_buffer_length,
@@ -492,7 +484,7 @@ static void msi2500_iso_free(struct msi2500_state *s)
 /* Both v4l2_lock and vb_queue_lock should be locked when calling this */
 static void msi2500_isoc_cleanup(struct msi2500_state *s)
 {
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        msi2500_iso_stop(s);
        msi2500_iso_free(s);
@@ -501,14 +493,12 @@ static void msi2500_isoc_cleanup(struct msi2500_state *s)
 /* Both v4l2_lock and vb_queue_lock should be locked when calling this */
 static int msi2500_isoc_init(struct msi2500_state *s)
 {
-       struct usb_device *udev;
        struct urb *urb;
        int i, j, ret;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        s->isoc_errors = 0;
-       udev = s->udev;
 
        ret = usb_set_interface(s->udev, 0, 1);
        if (ret)
@@ -518,23 +508,22 @@ static int msi2500_isoc_init(struct msi2500_state *s)
        for (i = 0; i < MAX_ISO_BUFS; i++) {
                urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
                if (urb == NULL) {
-                       dev_err(&s->udev->dev,
-                                       "Failed to allocate urb %d\n", i);
+                       dev_err(s->dev, "Failed to allocate urb %d\n", i);
                        msi2500_isoc_cleanup(s);
                        return -ENOMEM;
                }
                s->urbs[i] = urb;
-               dev_dbg(&s->udev->dev, "Allocated URB at 0x%p\n", urb);
+               dev_dbg(s->dev, "Allocated URB at 0x%p\n", urb);
 
                urb->interval = 1;
-               urb->dev = udev;
-               urb->pipe = usb_rcvisocpipe(udev, 0x81);
+               urb->dev = s->udev;
+               urb->pipe = usb_rcvisocpipe(s->udev, 0x81);
                urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-               urb->transfer_buffer = usb_alloc_coherent(udev, ISO_BUFFER_SIZE,
+               urb->transfer_buffer = usb_alloc_coherent(s->udev,
+                               ISO_BUFFER_SIZE,
                                GFP_KERNEL, &urb->transfer_dma);
                if (urb->transfer_buffer == NULL) {
-                       dev_err(&s->udev->dev,
-                                       "Failed to allocate urb buffer %d\n",
+                       dev_err(s->dev, "Failed to allocate urb buffer %d\n",
                                        i);
                        msi2500_isoc_cleanup(s);
                        return -ENOMEM;
@@ -554,13 +543,12 @@ static int msi2500_isoc_init(struct msi2500_state *s)
        for (i = 0; i < MAX_ISO_BUFS; i++) {
                ret = usb_submit_urb(s->urbs[i], GFP_KERNEL);
                if (ret) {
-                       dev_err(&s->udev->dev,
-                                       "isoc_init() submit_urb %d failed with error %d\n",
+                       dev_err(s->dev, "usb_submit_urb %d failed with error %d\n",
                                        i, ret);
                        msi2500_isoc_cleanup(s);
                        return ret;
                }
-               dev_dbg(&s->udev->dev, "URB 0x%p submitted.\n", s->urbs[i]);
+               dev_dbg(s->dev, "URB 0x%p submitted.\n", s->urbs[i]);
        }
 
        /* All is done... */
@@ -570,9 +558,9 @@ static int msi2500_isoc_init(struct msi2500_state *s)
 /* Must be called with vb_queue_lock hold */
 static void msi2500_cleanup_queued_bufs(struct msi2500_state *s)
 {
-       unsigned long flags = 0;
+       unsigned long flags;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        spin_lock_irqsave(&s->queued_bufs_lock, flags);
        while (!list_empty(&s->queued_bufs)) {
@@ -593,7 +581,7 @@ static void msi2500_disconnect(struct usb_interface *intf)
        struct msi2500_state *s =
                        container_of(v, struct msi2500_state, v4l2_dev);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        mutex_lock(&s->vb_queue_lock);
        mutex_lock(&s->v4l2_lock);
@@ -613,7 +601,7 @@ static int msi2500_querycap(struct file *file, void *fh,
 {
        struct msi2500_state *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
        strlcpy(cap->card, s->vdev.name, sizeof(cap->card));
@@ -631,14 +619,13 @@ static int msi2500_queue_setup(struct vb2_queue *vq,
 {
        struct msi2500_state *s = vb2_get_drv_priv(vq);
 
-       dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers);
+       dev_dbg(s->dev, "nbuffers=%d\n", *nbuffers);
 
        /* Absolute min and max number of buffers available for mmap() */
        *nbuffers = clamp_t(unsigned int, *nbuffers, 8, 32);
        *nplanes = 1;
        sizes[0] = PAGE_ALIGN(s->buffersize);
-       dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
-                       __func__, *nbuffers, sizes[0]);
+       dev_dbg(s->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]);
        return 0;
 }
 
@@ -647,7 +634,7 @@ static void msi2500_buf_queue(struct vb2_buffer *vb)
        struct msi2500_state *s = vb2_get_drv_priv(vb->vb2_queue);
        struct msi2500_frame_buf *buf =
                        container_of(vb, struct msi2500_frame_buf, vb);
-       unsigned long flags = 0;
+       unsigned long flags;
 
        /* Check the device has not disconnected between prep and queuing */
        if (unlikely(!s->udev)) {
@@ -665,16 +652,15 @@ static void msi2500_buf_queue(struct vb2_buffer *vb)
 #define CMD_STOP_STREAMING     0x45
 #define CMD_READ_UNKNOW        0x48
 
-#define msi2500_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \
+#define msi2500_dbg_usb_control_msg(_dev, _r, _t, _v, _i, _b, _l) { \
        char *_direction; \
        if (_t & USB_DIR_IN) \
                _direction = "<<<"; \
        else \
                _direction = ">>>"; \
-       dev_dbg(&_udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \
-                       "%s %*ph\n",  __func__, _t, _r, _v & 0xff, _v >> 8, \
-                       _i & 0xff, _i >> 8, _l & 0xff, _l >> 8, _direction, \
-                       _l, _b); \
+       dev_dbg(_dev, "%02x %02x %02x %02x %02x %02x %02x %02x %s %*ph\n", \
+                       _t, _r, _v & 0xff, _v >> 8, _i & 0xff, _i >> 8, \
+                       _l & 0xff, _l >> 8, _direction, _l, _b); \
 }
 
 static int msi2500_ctrl_msg(struct msi2500_state *s, u8 cmd, u32 data)
@@ -685,18 +671,16 @@ static int msi2500_ctrl_msg(struct msi2500_state *s, u8 cmd, u32 data)
        u16 value = (data >> 0) & 0xffff;
        u16 index = (data >> 16) & 0xffff;
 
-       msi2500_dbg_usb_control_msg(s->udev,
+       msi2500_dbg_usb_control_msg(s->dev,
                        request, requesttype, value, index, NULL, 0);
-
        ret = usb_control_msg(s->udev, usb_sndctrlpipe(s->udev, 0),
                        request, requesttype, value, index, NULL, 0, 2000);
-
        if (ret)
-               dev_err(&s->udev->dev, "%s: failed %d, cmd %02x, data %04x\n",
-                               __func__, ret, cmd, data);
+               dev_err(s->dev, "failed %d, cmd %02x, data %04x\n",
+                               ret, cmd, data);
 
        return ret;
-};
+}
 
 #define F_REF 24000000
 #define DIV_R_IN 2
@@ -785,8 +769,7 @@ static int msi2500_set_usb_adc(struct msi2500_state *s)
 
        for (div_r_out = 4; div_r_out < 16; div_r_out += 2) {
                f_vco = f_sr * div_r_out * 12;
-               dev_dbg(&s->udev->dev, "%s: div_r_out=%d f_vco=%d\n",
-                               __func__, div_r_out, f_vco);
+               dev_dbg(s->dev, "div_r_out=%d f_vco=%d\n", div_r_out, f_vco);
                if (f_vco >= 202000000)
                        break;
        }
@@ -800,10 +783,8 @@ static int msi2500_set_usb_adc(struct msi2500_state *s)
        reg3 |= ((fract >> 20) & 0x000001) << 15; /* [20] */
        reg4 |= ((fract >>  0) & 0x0fffff) <<  8; /* [19:0] */
 
-       dev_dbg(&s->udev->dev,
-                       "%s: f_sr=%d f_vco=%d div_n=%d div_m=%d div_r_out=%d reg3=%08x reg4=%08x\n",
-                       __func__, f_sr, f_vco, div_n, div_m, div_r_out, reg3,
-                       reg4);
+       dev_dbg(s->dev, "f_sr=%d f_vco=%d div_n=%d div_m=%d div_r_out=%d reg3=%08x reg4=%08x\n",
+                       f_sr, f_vco, div_n, div_m, div_r_out, reg3, reg4);
 
        ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00608008);
        if (ret)
@@ -838,14 +819,14 @@ static int msi2500_set_usb_adc(struct msi2500_state *s)
                goto err;
 err:
        return ret;
-};
+}
 
 static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
        struct msi2500_state *s = vb2_get_drv_priv(vq);
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        if (!s->udev)
                return -ENODEV;
@@ -873,7 +854,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq)
 {
        struct msi2500_state *s = vb2_get_drv_priv(vq);
 
-       dev_dbg(&s->udev->dev, "%s:\n", __func__);
+       dev_dbg(s->dev, "\n");
 
        mutex_lock(&s->v4l2_lock);
 
@@ -909,7 +890,7 @@ static int msi2500_enum_fmt_sdr_cap(struct file *file, void *priv,
 {
        struct msi2500_state *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index);
+       dev_dbg(s->dev, "index=%d\n", f->index);
 
        if (f->index >= s->num_formats)
                return -EINVAL;
@@ -925,7 +906,7 @@ static int msi2500_g_fmt_sdr_cap(struct file *file, void *priv,
 {
        struct msi2500_state *s = video_drvdata(file);
 
-       dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+       dev_dbg(s->dev, "pixelformat fourcc %4.4s\n",
                        (char *)&s->pixelformat);
 
        f->fmt.sdr.pixelformat = s->pixelformat;
@@ -942,7 +923,7 @@ static int msi2500_s_fmt_sdr_cap(struct file *file, void *priv,
        struct vb2_queue *q = &s->vb_queue;
        int i;
 
-       dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+       dev_dbg(s->dev, "pixelformat fourcc %4.4s\n",
                        (char *)&f->fmt.sdr.pixelformat);
 
        if (vb2_is_busy(q))
@@ -972,7 +953,7 @@ static int msi2500_try_fmt_sdr_cap(struct file *file, void *priv,
        struct msi2500_state *s = video_drvdata(file);
        int i;
 
-       dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+       dev_dbg(s->dev, "pixelformat fourcc %4.4s\n",
                        (char *)&f->fmt.sdr.pixelformat);
 
        memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
@@ -995,7 +976,7 @@ static int msi2500_s_tuner(struct file *file, void *priv,
        struct msi2500_state *s = video_drvdata(file);
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index);
+       dev_dbg(s->dev, "index=%d\n", v->index);
 
        if (v->index == 0)
                ret = 0;
@@ -1012,7 +993,7 @@ static int msi2500_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
        struct msi2500_state *s = video_drvdata(file);
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index);
+       dev_dbg(s->dev, "index=%d\n", v->index);
 
        if (v->index == 0) {
                strlcpy(v->name, "Mirics MSi2500", sizeof(v->name));
@@ -1036,8 +1017,7 @@ static int msi2500_g_frequency(struct file *file, void *priv,
        struct msi2500_state *s = video_drvdata(file);
        int ret  = 0;
 
-       dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n",
-                       __func__, f->tuner, f->type);
+       dev_dbg(s->dev, "tuner=%d type=%d\n", f->tuner, f->type);
 
        if (f->tuner == 0) {
                f->frequency = s->f_adc;
@@ -1058,15 +1038,14 @@ static int msi2500_s_frequency(struct file *file, void *priv,
        struct msi2500_state *s = video_drvdata(file);
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n",
-                       __func__, f->tuner, f->type, f->frequency);
+       dev_dbg(s->dev, "tuner=%d type=%d frequency=%u\n",
+                       f->tuner, f->type, f->frequency);
 
        if (f->tuner == 0) {
                s->f_adc = clamp_t(unsigned int, f->frequency,
                                bands[0].rangelow,
                                bands[0].rangehigh);
-               dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n",
-                               __func__, s->f_adc);
+               dev_dbg(s->dev, "ADC frequency=%u Hz\n", s->f_adc);
                ret = msi2500_set_usb_adc(s);
        } else if (f->tuner == 1) {
                ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_frequency, f);
@@ -1083,8 +1062,8 @@ static int msi2500_enum_freq_bands(struct file *file, void *priv,
        struct msi2500_state *s = video_drvdata(file);
        int ret;
 
-       dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n",
-                       __func__, band->tuner, band->type, band->index);
+       dev_dbg(s->dev, "tuner=%d type=%d index=%d\n",
+                       band->tuner, band->type, band->index);
 
        if (band->tuner == 0) {
                if (band->index >= ARRAY_SIZE(bands)) {
@@ -1169,8 +1148,7 @@ static int msi2500_transfer_one_message(struct spi_master *master,
        u32 data;
 
        list_for_each_entry(t, &m->transfers, transfer_list) {
-               dev_dbg(&s->udev->dev, "%s: msg=%*ph\n",
-                               __func__, t->len, t->tx_buf);
+               dev_dbg(s->dev, "msg=%*ph\n", t->len, t->tx_buf);
                data = 0x09; /* reg 9 is SPI adapter */
                data |= ((u8 *)t->tx_buf)[0] << 8;
                data |= ((u8 *)t->tx_buf)[1] << 16;
@@ -1186,8 +1164,7 @@ static int msi2500_transfer_one_message(struct spi_master *master,
 static int msi2500_probe(struct usb_interface *intf,
                const struct usb_device_id *id)
 {
-       struct usb_device *udev = interface_to_usbdev(intf);
-       struct msi2500_state *s = NULL;
+       struct msi2500_state *s;
        struct v4l2_subdev *sd;
        struct spi_master *master;
        int ret;
@@ -1200,7 +1177,7 @@ static int msi2500_probe(struct usb_interface *intf,
 
        s = kzalloc(sizeof(struct msi2500_state), GFP_KERNEL);
        if (s == NULL) {
-               pr_err("Could not allocate memory for msi2500_state\n");
+               dev_err(&intf->dev, "Could not allocate memory for state\n");
                return -ENOMEM;
        }
 
@@ -1208,12 +1185,13 @@ static int msi2500_probe(struct usb_interface *intf,
        mutex_init(&s->vb_queue_lock);
        spin_lock_init(&s->queued_bufs_lock);
        INIT_LIST_HEAD(&s->queued_bufs);
-       s->udev = udev;
+       s->dev = &intf->dev;
+       s->udev = interface_to_usbdev(intf);
        s->f_adc = bands[0].rangelow;
        s->pixelformat = formats[0].pixelformat;
        s->buffersize = formats[0].buffersize;
        s->num_formats = NUM_FORMATS;
-       if (msi2500_emulated_fmt == false)
+       if (!msi2500_emulated_fmt)
                s->num_formats -= 2;
 
        /* Init videobuf2 queue structure */
@@ -1226,7 +1204,7 @@ static int msi2500_probe(struct usb_interface *intf,
        s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        ret = vb2_queue_init(&s->vb_queue);
        if (ret) {
-               dev_err(&s->udev->dev, "Could not initialize vb2 queue\n");
+               dev_err(s->dev, "Could not initialize vb2 queue\n");
                goto err_free_mem;
        }
 
@@ -1240,13 +1218,12 @@ static int msi2500_probe(struct usb_interface *intf,
        s->v4l2_dev.release = msi2500_video_release;
        ret = v4l2_device_register(&intf->dev, &s->v4l2_dev);
        if (ret) {
-               dev_err(&s->udev->dev,
-                               "Failed to register v4l2-device (%d)\n", ret);
+               dev_err(s->dev, "Failed to register v4l2-device (%d)\n", ret);
                goto err_free_mem;
        }
 
        /* SPI master adapter */
-       master = spi_alloc_master(&s->udev->dev, 0);
+       master = spi_alloc_master(s->dev, 0);
        if (master == NULL) {
                ret = -ENOMEM;
                goto err_unregister_v4l2_dev;
@@ -1267,7 +1244,7 @@ static int msi2500_probe(struct usb_interface *intf,
        sd = v4l2_spi_new_subdev(&s->v4l2_dev, master, &board_info);
        s->v4l2_subdev = sd;
        if (sd == NULL) {
-               dev_err(&s->udev->dev, "cannot get v4l2 subdevice\n");
+               dev_err(s->dev, "cannot get v4l2 subdevice\n");
                ret = -ENODEV;
                goto err_unregister_master;
        }
@@ -1276,7 +1253,7 @@ static int msi2500_probe(struct usb_interface *intf,
        v4l2_ctrl_handler_init(&s->hdl, 0);
        if (s->hdl.error) {
                ret = s->hdl.error;
-               dev_err(&s->udev->dev, "Could not initialize controls\n");
+               dev_err(s->dev, "Could not initialize controls\n");
                goto err_free_controls;
        }
 
@@ -1289,16 +1266,13 @@ static int msi2500_probe(struct usb_interface *intf,
 
        ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
        if (ret) {
-               dev_err(&s->udev->dev,
-                               "Failed to register as video device (%d)\n",
+               dev_err(s->dev, "Failed to register as video device (%d)\n",
                                ret);
                goto err_unregister_v4l2_dev;
        }
-       dev_info(&s->udev->dev, "Registered as %s\n",
+       dev_info(s->dev, "Registered as %s\n",
                        video_device_node_name(&s->vdev));
-       dev_notice(&s->udev->dev,
-                       "%s: SDR API is still slightly experimental and functionality changes may follow\n",
-                       KBUILD_MODNAME);
+       dev_notice(s->dev, "SDR API is still slightly experimental and functionality changes may follow\n");
 
        return 0;
 
index aa7449eaca08d027ba752aeddffa9ea883ad7e86..3d987984602f1bad2042a778aeef9b79c39dab32 100644 (file)
@@ -52,7 +52,7 @@ enum { custom_autocontour, custom_contour, custom_noise_reduction,
        custom_awb_speed, custom_awb_delay,
        custom_save_user, custom_restore_user, custom_restore_factory };
 
-const char * const pwc_auto_whitebal_qmenu[] = {
+static const char * const pwc_auto_whitebal_qmenu[] = {
        "Indoor (Incandescant Lighting) Mode",
        "Outdoor (Sunlight) Mode",
        "Indoor (Fluorescent Lighting) Mode",
index 2c901861034afd746a6fec9540e1ebf47f2c0156..ccc00099b26144e4bd1c5b99cbfccbf266a50ad7 100644 (file)
@@ -2245,7 +2245,7 @@ static int s2255_probe(struct usb_interface *interface,
        }
 
        atomic_set(&dev->num_channels, 0);
-       dev->pid = le16_to_cpu(id->idProduct);
+       dev->pid = id->idProduct;
        dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
        if (!dev->fw_data)
                goto errorFWDATA1;
index 1836a416d80646151e80c67d8896f96fa6fd1fe2..94e10b10b66e8c41c95672a702bcb84c91f5b05b 100644 (file)
@@ -277,14 +277,14 @@ static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id)
                rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2),
                                  fw_buffer, fw->size, &dummy, 1000);
 
-               sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc);
+               sms_info("sent %zu(%d) bytes, rc %d", fw->size, dummy, rc);
 
                kfree(fw_buffer);
        } else {
                sms_err("failed to allocate firmware buffer");
                rc = -ENOMEM;
        }
-       sms_info("read FW %s, size=%zd", fw_filename, fw->size);
+       sms_info("read FW %s, size=%zu", fw_filename, fw->size);
 
        release_firmware(fw);
 
@@ -655,6 +655,8 @@ static const struct usb_device_id smsusb_id_table[] = {
                .driver_info = SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD },
        { USB_DEVICE(0x3275, 0x0080),
                .driver_info = SMS1XXX_BOARD_SIANO_RIO },
+       { USB_DEVICE(0x2013, 0x0257),
+               .driver_info = SMS1XXX_BOARD_PCTV_77E },
        { } /* Terminating entry */
        };
 
index 5c45c9d0712ddf949f0ac571b5649111585046ae..9c29552aedec2e7b1a08447619671cd65c170b77 100644 (file)
@@ -156,6 +156,9 @@ static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struc
                   0x00, 0x00, 0x00, 0x00,
                   0x00, 0x00 };
 
+       if (cmd->msg_len > sizeof(b) - 4)
+               return -EINVAL;
+
        memcpy(&b[4], cmd->msg, cmd->msg_len);
 
        state->config->send_command(fe, 0x72,
index 7c5b86006ee627c43a52003fb816c7fceb10b6f4..b833c5b9094edb33fbac7fd167a09710b065fbd2 100644 (file)
@@ -1,6 +1,7 @@
 config VIDEO_USBTV
         tristate "USBTV007 video capture support"
-        depends on VIDEO_V4L2
+        depends on VIDEO_V4L2 && SND
+        select SND_PCM
         select VIDEOBUF2_VMALLOC
 
         ---help---
index 775316a88ea6953210befcabf71e4a005c67bc15..f555cf8a3dd2ea886ad9ad3f2b41b69d61444922 100644 (file)
@@ -1,4 +1,5 @@
 usbtv-y := usbtv-core.o \
-       usbtv-video.o
+       usbtv-video.o \
+       usbtv-audio.o
 
 obj-$(CONFIG_VIDEO_USBTV) += usbtv.o
diff --git a/drivers/media/usb/usbtv/usbtv-audio.c b/drivers/media/usb/usbtv/usbtv-audio.c
new file mode 100644 (file)
index 0000000..78c12d2
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Fushicai USBTV007 Audio-Video Grabber Driver
+ *
+ * Product web site:
+ * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
+ *
+ * Copyright (c) 2013 Federico Simoncelli
+ * All rights reserved.
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ */
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+#include <sound/pcm_params.h>
+
+#include "usbtv.h"
+
+static struct snd_pcm_hardware snd_usbtv_digital_hw = {
+       .info = SNDRV_PCM_INFO_BATCH |
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .rates = SNDRV_PCM_RATE_48000,
+       .rate_min = 48000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .period_bytes_min = 11059,
+       .period_bytes_max = 13516,
+       .periods_min = 2,
+       .periods_max = 98,
+       .buffer_bytes_max = 62720 * 8, /* value in usbaudio.c */
+};
+
+static int snd_usbtv_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct usbtv *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       chip->snd_substream = substream;
+       runtime->hw = snd_usbtv_digital_hw;
+
+       return 0;
+}
+
+static int snd_usbtv_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct usbtv *chip = snd_pcm_substream_chip(substream);
+
+       if (atomic_read(&chip->snd_stream)) {
+               atomic_set(&chip->snd_stream, 0);
+               schedule_work(&chip->snd_trigger);
+       }
+
+       return 0;
+}
+
+static int snd_usbtv_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *hw_params)
+{
+       int rv;
+       struct usbtv *chip = snd_pcm_substream_chip(substream);
+
+       rv = snd_pcm_lib_malloc_pages(substream,
+               params_buffer_bytes(hw_params));
+
+       if (rv < 0) {
+               dev_warn(chip->dev, "pcm audio buffer allocation failure %i\n",
+                       rv);
+               return rv;
+       }
+
+       return 0;
+}
+
+static int snd_usbtv_hw_free(struct snd_pcm_substream *substream)
+{
+       snd_pcm_lib_free_pages(substream);
+       return 0;
+}
+
+static int snd_usbtv_prepare(struct snd_pcm_substream *substream)
+{
+       struct usbtv *chip = snd_pcm_substream_chip(substream);
+
+       chip->snd_buffer_pos = 0;
+       chip->snd_period_pos = 0;
+
+       return 0;
+}
+
+static void usbtv_audio_urb_received(struct urb *urb)
+{
+       struct usbtv *chip = urb->context;
+       struct snd_pcm_substream *substream = chip->snd_substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       size_t i, frame_bytes, chunk_length, buffer_pos, period_pos;
+       int period_elapsed;
+       void *urb_current;
+
+       switch (urb->status) {
+       case 0:
+       case -ETIMEDOUT:
+               break;
+       case -ENOENT:
+       case -EPROTO:
+       case -ECONNRESET:
+       case -ESHUTDOWN:
+               return;
+       default:
+               dev_warn(chip->dev, "unknown audio urb status %i\n",
+                       urb->status);
+       }
+
+       if (!atomic_read(&chip->snd_stream))
+               return;
+
+       frame_bytes = runtime->frame_bits >> 3;
+       chunk_length = USBTV_CHUNK / frame_bytes;
+
+       buffer_pos = chip->snd_buffer_pos;
+       period_pos = chip->snd_period_pos;
+       period_elapsed = 0;
+
+       for (i = 0; i < urb->actual_length; i += USBTV_CHUNK_SIZE) {
+               urb_current = urb->transfer_buffer + i + USBTV_AUDIO_HDRSIZE;
+
+               if (buffer_pos + chunk_length >= runtime->buffer_size) {
+                       size_t cnt = (runtime->buffer_size - buffer_pos) *
+                               frame_bytes;
+                       memcpy(runtime->dma_area + buffer_pos * frame_bytes,
+                               urb_current, cnt);
+                       memcpy(runtime->dma_area, urb_current + cnt,
+                               chunk_length * frame_bytes - cnt);
+               } else {
+                       memcpy(runtime->dma_area + buffer_pos * frame_bytes,
+                               urb_current, chunk_length * frame_bytes);
+               }
+
+               buffer_pos += chunk_length;
+               period_pos += chunk_length;
+
+               if (buffer_pos >= runtime->buffer_size)
+                       buffer_pos -= runtime->buffer_size;
+
+               if (period_pos >= runtime->period_size) {
+                       period_pos -= runtime->period_size;
+                       period_elapsed = 1;
+               }
+       }
+
+       snd_pcm_stream_lock(substream);
+
+       chip->snd_buffer_pos = buffer_pos;
+       chip->snd_period_pos = period_pos;
+
+       snd_pcm_stream_unlock(substream);
+
+       if (period_elapsed)
+               snd_pcm_period_elapsed(substream);
+
+       usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int usbtv_audio_start(struct usbtv *chip)
+{
+       unsigned int pipe;
+       static const u16 setup[][2] = {
+               /* These seem to enable the device. */
+               { USBTV_BASE + 0x0008, 0x0001 },
+               { USBTV_BASE + 0x01d0, 0x00ff },
+               { USBTV_BASE + 0x01d9, 0x0002 },
+
+               { USBTV_BASE + 0x01da, 0x0013 },
+               { USBTV_BASE + 0x01db, 0x0012 },
+               { USBTV_BASE + 0x01e9, 0x0002 },
+               { USBTV_BASE + 0x01ec, 0x006c },
+               { USBTV_BASE + 0x0294, 0x0020 },
+               { USBTV_BASE + 0x0255, 0x00cf },
+               { USBTV_BASE + 0x0256, 0x0020 },
+               { USBTV_BASE + 0x01eb, 0x0030 },
+               { USBTV_BASE + 0x027d, 0x00a6 },
+               { USBTV_BASE + 0x0280, 0x0011 },
+               { USBTV_BASE + 0x0281, 0x0040 },
+               { USBTV_BASE + 0x0282, 0x0011 },
+               { USBTV_BASE + 0x0283, 0x0040 },
+               { 0xf891, 0x0010 },
+
+               /* this sets the input from composite */
+               { USBTV_BASE + 0x0284, 0x00aa },
+       };
+
+       chip->snd_bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (chip->snd_bulk_urb == NULL)
+               goto err_alloc_urb;
+
+       pipe = usb_rcvbulkpipe(chip->udev, USBTV_AUDIO_ENDP);
+
+       chip->snd_bulk_urb->transfer_buffer = kzalloc(
+               USBTV_AUDIO_URBSIZE, GFP_KERNEL);
+       if (chip->snd_bulk_urb->transfer_buffer == NULL)
+               goto err_transfer_buffer;
+
+       usb_fill_bulk_urb(chip->snd_bulk_urb, chip->udev, pipe,
+               chip->snd_bulk_urb->transfer_buffer, USBTV_AUDIO_URBSIZE,
+               usbtv_audio_urb_received, chip);
+
+       /* starting the stream */
+       usbtv_set_regs(chip, setup, ARRAY_SIZE(setup));
+
+       usb_clear_halt(chip->udev, pipe);
+       usb_submit_urb(chip->snd_bulk_urb, GFP_ATOMIC);
+
+       return 0;
+
+err_transfer_buffer:
+       usb_free_urb(chip->snd_bulk_urb);
+       chip->snd_bulk_urb = NULL;
+
+err_alloc_urb:
+       return -ENOMEM;
+}
+
+static int usbtv_audio_stop(struct usbtv *chip)
+{
+       static const u16 setup[][2] = {
+       /* The original windows driver sometimes sends also:
+        *   { USBTV_BASE + 0x00a2, 0x0013 }
+        * but it seems useless and its real effects are untested at
+        * the moment.
+        */
+               { USBTV_BASE + 0x027d, 0x0000 },
+               { USBTV_BASE + 0x0280, 0x0010 },
+               { USBTV_BASE + 0x0282, 0x0010 },
+       };
+
+       if (chip->snd_bulk_urb) {
+               usb_kill_urb(chip->snd_bulk_urb);
+               kfree(chip->snd_bulk_urb->transfer_buffer);
+               usb_free_urb(chip->snd_bulk_urb);
+               chip->snd_bulk_urb = NULL;
+       }
+
+       usbtv_set_regs(chip, setup, ARRAY_SIZE(setup));
+
+       return 0;
+}
+
+void usbtv_audio_suspend(struct usbtv *usbtv)
+{
+       if (atomic_read(&usbtv->snd_stream) && usbtv->snd_bulk_urb)
+               usb_kill_urb(usbtv->snd_bulk_urb);
+}
+
+void usbtv_audio_resume(struct usbtv *usbtv)
+{
+       if (atomic_read(&usbtv->snd_stream) && usbtv->snd_bulk_urb)
+               usb_submit_urb(usbtv->snd_bulk_urb, GFP_ATOMIC);
+}
+
+static void snd_usbtv_trigger(struct work_struct *work)
+{
+       struct usbtv *chip = container_of(work, struct usbtv, snd_trigger);
+
+       if (atomic_read(&chip->snd_stream))
+               usbtv_audio_start(chip);
+       else
+               usbtv_audio_stop(chip);
+}
+
+static int snd_usbtv_card_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct usbtv *chip = snd_pcm_substream_chip(substream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               atomic_set(&chip->snd_stream, 1);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               atomic_set(&chip->snd_stream, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       schedule_work(&chip->snd_trigger);
+
+       return 0;
+}
+
+static snd_pcm_uframes_t snd_usbtv_pointer(struct snd_pcm_substream *substream)
+{
+       struct usbtv *chip = snd_pcm_substream_chip(substream);
+
+       return chip->snd_buffer_pos;
+}
+
+static struct snd_pcm_ops snd_usbtv_pcm_ops = {
+       .open = snd_usbtv_pcm_open,
+       .close = snd_usbtv_pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = snd_usbtv_hw_params,
+       .hw_free = snd_usbtv_hw_free,
+       .prepare = snd_usbtv_prepare,
+       .trigger = snd_usbtv_card_trigger,
+       .pointer = snd_usbtv_pointer,
+};
+
+int usbtv_audio_init(struct usbtv *usbtv)
+{
+       int rv;
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+
+       INIT_WORK(&usbtv->snd_trigger, snd_usbtv_trigger);
+       atomic_set(&usbtv->snd_stream, 0);
+
+       rv = snd_card_new(&usbtv->udev->dev, SNDRV_DEFAULT_IDX1, "usbtv",
+               THIS_MODULE, 0, &card);
+       if (rv < 0)
+               return rv;
+
+       strlcpy(card->driver, usbtv->dev->driver->name, sizeof(card->driver));
+       strlcpy(card->shortname, "usbtv", sizeof(card->shortname));
+       snprintf(card->longname, sizeof(card->longname),
+               "USBTV Audio at bus %d device %d", usbtv->udev->bus->busnum,
+               usbtv->udev->devnum);
+
+       snd_card_set_dev(card, usbtv->dev);
+
+       usbtv->snd = card;
+
+       rv = snd_pcm_new(card, "USBTV Audio", 0, 0, 1, &pcm);
+       if (rv < 0)
+               goto err;
+
+       strlcpy(pcm->name, "USBTV Audio Input", sizeof(pcm->name));
+       pcm->info_flags = 0;
+       pcm->private_data = usbtv;
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usbtv_pcm_ops);
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+               snd_dma_continuous_data(GFP_KERNEL), USBTV_AUDIO_BUFFER,
+               USBTV_AUDIO_BUFFER);
+
+       rv = snd_card_register(card);
+       if (rv)
+               goto err;
+
+       return 0;
+
+err:
+       usbtv->snd = NULL;
+       snd_card_free(card);
+
+       return rv;
+}
+
+void usbtv_audio_free(struct usbtv *usbtv)
+{
+       if (usbtv->snd && usbtv->udev) {
+               snd_card_free(usbtv->snd);
+               usbtv->snd = NULL;
+       }
+}
index 473fab81b602276b85cc2320eb76854611fe16ee..29428bef272c657346f2850f6372f55cb33208d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Fushicai USBTV007 Video Grabber Driver
+ * Fushicai USBTV007 Audio-Video Grabber Driver
  *
  * Product web site:
  * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
@@ -84,12 +84,19 @@ static int usbtv_probe(struct usb_interface *intf,
        if (ret < 0)
                goto usbtv_video_fail;
 
+       ret = usbtv_audio_init(usbtv);
+       if (ret < 0)
+               goto usbtv_audio_fail;
+
        /* for simplicity we exploit the v4l2_device reference counting */
        v4l2_device_get(&usbtv->v4l2_dev);
 
-       dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
+       dev_info(dev, "Fushicai USBTV007 Audio-Video Grabber\n");
        return 0;
 
+usbtv_audio_fail:
+       usbtv_video_free(usbtv);
+
 usbtv_video_fail:
        usb_set_intfdata(intf, NULL);
        usb_put_dev(usbtv->udev);
@@ -101,11 +108,13 @@ usbtv_video_fail:
 static void usbtv_disconnect(struct usb_interface *intf)
 {
        struct usbtv *usbtv = usb_get_intfdata(intf);
+
        usb_set_intfdata(intf, NULL);
 
        if (!usbtv)
                return;
 
+       usbtv_audio_free(usbtv);
        usbtv_video_free(usbtv);
 
        usb_put_dev(usbtv->udev);
@@ -122,8 +131,8 @@ static struct usb_device_id usbtv_id_table[] = {
 };
 MODULE_DEVICE_TABLE(usb, usbtv_id_table);
 
-MODULE_AUTHOR("Lubomir Rintel");
-MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
+MODULE_AUTHOR("Lubomir Rintel, Federico Simoncelli");
+MODULE_DESCRIPTION("Fushicai USBTV007 Audio-Video Grabber Driver");
 MODULE_LICENSE("Dual BSD/GPL");
 
 static struct usb_driver usbtv_usb_driver = {
index 030c5854b4b3e45913a6a5d70bb03cb3a5bcf51c..9d3525f659f066d3c013105052e9fc31a76874aa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Fushicai USBTV007 Video Grabber Driver
+ * Fushicai USBTV007 Audio-Video Grabber Driver
  *
  * Product web site:
  * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
@@ -79,7 +79,6 @@ static int usbtv_select_input(struct usbtv *usbtv, int input)
                { USBTV_BASE + 0x011f, 0x00f2 },
                { USBTV_BASE + 0x0127, 0x0060 },
                { USBTV_BASE + 0x00ae, 0x0010 },
-               { USBTV_BASE + 0x0284, 0x00aa },
                { USBTV_BASE + 0x0239, 0x0060 },
        };
 
@@ -88,7 +87,6 @@ static int usbtv_select_input(struct usbtv *usbtv, int input)
                { USBTV_BASE + 0x011f, 0x00ff },
                { USBTV_BASE + 0x0127, 0x0060 },
                { USBTV_BASE + 0x00ae, 0x0030 },
-               { USBTV_BASE + 0x0284, 0x0088 },
                { USBTV_BASE + 0x0239, 0x0060 },
        };
 
@@ -225,7 +223,6 @@ static int usbtv_setup_capture(struct usbtv *usbtv)
                { USBTV_BASE + 0x0159, 0x0006 },
                { USBTV_BASE + 0x015d, 0x0000 },
 
-               { USBTV_BASE + 0x0284, 0x0088 },
                { USBTV_BASE + 0x0003, 0x0004 },
                { USBTV_BASE + 0x0100, 0x00d3 },
                { USBTV_BASE + 0x0115, 0x0015 },
@@ -256,7 +253,7 @@ static int usbtv_setup_capture(struct usbtv *usbtv)
  * 720 pixel lines, as the chunk is 240 words long, which is 480 pixels.
  * Therefore, we break down the chunk into two halves before copyting,
  * so that we can interleave a line if needed. */
-static void usbtv_chunk_to_vbuf(u32 *frame, u32 *src, int chunk_no, int odd)
+static void usbtv_chunk_to_vbuf(u32 *frame, __be32 *src, int chunk_no, int odd)
 {
        int half;
 
@@ -266,6 +263,7 @@ static void usbtv_chunk_to_vbuf(u32 *frame, u32 *src, int chunk_no, int odd)
                int part_index = (line * 2 + !odd) * 3 + (part_no % 3);
 
                u32 *dst = &frame[part_index * USBTV_CHUNK/2];
+
                memcpy(dst, src, USBTV_CHUNK/2 * sizeof(*src));
                src += USBTV_CHUNK/2;
        }
@@ -274,7 +272,7 @@ static void usbtv_chunk_to_vbuf(u32 *frame, u32 *src, int chunk_no, int odd)
 /* Called for each 256-byte image chunk.
  * First word identifies the chunk, followed by 240 words of image
  * data and padding. */
-static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk)
+static void usbtv_image_chunk(struct usbtv *usbtv, __be32 *chunk)
 {
        int frame_id, odd, chunk_no;
        u32 *frame;
@@ -365,7 +363,7 @@ static void usbtv_iso_cb(struct urb *ip)
 
                for (offset = 0; USBTV_CHUNK_SIZE * offset < size; offset++)
                        usbtv_image_chunk(usbtv,
-                               (u32 *)&data[USBTV_CHUNK_SIZE * offset]);
+                               (__be32 *)&data[USBTV_CHUNK_SIZE * offset]);
        }
 
 resubmit:
@@ -410,6 +408,7 @@ static void usbtv_stop(struct usbtv *usbtv)
        /* Cancel running transfers. */
        for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) {
                struct urb *ip = usbtv->isoc_urbs[i];
+
                if (ip == NULL)
                        continue;
                usb_kill_urb(ip);
@@ -434,6 +433,8 @@ static int usbtv_start(struct usbtv *usbtv)
        int i;
        int ret;
 
+       usbtv_audio_suspend(usbtv);
+
        ret = usb_set_interface(usbtv->udev, 0, 0);
        if (ret < 0)
                return ret;
@@ -446,6 +447,8 @@ static int usbtv_start(struct usbtv *usbtv)
        if (ret < 0)
                return ret;
 
+       usbtv_audio_resume(usbtv);
+
        for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) {
                struct urb *ip;
 
@@ -559,6 +562,7 @@ static int usbtv_g_input(struct file *file, void *priv, unsigned int *i)
 static int usbtv_s_input(struct file *file, void *priv, unsigned int i)
 {
        struct usbtv *usbtv = video_drvdata(file);
+
        return usbtv_select_input(usbtv, i);
 }
 
index cb1d388cc647703d12736c4d37c5b98b67a1457b..968119581fab3e95adad4771576c3249ff56b0d5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Fushicai USBTV007 Video Grabber Driver
+ * Fushicai USBTV007 Audio-Video Grabber Driver
  *
  * Copyright (c) 2013 Lubomir Rintel
  * All rights reserved.
@@ -28,6 +28,7 @@
 
 /* Hardware. */
 #define USBTV_VIDEO_ENDP       0x81
+#define USBTV_AUDIO_ENDP       0x83
 #define USBTV_BASE             0xc000
 #define USBTV_REQUEST_REG      12
 
 #define USBTV_CHUNK_SIZE       256
 #define USBTV_CHUNK            240
 
+#define USBTV_AUDIO_URBSIZE    20480
+#define USBTV_AUDIO_HDRSIZE    4
+#define USBTV_AUDIO_BUFFER     65536
+
 /* Chunk header. */
 #define USBTV_MAGIC_OK(chunk)  ((be32_to_cpu(chunk[0]) & 0xff000000) \
                                                        == 0x88000000)
@@ -91,9 +96,23 @@ struct usbtv {
        int iso_size;
        unsigned int sequence;
        struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
+
+       /* audio */
+       struct snd_card *snd;
+       struct snd_pcm_substream *snd_substream;
+       atomic_t snd_stream;
+       struct work_struct snd_trigger;
+       struct urb *snd_bulk_urb;
+       size_t snd_buffer_pos;
+       size_t snd_period_pos;
 };
 
 int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size);
 
 int usbtv_video_init(struct usbtv *usbtv);
 void usbtv_video_free(struct usbtv *usbtv);
+
+int usbtv_audio_init(struct usbtv *usbtv);
+void usbtv_audio_free(struct usbtv *usbtv);
+void usbtv_audio_suspend(struct usbtv *usbtv);
+void usbtv_audio_resume(struct usbtv *usbtv);
index 0eb82106d2ff4015bc8cee2e0159626b8ba4ad8e..3e59b288b8a89cc70d9b4c086c5784e922ff1e65 100644 (file)
@@ -309,9 +309,8 @@ static struct uvc_control_info uvc_ctrls[] = {
                .selector       = UVC_CT_PANTILT_RELATIVE_CONTROL,
                .index          = 12,
                .size           = 4,
-               .flags          = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
-                               | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
-                               | UVC_CTRL_FLAG_GET_DEF
+               .flags          = UVC_CTRL_FLAG_SET_CUR
+                               | UVC_CTRL_FLAG_GET_RANGE
                                | UVC_CTRL_FLAG_AUTO_UPDATE,
        },
        {
@@ -391,6 +390,35 @@ static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping,
        data[2] = min((int)abs(value), 0xff);
 }
 
+static __s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping,
+       __u8 query, const __u8 *data)
+{
+       unsigned int first = mapping->offset / 8;
+       __s8 rel = (__s8)data[first];
+
+       switch (query) {
+       case UVC_GET_CUR:
+               return (rel == 0) ? 0 : (rel > 0 ? data[first+1]
+                                                : -data[first+1]);
+       case UVC_GET_MIN:
+               return -data[first+1];
+       case UVC_GET_MAX:
+       case UVC_GET_RES:
+       case UVC_GET_DEF:
+       default:
+               return data[first+1];
+       }
+}
+
+static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
+       __s32 value, __u8 *data)
+{
+       unsigned int first = mapping->offset / 8;
+
+       data[first] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;
+       data[first+1] = min_t(int, abs(value), 0xff);
+}
+
 static struct uvc_control_mapping uvc_ctrl_mappings[] = {
        {
                .id             = V4L2_CID_BRIGHTNESS,
@@ -676,6 +704,30 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
                .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
                .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
        },
+       {
+               .id             = V4L2_CID_PAN_SPEED,
+               .name           = "Pan (Speed)",
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = UVC_CT_PANTILT_RELATIVE_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+               .get            = uvc_ctrl_get_rel_speed,
+               .set            = uvc_ctrl_set_rel_speed,
+       },
+       {
+               .id             = V4L2_CID_TILT_SPEED,
+               .name           = "Tilt (Speed)",
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = UVC_CT_PANTILT_RELATIVE_CONTROL,
+               .size           = 16,
+               .offset         = 16,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+               .get            = uvc_ctrl_get_rel_speed,
+               .set            = uvc_ctrl_set_rel_speed,
+       },
        {
                .id             = V4L2_CID_PRIVACY,
                .name           = "Privacy",
@@ -1795,7 +1847,7 @@ done:
  * - Handle restore order (Auto-Exposure Mode should be restored before
  *   Exposure Time).
  */
-int uvc_ctrl_resume_device(struct uvc_device *dev)
+int uvc_ctrl_restore_values(struct uvc_device *dev)
 {
        struct uvc_control *ctrl;
        struct uvc_entity *entity;
index f8135f4e3b5262793e511193033d9b9bb9ad5abb..7c8322d4fc63fae2e2e65f24f080d1db7fd604a4 100644 (file)
@@ -2000,7 +2000,7 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
                int ret = 0;
 
                if (reset) {
-                       ret = uvc_ctrl_resume_device(dev);
+                       ret = uvc_ctrl_restore_values(dev);
                        if (ret < 0)
                                return ret;
                }
@@ -2175,6 +2175,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceClass      = USB_CLASS_VENDOR_SPEC,
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0 },
+       /* Logitech HD Pro Webcam C920 */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x046d,
+         .idProduct            = 0x082d,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_RESTORE_CTRLS_ON_INIT },
        /* Chicony CNF7129 (Asus EEE 100HE) */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2229,6 +2238,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_PROBE_DEF },
+       /* Dell XPS M1330 (OmniVision OV7670 webcam) */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x05a9,
+         .idProduct            = 0x7670,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_DEF },
        /* Apple Built-In iSight */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
index 378ae02e593b93a19bb9010a23f62175029bbefd..60a8e2c3631e0b155b241e624092ccef271ae030 100644 (file)
@@ -318,6 +318,7 @@ static int uvc_v4l2_set_format(struct uvc_streaming *stream,
        stream->ctrl = probe;
        stream->cur_format = format;
        stream->cur_frame = frame;
+       stream->frame_size = fmt->fmt.pix.sizeimage;
 
 done:
        mutex_unlock(&stream->mutex);
index 9144a2f3ed826c541ef9d5d1b3286e00ce7c1d6d..9ace520bb079792b840d8aff38b68945fef0eeb0 100644 (file)
@@ -1143,7 +1143,7 @@ static int uvc_video_encode_data(struct uvc_streaming *stream,
 static void uvc_video_validate_buffer(const struct uvc_streaming *stream,
                                      struct uvc_buffer *buf)
 {
-       if (buf->length != buf->bytesused &&
+       if (stream->frame_size != buf->bytesused &&
            !(stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED))
                buf->error = 1;
 }
@@ -1463,7 +1463,7 @@ static unsigned int uvc_endpoint_max_bpi(struct usb_device *dev,
 
        switch (dev->speed) {
        case USB_SPEED_SUPER:
-               return ep->ss_ep_comp.wBytesPerInterval;
+               return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
        case USB_SPEED_HIGH:
                psize = usb_endpoint_maxp(&ep->desc);
                return (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
@@ -1678,6 +1678,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
                }
        }
 
+       /* The Logitech C920 temporarily forgets that it should not be adjusting
+        * Exposure Absolute during init so restore controls to stored values.
+        */
+       if (stream->dev->quirks & UVC_QUIRK_RESTORE_CTRLS_ON_INIT)
+               uvc_ctrl_restore_values(stream->dev);
+
        return 0;
 }
 
index b1f69a6d4068f1c64aec057142efcf02b9b6f82e..6f676c29ec09fd997b8833d78df1b8b72fdf0d91 100644 (file)
 #define UVC_QUIRK_FIX_BANDWIDTH                0x00000080
 #define UVC_QUIRK_PROBE_DEF            0x00000100
 #define UVC_QUIRK_RESTRICT_FRAME_RATE  0x00000200
+#define UVC_QUIRK_RESTORE_CTRLS_ON_INIT        0x00000400
 
 /* Format flags */
 #define UVC_FMT_FLAG_COMPRESSED                0x00000001
@@ -456,6 +457,8 @@ struct uvc_streaming {
        struct uvc_format *def_format;
        struct uvc_format *cur_format;
        struct uvc_frame *cur_frame;
+       size_t frame_size;
+
        /* Protect access to ctrl, cur_format, cur_frame and hardware video
         * probe control.
         */
@@ -688,7 +691,7 @@ extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
                const struct uvc_control_mapping *mapping);
 extern int uvc_ctrl_init_device(struct uvc_device *dev);
 extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
-extern int uvc_ctrl_resume_device(struct uvc_device *dev);
+extern int uvc_ctrl_restore_values(struct uvc_device *dev);
 
 extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
 extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
index 06c18ba16fa0427cc71fff76f08c140f55b05384..559f8372e2eb3d9bfc021da1196582d3f438ba21 100644 (file)
@@ -601,7 +601,7 @@ static int tuner_probe(struct i2c_client *client,
        t->name = "(tuner unset)";
        t->type = UNSET;
        t->audmode = V4L2_TUNER_MODE_STEREO;
-       t->standby = 1;
+       t->standby = true;
        t->radio_freq = 87.5 * 16000;   /* Initial freq range */
        t->tv_freq = 400 * 16; /* Sets freq to VHF High - needed for some PLL's to properly start */
 
@@ -1260,7 +1260,9 @@ static int tuner_suspend(struct device *dev)
 
        tuner_dbg("suspend\n");
 
-       if (!t->standby && analog_ops->standby)
+       if (t->fe.ops.tuner_ops.suspend)
+               t->fe.ops.tuner_ops.suspend(&t->fe);
+       else if (!t->standby && analog_ops->standby)
                analog_ops->standby(&t->fe);
 
        return 0;
@@ -1273,7 +1275,9 @@ static int tuner_resume(struct device *dev)
 
        tuner_dbg("resume\n");
 
-       if (!t->standby)
+       if (t->fe.ops.tuner_ops.resume)
+               t->fe.ops.tuner_ops.resume(&t->fe);
+       else if (!t->standby)
                if (set_mode(t, t->mode) == 0)
                        set_freq(t, 0);
 
index ccaa38f65cf189cf1874e9ed514ea62aa603d50f..2e9d81f4c1a57e9f51cd728b991c887558d11c72 100644 (file)
@@ -435,16 +435,13 @@ static unsigned int clamp_align(unsigned int x, unsigned int min,
        /* Bits that must be zero to be aligned */
        unsigned int mask = ~((1 << align) - 1);
 
+       /* Clamp to aligned min and max */
+       x = clamp(x, (min + ~mask) & mask, max & mask);
+
        /* Round to nearest aligned value */
        if (align)
                x = (x + (1 << (align - 1))) & mask;
 
-       /* Clamp to aligned value of min and max */
-       if (x < min)
-               x = (min + ~mask) & mask;
-       else if (x > max)
-               x = max & mask;
-
        return x;
 }
 
index cca6c2f76b3a3ed75c4398a2f67d302be41ee384..e502a5fb299471b2912bf15f25b5aada4aede3dd 100644 (file)
@@ -328,7 +328,7 @@ struct v4l2_buffer32 {
        __u32                   reserved;
 };
 
-static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
+static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
                                enum v4l2_memory memory)
 {
        void __user *up_pln;
@@ -357,7 +357,7 @@ static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
        return 0;
 }
 
-static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
+static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
                                enum v4l2_memory memory)
 {
        if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
@@ -427,7 +427,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
                 * by passing a very big num_planes value */
                uplane = compat_alloc_user_space(num_planes *
                                                sizeof(struct v4l2_plane));
-               kp->m.planes = uplane;
+               kp->m.planes = (__force struct v4l2_plane *)uplane;
 
                while (--num_planes >= 0) {
                        ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
@@ -498,7 +498,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
                if (num_planes == 0)
                        return 0;
 
-               uplane = kp->m.planes;
+               uplane = (__force struct v4l2_plane __user *)kp->m.planes;
                if (get_user(p, &up->m.planes))
                        return -EFAULT;
                uplane32 = compat_ptr(p);
@@ -562,7 +562,7 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
                get_user(kp->flags, &up->flags) ||
                copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
                        return -EFAULT;
-       kp->base = compat_ptr(tmp);
+       kp->base = (__force void *)compat_ptr(tmp);
        return 0;
 }
 
@@ -667,11 +667,15 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
                        n * sizeof(struct v4l2_ext_control32)))
                return -EFAULT;
        kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
-       kp->controls = kcontrols;
+       kp->controls = (__force struct v4l2_ext_control *)kcontrols;
        while (--n >= 0) {
+               u32 id;
+
                if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
                        return -EFAULT;
-               if (ctrl_is_pointer(kcontrols->id)) {
+               if (get_user(id, &kcontrols->id))
+                       return -EFAULT;
+               if (ctrl_is_pointer(id)) {
                        void __user *s;
 
                        if (get_user(p, &ucontrols->string))
@@ -689,7 +693,8 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
 {
        struct v4l2_ext_control32 __user *ucontrols;
-       struct v4l2_ext_control __user *kcontrols = kp->controls;
+       struct v4l2_ext_control __user *kcontrols =
+               (__force struct v4l2_ext_control __user *)kp->controls;
        int n = kp->count;
        compat_caddr_t p;
 
@@ -711,11 +716,14 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 
        while (--n >= 0) {
                unsigned size = sizeof(*ucontrols);
+               u32 id;
 
+               if (get_user(id, &kcontrols->id))
+                       return -EFAULT;
                /* Do not modify the pointer when copying a pointer control.
                   The contents of the pointer was changed, not the pointer
                   itself. */
-               if (ctrl_is_pointer(kcontrols->id))
+               if (ctrl_is_pointer(id))
                        size -= sizeof(ucontrols->value64);
                if (copy_in_user(ucontrols, kcontrols, size))
                        return -EFAULT;
@@ -770,7 +778,7 @@ static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
                get_user(tmp, &up->edid) ||
                copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
                        return -EFAULT;
-       kp->edid = compat_ptr(tmp);
+       kp->edid = (__force u8 *)compat_ptr(tmp);
        return 0;
 }
 
@@ -783,7 +791,7 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
                put_user(kp->start_block, &up->start_block) ||
                put_user(kp->blocks, &up->blocks) ||
                put_user(tmp, &up->edid) ||
-               copy_to_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+               copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
                        return -EFAULT;
        return 0;
 }
index f030d6a9e04421f2fcd63c9fce6fc713cdc897f1..86012140923fcf57526952c975a9516084f174f1 100644 (file)
@@ -796,6 +796,8 @@ const char *v4l2_ctrl_get_name(u32 id)
        case V4L2_CID_AUTO_FOCUS_STOP:          return "Auto Focus, Stop";
        case V4L2_CID_AUTO_FOCUS_STATUS:        return "Auto Focus, Status";
        case V4L2_CID_AUTO_FOCUS_RANGE:         return "Auto Focus, Range";
+       case V4L2_CID_PAN_SPEED:                return "Pan, Speed";
+       case V4L2_CID_TILT_SPEED:               return "Tilt, Speed";
 
        /* FM Radio Modulator controls */
        /* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -859,6 +861,10 @@ const char *v4l2_ctrl_get_name(u32 id)
        case V4L2_CID_VBLANK:                   return "Vertical Blanking";
        case V4L2_CID_HBLANK:                   return "Horizontal Blanking";
        case V4L2_CID_ANALOGUE_GAIN:            return "Analogue Gain";
+       case V4L2_CID_TEST_PATTERN_RED:         return "Red Pixel Value";
+       case V4L2_CID_TEST_PATTERN_GREENR:      return "Green (Red) Pixel Value";
+       case V4L2_CID_TEST_PATTERN_BLUE:        return "Blue Pixel Value";
+       case V4L2_CID_TEST_PATTERN_GREENB:      return "Green (Blue) Pixel Value";
 
        /* Image processing controls */
        /* Keep the order of the 'case's the same as in v4l2-controls.h! */
index ce1c9f5d9dee1040c43f798b5163c4821feabbfd..b1d8dbb39665eab512f8dd59abdfac63fbed43e2 100644 (file)
@@ -164,7 +164,8 @@ bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t,
            bt->width > cap->max_width ||
            bt->pixelclock < cap->min_pixelclock ||
            bt->pixelclock > cap->max_pixelclock ||
-           (cap->standards && !(bt->standards & cap->standards)) ||
+           (cap->standards && bt->standards &&
+            !(bt->standards & cap->standards)) ||
            (bt->interlaced && !(caps & V4L2_DV_BT_CAP_INTERLACED)) ||
            (!bt->interlaced && !(caps & V4L2_DV_BT_CAP_PROGRESSIVE)))
                return false;
index d15e16737eef4e0fd6d58d9babcba00ddcd2df77..9ccb19a435ef7a04a7d125af5a5b86c3b130a79d 100644 (file)
@@ -562,7 +562,7 @@ static void v4l_print_ext_controls(const void *arg, bool write_only)
        pr_cont("class=0x%x, count=%d, error_idx=%d",
                        p->ctrl_class, p->count, p->error_idx);
        for (i = 0; i < p->count; i++) {
-               if (p->controls[i].size)
+               if (!p->controls[i].size)
                        pr_cont(", id/val=0x%x/0x%x",
                                p->controls[i].id, p->controls[i].value);
                else
@@ -1153,9 +1153,9 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
        switch (p->type) {
        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: {
-               struct v4l2_clip *clips = p->fmt.win.clips;
+               struct v4l2_clip __user *clips = p->fmt.win.clips;
                u32 clipcount = p->fmt.win.clipcount;
-               void *bitmap = p->fmt.win.bitmap;
+               void __user *bitmap = p->fmt.win.bitmap;
 
                memset(&p->fmt, 0, sizeof(p->fmt));
                p->fmt.win.clips = clips;
index b4d235c13fbfc43d12decce919e9dc367e094ea5..543631c3557ab94ec264164532403d33b0eb5497 100644 (file)
@@ -501,11 +501,20 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
                                      struct v4l2_subdev_format *source_fmt,
                                      struct v4l2_subdev_format *sink_fmt)
 {
+       /* The width, height and code must match. */
        if (source_fmt->format.width != sink_fmt->format.width
            || source_fmt->format.height != sink_fmt->format.height
            || source_fmt->format.code != sink_fmt->format.code)
                return -EINVAL;
 
+       /* The field order must match, or the sink field order must be NONE
+        * to support interlaced hardware connected to bridges that support
+        * progressive formats only.
+        */
+       if (source_fmt->format.field != sink_fmt->format.field &&
+           sink_fmt->format.field != V4L2_FIELD_NONE)
+               return -EINVAL;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);
index fb5ee5dd8fe933cbdd371aeecf41a3047d2e0e39..b91a266d0b7efcced9ad3bbca2b08cb398377505 100644 (file)
@@ -441,11 +441,6 @@ int videobuf_reqbufs(struct videobuf_queue *q,
        unsigned int size, count;
        int retval;
 
-       if (req->count < 1) {
-               dprintk(1, "reqbufs: count invalid (%d)\n", req->count);
-               return -EINVAL;
-       }
-
        if (req->memory != V4L2_MEMORY_MMAP     &&
            req->memory != V4L2_MEMORY_USERPTR  &&
            req->memory != V4L2_MEMORY_OVERLAY) {
@@ -471,6 +466,12 @@ int videobuf_reqbufs(struct videobuf_queue *q,
                goto done;
        }
 
+       if (req->count == 0) {
+               dprintk(1, "reqbufs: count invalid (%d)\n", req->count);
+               retval = __videobuf_free(q);
+               goto done;
+       }
+
        count = req->count;
        if (count > VIDEO_MAX_FRAME)
                count = VIDEO_MAX_FRAME;
index 3c8cc023a5a5e3c2718545b8814664fbf02be291..3ff15f1c9d702219b33a43e3df0e9eb07fab93e6 100644 (file)
@@ -253,9 +253,11 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
        return 0;
 out_free_pages:
        while (i > 0) {
-               void *addr = page_address(dma->vaddr_pages[i]);
-               dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]);
+               void *addr;
+
                i--;
+               addr = page_address(dma->vaddr_pages[i]);
+               dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]);
        }
        kfree(dma->dma_addr);
        dma->dma_addr = NULL;
index 25d3ae2188cb29fc2338e0dc976d21db17829447..f2e43de3dd879330fbc6a230d6d267f5c6efdf0d 100644 (file)
@@ -36,7 +36,7 @@ module_param(debug, int, 0644);
 #define dprintk(level, fmt, arg...)                                          \
        do {                                                                  \
                if (debug >= level)                                           \
-                       pr_debug("vb2: %s: " fmt, __func__, ## arg); \
+                       pr_info("vb2: %s: " fmt, __func__, ## arg); \
        } while (0)
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -882,7 +882,9 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
                 * We already have buffers allocated, so first check if they
                 * are not in use and can be freed.
                 */
+               mutex_lock(&q->mmap_lock);
                if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
+                       mutex_unlock(&q->mmap_lock);
                        dprintk(1, "memory in use, cannot free\n");
                        return -EBUSY;
                }
@@ -894,6 +896,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
                 */
                __vb2_queue_cancel(q);
                ret = __vb2_queue_free(q, q->num_buffers);
+               mutex_unlock(&q->mmap_lock);
                if (ret)
                        return ret;
 
@@ -955,6 +958,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
                 */
        }
 
+       mutex_lock(&q->mmap_lock);
        q->num_buffers = allocated_buffers;
 
        if (ret < 0) {
@@ -963,8 +967,10 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
                 * from q->num_buffers.
                 */
                __vb2_queue_free(q, allocated_buffers);
+               mutex_unlock(&q->mmap_lock);
                return ret;
        }
+       mutex_unlock(&q->mmap_lock);
 
        /*
         * Return the number of successfully allocated buffers
@@ -1063,6 +1069,7 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
                 */
        }
 
+       mutex_lock(&q->mmap_lock);
        q->num_buffers += allocated_buffers;
 
        if (ret < 0) {
@@ -1071,8 +1078,10 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
                 * from q->num_buffers.
                 */
                __vb2_queue_free(q, allocated_buffers);
+               mutex_unlock(&q->mmap_lock);
                return -ENOMEM;
        }
+       mutex_unlock(&q->mmap_lock);
 
        /*
         * Return the number of successfully allocated buffers
@@ -1581,7 +1590,6 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 {
        struct vb2_queue *q = vb->vb2_queue;
-       struct rw_semaphore *mmap_sem;
        int ret;
 
        ret = __verify_length(vb, b);
@@ -1618,26 +1626,9 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                ret = __qbuf_mmap(vb, b);
                break;
        case V4L2_MEMORY_USERPTR:
-               /*
-                * In case of user pointer buffers vb2 allocators need to get
-                * direct access to userspace pages. This requires getting
-                * the mmap semaphore for read access in the current process
-                * structure. The same semaphore is taken before calling mmap
-                * operation, while both qbuf/prepare_buf and mmap are called
-                * by the driver or v4l2 core with the driver's lock held.
-                * To avoid an AB-BA deadlock (mmap_sem then driver's lock in
-                * mmap and driver's lock then mmap_sem in qbuf/prepare_buf),
-                * the videobuf2 core releases the driver's lock, takes
-                * mmap_sem and then takes the driver's lock again.
-                */
-               mmap_sem = &current->mm->mmap_sem;
-               call_void_qop(q, wait_prepare, q);
-               down_read(mmap_sem);
-               call_void_qop(q, wait_finish, q);
-
+               down_read(&current->mm->mmap_sem);
                ret = __qbuf_userptr(vb, b);
-
-               up_read(mmap_sem);
+               up_read(&current->mm->mmap_sem);
                break;
        case V4L2_MEMORY_DMABUF:
                ret = __qbuf_dmabuf(vb, b);
@@ -2504,7 +2495,9 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
                return -EINVAL;
        }
 
+       mutex_lock(&q->mmap_lock);
        ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma);
+       mutex_unlock(&q->mmap_lock);
        if (ret)
                return ret;
 
@@ -2523,6 +2516,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
        unsigned long off = pgoff << PAGE_SHIFT;
        struct vb2_buffer *vb;
        unsigned int buffer, plane;
+       void *vaddr;
        int ret;
 
        if (q->memory != V4L2_MEMORY_MMAP) {
@@ -2539,7 +2533,8 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
 
        vb = q->bufs[buffer];
 
-       return (unsigned long)vb2_plane_vaddr(vb, plane);
+       vaddr = vb2_plane_vaddr(vb, plane);
+       return vaddr ? (unsigned long)vaddr : -EINVAL;
 }
 EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
 #endif
@@ -2686,6 +2681,7 @@ int vb2_queue_init(struct vb2_queue *q)
        INIT_LIST_HEAD(&q->queued_list);
        INIT_LIST_HEAD(&q->done_list);
        spin_lock_init(&q->done_lock);
+       mutex_init(&q->mmap_lock);
        init_waitqueue_head(&q->done_wq);
 
        if (q->buf_struct_size == 0)
@@ -2707,7 +2703,9 @@ void vb2_queue_release(struct vb2_queue *q)
 {
        __vb2_cleanup_fileio(q);
        __vb2_queue_cancel(q);
+       mutex_lock(&q->mmap_lock);
        __vb2_queue_free(q, q->num_buffers);
+       mutex_unlock(&q->mmap_lock);
 }
 EXPORT_SYMBOL_GPL(vb2_queue_release);
 
@@ -2985,6 +2983,12 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
                buf->queued = 0;
                buf->size = read ? vb2_get_plane_payload(q->bufs[index], 0)
                                 : vb2_plane_size(q->bufs[index], 0);
+               /* Compensate for data_offset on read in the multiplanar case. */
+               if (is_multiplanar && read &&
+                   fileio->b.m.planes[0].data_offset < buf->size) {
+                       buf->pos = fileio->b.m.planes[0].data_offset;
+                       buf->size -= buf->pos;
+               }
        } else {
                buf = &fileio->bufs[index];
        }
@@ -3372,15 +3376,8 @@ EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf);
 int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
 {
        struct video_device *vdev = video_devdata(file);
-       struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
-       int err;
 
-       if (lock && mutex_lock_interruptible(lock))
-               return -ERESTARTSYS;
-       err = vb2_mmap(vdev->queue, vma);
-       if (lock)
-               mutex_unlock(lock);
-       return err;
+       return vb2_mmap(vdev->queue, vma);
 }
 EXPORT_SYMBOL_GPL(vb2_fop_mmap);
 
@@ -3499,15 +3496,8 @@ unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr,
                unsigned long len, unsigned long pgoff, unsigned long flags)
 {
        struct video_device *vdev = video_devdata(file);
-       struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
-       int ret;
 
-       if (lock && mutex_lock_interruptible(lock))
-               return -ERESTARTSYS;
-       ret = vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags);
-       if (lock)
-               mutex_unlock(lock);
-       return ret;
+       return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags);
 }
 EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area);
 #endif
index de5abf244746ac7dc32e99d02b61d131e4877a77..cf66ef1ffaf32d59b60ed2ff30873988ec2bd3e4 100644 (file)
@@ -454,6 +454,21 @@ config MFD_MAX8998
          additional drivers must be enabled in order to use the functionality
          of the device.
 
+config MFD_MENF21BMC
+       tristate "MEN 14F021P00 Board Management Controller Support"
+       depends on I2C
+       select MFD_CORE
+       help
+         Say yes here to add support for the MEN 14F021P00 BMC
+         which is a Board Management Controller connected to the I2C bus.
+         The device supports multiple sub-devices like LED, HWMON and WDT.
+         This driver provides common support for accessing the devices;
+         additional drivers must be enabled in order to use the
+         functionality of the BMC device.
+
+         This driver can also be built as a module. If so the module
+         will be called menf21bmc.
+
 config EZX_PCAP
        bool "Motorola EZXPCAP Support"
        depends on SPI_MASTER
index f00148782d9b3c234609ad10827430ec84c2a56b..d58068aa8aa9492fbbdd432123e4b5749d819bbf 100644 (file)
@@ -169,6 +169,7 @@ obj-$(CONFIG_MFD_AS3711)    += as3711.o
 obj-$(CONFIG_MFD_AS3722)       += as3722.o
 obj-$(CONFIG_MFD_STW481X)      += stw481x.o
 obj-$(CONFIG_MFD_IPAQ_MICRO)   += ipaq-micro.o
+obj-$(CONFIG_MFD_MENF21BMC)    += menf21bmc.o
 
 intel-soc-pmic-objs            := intel_soc_pmic_core.o intel_soc_pmic_crc.o
 obj-$(CONFIG_INTEL_SOC_PMIC)   += intel-soc-pmic.o
index 9fc4186d41328af729beb99cb30488a89b64d937..977bd3a3eed01149d9d5ef670cfa175922250232 100644 (file)
@@ -605,7 +605,8 @@ static int asic3_gpio_remove(struct platform_device *pdev)
 {
        struct asic3 *asic = platform_get_drvdata(pdev);
 
-       return gpiochip_remove(&asic->gpio);
+       gpiochip_remove(&asic->gpio);
+       return 0;
 }
 
 static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
index 6bdb78c2ac77de535214d80a09cd9a302653d219..adbbce0ff630edfb02de146a13b711790e6e29a8 100644 (file)
@@ -481,15 +481,9 @@ static int htcpld_register_chip_gpio(
 
        ret = gpiochip_add(&(chip->chip_in));
        if (ret) {
-               int error;
-
                dev_warn(dev, "Unable to register input GPIOs for 0x%x: %d\n",
                         plat_chip_data->addr, ret);
-
-               error = gpiochip_remove(&(chip->chip_out));
-               if (error)
-                       dev_warn(dev, "Error while trying to unregister gpio chip: %d\n", error);
-
+               gpiochip_remove(&(chip->chip_out));
                return ret;
        }
 
diff --git a/drivers/mfd/menf21bmc.c b/drivers/mfd/menf21bmc.c
new file mode 100644 (file)
index 0000000..1c27434
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ *  MEN 14F021P00 Board Management Controller (BMC) MFD Core Driver.
+ *
+ *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ *  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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+
+#define BMC_CMD_WDT_EXIT_PROD  0x18
+#define BMC_CMD_WDT_PROD_STAT  0x19
+#define BMC_CMD_REV_MAJOR      0x80
+#define BMC_CMD_REV_MINOR      0x81
+#define BMC_CMD_REV_MAIN       0x82
+
+static struct mfd_cell menf21bmc_cell[] = {
+       { .name = "menf21bmc_wdt", },
+       { .name = "menf21bmc_led", },
+       { .name = "menf21bmc_hwmon", }
+};
+
+static int menf21bmc_wdt_exit_prod_mode(struct i2c_client *client)
+{
+       int val, ret;
+
+       val = i2c_smbus_read_byte_data(client, BMC_CMD_WDT_PROD_STAT);
+       if (val < 0)
+               return val;
+
+       /*
+        * Production mode should be not active after delivery of the Board.
+        * To be sure we check it, inform the user and exit the mode
+        * if active.
+        */
+       if (val == 0x00) {
+               dev_info(&client->dev,
+                       "BMC in production mode. Exit production mode\n");
+
+               ret = i2c_smbus_write_byte(client, BMC_CMD_WDT_EXIT_PROD);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int
+menf21bmc_probe(struct i2c_client *client, const struct i2c_device_id *ids)
+{
+       int rev_major, rev_minor, rev_main;
+       int ret;
+
+       ret = i2c_check_functionality(client->adapter,
+                                     I2C_FUNC_SMBUS_BYTE_DATA |
+                                     I2C_FUNC_SMBUS_WORD_DATA |
+                                     I2C_FUNC_SMBUS_BYTE);
+       if (!ret)
+               return -ENODEV;
+
+       rev_major = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAJOR);
+       if (rev_major < 0) {
+               dev_err(&client->dev, "failed to get BMC major revision\n");
+               return rev_major;
+       }
+
+       rev_minor = i2c_smbus_read_word_data(client, BMC_CMD_REV_MINOR);
+       if (rev_minor < 0) {
+               dev_err(&client->dev, "failed to get BMC minor revision\n");
+               return rev_minor;
+       }
+
+       rev_main = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAIN);
+       if (rev_main < 0) {
+               dev_err(&client->dev, "failed to get BMC main revision\n");
+               return rev_main;
+       }
+
+       dev_info(&client->dev, "FW Revision: %02d.%02d.%02d\n",
+                rev_major, rev_minor, rev_main);
+
+       /*
+        * We have to exit the Production Mode of the BMC to activate the
+        * Watchdog functionality and the BIOS life sign monitoring.
+        */
+       ret = menf21bmc_wdt_exit_prod_mode(client);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed to leave production mode\n");
+               return ret;
+       }
+
+       ret = mfd_add_devices(&client->dev, 0, menf21bmc_cell,
+                             ARRAY_SIZE(menf21bmc_cell), NULL, 0, NULL);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed to add BMC sub-devices\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int menf21bmc_remove(struct i2c_client *client)
+{
+       mfd_remove_devices(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id menf21bmc_id_table[] = {
+       { "menf21bmc" },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, menf21bmc_id_table);
+
+static struct i2c_driver menf21bmc_driver = {
+       .driver.name    = "menf21bmc",
+       .id_table       = menf21bmc_id_table,
+       .probe          = menf21bmc_probe,
+       .remove         = menf21bmc_remove,
+};
+
+module_i2c_driver(menf21bmc_driver);
+
+MODULE_DESCRIPTION("MEN 14F021P00 BMC mfd core driver");
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_LICENSE("GPL v2");
index 81e6d0932bf0afa096e84387cb241c4f6e57785e..02027b7f1223b1e00199d1c9915d21294fb6cb72 100644 (file)
@@ -1047,7 +1047,6 @@ static int sm501_register_gpio(struct sm501_devdata *sm)
        struct sm501_gpio *gpio = &sm->gpio;
        resource_size_t iobase = sm->io_res->start + SM501_GPIO;
        int ret;
-       int tmp;
 
        dev_dbg(sm->dev, "registering gpio block %08llx\n",
                (unsigned long long)iobase);
@@ -1086,11 +1085,7 @@ static int sm501_register_gpio(struct sm501_devdata *sm)
        return 0;
 
  err_low_chip:
-       tmp = gpiochip_remove(&gpio->low.gpio);
-       if (tmp) {
-               dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
-               return ret;
-       }
+       gpiochip_remove(&gpio->low.gpio);
 
  err_mapped:
        iounmap(gpio->regs);
@@ -1105,18 +1100,12 @@ static int sm501_register_gpio(struct sm501_devdata *sm)
 static void sm501_gpio_remove(struct sm501_devdata *sm)
 {
        struct sm501_gpio *gpio = &sm->gpio;
-       int ret;
 
        if (!sm->gpio.registered)
                return;
 
-       ret = gpiochip_remove(&gpio->low.gpio);
-       if (ret)
-               dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
-
-       ret = gpiochip_remove(&gpio->high.gpio);
-       if (ret)
-               dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n");
+       gpiochip_remove(&gpio->low.gpio);
+       gpiochip_remove(&gpio->high.gpio);
 
        iounmap(gpio->regs);
        release_resource(gpio->regs_res);
index 11c19e5385510ccf8d8272e709e7b95264b66afc..4fac16bcd7320dd495ebdf9683ee6bb6d3db76dd 100644 (file)
@@ -607,7 +607,7 @@ static int tc6393xb_probe(struct platform_device *dev)
        struct tc6393xb_platform_data *tcpd = dev_get_platdata(&dev->dev);
        struct tc6393xb *tc6393xb;
        struct resource *iomem, *rscr;
-       int ret, temp;
+       int ret;
 
        iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
        if (!iomem)
@@ -714,7 +714,7 @@ err_setup:
 
 err_gpio_add:
        if (tc6393xb->gpio.base != -1)
-               temp = gpiochip_remove(&tc6393xb->gpio);
+               gpiochip_remove(&tc6393xb->gpio);
        tcpd->disable(dev);
 err_enable:
        clk_disable(tc6393xb->clk);
@@ -744,13 +744,8 @@ static int tc6393xb_remove(struct platform_device *dev)
 
        tc6393xb_detach_irq(dev);
 
-       if (tc6393xb->gpio.base != -1) {
-               ret = gpiochip_remove(&tc6393xb->gpio);
-               if (ret) {
-                       dev_err(&dev->dev, "Can't remove gpio chip: %d\n", ret);
-                       return ret;
-               }
-       }
+       if (tc6393xb->gpio.base != -1)
+               gpiochip_remove(&tc6393xb->gpio);
 
        ret = tcpd->disable(dev);
        clk_disable(tc6393xb->clk);
index 153d595afaacb50306bd017a1893c8f27b1c0ca3..58ea9fdd3a15c07c2331544e78e001d99f4abfcf 100644 (file)
@@ -621,7 +621,6 @@ static void ucb1x00_remove(struct mcp *mcp)
        struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data;
        struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
        struct list_head *l, *n;
-       int ret;
 
        mutex_lock(&ucb1x00_mutex);
        list_del(&ucb->node);
@@ -631,11 +630,8 @@ static void ucb1x00_remove(struct mcp *mcp)
        }
        mutex_unlock(&ucb1x00_mutex);
 
-       if (ucb->gpio.base != -1) {
-               ret = gpiochip_remove(&ucb->gpio);
-               if (ret)
-                       dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret);
-       }
+       if (ucb->gpio.base != -1)
+               gpiochip_remove(&ucb->gpio);
 
        irq_set_chained_handler(ucb->irq, NULL);
        irq_free_descs(ucb->irq_base, 16);
index b841180c7c742f57f63f1345453720b4bb9725a3..bbeb4516facf239b7bce979f6993ae38f1e4e459 100644 (file)
@@ -527,4 +527,5 @@ source "drivers/misc/vmw_vmci/Kconfig"
 source "drivers/misc/mic/Kconfig"
 source "drivers/misc/genwqe/Kconfig"
 source "drivers/misc/echo/Kconfig"
+source "drivers/misc/cxl/Kconfig"
 endmenu
index 5497d026e651f47ffdaaf8bcac9a67cd30c4b2a9..7d5c4cd118c44fbf052425d2d61e3ec3cc1ae455 100644 (file)
@@ -55,3 +55,4 @@ obj-y                         += mic/
 obj-$(CONFIG_GENWQE)           += genwqe/
 obj-$(CONFIG_ECHO)             += echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)  += vexpress-syscfg.o
+obj-$(CONFIG_CXL_BASE)         += cxl/
diff --git a/drivers/misc/cxl/Kconfig b/drivers/misc/cxl/Kconfig
new file mode 100644 (file)
index 0000000..a990b39
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# IBM Coherent Accelerator (CXL) compatible devices
+#
+
+config CXL_BASE
+       bool
+       default n
+       select PPC_COPRO_BASE
+
+config CXL
+       tristate "Support for IBM Coherent Accelerators (CXL)"
+       depends on PPC_POWERNV && PCI_MSI
+       select CXL_BASE
+       default m
+       help
+         Select this option to enable driver support for IBM Coherent
+         Accelerators (CXL).  CXL is otherwise known as Coherent Accelerator
+         Processor Interface (CAPI).  CAPI allows accelerators in FPGAs to be
+         coherently attached to a CPU via an MMU.  This driver enables
+         userspace programs to access these accelerators via /dev/cxl/afuM.N
+         devices.
+
+         CAPI adapters are found in POWER8 based systems.
+
+         If unsure, say N.
diff --git a/drivers/misc/cxl/Makefile b/drivers/misc/cxl/Makefile
new file mode 100644 (file)
index 0000000..165e98f
--- /dev/null
@@ -0,0 +1,3 @@
+cxl-y                          += main.o file.o irq.o fault.o native.o context.o sysfs.o debugfs.o pci.o
+obj-$(CONFIG_CXL)              += cxl.o
+obj-$(CONFIG_CXL_BASE)         += base.o
diff --git a/drivers/misc/cxl/base.c b/drivers/misc/cxl/base.c
new file mode 100644 (file)
index 0000000..0654ad8
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/rcupdate.h>
+#include <asm/errno.h>
+#include <misc/cxl.h>
+#include "cxl.h"
+
+/* protected by rcu */
+static struct cxl_calls *cxl_calls;
+
+atomic_t cxl_use_count = ATOMIC_INIT(0);
+EXPORT_SYMBOL(cxl_use_count);
+
+#ifdef CONFIG_CXL_MODULE
+
+static inline struct cxl_calls *cxl_calls_get(void)
+{
+       struct cxl_calls *calls = NULL;
+
+       rcu_read_lock();
+       calls = rcu_dereference(cxl_calls);
+       if (calls && !try_module_get(calls->owner))
+               calls = NULL;
+       rcu_read_unlock();
+
+       return calls;
+}
+
+static inline void cxl_calls_put(struct cxl_calls *calls)
+{
+       BUG_ON(calls != cxl_calls);
+
+       /* we don't need to rcu this, as we hold a reference to the module */
+       module_put(cxl_calls->owner);
+}
+
+#else /* !defined CONFIG_CXL_MODULE */
+
+static inline struct cxl_calls *cxl_calls_get(void)
+{
+       return cxl_calls;
+}
+
+static inline void cxl_calls_put(struct cxl_calls *calls) { }
+
+#endif /* CONFIG_CXL_MODULE */
+
+void cxl_slbia(struct mm_struct *mm)
+{
+       struct cxl_calls *calls;
+
+       calls = cxl_calls_get();
+       if (!calls)
+               return;
+
+       if (cxl_ctx_in_use())
+           calls->cxl_slbia(mm);
+
+       cxl_calls_put(calls);
+}
+
+int register_cxl_calls(struct cxl_calls *calls)
+{
+       if (cxl_calls)
+               return -EBUSY;
+
+       rcu_assign_pointer(cxl_calls, calls);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(register_cxl_calls);
+
+void unregister_cxl_calls(struct cxl_calls *calls)
+{
+       BUG_ON(cxl_calls->owner != calls->owner);
+       RCU_INIT_POINTER(cxl_calls, NULL);
+       synchronize_rcu();
+}
+EXPORT_SYMBOL_GPL(unregister_cxl_calls);
diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c
new file mode 100644 (file)
index 0000000..cca4721
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/sched.h>
+#include <linux/pid.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <asm/cputable.h>
+#include <asm/current.h>
+#include <asm/copro.h>
+
+#include "cxl.h"
+
+/*
+ * Allocates space for a CXL context.
+ */
+struct cxl_context *cxl_context_alloc(void)
+{
+       return kzalloc(sizeof(struct cxl_context), GFP_KERNEL);
+}
+
+/*
+ * Initialises a CXL context.
+ */
+int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master)
+{
+       int i;
+
+       spin_lock_init(&ctx->sste_lock);
+       ctx->afu = afu;
+       ctx->master = master;
+       ctx->pid = NULL; /* Set in start work ioctl */
+
+       /*
+        * Allocate the segment table before we put it in the IDR so that we
+        * can always access it when dereferenced from IDR. For the same
+        * reason, the segment table is only destroyed after the context is
+        * removed from the IDR.  Access to this in the IOCTL is protected by
+        * Linux filesytem symantics (can't IOCTL until open is complete).
+        */
+       i = cxl_alloc_sst(ctx);
+       if (i)
+               return i;
+
+       INIT_WORK(&ctx->fault_work, cxl_handle_fault);
+
+       init_waitqueue_head(&ctx->wq);
+       spin_lock_init(&ctx->lock);
+
+       ctx->irq_bitmap = NULL;
+       ctx->pending_irq = false;
+       ctx->pending_fault = false;
+       ctx->pending_afu_err = false;
+
+       /*
+        * When we have to destroy all contexts in cxl_context_detach_all() we
+        * end up with afu_release_irqs() called from inside a
+        * idr_for_each_entry(). Hence we need to make sure that anything
+        * dereferenced from this IDR is ok before we allocate the IDR here.
+        * This clears out the IRQ ranges to ensure this.
+        */
+       for (i = 0; i < CXL_IRQ_RANGES; i++)
+               ctx->irqs.range[i] = 0;
+
+       mutex_init(&ctx->status_mutex);
+
+       ctx->status = OPENED;
+
+       /*
+        * Allocating IDR! We better make sure everything's setup that
+        * dereferences from it.
+        */
+       idr_preload(GFP_KERNEL);
+       spin_lock(&afu->contexts_lock);
+       i = idr_alloc(&ctx->afu->contexts_idr, ctx, 0,
+                     ctx->afu->num_procs, GFP_NOWAIT);
+       spin_unlock(&afu->contexts_lock);
+       idr_preload_end();
+       if (i < 0)
+               return i;
+
+       ctx->pe = i;
+       ctx->elem = &ctx->afu->spa[i];
+       ctx->pe_inserted = false;
+       return 0;
+}
+
+/*
+ * Map a per-context mmio space into the given vma.
+ */
+int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma)
+{
+       u64 len = vma->vm_end - vma->vm_start;
+       len = min(len, ctx->psn_size);
+
+       if (ctx->afu->current_mode == CXL_MODE_DEDICATED) {
+               vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+               return vm_iomap_memory(vma, ctx->afu->psn_phys, ctx->afu->adapter->ps_size);
+       }
+
+       /* make sure there is a valid per process space for this AFU */
+       if ((ctx->master && !ctx->afu->psa) || (!ctx->afu->pp_psa)) {
+               pr_devel("AFU doesn't support mmio space\n");
+               return -EINVAL;
+       }
+
+       /* Can't mmap until the AFU is enabled */
+       if (!ctx->afu->enabled)
+               return -EBUSY;
+
+       pr_devel("%s: mmio physical: %llx pe: %i master:%i\n", __func__,
+                ctx->psn_phys, ctx->pe , ctx->master);
+
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+       return vm_iomap_memory(vma, ctx->psn_phys, len);
+}
+
+/*
+ * Detach a context from the hardware. This disables interrupts and doesn't
+ * return until all outstanding interrupts for this context have completed. The
+ * hardware should no longer access *ctx after this has returned.
+ */
+static void __detach_context(struct cxl_context *ctx)
+{
+       enum cxl_context_status status;
+
+       mutex_lock(&ctx->status_mutex);
+       status = ctx->status;
+       ctx->status = CLOSED;
+       mutex_unlock(&ctx->status_mutex);
+       if (status != STARTED)
+               return;
+
+       WARN_ON(cxl_detach_process(ctx));
+       afu_release_irqs(ctx);
+       flush_work(&ctx->fault_work); /* Only needed for dedicated process */
+       wake_up_all(&ctx->wq);
+}
+
+/*
+ * Detach the given context from the AFU. This doesn't actually
+ * free the context but it should stop the context running in hardware
+ * (ie. prevent this context from generating any further interrupts
+ * so that it can be freed).
+ */
+void cxl_context_detach(struct cxl_context *ctx)
+{
+       __detach_context(ctx);
+}
+
+/*
+ * Detach all contexts on the given AFU.
+ */
+void cxl_context_detach_all(struct cxl_afu *afu)
+{
+       struct cxl_context *ctx;
+       int tmp;
+
+       rcu_read_lock();
+       idr_for_each_entry(&afu->contexts_idr, ctx, tmp)
+               /*
+                * Anything done in here needs to be setup before the IDR is
+                * created and torn down after the IDR removed
+                */
+               __detach_context(ctx);
+       rcu_read_unlock();
+}
+
+void cxl_context_free(struct cxl_context *ctx)
+{
+       spin_lock(&ctx->afu->contexts_lock);
+       idr_remove(&ctx->afu->contexts_idr, ctx->pe);
+       spin_unlock(&ctx->afu->contexts_lock);
+       synchronize_rcu();
+
+       free_page((u64)ctx->sstp);
+       ctx->sstp = NULL;
+
+       put_pid(ctx->pid);
+       kfree(ctx);
+}
diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h
new file mode 100644 (file)
index 0000000..3d2b867
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#ifndef _CXL_H_
+#define _CXL_H_
+
+#include <linux/interrupt.h>
+#include <linux/semaphore.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/pid.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <asm/cputable.h>
+#include <asm/mmu.h>
+#include <asm/reg.h>
+#include <misc/cxl.h>
+
+#include <uapi/misc/cxl.h>
+
+extern uint cxl_verbose;
+
+#define CXL_TIMEOUT 5
+
+/*
+ * Bump version each time a user API change is made, whether it is
+ * backwards compatible ot not.
+ */
+#define CXL_API_VERSION 1
+#define CXL_API_VERSION_COMPATIBLE 1
+
+/*
+ * Opaque types to avoid accidentally passing registers for the wrong MMIO
+ *
+ * At the end of the day, I'm not married to using typedef here, but it might
+ * (and has!) help avoid bugs like mixing up CXL_PSL_CtxTime and
+ * CXL_PSL_CtxTime_An, or calling cxl_p1n_write instead of cxl_p1_write.
+ *
+ * I'm quite happy if these are changed back to #defines before upstreaming, it
+ * should be little more than a regexp search+replace operation in this file.
+ */
+typedef struct {
+       const int x;
+} cxl_p1_reg_t;
+typedef struct {
+       const int x;
+} cxl_p1n_reg_t;
+typedef struct {
+       const int x;
+} cxl_p2n_reg_t;
+#define cxl_reg_off(reg) \
+       (reg.x)
+
+/* Memory maps. Ref CXL Appendix A */
+
+/* PSL Privilege 1 Memory Map */
+/* Configuration and Control area */
+static const cxl_p1_reg_t CXL_PSL_CtxTime = {0x0000};
+static const cxl_p1_reg_t CXL_PSL_ErrIVTE = {0x0008};
+static const cxl_p1_reg_t CXL_PSL_KEY1    = {0x0010};
+static const cxl_p1_reg_t CXL_PSL_KEY2    = {0x0018};
+static const cxl_p1_reg_t CXL_PSL_Control = {0x0020};
+/* Downloading */
+static const cxl_p1_reg_t CXL_PSL_DLCNTL  = {0x0060};
+static const cxl_p1_reg_t CXL_PSL_DLADDR  = {0x0068};
+
+/* PSL Lookaside Buffer Management Area */
+static const cxl_p1_reg_t CXL_PSL_LBISEL  = {0x0080};
+static const cxl_p1_reg_t CXL_PSL_SLBIE   = {0x0088};
+static const cxl_p1_reg_t CXL_PSL_SLBIA   = {0x0090};
+static const cxl_p1_reg_t CXL_PSL_TLBIE   = {0x00A0};
+static const cxl_p1_reg_t CXL_PSL_TLBIA   = {0x00A8};
+static const cxl_p1_reg_t CXL_PSL_AFUSEL  = {0x00B0};
+
+/* 0x00C0:7EFF Implementation dependent area */
+static const cxl_p1_reg_t CXL_PSL_FIR1      = {0x0100};
+static const cxl_p1_reg_t CXL_PSL_FIR2      = {0x0108};
+static const cxl_p1_reg_t CXL_PSL_VERSION   = {0x0118};
+static const cxl_p1_reg_t CXL_PSL_RESLCKTO  = {0x0128};
+static const cxl_p1_reg_t CXL_PSL_FIR_CNTL  = {0x0148};
+static const cxl_p1_reg_t CXL_PSL_DSNDCTL   = {0x0150};
+static const cxl_p1_reg_t CXL_PSL_SNWRALLOC = {0x0158};
+static const cxl_p1_reg_t CXL_PSL_TRACE     = {0x0170};
+/* 0x7F00:7FFF Reserved PCIe MSI-X Pending Bit Array area */
+/* 0x8000:FFFF Reserved PCIe MSI-X Table Area */
+
+/* PSL Slice Privilege 1 Memory Map */
+/* Configuration Area */
+static const cxl_p1n_reg_t CXL_PSL_SR_An          = {0x00};
+static const cxl_p1n_reg_t CXL_PSL_LPID_An        = {0x08};
+static const cxl_p1n_reg_t CXL_PSL_AMBAR_An       = {0x10};
+static const cxl_p1n_reg_t CXL_PSL_SPOffset_An    = {0x18};
+static const cxl_p1n_reg_t CXL_PSL_ID_An          = {0x20};
+static const cxl_p1n_reg_t CXL_PSL_SERR_An        = {0x28};
+/* Memory Management and Lookaside Buffer Management */
+static const cxl_p1n_reg_t CXL_PSL_SDR_An         = {0x30};
+static const cxl_p1n_reg_t CXL_PSL_AMOR_An        = {0x38};
+/* Pointer Area */
+static const cxl_p1n_reg_t CXL_HAURP_An           = {0x80};
+static const cxl_p1n_reg_t CXL_PSL_SPAP_An        = {0x88};
+static const cxl_p1n_reg_t CXL_PSL_LLCMD_An       = {0x90};
+/* Control Area */
+static const cxl_p1n_reg_t CXL_PSL_SCNTL_An       = {0xA0};
+static const cxl_p1n_reg_t CXL_PSL_CtxTime_An     = {0xA8};
+static const cxl_p1n_reg_t CXL_PSL_IVTE_Offset_An = {0xB0};
+static const cxl_p1n_reg_t CXL_PSL_IVTE_Limit_An  = {0xB8};
+/* 0xC0:FF Implementation Dependent Area */
+static const cxl_p1n_reg_t CXL_PSL_FIR_SLICE_An   = {0xC0};
+static const cxl_p1n_reg_t CXL_AFU_DEBUG_An       = {0xC8};
+static const cxl_p1n_reg_t CXL_PSL_APCALLOC_A     = {0xD0};
+static const cxl_p1n_reg_t CXL_PSL_COALLOC_A      = {0xD8};
+static const cxl_p1n_reg_t CXL_PSL_RXCTL_A        = {0xE0};
+static const cxl_p1n_reg_t CXL_PSL_SLICE_TRACE    = {0xE8};
+
+/* PSL Slice Privilege 2 Memory Map */
+/* Configuration and Control Area */
+static const cxl_p2n_reg_t CXL_PSL_PID_TID_An = {0x000};
+static const cxl_p2n_reg_t CXL_CSRP_An        = {0x008};
+static const cxl_p2n_reg_t CXL_AURP0_An       = {0x010};
+static const cxl_p2n_reg_t CXL_AURP1_An       = {0x018};
+static const cxl_p2n_reg_t CXL_SSTP0_An       = {0x020};
+static const cxl_p2n_reg_t CXL_SSTP1_An       = {0x028};
+static const cxl_p2n_reg_t CXL_PSL_AMR_An     = {0x030};
+/* Segment Lookaside Buffer Management */
+static const cxl_p2n_reg_t CXL_SLBIE_An       = {0x040};
+static const cxl_p2n_reg_t CXL_SLBIA_An       = {0x048};
+static const cxl_p2n_reg_t CXL_SLBI_Select_An = {0x050};
+/* Interrupt Registers */
+static const cxl_p2n_reg_t CXL_PSL_DSISR_An   = {0x060};
+static const cxl_p2n_reg_t CXL_PSL_DAR_An     = {0x068};
+static const cxl_p2n_reg_t CXL_PSL_DSR_An     = {0x070};
+static const cxl_p2n_reg_t CXL_PSL_TFC_An     = {0x078};
+static const cxl_p2n_reg_t CXL_PSL_PEHandle_An = {0x080};
+static const cxl_p2n_reg_t CXL_PSL_ErrStat_An = {0x088};
+/* AFU Registers */
+static const cxl_p2n_reg_t CXL_AFU_Cntl_An    = {0x090};
+static const cxl_p2n_reg_t CXL_AFU_ERR_An     = {0x098};
+/* Work Element Descriptor */
+static const cxl_p2n_reg_t CXL_PSL_WED_An     = {0x0A0};
+/* 0x0C0:FFF Implementation Dependent Area */
+
+#define CXL_PSL_SPAP_Addr 0x0ffffffffffff000ULL
+#define CXL_PSL_SPAP_Size 0x0000000000000ff0ULL
+#define CXL_PSL_SPAP_Size_Shift 4
+#define CXL_PSL_SPAP_V    0x0000000000000001ULL
+
+/****** CXL_PSL_DLCNTL *****************************************************/
+#define CXL_PSL_DLCNTL_D (0x1ull << (63-28))
+#define CXL_PSL_DLCNTL_C (0x1ull << (63-29))
+#define CXL_PSL_DLCNTL_E (0x1ull << (63-30))
+#define CXL_PSL_DLCNTL_S (0x1ull << (63-31))
+#define CXL_PSL_DLCNTL_CE (CXL_PSL_DLCNTL_C | CXL_PSL_DLCNTL_E)
+#define CXL_PSL_DLCNTL_DCES (CXL_PSL_DLCNTL_D | CXL_PSL_DLCNTL_CE | CXL_PSL_DLCNTL_S)
+
+/****** CXL_PSL_SR_An ******************************************************/
+#define CXL_PSL_SR_An_SF  MSR_SF            /* 64bit */
+#define CXL_PSL_SR_An_TA  (1ull << (63-1))  /* Tags active,   GA1: 0 */
+#define CXL_PSL_SR_An_HV  MSR_HV            /* Hypervisor,    GA1: 0 */
+#define CXL_PSL_SR_An_PR  MSR_PR            /* Problem state, GA1: 1 */
+#define CXL_PSL_SR_An_ISL (1ull << (63-53)) /* Ignore Segment Large Page */
+#define CXL_PSL_SR_An_TC  (1ull << (63-54)) /* Page Table secondary hash */
+#define CXL_PSL_SR_An_US  (1ull << (63-56)) /* User state,    GA1: X */
+#define CXL_PSL_SR_An_SC  (1ull << (63-58)) /* Segment Table secondary hash */
+#define CXL_PSL_SR_An_R   MSR_DR            /* Relocate,      GA1: 1 */
+#define CXL_PSL_SR_An_MP  (1ull << (63-62)) /* Master Process */
+#define CXL_PSL_SR_An_LE  (1ull << (63-63)) /* Little Endian */
+
+/****** CXL_PSL_LLCMD_An ****************************************************/
+#define CXL_LLCMD_TERMINATE   0x0001000000000000ULL
+#define CXL_LLCMD_REMOVE      0x0002000000000000ULL
+#define CXL_LLCMD_SUSPEND     0x0003000000000000ULL
+#define CXL_LLCMD_RESUME      0x0004000000000000ULL
+#define CXL_LLCMD_ADD         0x0005000000000000ULL
+#define CXL_LLCMD_UPDATE      0x0006000000000000ULL
+#define CXL_LLCMD_HANDLE_MASK 0x000000000000ffffULL
+
+/****** CXL_PSL_ID_An ****************************************************/
+#define CXL_PSL_ID_An_F        (1ull << (63-31))
+#define CXL_PSL_ID_An_L        (1ull << (63-30))
+
+/****** CXL_PSL_SCNTL_An ****************************************************/
+#define CXL_PSL_SCNTL_An_CR          (0x1ull << (63-15))
+/* Programming Modes: */
+#define CXL_PSL_SCNTL_An_PM_MASK     (0xffffull << (63-31))
+#define CXL_PSL_SCNTL_An_PM_Shared   (0x0000ull << (63-31))
+#define CXL_PSL_SCNTL_An_PM_OS       (0x0001ull << (63-31))
+#define CXL_PSL_SCNTL_An_PM_Process  (0x0002ull << (63-31))
+#define CXL_PSL_SCNTL_An_PM_AFU      (0x0004ull << (63-31))
+#define CXL_PSL_SCNTL_An_PM_AFU_PBT  (0x0104ull << (63-31))
+/* Purge Status (ro) */
+#define CXL_PSL_SCNTL_An_Ps_MASK     (0x3ull << (63-39))
+#define CXL_PSL_SCNTL_An_Ps_Pending  (0x1ull << (63-39))
+#define CXL_PSL_SCNTL_An_Ps_Complete (0x3ull << (63-39))
+/* Purge */
+#define CXL_PSL_SCNTL_An_Pc          (0x1ull << (63-48))
+/* Suspend Status (ro) */
+#define CXL_PSL_SCNTL_An_Ss_MASK     (0x3ull << (63-55))
+#define CXL_PSL_SCNTL_An_Ss_Pending  (0x1ull << (63-55))
+#define CXL_PSL_SCNTL_An_Ss_Complete (0x3ull << (63-55))
+/* Suspend Control */
+#define CXL_PSL_SCNTL_An_Sc          (0x1ull << (63-63))
+
+/* AFU Slice Enable Status (ro) */
+#define CXL_AFU_Cntl_An_ES_MASK     (0x7ull << (63-2))
+#define CXL_AFU_Cntl_An_ES_Disabled (0x0ull << (63-2))
+#define CXL_AFU_Cntl_An_ES_Enabled  (0x4ull << (63-2))
+/* AFU Slice Enable */
+#define CXL_AFU_Cntl_An_E           (0x1ull << (63-3))
+/* AFU Slice Reset status (ro) */
+#define CXL_AFU_Cntl_An_RS_MASK     (0x3ull << (63-5))
+#define CXL_AFU_Cntl_An_RS_Pending  (0x1ull << (63-5))
+#define CXL_AFU_Cntl_An_RS_Complete (0x2ull << (63-5))
+/* AFU Slice Reset */
+#define CXL_AFU_Cntl_An_RA          (0x1ull << (63-7))
+
+/****** CXL_SSTP0/1_An ******************************************************/
+/* These top bits are for the segment that CONTAINS the segment table */
+#define CXL_SSTP0_An_B_SHIFT    SLB_VSID_SSIZE_SHIFT
+#define CXL_SSTP0_An_KS             (1ull << (63-2))
+#define CXL_SSTP0_An_KP             (1ull << (63-3))
+#define CXL_SSTP0_An_N              (1ull << (63-4))
+#define CXL_SSTP0_An_L              (1ull << (63-5))
+#define CXL_SSTP0_An_C              (1ull << (63-6))
+#define CXL_SSTP0_An_TA             (1ull << (63-7))
+#define CXL_SSTP0_An_LP_SHIFT                (63-9)  /* 2 Bits */
+/* And finally, the virtual address & size of the segment table: */
+#define CXL_SSTP0_An_SegTableSize_SHIFT      (63-31) /* 12 Bits */
+#define CXL_SSTP0_An_SegTableSize_MASK \
+       (((1ull << 12) - 1) << CXL_SSTP0_An_SegTableSize_SHIFT)
+#define CXL_SSTP0_An_STVA_U_MASK   ((1ull << (63-49))-1)
+#define CXL_SSTP1_An_STVA_L_MASK (~((1ull << (63-55))-1))
+#define CXL_SSTP1_An_V              (1ull << (63-63))
+
+/****** CXL_PSL_SLBIE_[An] **************************************************/
+/* write: */
+#define CXL_SLBIE_C        PPC_BIT(36)         /* Class */
+#define CXL_SLBIE_SS       PPC_BITMASK(37, 38) /* Segment Size */
+#define CXL_SLBIE_SS_SHIFT PPC_BITLSHIFT(38)
+#define CXL_SLBIE_TA       PPC_BIT(38)         /* Tags Active */
+/* read: */
+#define CXL_SLBIE_MAX      PPC_BITMASK(24, 31)
+#define CXL_SLBIE_PENDING  PPC_BITMASK(56, 63)
+
+/****** Common to all CXL_TLBIA/SLBIA_[An] **********************************/
+#define CXL_TLB_SLB_P          (1ull) /* Pending (read) */
+
+/****** Common to all CXL_TLB/SLB_IA/IE_[An] registers **********************/
+#define CXL_TLB_SLB_IQ_ALL     (0ull) /* Inv qualifier */
+#define CXL_TLB_SLB_IQ_LPID    (1ull) /* Inv qualifier */
+#define CXL_TLB_SLB_IQ_LPIDPID (3ull) /* Inv qualifier */
+
+/****** CXL_PSL_AFUSEL ******************************************************/
+#define CXL_PSL_AFUSEL_A (1ull << (63-55)) /* Adapter wide invalidates affect all AFUs */
+
+/****** CXL_PSL_DSISR_An ****************************************************/
+#define CXL_PSL_DSISR_An_DS (1ull << (63-0))  /* Segment not found */
+#define CXL_PSL_DSISR_An_DM (1ull << (63-1))  /* PTE not found (See also: M) or protection fault */
+#define CXL_PSL_DSISR_An_ST (1ull << (63-2))  /* Segment Table PTE not found */
+#define CXL_PSL_DSISR_An_UR (1ull << (63-3))  /* AURP PTE not found */
+#define CXL_PSL_DSISR_TRANS (CXL_PSL_DSISR_An_DS | CXL_PSL_DSISR_An_DM | CXL_PSL_DSISR_An_ST | CXL_PSL_DSISR_An_UR)
+#define CXL_PSL_DSISR_An_PE (1ull << (63-4))  /* PSL Error (implementation specific) */
+#define CXL_PSL_DSISR_An_AE (1ull << (63-5))  /* AFU Error */
+#define CXL_PSL_DSISR_An_OC (1ull << (63-6))  /* OS Context Warning */
+/* NOTE: Bits 32:63 are undefined if DSISR[DS] = 1 */
+#define CXL_PSL_DSISR_An_M  DSISR_NOHPTE      /* PTE not found */
+#define CXL_PSL_DSISR_An_P  DSISR_PROTFAULT   /* Storage protection violation */
+#define CXL_PSL_DSISR_An_A  (1ull << (63-37)) /* AFU lock access to write through or cache inhibited storage */
+#define CXL_PSL_DSISR_An_S  DSISR_ISSTORE     /* Access was afu_wr or afu_zero */
+#define CXL_PSL_DSISR_An_K  DSISR_KEYFAULT    /* Access not permitted by virtual page class key protection */
+
+/****** CXL_PSL_TFC_An ******************************************************/
+#define CXL_PSL_TFC_An_A  (1ull << (63-28)) /* Acknowledge non-translation fault */
+#define CXL_PSL_TFC_An_C  (1ull << (63-29)) /* Continue (abort transaction) */
+#define CXL_PSL_TFC_An_AE (1ull << (63-30)) /* Restart PSL with address error */
+#define CXL_PSL_TFC_An_R  (1ull << (63-31)) /* Restart PSL transaction */
+
+/* cxl_process_element->software_status */
+#define CXL_PE_SOFTWARE_STATE_V (1ul << (31 -  0)) /* Valid */
+#define CXL_PE_SOFTWARE_STATE_C (1ul << (31 - 29)) /* Complete */
+#define CXL_PE_SOFTWARE_STATE_S (1ul << (31 - 30)) /* Suspend */
+#define CXL_PE_SOFTWARE_STATE_T (1ul << (31 - 31)) /* Terminate */
+
+/* SPA->sw_command_status */
+#define CXL_SPA_SW_CMD_MASK         0xffff000000000000ULL
+#define CXL_SPA_SW_CMD_TERMINATE    0x0001000000000000ULL
+#define CXL_SPA_SW_CMD_REMOVE       0x0002000000000000ULL
+#define CXL_SPA_SW_CMD_SUSPEND      0x0003000000000000ULL
+#define CXL_SPA_SW_CMD_RESUME       0x0004000000000000ULL
+#define CXL_SPA_SW_CMD_ADD          0x0005000000000000ULL
+#define CXL_SPA_SW_CMD_UPDATE       0x0006000000000000ULL
+#define CXL_SPA_SW_STATE_MASK       0x0000ffff00000000ULL
+#define CXL_SPA_SW_STATE_TERMINATED 0x0000000100000000ULL
+#define CXL_SPA_SW_STATE_REMOVED    0x0000000200000000ULL
+#define CXL_SPA_SW_STATE_SUSPENDED  0x0000000300000000ULL
+#define CXL_SPA_SW_STATE_RESUMED    0x0000000400000000ULL
+#define CXL_SPA_SW_STATE_ADDED      0x0000000500000000ULL
+#define CXL_SPA_SW_STATE_UPDATED    0x0000000600000000ULL
+#define CXL_SPA_SW_PSL_ID_MASK      0x00000000ffff0000ULL
+#define CXL_SPA_SW_LINK_MASK        0x000000000000ffffULL
+
+#define CXL_MAX_SLICES 4
+#define MAX_AFU_MMIO_REGS 3
+
+#define CXL_MODE_DEDICATED   0x1
+#define CXL_MODE_DIRECTED    0x2
+#define CXL_MODE_TIME_SLICED 0x4
+#define CXL_SUPPORTED_MODES (CXL_MODE_DEDICATED | CXL_MODE_DIRECTED)
+
+enum cxl_context_status {
+       CLOSED,
+       OPENED,
+       STARTED
+};
+
+enum prefault_modes {
+       CXL_PREFAULT_NONE,
+       CXL_PREFAULT_WED,
+       CXL_PREFAULT_ALL,
+};
+
+struct cxl_sste {
+       __be64 esid_data;
+       __be64 vsid_data;
+};
+
+#define to_cxl_adapter(d) container_of(d, struct cxl, dev)
+#define to_cxl_afu(d) container_of(d, struct cxl_afu, dev)
+
+struct cxl_afu {
+       irq_hw_number_t psl_hwirq;
+       irq_hw_number_t serr_hwirq;
+       unsigned int serr_virq;
+       void __iomem *p1n_mmio;
+       void __iomem *p2n_mmio;
+       phys_addr_t psn_phys;
+       u64 pp_offset;
+       u64 pp_size;
+       void __iomem *afu_desc_mmio;
+       struct cxl *adapter;
+       struct device dev;
+       struct cdev afu_cdev_s, afu_cdev_m, afu_cdev_d;
+       struct device *chardev_s, *chardev_m, *chardev_d;
+       struct idr contexts_idr;
+       struct dentry *debugfs;
+       spinlock_t contexts_lock;
+       struct mutex spa_mutex;
+       spinlock_t afu_cntl_lock;
+
+       /*
+        * Only the first part of the SPA is used for the process element
+        * linked list. The only other part that software needs to worry about
+        * is sw_command_status, which we store a separate pointer to.
+        * Everything else in the SPA is only used by hardware
+        */
+       struct cxl_process_element *spa;
+       __be64 *sw_command_status;
+       unsigned int spa_size;
+       int spa_order;
+       int spa_max_procs;
+       unsigned int psl_virq;
+
+       int pp_irqs;
+       int irqs_max;
+       int num_procs;
+       int max_procs_virtualised;
+       int slice;
+       int modes_supported;
+       int current_mode;
+       enum prefault_modes prefault_mode;
+       bool psa;
+       bool pp_psa;
+       bool enabled;
+};
+
+/*
+ * This is a cxl context.  If the PSL is in dedicated mode, there will be one
+ * of these per AFU.  If in AFU directed there can be lots of these.
+ */
+struct cxl_context {
+       struct cxl_afu *afu;
+
+       /* Problem state MMIO */
+       phys_addr_t psn_phys;
+       u64 psn_size;
+
+       spinlock_t sste_lock; /* Protects segment table entries */
+       struct cxl_sste *sstp;
+       u64 sstp0, sstp1;
+       unsigned int sst_size, sst_lru;
+
+       wait_queue_head_t wq;
+       struct pid *pid;
+       spinlock_t lock; /* Protects pending_irq_mask, pending_fault and fault_addr */
+       /* Only used in PR mode */
+       u64 process_token;
+
+       unsigned long *irq_bitmap; /* Accessed from IRQ context */
+       struct cxl_irq_ranges irqs;
+       u64 fault_addr;
+       u64 fault_dsisr;
+       u64 afu_err;
+
+       /*
+        * This status and it's lock pretects start and detach context
+        * from racing.  It also prevents detach from racing with
+        * itself
+        */
+       enum cxl_context_status status;
+       struct mutex status_mutex;
+
+
+       /* XXX: Is it possible to need multiple work items at once? */
+       struct work_struct fault_work;
+       u64 dsisr;
+       u64 dar;
+
+       struct cxl_process_element *elem;
+
+       int pe; /* process element handle */
+       u32 irq_count;
+       bool pe_inserted;
+       bool master;
+       bool kernel;
+       bool pending_irq;
+       bool pending_fault;
+       bool pending_afu_err;
+};
+
+struct cxl {
+       void __iomem *p1_mmio;
+       void __iomem *p2_mmio;
+       irq_hw_number_t err_hwirq;
+       unsigned int err_virq;
+       spinlock_t afu_list_lock;
+       struct cxl_afu *afu[CXL_MAX_SLICES];
+       struct device dev;
+       struct dentry *trace;
+       struct dentry *psl_err_chk;
+       struct dentry *debugfs;
+       struct bin_attribute cxl_attr;
+       int adapter_num;
+       int user_irqs;
+       u64 afu_desc_off;
+       u64 afu_desc_size;
+       u64 ps_off;
+       u64 ps_size;
+       u16 psl_rev;
+       u16 base_image;
+       u8 vsec_status;
+       u8 caia_major;
+       u8 caia_minor;
+       u8 slices;
+       bool user_image_loaded;
+       bool perst_loads_image;
+       bool perst_select_user;
+};
+
+int cxl_alloc_one_irq(struct cxl *adapter);
+void cxl_release_one_irq(struct cxl *adapter, int hwirq);
+int cxl_alloc_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter, unsigned int num);
+void cxl_release_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter);
+int cxl_setup_irq(struct cxl *adapter, unsigned int hwirq, unsigned int virq);
+
+/* common == phyp + powernv */
+struct cxl_process_element_common {
+       __be32 tid;
+       __be32 pid;
+       __be64 csrp;
+       __be64 aurp0;
+       __be64 aurp1;
+       __be64 sstp0;
+       __be64 sstp1;
+       __be64 amr;
+       u8     reserved3[4];
+       __be64 wed;
+} __packed;
+
+/* just powernv */
+struct cxl_process_element {
+       __be64 sr;
+       __be64 SPOffset;
+       __be64 sdr;
+       __be64 haurp;
+       __be32 ctxtime;
+       __be16 ivte_offsets[4];
+       __be16 ivte_ranges[4];
+       __be32 lpid;
+       struct cxl_process_element_common common;
+       __be32 software_state;
+} __packed;
+
+static inline void __iomem *_cxl_p1_addr(struct cxl *cxl, cxl_p1_reg_t reg)
+{
+       WARN_ON(!cpu_has_feature(CPU_FTR_HVMODE));
+       return cxl->p1_mmio + cxl_reg_off(reg);
+}
+
+#define cxl_p1_write(cxl, reg, val) \
+       out_be64(_cxl_p1_addr(cxl, reg), val)
+#define cxl_p1_read(cxl, reg) \
+       in_be64(_cxl_p1_addr(cxl, reg))
+
+static inline void __iomem *_cxl_p1n_addr(struct cxl_afu *afu, cxl_p1n_reg_t reg)
+{
+       WARN_ON(!cpu_has_feature(CPU_FTR_HVMODE));
+       return afu->p1n_mmio + cxl_reg_off(reg);
+}
+
+#define cxl_p1n_write(afu, reg, val) \
+       out_be64(_cxl_p1n_addr(afu, reg), val)
+#define cxl_p1n_read(afu, reg) \
+       in_be64(_cxl_p1n_addr(afu, reg))
+
+static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg)
+{
+       return afu->p2n_mmio + cxl_reg_off(reg);
+}
+
+#define cxl_p2n_write(afu, reg, val) \
+       out_be64(_cxl_p2n_addr(afu, reg), val)
+#define cxl_p2n_read(afu, reg) \
+       in_be64(_cxl_p2n_addr(afu, reg))
+
+struct cxl_calls {
+       void (*cxl_slbia)(struct mm_struct *mm);
+       struct module *owner;
+};
+int register_cxl_calls(struct cxl_calls *calls);
+void unregister_cxl_calls(struct cxl_calls *calls);
+
+int cxl_alloc_adapter_nr(struct cxl *adapter);
+void cxl_remove_adapter_nr(struct cxl *adapter);
+
+int cxl_file_init(void);
+void cxl_file_exit(void);
+int cxl_register_adapter(struct cxl *adapter);
+int cxl_register_afu(struct cxl_afu *afu);
+int cxl_chardev_d_afu_add(struct cxl_afu *afu);
+int cxl_chardev_m_afu_add(struct cxl_afu *afu);
+int cxl_chardev_s_afu_add(struct cxl_afu *afu);
+void cxl_chardev_afu_remove(struct cxl_afu *afu);
+
+void cxl_context_detach_all(struct cxl_afu *afu);
+void cxl_context_free(struct cxl_context *ctx);
+void cxl_context_detach(struct cxl_context *ctx);
+
+int cxl_sysfs_adapter_add(struct cxl *adapter);
+void cxl_sysfs_adapter_remove(struct cxl *adapter);
+int cxl_sysfs_afu_add(struct cxl_afu *afu);
+void cxl_sysfs_afu_remove(struct cxl_afu *afu);
+int cxl_sysfs_afu_m_add(struct cxl_afu *afu);
+void cxl_sysfs_afu_m_remove(struct cxl_afu *afu);
+
+int cxl_afu_activate_mode(struct cxl_afu *afu, int mode);
+int _cxl_afu_deactivate_mode(struct cxl_afu *afu, int mode);
+int cxl_afu_deactivate_mode(struct cxl_afu *afu);
+int cxl_afu_select_best_mode(struct cxl_afu *afu);
+
+unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq,
+                        irq_handler_t handler, void *cookie);
+void cxl_unmap_irq(unsigned int virq, void *cookie);
+int cxl_register_psl_irq(struct cxl_afu *afu);
+void cxl_release_psl_irq(struct cxl_afu *afu);
+int cxl_register_psl_err_irq(struct cxl *adapter);
+void cxl_release_psl_err_irq(struct cxl *adapter);
+int cxl_register_serr_irq(struct cxl_afu *afu);
+void cxl_release_serr_irq(struct cxl_afu *afu);
+int afu_register_irqs(struct cxl_context *ctx, u32 count);
+void afu_release_irqs(struct cxl_context *ctx);
+irqreturn_t cxl_slice_irq_err(int irq, void *data);
+
+int cxl_debugfs_init(void);
+void cxl_debugfs_exit(void);
+int cxl_debugfs_adapter_add(struct cxl *adapter);
+void cxl_debugfs_adapter_remove(struct cxl *adapter);
+int cxl_debugfs_afu_add(struct cxl_afu *afu);
+void cxl_debugfs_afu_remove(struct cxl_afu *afu);
+
+void cxl_handle_fault(struct work_struct *work);
+void cxl_prefault(struct cxl_context *ctx, u64 wed);
+
+struct cxl *get_cxl_adapter(int num);
+int cxl_alloc_sst(struct cxl_context *ctx);
+
+void init_cxl_native(void);
+
+struct cxl_context *cxl_context_alloc(void);
+int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master);
+void cxl_context_free(struct cxl_context *ctx);
+int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma);
+
+/* This matches the layout of the H_COLLECT_CA_INT_INFO retbuf */
+struct cxl_irq_info {
+       u64 dsisr;
+       u64 dar;
+       u64 dsr;
+       u32 pid;
+       u32 tid;
+       u64 afu_err;
+       u64 errstat;
+       u64 padding[3]; /* to match the expected retbuf size for plpar_hcall9 */
+};
+
+int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed,
+                           u64 amr);
+int cxl_detach_process(struct cxl_context *ctx);
+
+int cxl_get_irq(struct cxl_context *ctx, struct cxl_irq_info *info);
+int cxl_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask);
+
+int cxl_check_error(struct cxl_afu *afu);
+int cxl_afu_slbia(struct cxl_afu *afu);
+int cxl_tlb_slb_invalidate(struct cxl *adapter);
+int cxl_afu_disable(struct cxl_afu *afu);
+int cxl_afu_reset(struct cxl_afu *afu);
+int cxl_psl_purge(struct cxl_afu *afu);
+
+void cxl_stop_trace(struct cxl *cxl);
+
+extern struct pci_driver cxl_pci_driver;
+
+#endif
diff --git a/drivers/misc/cxl/debugfs.c b/drivers/misc/cxl/debugfs.c
new file mode 100644 (file)
index 0000000..825c412
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "cxl.h"
+
+static struct dentry *cxl_debugfs;
+
+void cxl_stop_trace(struct cxl *adapter)
+{
+       int slice;
+
+       /* Stop the trace */
+       cxl_p1_write(adapter, CXL_PSL_TRACE, 0x8000000000000017LL);
+
+       /* Stop the slice traces */
+       spin_lock(&adapter->afu_list_lock);
+       for (slice = 0; slice < adapter->slices; slice++) {
+               if (adapter->afu[slice])
+                       cxl_p1n_write(adapter->afu[slice], CXL_PSL_SLICE_TRACE, 0x8000000000000000LL);
+       }
+       spin_unlock(&adapter->afu_list_lock);
+}
+
+/* Helpers to export CXL mmaped IO registers via debugfs */
+static int debugfs_io_u64_get(void *data, u64 *val)
+{
+       *val = in_be64((u64 __iomem *)data);
+       return 0;
+}
+
+static int debugfs_io_u64_set(void *data, u64 val)
+{
+       out_be64((u64 __iomem *)data, val);
+       return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_io_x64, debugfs_io_u64_get, debugfs_io_u64_set, "0x%016llx\n");
+
+static struct dentry *debugfs_create_io_x64(const char *name, umode_t mode,
+                                           struct dentry *parent, u64 __iomem *value)
+{
+       return debugfs_create_file(name, mode, parent, (void *)value, &fops_io_x64);
+}
+
+int cxl_debugfs_adapter_add(struct cxl *adapter)
+{
+       struct dentry *dir;
+       char buf[32];
+
+       if (!cxl_debugfs)
+               return -ENODEV;
+
+       snprintf(buf, 32, "card%i", adapter->adapter_num);
+       dir = debugfs_create_dir(buf, cxl_debugfs);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
+       adapter->debugfs = dir;
+
+       debugfs_create_io_x64("fir1",     S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_FIR1));
+       debugfs_create_io_x64("fir2",     S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_FIR2));
+       debugfs_create_io_x64("fir_cntl", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_FIR_CNTL));
+       debugfs_create_io_x64("err_ivte", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_ErrIVTE));
+
+       debugfs_create_io_x64("trace", S_IRUSR | S_IWUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_TRACE));
+
+       return 0;
+}
+
+void cxl_debugfs_adapter_remove(struct cxl *adapter)
+{
+       debugfs_remove_recursive(adapter->debugfs);
+}
+
+int cxl_debugfs_afu_add(struct cxl_afu *afu)
+{
+       struct dentry *dir;
+       char buf[32];
+
+       if (!afu->adapter->debugfs)
+               return -ENODEV;
+
+       snprintf(buf, 32, "psl%i.%i", afu->adapter->adapter_num, afu->slice);
+       dir = debugfs_create_dir(buf, afu->adapter->debugfs);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
+       afu->debugfs = dir;
+
+       debugfs_create_io_x64("fir",        S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_FIR_SLICE_An));
+       debugfs_create_io_x64("serr",       S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_SERR_An));
+       debugfs_create_io_x64("afu_debug",  S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_AFU_DEBUG_An));
+       debugfs_create_io_x64("sr",         S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_SR_An));
+
+       debugfs_create_io_x64("dsisr",      S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_PSL_DSISR_An));
+       debugfs_create_io_x64("dar",        S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_PSL_DAR_An));
+       debugfs_create_io_x64("sstp0",      S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_SSTP0_An));
+       debugfs_create_io_x64("sstp1",      S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_SSTP1_An));
+       debugfs_create_io_x64("err_status", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_PSL_ErrStat_An));
+
+       debugfs_create_io_x64("trace", S_IRUSR | S_IWUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_SLICE_TRACE));
+
+       return 0;
+}
+
+void cxl_debugfs_afu_remove(struct cxl_afu *afu)
+{
+       debugfs_remove_recursive(afu->debugfs);
+}
+
+int __init cxl_debugfs_init(void)
+{
+       struct dentry *ent;
+       ent = debugfs_create_dir("cxl", NULL);
+       if (IS_ERR(ent))
+               return PTR_ERR(ent);
+       cxl_debugfs = ent;
+
+       return 0;
+}
+
+void cxl_debugfs_exit(void)
+{
+       debugfs_remove_recursive(cxl_debugfs);
+}
diff --git a/drivers/misc/cxl/fault.c b/drivers/misc/cxl/fault.c
new file mode 100644 (file)
index 0000000..69506eb
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/pid.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "cxl" "."
+#include <asm/current.h>
+#include <asm/copro.h>
+#include <asm/mmu.h>
+
+#include "cxl.h"
+
+static struct cxl_sste* find_free_sste(struct cxl_sste *primary_group,
+                                      bool sec_hash,
+                                      struct cxl_sste *secondary_group,
+                                      unsigned int *lru)
+{
+       unsigned int i, entry;
+       struct cxl_sste *sste, *group = primary_group;
+
+       for (i = 0; i < 2; i++) {
+               for (entry = 0; entry < 8; entry++) {
+                       sste = group + entry;
+                       if (!(be64_to_cpu(sste->esid_data) & SLB_ESID_V))
+                               return sste;
+               }
+               if (!sec_hash)
+                       break;
+               group = secondary_group;
+       }
+       /* Nothing free, select an entry to cast out */
+       if (sec_hash && (*lru & 0x8))
+               sste = secondary_group + (*lru & 0x7);
+       else
+               sste = primary_group + (*lru & 0x7);
+       *lru = (*lru + 1) & 0xf;
+
+       return sste;
+}
+
+static void cxl_load_segment(struct cxl_context *ctx, struct copro_slb *slb)
+{
+       /* mask is the group index, we search primary and secondary here. */
+       unsigned int mask = (ctx->sst_size >> 7)-1; /* SSTP0[SegTableSize] */
+       bool sec_hash = 1;
+       struct cxl_sste *sste;
+       unsigned int hash;
+       unsigned long flags;
+
+
+       sec_hash = !!(cxl_p1n_read(ctx->afu, CXL_PSL_SR_An) & CXL_PSL_SR_An_SC);
+
+       if (slb->vsid & SLB_VSID_B_1T)
+               hash = (slb->esid >> SID_SHIFT_1T) & mask;
+       else /* 256M */
+               hash = (slb->esid >> SID_SHIFT) & mask;
+
+       spin_lock_irqsave(&ctx->sste_lock, flags);
+       sste = find_free_sste(ctx->sstp + (hash << 3), sec_hash,
+                             ctx->sstp + ((~hash & mask) << 3), &ctx->sst_lru);
+
+       pr_devel("CXL Populating SST[%li]: %#llx %#llx\n",
+                       sste - ctx->sstp, slb->vsid, slb->esid);
+
+       sste->vsid_data = cpu_to_be64(slb->vsid);
+       sste->esid_data = cpu_to_be64(slb->esid);
+       spin_unlock_irqrestore(&ctx->sste_lock, flags);
+}
+
+static int cxl_fault_segment(struct cxl_context *ctx, struct mm_struct *mm,
+                            u64 ea)
+{
+       struct copro_slb slb = {0,0};
+       int rc;
+
+       if (!(rc = copro_calculate_slb(mm, ea, &slb))) {
+               cxl_load_segment(ctx, &slb);
+       }
+
+       return rc;
+}
+
+static void cxl_ack_ae(struct cxl_context *ctx)
+{
+       unsigned long flags;
+
+       cxl_ack_irq(ctx, CXL_PSL_TFC_An_AE, 0);
+
+       spin_lock_irqsave(&ctx->lock, flags);
+       ctx->pending_fault = true;
+       ctx->fault_addr = ctx->dar;
+       ctx->fault_dsisr = ctx->dsisr;
+       spin_unlock_irqrestore(&ctx->lock, flags);
+
+       wake_up_all(&ctx->wq);
+}
+
+static int cxl_handle_segment_miss(struct cxl_context *ctx,
+                                  struct mm_struct *mm, u64 ea)
+{
+       int rc;
+
+       pr_devel("CXL interrupt: Segment fault pe: %i ea: %#llx\n", ctx->pe, ea);
+
+       if ((rc = cxl_fault_segment(ctx, mm, ea)))
+               cxl_ack_ae(ctx);
+       else {
+
+               mb(); /* Order seg table write to TFC MMIO write */
+               cxl_ack_irq(ctx, CXL_PSL_TFC_An_R, 0);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void cxl_handle_page_fault(struct cxl_context *ctx,
+                                 struct mm_struct *mm, u64 dsisr, u64 dar)
+{
+       unsigned flt = 0;
+       int result;
+       unsigned long access, flags;
+
+       if ((result = copro_handle_mm_fault(mm, dar, dsisr, &flt))) {
+               pr_devel("copro_handle_mm_fault failed: %#x\n", result);
+               return cxl_ack_ae(ctx);
+       }
+
+       /*
+        * update_mmu_cache() will not have loaded the hash since current->trap
+        * is not a 0x400 or 0x300, so just call hash_page_mm() here.
+        */
+       access = _PAGE_PRESENT;
+       if (dsisr & CXL_PSL_DSISR_An_S)
+               access |= _PAGE_RW;
+       if ((!ctx->kernel) || ~(dar & (1ULL << 63)))
+               access |= _PAGE_USER;
+       local_irq_save(flags);
+       hash_page_mm(mm, dar, access, 0x300);
+       local_irq_restore(flags);
+
+       pr_devel("Page fault successfully handled for pe: %i!\n", ctx->pe);
+       cxl_ack_irq(ctx, CXL_PSL_TFC_An_R, 0);
+}
+
+void cxl_handle_fault(struct work_struct *fault_work)
+{
+       struct cxl_context *ctx =
+               container_of(fault_work, struct cxl_context, fault_work);
+       u64 dsisr = ctx->dsisr;
+       u64 dar = ctx->dar;
+       struct task_struct *task;
+       struct mm_struct *mm;
+
+       if (cxl_p2n_read(ctx->afu, CXL_PSL_DSISR_An) != dsisr ||
+           cxl_p2n_read(ctx->afu, CXL_PSL_DAR_An) != dar ||
+           cxl_p2n_read(ctx->afu, CXL_PSL_PEHandle_An) != ctx->pe) {
+               /* Most likely explanation is harmless - a dedicated process
+                * has detached and these were cleared by the PSL purge, but
+                * warn about it just in case */
+               dev_notice(&ctx->afu->dev, "cxl_handle_fault: Translation fault regs changed\n");
+               return;
+       }
+
+       pr_devel("CXL BOTTOM HALF handling fault for afu pe: %i. "
+               "DSISR: %#llx DAR: %#llx\n", ctx->pe, dsisr, dar);
+
+       if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) {
+               pr_devel("cxl_handle_fault unable to get task %i\n",
+                        pid_nr(ctx->pid));
+               cxl_ack_ae(ctx);
+               return;
+       }
+       if (!(mm = get_task_mm(task))) {
+               pr_devel("cxl_handle_fault unable to get mm %i\n",
+                        pid_nr(ctx->pid));
+               cxl_ack_ae(ctx);
+               goto out;
+       }
+
+       if (dsisr & CXL_PSL_DSISR_An_DS)
+               cxl_handle_segment_miss(ctx, mm, dar);
+       else if (dsisr & CXL_PSL_DSISR_An_DM)
+               cxl_handle_page_fault(ctx, mm, dsisr, dar);
+       else
+               WARN(1, "cxl_handle_fault has nothing to handle\n");
+
+       mmput(mm);
+out:
+       put_task_struct(task);
+}
+
+static void cxl_prefault_one(struct cxl_context *ctx, u64 ea)
+{
+       int rc;
+       struct task_struct *task;
+       struct mm_struct *mm;
+
+       if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) {
+               pr_devel("cxl_prefault_one unable to get task %i\n",
+                        pid_nr(ctx->pid));
+               return;
+       }
+       if (!(mm = get_task_mm(task))) {
+               pr_devel("cxl_prefault_one unable to get mm %i\n",
+                        pid_nr(ctx->pid));
+               put_task_struct(task);
+               return;
+       }
+
+       rc = cxl_fault_segment(ctx, mm, ea);
+
+       mmput(mm);
+       put_task_struct(task);
+}
+
+static u64 next_segment(u64 ea, u64 vsid)
+{
+       if (vsid & SLB_VSID_B_1T)
+               ea |= (1ULL << 40) - 1;
+       else
+               ea |= (1ULL << 28) - 1;
+
+       return ea + 1;
+}
+
+static void cxl_prefault_vma(struct cxl_context *ctx)
+{
+       u64 ea, last_esid = 0;
+       struct copro_slb slb;
+       struct vm_area_struct *vma;
+       int rc;
+       struct task_struct *task;
+       struct mm_struct *mm;
+
+       if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) {
+               pr_devel("cxl_prefault_vma unable to get task %i\n",
+                        pid_nr(ctx->pid));
+               return;
+       }
+       if (!(mm = get_task_mm(task))) {
+               pr_devel("cxl_prefault_vm unable to get mm %i\n",
+                        pid_nr(ctx->pid));
+               goto out1;
+       }
+
+       down_read(&mm->mmap_sem);
+       for (vma = mm->mmap; vma; vma = vma->vm_next) {
+               for (ea = vma->vm_start; ea < vma->vm_end;
+                               ea = next_segment(ea, slb.vsid)) {
+                       rc = copro_calculate_slb(mm, ea, &slb);
+                       if (rc)
+                               continue;
+
+                       if (last_esid == slb.esid)
+                               continue;
+
+                       cxl_load_segment(ctx, &slb);
+                       last_esid = slb.esid;
+               }
+       }
+       up_read(&mm->mmap_sem);
+
+       mmput(mm);
+out1:
+       put_task_struct(task);
+}
+
+void cxl_prefault(struct cxl_context *ctx, u64 wed)
+{
+       switch (ctx->afu->prefault_mode) {
+       case CXL_PREFAULT_WED:
+               cxl_prefault_one(ctx, wed);
+               break;
+       case CXL_PREFAULT_ALL:
+               cxl_prefault_vma(ctx);
+               break;
+       default:
+               break;
+       }
+}
diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c
new file mode 100644 (file)
index 0000000..378b099
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/pid.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/cputable.h>
+#include <asm/current.h>
+#include <asm/copro.h>
+
+#include "cxl.h"
+
+#define CXL_NUM_MINORS 256 /* Total to reserve */
+#define CXL_DEV_MINORS 13   /* 1 control + 4 AFUs * 3 (dedicated/master/shared) */
+
+#define CXL_CARD_MINOR(adapter) (adapter->adapter_num * CXL_DEV_MINORS)
+#define CXL_AFU_MINOR_D(afu) (CXL_CARD_MINOR(afu->adapter) + 1 + (3 * afu->slice))
+#define CXL_AFU_MINOR_M(afu) (CXL_AFU_MINOR_D(afu) + 1)
+#define CXL_AFU_MINOR_S(afu) (CXL_AFU_MINOR_D(afu) + 2)
+#define CXL_AFU_MKDEV_D(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_D(afu))
+#define CXL_AFU_MKDEV_M(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_M(afu))
+#define CXL_AFU_MKDEV_S(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_S(afu))
+
+#define CXL_DEVT_ADAPTER(dev) (MINOR(dev) / CXL_DEV_MINORS)
+#define CXL_DEVT_AFU(dev) ((MINOR(dev) % CXL_DEV_MINORS - 1) / 3)
+
+#define CXL_DEVT_IS_CARD(dev) (MINOR(dev) % CXL_DEV_MINORS == 0)
+
+static dev_t cxl_dev;
+
+static struct class *cxl_class;
+
+static int __afu_open(struct inode *inode, struct file *file, bool master)
+{
+       struct cxl *adapter;
+       struct cxl_afu *afu;
+       struct cxl_context *ctx;
+       int adapter_num = CXL_DEVT_ADAPTER(inode->i_rdev);
+       int slice = CXL_DEVT_AFU(inode->i_rdev);
+       int rc = -ENODEV;
+
+       pr_devel("afu_open afu%i.%i\n", slice, adapter_num);
+
+       if (!(adapter = get_cxl_adapter(adapter_num)))
+               return -ENODEV;
+
+       if (slice > adapter->slices)
+               goto err_put_adapter;
+
+       spin_lock(&adapter->afu_list_lock);
+       if (!(afu = adapter->afu[slice])) {
+               spin_unlock(&adapter->afu_list_lock);
+               goto err_put_adapter;
+       }
+       get_device(&afu->dev);
+       spin_unlock(&adapter->afu_list_lock);
+
+       if (!afu->current_mode)
+               goto err_put_afu;
+
+       if (!(ctx = cxl_context_alloc())) {
+               rc = -ENOMEM;
+               goto err_put_afu;
+       }
+
+       if ((rc = cxl_context_init(ctx, afu, master)))
+               goto err_put_afu;
+
+       pr_devel("afu_open pe: %i\n", ctx->pe);
+       file->private_data = ctx;
+       cxl_ctx_get();
+
+       /* Our ref on the AFU will now hold the adapter */
+       put_device(&adapter->dev);
+
+       return 0;
+
+err_put_afu:
+       put_device(&afu->dev);
+err_put_adapter:
+       put_device(&adapter->dev);
+       return rc;
+}
+static int afu_open(struct inode *inode, struct file *file)
+{
+       return __afu_open(inode, file, false);
+}
+
+static int afu_master_open(struct inode *inode, struct file *file)
+{
+       return __afu_open(inode, file, true);
+}
+
+static int afu_release(struct inode *inode, struct file *file)
+{
+       struct cxl_context *ctx = file->private_data;
+
+       pr_devel("%s: closing cxl file descriptor. pe: %i\n",
+                __func__, ctx->pe);
+       cxl_context_detach(ctx);
+
+       put_device(&ctx->afu->dev);
+
+       /*
+        * At this this point all bottom halfs have finished and we should be
+        * getting no more IRQs from the hardware for this context.  Once it's
+        * removed from the IDR (and RCU synchronised) it's safe to free the
+        * sstp and context.
+        */
+       cxl_context_free(ctx);
+
+       cxl_ctx_put();
+       return 0;
+}
+
+static long afu_ioctl_start_work(struct cxl_context *ctx,
+                                struct cxl_ioctl_start_work __user *uwork)
+{
+       struct cxl_ioctl_start_work work;
+       u64 amr = 0;
+       int rc;
+
+       pr_devel("%s: pe: %i\n", __func__, ctx->pe);
+
+       mutex_lock(&ctx->status_mutex);
+       if (ctx->status != OPENED) {
+               rc = -EIO;
+               goto out;
+       }
+
+       if (copy_from_user(&work, uwork,
+                          sizeof(struct cxl_ioctl_start_work))) {
+               rc = -EFAULT;
+               goto out;
+       }
+
+       /*
+        * if any of the reserved fields are set or any of the unused
+        * flags are set it's invalid
+        */
+       if (work.reserved1 || work.reserved2 || work.reserved3 ||
+           work.reserved4 || work.reserved5 || work.reserved6 ||
+           (work.flags & ~CXL_START_WORK_ALL)) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       if (!(work.flags & CXL_START_WORK_NUM_IRQS))
+               work.num_interrupts = ctx->afu->pp_irqs;
+       else if ((work.num_interrupts < ctx->afu->pp_irqs) ||
+                (work.num_interrupts > ctx->afu->irqs_max)) {
+               rc =  -EINVAL;
+               goto out;
+       }
+       if ((rc = afu_register_irqs(ctx, work.num_interrupts)))
+               goto out;
+
+       if (work.flags & CXL_START_WORK_AMR)
+               amr = work.amr & mfspr(SPRN_UAMOR);
+
+       /*
+        * We grab the PID here and not in the file open to allow for the case
+        * where a process (master, some daemon, etc) has opened the chardev on
+        * behalf of another process, so the AFU's mm gets bound to the process
+        * that performs this ioctl and not the process that opened the file.
+        */
+       ctx->pid = get_pid(get_task_pid(current, PIDTYPE_PID));
+
+       if ((rc = cxl_attach_process(ctx, false, work.work_element_descriptor,
+                                    amr)))
+               goto out;
+
+       ctx->status = STARTED;
+       rc = 0;
+out:
+       mutex_unlock(&ctx->status_mutex);
+       return rc;
+}
+static long afu_ioctl_process_element(struct cxl_context *ctx,
+                                     int __user *upe)
+{
+       pr_devel("%s: pe: %i\n", __func__, ctx->pe);
+
+       if (copy_to_user(upe, &ctx->pe, sizeof(__u32)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct cxl_context *ctx = file->private_data;
+
+       if (ctx->status == CLOSED)
+               return -EIO;
+
+       pr_devel("afu_ioctl\n");
+       switch (cmd) {
+       case CXL_IOCTL_START_WORK:
+               return afu_ioctl_start_work(ctx, (struct cxl_ioctl_start_work __user *)arg);
+       case CXL_IOCTL_GET_PROCESS_ELEMENT:
+               return afu_ioctl_process_element(ctx, (__u32 __user *)arg);
+       }
+       return -EINVAL;
+}
+
+static long afu_compat_ioctl(struct file *file, unsigned int cmd,
+                            unsigned long arg)
+{
+       return afu_ioctl(file, cmd, arg);
+}
+
+static int afu_mmap(struct file *file, struct vm_area_struct *vm)
+{
+       struct cxl_context *ctx = file->private_data;
+
+       /* AFU must be started before we can MMIO */
+       if (ctx->status != STARTED)
+               return -EIO;
+
+       return cxl_context_iomap(ctx, vm);
+}
+
+static unsigned int afu_poll(struct file *file, struct poll_table_struct *poll)
+{
+       struct cxl_context *ctx = file->private_data;
+       int mask = 0;
+       unsigned long flags;
+
+
+       poll_wait(file, &ctx->wq, poll);
+
+       pr_devel("afu_poll wait done pe: %i\n", ctx->pe);
+
+       spin_lock_irqsave(&ctx->lock, flags);
+       if (ctx->pending_irq || ctx->pending_fault ||
+           ctx->pending_afu_err)
+               mask |= POLLIN | POLLRDNORM;
+       else if (ctx->status == CLOSED)
+               /* Only error on closed when there are no futher events pending
+                */
+               mask |= POLLERR;
+       spin_unlock_irqrestore(&ctx->lock, flags);
+
+       pr_devel("afu_poll pe: %i returning %#x\n", ctx->pe, mask);
+
+       return mask;
+}
+
+static inline int ctx_event_pending(struct cxl_context *ctx)
+{
+       return (ctx->pending_irq || ctx->pending_fault ||
+           ctx->pending_afu_err || (ctx->status == CLOSED));
+}
+
+static ssize_t afu_read(struct file *file, char __user *buf, size_t count,
+                       loff_t *off)
+{
+       struct cxl_context *ctx = file->private_data;
+       struct cxl_event event;
+       unsigned long flags;
+       int rc;
+       DEFINE_WAIT(wait);
+
+       if (count < CXL_READ_MIN_SIZE)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ctx->lock, flags);
+
+       for (;;) {
+               prepare_to_wait(&ctx->wq, &wait, TASK_INTERRUPTIBLE);
+               if (ctx_event_pending(ctx))
+                       break;
+
+               if (file->f_flags & O_NONBLOCK) {
+                       rc = -EAGAIN;
+                       goto out;
+               }
+
+               if (signal_pending(current)) {
+                       rc = -ERESTARTSYS;
+                       goto out;
+               }
+
+               spin_unlock_irqrestore(&ctx->lock, flags);
+               pr_devel("afu_read going to sleep...\n");
+               schedule();
+               pr_devel("afu_read woken up\n");
+               spin_lock_irqsave(&ctx->lock, flags);
+       }
+
+       finish_wait(&ctx->wq, &wait);
+
+       memset(&event, 0, sizeof(event));
+       event.header.process_element = ctx->pe;
+       event.header.size = sizeof(struct cxl_event_header);
+       if (ctx->pending_irq) {
+               pr_devel("afu_read delivering AFU interrupt\n");
+               event.header.size += sizeof(struct cxl_event_afu_interrupt);
+               event.header.type = CXL_EVENT_AFU_INTERRUPT;
+               event.irq.irq = find_first_bit(ctx->irq_bitmap, ctx->irq_count) + 1;
+               clear_bit(event.irq.irq - 1, ctx->irq_bitmap);
+               if (bitmap_empty(ctx->irq_bitmap, ctx->irq_count))
+                       ctx->pending_irq = false;
+       } else if (ctx->pending_fault) {
+               pr_devel("afu_read delivering data storage fault\n");
+               event.header.size += sizeof(struct cxl_event_data_storage);
+               event.header.type = CXL_EVENT_DATA_STORAGE;
+               event.fault.addr = ctx->fault_addr;
+               event.fault.dsisr = ctx->fault_dsisr;
+               ctx->pending_fault = false;
+       } else if (ctx->pending_afu_err) {
+               pr_devel("afu_read delivering afu error\n");
+               event.header.size += sizeof(struct cxl_event_afu_error);
+               event.header.type = CXL_EVENT_AFU_ERROR;
+               event.afu_error.error = ctx->afu_err;
+               ctx->pending_afu_err = false;
+       } else if (ctx->status == CLOSED) {
+               pr_devel("afu_read fatal error\n");
+               spin_unlock_irqrestore(&ctx->lock, flags);
+               return -EIO;
+       } else
+               WARN(1, "afu_read must be buggy\n");
+
+       spin_unlock_irqrestore(&ctx->lock, flags);
+
+       if (copy_to_user(buf, &event, event.header.size))
+               return -EFAULT;
+       return event.header.size;
+
+out:
+       finish_wait(&ctx->wq, &wait);
+       spin_unlock_irqrestore(&ctx->lock, flags);
+       return rc;
+}
+
+static const struct file_operations afu_fops = {
+       .owner          = THIS_MODULE,
+       .open           = afu_open,
+       .poll           = afu_poll,
+       .read           = afu_read,
+       .release        = afu_release,
+       .unlocked_ioctl = afu_ioctl,
+       .compat_ioctl   = afu_compat_ioctl,
+       .mmap           = afu_mmap,
+};
+
+static const struct file_operations afu_master_fops = {
+       .owner          = THIS_MODULE,
+       .open           = afu_master_open,
+       .poll           = afu_poll,
+       .read           = afu_read,
+       .release        = afu_release,
+       .unlocked_ioctl = afu_ioctl,
+       .compat_ioctl   = afu_compat_ioctl,
+       .mmap           = afu_mmap,
+};
+
+
+static char *cxl_devnode(struct device *dev, umode_t *mode)
+{
+       if (CXL_DEVT_IS_CARD(dev->devt)) {
+               /*
+                * These minor numbers will eventually be used to program the
+                * PSL and AFUs once we have dynamic reprogramming support
+                */
+               return NULL;
+       }
+       return kasprintf(GFP_KERNEL, "cxl/%s", dev_name(dev));
+}
+
+extern struct class *cxl_class;
+
+static int cxl_add_chardev(struct cxl_afu *afu, dev_t devt, struct cdev *cdev,
+                          struct device **chardev, char *postfix, char *desc,
+                          const struct file_operations *fops)
+{
+       struct device *dev;
+       int rc;
+
+       cdev_init(cdev, fops);
+       if ((rc = cdev_add(cdev, devt, 1))) {
+               dev_err(&afu->dev, "Unable to add %s chardev: %i\n", desc, rc);
+               return rc;
+       }
+
+       dev = device_create(cxl_class, &afu->dev, devt, afu,
+                       "afu%i.%i%s", afu->adapter->adapter_num, afu->slice, postfix);
+       if (IS_ERR(dev)) {
+               dev_err(&afu->dev, "Unable to create %s chardev in sysfs: %i\n", desc, rc);
+               rc = PTR_ERR(dev);
+               goto err;
+       }
+
+       *chardev = dev;
+
+       return 0;
+err:
+       cdev_del(cdev);
+       return rc;
+}
+
+int cxl_chardev_d_afu_add(struct cxl_afu *afu)
+{
+       return cxl_add_chardev(afu, CXL_AFU_MKDEV_D(afu), &afu->afu_cdev_d,
+                              &afu->chardev_d, "d", "dedicated",
+                              &afu_master_fops); /* Uses master fops */
+}
+
+int cxl_chardev_m_afu_add(struct cxl_afu *afu)
+{
+       return cxl_add_chardev(afu, CXL_AFU_MKDEV_M(afu), &afu->afu_cdev_m,
+                              &afu->chardev_m, "m", "master",
+                              &afu_master_fops);
+}
+
+int cxl_chardev_s_afu_add(struct cxl_afu *afu)
+{
+       return cxl_add_chardev(afu, CXL_AFU_MKDEV_S(afu), &afu->afu_cdev_s,
+                              &afu->chardev_s, "s", "shared",
+                              &afu_fops);
+}
+
+void cxl_chardev_afu_remove(struct cxl_afu *afu)
+{
+       if (afu->chardev_d) {
+               cdev_del(&afu->afu_cdev_d);
+               device_unregister(afu->chardev_d);
+               afu->chardev_d = NULL;
+       }
+       if (afu->chardev_m) {
+               cdev_del(&afu->afu_cdev_m);
+               device_unregister(afu->chardev_m);
+               afu->chardev_m = NULL;
+       }
+       if (afu->chardev_s) {
+               cdev_del(&afu->afu_cdev_s);
+               device_unregister(afu->chardev_s);
+               afu->chardev_s = NULL;
+       }
+}
+
+int cxl_register_afu(struct cxl_afu *afu)
+{
+       afu->dev.class = cxl_class;
+
+       return device_register(&afu->dev);
+}
+
+int cxl_register_adapter(struct cxl *adapter)
+{
+       adapter->dev.class = cxl_class;
+
+       /*
+        * Future: When we support dynamically reprogramming the PSL & AFU we
+        * will expose the interface to do that via a chardev:
+        * adapter->dev.devt = CXL_CARD_MKDEV(adapter);
+        */
+
+       return device_register(&adapter->dev);
+}
+
+int __init cxl_file_init(void)
+{
+       int rc;
+
+       /*
+        * If these change we really need to update API.  Either change some
+        * flags or update API version number CXL_API_VERSION.
+        */
+       BUILD_BUG_ON(CXL_API_VERSION != 1);
+       BUILD_BUG_ON(sizeof(struct cxl_ioctl_start_work) != 64);
+       BUILD_BUG_ON(sizeof(struct cxl_event_header) != 8);
+       BUILD_BUG_ON(sizeof(struct cxl_event_afu_interrupt) != 8);
+       BUILD_BUG_ON(sizeof(struct cxl_event_data_storage) != 32);
+       BUILD_BUG_ON(sizeof(struct cxl_event_afu_error) != 16);
+
+       if ((rc = alloc_chrdev_region(&cxl_dev, 0, CXL_NUM_MINORS, "cxl"))) {
+               pr_err("Unable to allocate CXL major number: %i\n", rc);
+               return rc;
+       }
+
+       pr_devel("CXL device allocated, MAJOR %i\n", MAJOR(cxl_dev));
+
+       cxl_class = class_create(THIS_MODULE, "cxl");
+       if (IS_ERR(cxl_class)) {
+               pr_err("Unable to create CXL class\n");
+               rc = PTR_ERR(cxl_class);
+               goto err;
+       }
+       cxl_class->devnode = cxl_devnode;
+
+       return 0;
+
+err:
+       unregister_chrdev_region(cxl_dev, CXL_NUM_MINORS);
+       return rc;
+}
+
+void cxl_file_exit(void)
+{
+       unregister_chrdev_region(cxl_dev, CXL_NUM_MINORS);
+       class_destroy(cxl_class);
+}
diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c
new file mode 100644 (file)
index 0000000..336020c
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/pid.h>
+#include <asm/cputable.h>
+#include <misc/cxl.h>
+
+#include "cxl.h"
+
+/* XXX: This is implementation specific */
+static irqreturn_t handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr, u64 errstat)
+{
+       u64 fir1, fir2, fir_slice, serr, afu_debug;
+
+       fir1 = cxl_p1_read(ctx->afu->adapter, CXL_PSL_FIR1);
+       fir2 = cxl_p1_read(ctx->afu->adapter, CXL_PSL_FIR2);
+       fir_slice = cxl_p1n_read(ctx->afu, CXL_PSL_FIR_SLICE_An);
+       serr = cxl_p1n_read(ctx->afu, CXL_PSL_SERR_An);
+       afu_debug = cxl_p1n_read(ctx->afu, CXL_AFU_DEBUG_An);
+
+       dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat);
+       dev_crit(&ctx->afu->dev, "PSL_FIR1: 0x%.16llx\n", fir1);
+       dev_crit(&ctx->afu->dev, "PSL_FIR2: 0x%.16llx\n", fir2);
+       dev_crit(&ctx->afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr);
+       dev_crit(&ctx->afu->dev, "PSL_FIR_SLICE_An: 0x%.16llx\n", fir_slice);
+       dev_crit(&ctx->afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%.16llx\n", afu_debug);
+
+       dev_crit(&ctx->afu->dev, "STOPPING CXL TRACE\n");
+       cxl_stop_trace(ctx->afu->adapter);
+
+       return cxl_ack_irq(ctx, 0, errstat);
+}
+
+irqreturn_t cxl_slice_irq_err(int irq, void *data)
+{
+       struct cxl_afu *afu = data;
+       u64 fir_slice, errstat, serr, afu_debug;
+
+       WARN(irq, "CXL SLICE ERROR interrupt %i\n", irq);
+
+       serr = cxl_p1n_read(afu, CXL_PSL_SERR_An);
+       fir_slice = cxl_p1n_read(afu, CXL_PSL_FIR_SLICE_An);
+       errstat = cxl_p2n_read(afu, CXL_PSL_ErrStat_An);
+       afu_debug = cxl_p1n_read(afu, CXL_AFU_DEBUG_An);
+       dev_crit(&afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr);
+       dev_crit(&afu->dev, "PSL_FIR_SLICE_An: 0x%.16llx\n", fir_slice);
+       dev_crit(&afu->dev, "CXL_PSL_ErrStat_An: 0x%.16llx\n", errstat);
+       dev_crit(&afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%.16llx\n", afu_debug);
+
+       cxl_p1n_write(afu, CXL_PSL_SERR_An, serr);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t cxl_irq_err(int irq, void *data)
+{
+       struct cxl *adapter = data;
+       u64 fir1, fir2, err_ivte;
+
+       WARN(1, "CXL ERROR interrupt %i\n", irq);
+
+       err_ivte = cxl_p1_read(adapter, CXL_PSL_ErrIVTE);
+       dev_crit(&adapter->dev, "PSL_ErrIVTE: 0x%.16llx\n", err_ivte);
+
+       dev_crit(&adapter->dev, "STOPPING CXL TRACE\n");
+       cxl_stop_trace(adapter);
+
+       fir1 = cxl_p1_read(adapter, CXL_PSL_FIR1);
+       fir2 = cxl_p1_read(adapter, CXL_PSL_FIR2);
+
+       dev_crit(&adapter->dev, "PSL_FIR1: 0x%.16llx\nPSL_FIR2: 0x%.16llx\n", fir1, fir2);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t schedule_cxl_fault(struct cxl_context *ctx, u64 dsisr, u64 dar)
+{
+       ctx->dsisr = dsisr;
+       ctx->dar = dar;
+       schedule_work(&ctx->fault_work);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t cxl_irq(int irq, void *data)
+{
+       struct cxl_context *ctx = data;
+       struct cxl_irq_info irq_info;
+       u64 dsisr, dar;
+       int result;
+
+       if ((result = cxl_get_irq(ctx, &irq_info))) {
+               WARN(1, "Unable to get CXL IRQ Info: %i\n", result);
+               return IRQ_HANDLED;
+       }
+
+       dsisr = irq_info.dsisr;
+       dar = irq_info.dar;
+
+       pr_devel("CXL interrupt %i for afu pe: %i DSISR: %#llx DAR: %#llx\n", irq, ctx->pe, dsisr, dar);
+
+       if (dsisr & CXL_PSL_DSISR_An_DS) {
+               /*
+                * We don't inherently need to sleep to handle this, but we do
+                * need to get a ref to the task's mm, which we can't do from
+                * irq context without the potential for a deadlock since it
+                * takes the task_lock. An alternate option would be to keep a
+                * reference to the task's mm the entire time it has cxl open,
+                * but to do that we need to solve the issue where we hold a
+                * ref to the mm, but the mm can hold a ref to the fd after an
+                * mmap preventing anything from being cleaned up.
+                */
+               pr_devel("Scheduling segment miss handling for later pe: %i\n", ctx->pe);
+               return schedule_cxl_fault(ctx, dsisr, dar);
+       }
+
+       if (dsisr & CXL_PSL_DSISR_An_M)
+               pr_devel("CXL interrupt: PTE not found\n");
+       if (dsisr & CXL_PSL_DSISR_An_P)
+               pr_devel("CXL interrupt: Storage protection violation\n");
+       if (dsisr & CXL_PSL_DSISR_An_A)
+               pr_devel("CXL interrupt: AFU lock access to write through or cache inhibited storage\n");
+       if (dsisr & CXL_PSL_DSISR_An_S)
+               pr_devel("CXL interrupt: Access was afu_wr or afu_zero\n");
+       if (dsisr & CXL_PSL_DSISR_An_K)
+               pr_devel("CXL interrupt: Access not permitted by virtual page class key protection\n");
+
+       if (dsisr & CXL_PSL_DSISR_An_DM) {
+               /*
+                * In some cases we might be able to handle the fault
+                * immediately if hash_page would succeed, but we still need
+                * the task's mm, which as above we can't get without a lock
+                */
+               pr_devel("Scheduling page fault handling for later pe: %i\n", ctx->pe);
+               return schedule_cxl_fault(ctx, dsisr, dar);
+       }
+       if (dsisr & CXL_PSL_DSISR_An_ST)
+               WARN(1, "CXL interrupt: Segment Table PTE not found\n");
+       if (dsisr & CXL_PSL_DSISR_An_UR)
+               pr_devel("CXL interrupt: AURP PTE not found\n");
+       if (dsisr & CXL_PSL_DSISR_An_PE)
+               return handle_psl_slice_error(ctx, dsisr, irq_info.errstat);
+       if (dsisr & CXL_PSL_DSISR_An_AE) {
+               pr_devel("CXL interrupt: AFU Error %.llx\n", irq_info.afu_err);
+
+               if (ctx->pending_afu_err) {
+                       /*
+                        * This shouldn't happen - the PSL treats these errors
+                        * as fatal and will have reset the AFU, so there's not
+                        * much point buffering multiple AFU errors.
+                        * OTOH if we DO ever see a storm of these come in it's
+                        * probably best that we log them somewhere:
+                        */
+                       dev_err_ratelimited(&ctx->afu->dev, "CXL AFU Error "
+                                           "undelivered to pe %i: %.llx\n",
+                                           ctx->pe, irq_info.afu_err);
+               } else {
+                       spin_lock(&ctx->lock);
+                       ctx->afu_err = irq_info.afu_err;
+                       ctx->pending_afu_err = 1;
+                       spin_unlock(&ctx->lock);
+
+                       wake_up_all(&ctx->wq);
+               }
+
+               cxl_ack_irq(ctx, CXL_PSL_TFC_An_A, 0);
+       }
+       if (dsisr & CXL_PSL_DSISR_An_OC)
+               pr_devel("CXL interrupt: OS Context Warning\n");
+
+       WARN(1, "Unhandled CXL PSL IRQ\n");
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t cxl_irq_multiplexed(int irq, void *data)
+{
+       struct cxl_afu *afu = data;
+       struct cxl_context *ctx;
+       int ph = cxl_p2n_read(afu, CXL_PSL_PEHandle_An) & 0xffff;
+       int ret;
+
+       rcu_read_lock();
+       ctx = idr_find(&afu->contexts_idr, ph);
+       if (ctx) {
+               ret = cxl_irq(irq, ctx);
+               rcu_read_unlock();
+               return ret;
+       }
+       rcu_read_unlock();
+
+       WARN(1, "Unable to demultiplex CXL PSL IRQ\n");
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t cxl_irq_afu(int irq, void *data)
+{
+       struct cxl_context *ctx = data;
+       irq_hw_number_t hwirq = irqd_to_hwirq(irq_get_irq_data(irq));
+       int irq_off, afu_irq = 1;
+       __u16 range;
+       int r;
+
+       for (r = 1; r < CXL_IRQ_RANGES; r++) {
+               irq_off = hwirq - ctx->irqs.offset[r];
+               range = ctx->irqs.range[r];
+               if (irq_off >= 0 && irq_off < range) {
+                       afu_irq += irq_off;
+                       break;
+               }
+               afu_irq += range;
+       }
+       if (unlikely(r >= CXL_IRQ_RANGES)) {
+               WARN(1, "Recieved AFU IRQ out of range for pe %i (virq %i hwirq %lx)\n",
+                    ctx->pe, irq, hwirq);
+               return IRQ_HANDLED;
+       }
+
+       pr_devel("Received AFU interrupt %i for pe: %i (virq %i hwirq %lx)\n",
+              afu_irq, ctx->pe, irq, hwirq);
+
+       if (unlikely(!ctx->irq_bitmap)) {
+               WARN(1, "Recieved AFU IRQ for context with no IRQ bitmap\n");
+               return IRQ_HANDLED;
+       }
+       spin_lock(&ctx->lock);
+       set_bit(afu_irq - 1, ctx->irq_bitmap);
+       ctx->pending_irq = true;
+       spin_unlock(&ctx->lock);
+
+       wake_up_all(&ctx->wq);
+
+       return IRQ_HANDLED;
+}
+
+unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq,
+                        irq_handler_t handler, void *cookie)
+{
+       unsigned int virq;
+       int result;
+
+       /* IRQ Domain? */
+       virq = irq_create_mapping(NULL, hwirq);
+       if (!virq) {
+               dev_warn(&adapter->dev, "cxl_map_irq: irq_create_mapping failed\n");
+               return 0;
+       }
+
+       cxl_setup_irq(adapter, hwirq, virq);
+
+       pr_devel("hwirq %#lx mapped to virq %u\n", hwirq, virq);
+
+       result = request_irq(virq, handler, 0, "cxl", cookie);
+       if (result) {
+               dev_warn(&adapter->dev, "cxl_map_irq: request_irq failed: %i\n", result);
+               return 0;
+       }
+
+       return virq;
+}
+
+void cxl_unmap_irq(unsigned int virq, void *cookie)
+{
+       free_irq(virq, cookie);
+       irq_dispose_mapping(virq);
+}
+
+static int cxl_register_one_irq(struct cxl *adapter,
+                               irq_handler_t handler,
+                               void *cookie,
+                               irq_hw_number_t *dest_hwirq,
+                               unsigned int *dest_virq)
+{
+       int hwirq, virq;
+
+       if ((hwirq = cxl_alloc_one_irq(adapter)) < 0)
+               return hwirq;
+
+       if (!(virq = cxl_map_irq(adapter, hwirq, handler, cookie)))
+               goto err;
+
+       *dest_hwirq = hwirq;
+       *dest_virq = virq;
+
+       return 0;
+
+err:
+       cxl_release_one_irq(adapter, hwirq);
+       return -ENOMEM;
+}
+
+int cxl_register_psl_err_irq(struct cxl *adapter)
+{
+       int rc;
+
+       if ((rc = cxl_register_one_irq(adapter, cxl_irq_err, adapter,
+                                      &adapter->err_hwirq,
+                                      &adapter->err_virq)))
+               return rc;
+
+       cxl_p1_write(adapter, CXL_PSL_ErrIVTE, adapter->err_hwirq & 0xffff);
+
+       return 0;
+}
+
+void cxl_release_psl_err_irq(struct cxl *adapter)
+{
+       cxl_p1_write(adapter, CXL_PSL_ErrIVTE, 0x0000000000000000);
+       cxl_unmap_irq(adapter->err_virq, adapter);
+       cxl_release_one_irq(adapter, adapter->err_hwirq);
+}
+
+int cxl_register_serr_irq(struct cxl_afu *afu)
+{
+       u64 serr;
+       int rc;
+
+       if ((rc = cxl_register_one_irq(afu->adapter, cxl_slice_irq_err, afu,
+                                      &afu->serr_hwirq,
+                                      &afu->serr_virq)))
+               return rc;
+
+       serr = cxl_p1n_read(afu, CXL_PSL_SERR_An);
+       serr = (serr & 0x00ffffffffff0000ULL) | (afu->serr_hwirq & 0xffff);
+       cxl_p1n_write(afu, CXL_PSL_SERR_An, serr);
+
+       return 0;
+}
+
+void cxl_release_serr_irq(struct cxl_afu *afu)
+{
+       cxl_p1n_write(afu, CXL_PSL_SERR_An, 0x0000000000000000);
+       cxl_unmap_irq(afu->serr_virq, afu);
+       cxl_release_one_irq(afu->adapter, afu->serr_hwirq);
+}
+
+int cxl_register_psl_irq(struct cxl_afu *afu)
+{
+       return cxl_register_one_irq(afu->adapter, cxl_irq_multiplexed, afu,
+                       &afu->psl_hwirq, &afu->psl_virq);
+}
+
+void cxl_release_psl_irq(struct cxl_afu *afu)
+{
+       cxl_unmap_irq(afu->psl_virq, afu);
+       cxl_release_one_irq(afu->adapter, afu->psl_hwirq);
+}
+
+int afu_register_irqs(struct cxl_context *ctx, u32 count)
+{
+       irq_hw_number_t hwirq;
+       int rc, r, i;
+
+       if ((rc = cxl_alloc_irq_ranges(&ctx->irqs, ctx->afu->adapter, count)))
+               return rc;
+
+       /* Multiplexed PSL Interrupt */
+       ctx->irqs.offset[0] = ctx->afu->psl_hwirq;
+       ctx->irqs.range[0] = 1;
+
+       ctx->irq_count = count;
+       ctx->irq_bitmap = kcalloc(BITS_TO_LONGS(count),
+                                 sizeof(*ctx->irq_bitmap), GFP_KERNEL);
+       if (!ctx->irq_bitmap)
+               return -ENOMEM;
+       for (r = 1; r < CXL_IRQ_RANGES; r++) {
+               hwirq = ctx->irqs.offset[r];
+               for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) {
+                       cxl_map_irq(ctx->afu->adapter, hwirq,
+                                    cxl_irq_afu, ctx);
+               }
+       }
+
+       return 0;
+}
+
+void afu_release_irqs(struct cxl_context *ctx)
+{
+       irq_hw_number_t hwirq;
+       unsigned int virq;
+       int r, i;
+
+       for (r = 1; r < CXL_IRQ_RANGES; r++) {
+               hwirq = ctx->irqs.offset[r];
+               for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) {
+                       virq = irq_find_mapping(NULL, hwirq);
+                       if (virq)
+                               cxl_unmap_irq(virq, ctx);
+               }
+       }
+
+       cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter);
+}
diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c
new file mode 100644 (file)
index 0000000..4cde9b6
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/pci.h>
+#include <asm/cputable.h>
+#include <misc/cxl.h>
+
+#include "cxl.h"
+
+static DEFINE_SPINLOCK(adapter_idr_lock);
+static DEFINE_IDR(cxl_adapter_idr);
+
+uint cxl_verbose;
+module_param_named(verbose, cxl_verbose, uint, 0600);
+MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
+
+static inline void _cxl_slbia(struct cxl_context *ctx, struct mm_struct *mm)
+{
+       struct task_struct *task;
+       unsigned long flags;
+       if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) {
+               pr_devel("%s unable to get task %i\n",
+                        __func__, pid_nr(ctx->pid));
+               return;
+       }
+
+       if (task->mm != mm)
+               goto out_put;
+
+       pr_devel("%s matched mm - card: %i afu: %i pe: %i\n", __func__,
+                ctx->afu->adapter->adapter_num, ctx->afu->slice, ctx->pe);
+
+       spin_lock_irqsave(&ctx->sste_lock, flags);
+       memset(ctx->sstp, 0, ctx->sst_size);
+       spin_unlock_irqrestore(&ctx->sste_lock, flags);
+       mb();
+       cxl_afu_slbia(ctx->afu);
+out_put:
+       put_task_struct(task);
+}
+
+static inline void cxl_slbia_core(struct mm_struct *mm)
+{
+       struct cxl *adapter;
+       struct cxl_afu *afu;
+       struct cxl_context *ctx;
+       int card, slice, id;
+
+       pr_devel("%s called\n", __func__);
+
+       spin_lock(&adapter_idr_lock);
+       idr_for_each_entry(&cxl_adapter_idr, adapter, card) {
+               /* XXX: Make this lookup faster with link from mm to ctx */
+               spin_lock(&adapter->afu_list_lock);
+               for (slice = 0; slice < adapter->slices; slice++) {
+                       afu = adapter->afu[slice];
+                       if (!afu->enabled)
+                               continue;
+                       rcu_read_lock();
+                       idr_for_each_entry(&afu->contexts_idr, ctx, id)
+                               _cxl_slbia(ctx, mm);
+                       rcu_read_unlock();
+               }
+               spin_unlock(&adapter->afu_list_lock);
+       }
+       spin_unlock(&adapter_idr_lock);
+}
+
+static struct cxl_calls cxl_calls = {
+       .cxl_slbia = cxl_slbia_core,
+       .owner = THIS_MODULE,
+};
+
+int cxl_alloc_sst(struct cxl_context *ctx)
+{
+       unsigned long vsid;
+       u64 ea_mask, size, sstp0, sstp1;
+
+       sstp0 = 0;
+       sstp1 = 0;
+
+       ctx->sst_size = PAGE_SIZE;
+       ctx->sst_lru = 0;
+       ctx->sstp = (struct cxl_sste *)get_zeroed_page(GFP_KERNEL);
+       if (!ctx->sstp) {
+               pr_err("cxl_alloc_sst: Unable to allocate segment table\n");
+               return -ENOMEM;
+       }
+       pr_devel("SSTP allocated at 0x%p\n", ctx->sstp);
+
+       vsid  = get_kernel_vsid((u64)ctx->sstp, mmu_kernel_ssize) << 12;
+
+       sstp0 |= (u64)mmu_kernel_ssize << CXL_SSTP0_An_B_SHIFT;
+       sstp0 |= (SLB_VSID_KERNEL | mmu_psize_defs[mmu_linear_psize].sllp) << 50;
+
+       size = (((u64)ctx->sst_size >> 8) - 1) << CXL_SSTP0_An_SegTableSize_SHIFT;
+       if (unlikely(size & ~CXL_SSTP0_An_SegTableSize_MASK)) {
+               WARN(1, "Impossible segment table size\n");
+               return -EINVAL;
+       }
+       sstp0 |= size;
+
+       if (mmu_kernel_ssize == MMU_SEGSIZE_256M)
+               ea_mask = 0xfffff00ULL;
+       else
+               ea_mask = 0xffffffff00ULL;
+
+       sstp0 |=  vsid >>     (50-14);  /*   Top 14 bits of VSID */
+       sstp1 |= (vsid << (64-(50-14))) & ~ea_mask;
+       sstp1 |= (u64)ctx->sstp & ea_mask;
+       sstp1 |= CXL_SSTP1_An_V;
+
+       pr_devel("Looked up %#llx: slbfee. %#llx (ssize: %x, vsid: %#lx), copied to SSTP0: %#llx, SSTP1: %#llx\n",
+                       (u64)ctx->sstp, (u64)ctx->sstp & ESID_MASK, mmu_kernel_ssize, vsid, sstp0, sstp1);
+
+       /* Store calculated sstp hardware points for use later */
+       ctx->sstp0 = sstp0;
+       ctx->sstp1 = sstp1;
+
+       return 0;
+}
+
+/* Find a CXL adapter by it's number and increase it's refcount */
+struct cxl *get_cxl_adapter(int num)
+{
+       struct cxl *adapter;
+
+       spin_lock(&adapter_idr_lock);
+       if ((adapter = idr_find(&cxl_adapter_idr, num)))
+               get_device(&adapter->dev);
+       spin_unlock(&adapter_idr_lock);
+
+       return adapter;
+}
+
+int cxl_alloc_adapter_nr(struct cxl *adapter)
+{
+       int i;
+
+       idr_preload(GFP_KERNEL);
+       spin_lock(&adapter_idr_lock);
+       i = idr_alloc(&cxl_adapter_idr, adapter, 0, 0, GFP_NOWAIT);
+       spin_unlock(&adapter_idr_lock);
+       idr_preload_end();
+       if (i < 0)
+               return i;
+
+       adapter->adapter_num = i;
+
+       return 0;
+}
+
+void cxl_remove_adapter_nr(struct cxl *adapter)
+{
+       idr_remove(&cxl_adapter_idr, adapter->adapter_num);
+}
+
+int cxl_afu_select_best_mode(struct cxl_afu *afu)
+{
+       if (afu->modes_supported & CXL_MODE_DIRECTED)
+               return cxl_afu_activate_mode(afu, CXL_MODE_DIRECTED);
+
+       if (afu->modes_supported & CXL_MODE_DEDICATED)
+               return cxl_afu_activate_mode(afu, CXL_MODE_DEDICATED);
+
+       dev_warn(&afu->dev, "No supported programming modes available\n");
+       /* We don't fail this so the user can inspect sysfs */
+       return 0;
+}
+
+static int __init init_cxl(void)
+{
+       int rc = 0;
+
+       if (!cpu_has_feature(CPU_FTR_HVMODE))
+               return -EPERM;
+
+       if ((rc = cxl_file_init()))
+               return rc;
+
+       cxl_debugfs_init();
+
+       if ((rc = register_cxl_calls(&cxl_calls)))
+               goto err;
+
+       if ((rc = pci_register_driver(&cxl_pci_driver)))
+               goto err1;
+
+       return 0;
+err1:
+       unregister_cxl_calls(&cxl_calls);
+err:
+       cxl_debugfs_exit();
+       cxl_file_exit();
+
+       return rc;
+}
+
+static void exit_cxl(void)
+{
+       pci_unregister_driver(&cxl_pci_driver);
+
+       cxl_debugfs_exit();
+       cxl_file_exit();
+       unregister_cxl_calls(&cxl_calls);
+}
+
+module_init(init_cxl);
+module_exit(exit_cxl);
+
+MODULE_DESCRIPTION("IBM Coherent Accelerator");
+MODULE_AUTHOR("Ian Munsie <imunsie@au1.ibm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c
new file mode 100644 (file)
index 0000000..623286a
--- /dev/null
@@ -0,0 +1,683 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include <asm/synch.h>
+#include <misc/cxl.h>
+
+#include "cxl.h"
+
+static int afu_control(struct cxl_afu *afu, u64 command,
+                      u64 result, u64 mask, bool enabled)
+{
+       u64 AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
+       unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT);
+
+       spin_lock(&afu->afu_cntl_lock);
+       pr_devel("AFU command starting: %llx\n", command);
+
+       cxl_p2n_write(afu, CXL_AFU_Cntl_An, AFU_Cntl | command);
+
+       AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
+       while ((AFU_Cntl & mask) != result) {
+               if (time_after_eq(jiffies, timeout)) {
+                       dev_warn(&afu->dev, "WARNING: AFU control timed out!\n");
+                       spin_unlock(&afu->afu_cntl_lock);
+                       return -EBUSY;
+               }
+               pr_devel_ratelimited("AFU control... (0x%.16llx)\n",
+                                    AFU_Cntl | command);
+               cpu_relax();
+               AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
+       };
+       pr_devel("AFU command complete: %llx\n", command);
+       afu->enabled = enabled;
+       spin_unlock(&afu->afu_cntl_lock);
+
+       return 0;
+}
+
+static int afu_enable(struct cxl_afu *afu)
+{
+       pr_devel("AFU enable request\n");
+
+       return afu_control(afu, CXL_AFU_Cntl_An_E,
+                          CXL_AFU_Cntl_An_ES_Enabled,
+                          CXL_AFU_Cntl_An_ES_MASK, true);
+}
+
+int cxl_afu_disable(struct cxl_afu *afu)
+{
+       pr_devel("AFU disable request\n");
+
+       return afu_control(afu, 0, CXL_AFU_Cntl_An_ES_Disabled,
+                          CXL_AFU_Cntl_An_ES_MASK, false);
+}
+
+/* This will disable as well as reset */
+int cxl_afu_reset(struct cxl_afu *afu)
+{
+       pr_devel("AFU reset request\n");
+
+       return afu_control(afu, CXL_AFU_Cntl_An_RA,
+                          CXL_AFU_Cntl_An_RS_Complete | CXL_AFU_Cntl_An_ES_Disabled,
+                          CXL_AFU_Cntl_An_RS_MASK | CXL_AFU_Cntl_An_ES_MASK,
+                          false);
+}
+
+static int afu_check_and_enable(struct cxl_afu *afu)
+{
+       if (afu->enabled)
+               return 0;
+       return afu_enable(afu);
+}
+
+int cxl_psl_purge(struct cxl_afu *afu)
+{
+       u64 PSL_CNTL = cxl_p1n_read(afu, CXL_PSL_SCNTL_An);
+       u64 AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
+       u64 dsisr, dar;
+       u64 start, end;
+       unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT);
+
+       pr_devel("PSL purge request\n");
+
+       if ((AFU_Cntl & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) {
+               WARN(1, "psl_purge request while AFU not disabled!\n");
+               cxl_afu_disable(afu);
+       }
+
+       cxl_p1n_write(afu, CXL_PSL_SCNTL_An,
+                      PSL_CNTL | CXL_PSL_SCNTL_An_Pc);
+       start = local_clock();
+       PSL_CNTL = cxl_p1n_read(afu, CXL_PSL_SCNTL_An);
+       while ((PSL_CNTL &  CXL_PSL_SCNTL_An_Ps_MASK)
+                       == CXL_PSL_SCNTL_An_Ps_Pending) {
+               if (time_after_eq(jiffies, timeout)) {
+                       dev_warn(&afu->dev, "WARNING: PSL Purge timed out!\n");
+                       return -EBUSY;
+               }
+               dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
+               pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%.16llx  PSL_DSISR: 0x%.16llx\n", PSL_CNTL, dsisr);
+               if (dsisr & CXL_PSL_DSISR_TRANS) {
+                       dar = cxl_p2n_read(afu, CXL_PSL_DAR_An);
+                       dev_notice(&afu->dev, "PSL purge terminating pending translation, DSISR: 0x%.16llx, DAR: 0x%.16llx\n", dsisr, dar);
+                       cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE);
+               } else if (dsisr) {
+                       dev_notice(&afu->dev, "PSL purge acknowledging pending non-translation fault, DSISR: 0x%.16llx\n", dsisr);
+                       cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A);
+               } else {
+                       cpu_relax();
+               }
+               PSL_CNTL = cxl_p1n_read(afu, CXL_PSL_SCNTL_An);
+       };
+       end = local_clock();
+       pr_devel("PSL purged in %lld ns\n", end - start);
+
+       cxl_p1n_write(afu, CXL_PSL_SCNTL_An,
+                      PSL_CNTL & ~CXL_PSL_SCNTL_An_Pc);
+       return 0;
+}
+
+static int spa_max_procs(int spa_size)
+{
+       /*
+        * From the CAIA:
+        *    end_of_SPA_area = SPA_Base + ((n+4) * 128) + (( ((n*8) + 127) >> 7) * 128) + 255
+        * Most of that junk is really just an overly-complicated way of saying
+        * the last 256 bytes are __aligned(128), so it's really:
+        *    end_of_SPA_area = end_of_PSL_queue_area + __aligned(128) 255
+        * and
+        *    end_of_PSL_queue_area = SPA_Base + ((n+4) * 128) + (n*8) - 1
+        * so
+        *    sizeof(SPA) = ((n+4) * 128) + (n*8) + __aligned(128) 256
+        * Ignore the alignment (which is safe in this case as long as we are
+        * careful with our rounding) and solve for n:
+        */
+       return ((spa_size / 8) - 96) / 17;
+}
+
+static int alloc_spa(struct cxl_afu *afu)
+{
+       u64 spap;
+
+       /* Work out how many pages to allocate */
+       afu->spa_order = 0;
+       do {
+               afu->spa_order++;
+               afu->spa_size = (1 << afu->spa_order) * PAGE_SIZE;
+               afu->spa_max_procs = spa_max_procs(afu->spa_size);
+       } while (afu->spa_max_procs < afu->num_procs);
+
+       WARN_ON(afu->spa_size > 0x100000); /* Max size supported by the hardware */
+
+       if (!(afu->spa = (struct cxl_process_element *)
+             __get_free_pages(GFP_KERNEL | __GFP_ZERO, afu->spa_order))) {
+               pr_err("cxl_alloc_spa: Unable to allocate scheduled process area\n");
+               return -ENOMEM;
+       }
+       pr_devel("spa pages: %i afu->spa_max_procs: %i   afu->num_procs: %i\n",
+                1<<afu->spa_order, afu->spa_max_procs, afu->num_procs);
+
+       afu->sw_command_status = (__be64 *)((char *)afu->spa +
+                                           ((afu->spa_max_procs + 3) * 128));
+
+       spap = virt_to_phys(afu->spa) & CXL_PSL_SPAP_Addr;
+       spap |= ((afu->spa_size >> (12 - CXL_PSL_SPAP_Size_Shift)) - 1) & CXL_PSL_SPAP_Size;
+       spap |= CXL_PSL_SPAP_V;
+       pr_devel("cxl: SPA allocated at 0x%p. Max processes: %i, sw_command_status: 0x%p CXL_PSL_SPAP_An=0x%016llx\n", afu->spa, afu->spa_max_procs, afu->sw_command_status, spap);
+       cxl_p1n_write(afu, CXL_PSL_SPAP_An, spap);
+
+       return 0;
+}
+
+static void release_spa(struct cxl_afu *afu)
+{
+       free_pages((unsigned long) afu->spa, afu->spa_order);
+}
+
+int cxl_tlb_slb_invalidate(struct cxl *adapter)
+{
+       unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT);
+
+       pr_devel("CXL adapter wide TLBIA & SLBIA\n");
+
+       cxl_p1_write(adapter, CXL_PSL_AFUSEL, CXL_PSL_AFUSEL_A);
+
+       cxl_p1_write(adapter, CXL_PSL_TLBIA, CXL_TLB_SLB_IQ_ALL);
+       while (cxl_p1_read(adapter, CXL_PSL_TLBIA) & CXL_TLB_SLB_P) {
+               if (time_after_eq(jiffies, timeout)) {
+                       dev_warn(&adapter->dev, "WARNING: CXL adapter wide TLBIA timed out!\n");
+                       return -EBUSY;
+               }
+               cpu_relax();
+       }
+
+       cxl_p1_write(adapter, CXL_PSL_SLBIA, CXL_TLB_SLB_IQ_ALL);
+       while (cxl_p1_read(adapter, CXL_PSL_SLBIA) & CXL_TLB_SLB_P) {
+               if (time_after_eq(jiffies, timeout)) {
+                       dev_warn(&adapter->dev, "WARNING: CXL adapter wide SLBIA timed out!\n");
+                       return -EBUSY;
+               }
+               cpu_relax();
+       }
+       return 0;
+}
+
+int cxl_afu_slbia(struct cxl_afu *afu)
+{
+       unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT);
+
+       pr_devel("cxl_afu_slbia issuing SLBIA command\n");
+       cxl_p2n_write(afu, CXL_SLBIA_An, CXL_TLB_SLB_IQ_ALL);
+       while (cxl_p2n_read(afu, CXL_SLBIA_An) & CXL_TLB_SLB_P) {
+               if (time_after_eq(jiffies, timeout)) {
+                       dev_warn(&afu->dev, "WARNING: CXL AFU SLBIA timed out!\n");
+                       return -EBUSY;
+               }
+               cpu_relax();
+       }
+       return 0;
+}
+
+static int cxl_write_sstp(struct cxl_afu *afu, u64 sstp0, u64 sstp1)
+{
+       int rc;
+
+       /* 1. Disable SSTP by writing 0 to SSTP1[V] */
+       cxl_p2n_write(afu, CXL_SSTP1_An, 0);
+
+       /* 2. Invalidate all SLB entries */
+       if ((rc = cxl_afu_slbia(afu)))
+               return rc;
+
+       /* 3. Set SSTP0_An */
+       cxl_p2n_write(afu, CXL_SSTP0_An, sstp0);
+
+       /* 4. Set SSTP1_An */
+       cxl_p2n_write(afu, CXL_SSTP1_An, sstp1);
+
+       return 0;
+}
+
+/* Using per slice version may improve performance here. (ie. SLBIA_An) */
+static void slb_invalid(struct cxl_context *ctx)
+{
+       struct cxl *adapter = ctx->afu->adapter;
+       u64 slbia;
+
+       WARN_ON(!mutex_is_locked(&ctx->afu->spa_mutex));
+
+       cxl_p1_write(adapter, CXL_PSL_LBISEL,
+                       ((u64)be32_to_cpu(ctx->elem->common.pid) << 32) |
+                       be32_to_cpu(ctx->elem->lpid));
+       cxl_p1_write(adapter, CXL_PSL_SLBIA, CXL_TLB_SLB_IQ_LPIDPID);
+
+       while (1) {
+               slbia = cxl_p1_read(adapter, CXL_PSL_SLBIA);
+               if (!(slbia & CXL_TLB_SLB_P))
+                       break;
+               cpu_relax();
+       }
+}
+
+static int do_process_element_cmd(struct cxl_context *ctx,
+                                 u64 cmd, u64 pe_state)
+{
+       u64 state;
+
+       WARN_ON(!ctx->afu->enabled);
+
+       ctx->elem->software_state = cpu_to_be32(pe_state);
+       smp_wmb();
+       *(ctx->afu->sw_command_status) = cpu_to_be64(cmd | 0 | ctx->pe);
+       smp_mb();
+       cxl_p1n_write(ctx->afu, CXL_PSL_LLCMD_An, cmd | ctx->pe);
+       while (1) {
+               state = be64_to_cpup(ctx->afu->sw_command_status);
+               if (state == ~0ULL) {
+                       pr_err("cxl: Error adding process element to AFU\n");
+                       return -1;
+               }
+               if ((state & (CXL_SPA_SW_CMD_MASK | CXL_SPA_SW_STATE_MASK  | CXL_SPA_SW_LINK_MASK)) ==
+                   (cmd | (cmd >> 16) | ctx->pe))
+                       break;
+               /*
+                * The command won't finish in the PSL if there are
+                * outstanding DSIs.  Hence we need to yield here in
+                * case there are outstanding DSIs that we need to
+                * service.  Tuning possiblity: we could wait for a
+                * while before sched
+                */
+               schedule();
+
+       }
+       return 0;
+}
+
+static int add_process_element(struct cxl_context *ctx)
+{
+       int rc = 0;
+
+       mutex_lock(&ctx->afu->spa_mutex);
+       pr_devel("%s Adding pe: %i started\n", __func__, ctx->pe);
+       if (!(rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_ADD, CXL_PE_SOFTWARE_STATE_V)))
+               ctx->pe_inserted = true;
+       pr_devel("%s Adding pe: %i finished\n", __func__, ctx->pe);
+       mutex_unlock(&ctx->afu->spa_mutex);
+       return rc;
+}
+
+static int terminate_process_element(struct cxl_context *ctx)
+{
+       int rc = 0;
+
+       /* fast path terminate if it's already invalid */
+       if (!(ctx->elem->software_state & cpu_to_be32(CXL_PE_SOFTWARE_STATE_V)))
+               return rc;
+
+       mutex_lock(&ctx->afu->spa_mutex);
+       pr_devel("%s Terminate pe: %i started\n", __func__, ctx->pe);
+       rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_TERMINATE,
+                                   CXL_PE_SOFTWARE_STATE_V | CXL_PE_SOFTWARE_STATE_T);
+       ctx->elem->software_state = 0;  /* Remove Valid bit */
+       pr_devel("%s Terminate pe: %i finished\n", __func__, ctx->pe);
+       mutex_unlock(&ctx->afu->spa_mutex);
+       return rc;
+}
+
+static int remove_process_element(struct cxl_context *ctx)
+{
+       int rc = 0;
+
+       mutex_lock(&ctx->afu->spa_mutex);
+       pr_devel("%s Remove pe: %i started\n", __func__, ctx->pe);
+       if (!(rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_REMOVE, 0)))
+               ctx->pe_inserted = false;
+       slb_invalid(ctx);
+       pr_devel("%s Remove pe: %i finished\n", __func__, ctx->pe);
+       mutex_unlock(&ctx->afu->spa_mutex);
+
+       return rc;
+}
+
+
+static void assign_psn_space(struct cxl_context *ctx)
+{
+       if (!ctx->afu->pp_size || ctx->master) {
+               ctx->psn_phys = ctx->afu->psn_phys;
+               ctx->psn_size = ctx->afu->adapter->ps_size;
+       } else {
+               ctx->psn_phys = ctx->afu->psn_phys +
+                       (ctx->afu->pp_offset + ctx->afu->pp_size * ctx->pe);
+               ctx->psn_size = ctx->afu->pp_size;
+       }
+}
+
+static int activate_afu_directed(struct cxl_afu *afu)
+{
+       int rc;
+
+       dev_info(&afu->dev, "Activating AFU directed mode\n");
+
+       if (alloc_spa(afu))
+               return -ENOMEM;
+
+       cxl_p1n_write(afu, CXL_PSL_SCNTL_An, CXL_PSL_SCNTL_An_PM_AFU);
+       cxl_p1n_write(afu, CXL_PSL_AMOR_An, 0xFFFFFFFFFFFFFFFFULL);
+       cxl_p1n_write(afu, CXL_PSL_ID_An, CXL_PSL_ID_An_F | CXL_PSL_ID_An_L);
+
+       afu->current_mode = CXL_MODE_DIRECTED;
+       afu->num_procs = afu->max_procs_virtualised;
+
+       if ((rc = cxl_chardev_m_afu_add(afu)))
+               return rc;
+
+       if ((rc = cxl_sysfs_afu_m_add(afu)))
+               goto err;
+
+       if ((rc = cxl_chardev_s_afu_add(afu)))
+               goto err1;
+
+       return 0;
+err1:
+       cxl_sysfs_afu_m_remove(afu);
+err:
+       cxl_chardev_afu_remove(afu);
+       return rc;
+}
+
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+#define set_endian(sr) ((sr) |= CXL_PSL_SR_An_LE)
+#else
+#define set_endian(sr) ((sr) &= ~(CXL_PSL_SR_An_LE))
+#endif
+
+static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr)
+{
+       u64 sr;
+       int r, result;
+
+       assign_psn_space(ctx);
+
+       ctx->elem->ctxtime = 0; /* disable */
+       ctx->elem->lpid = cpu_to_be32(mfspr(SPRN_LPID));
+       ctx->elem->haurp = 0; /* disable */
+       ctx->elem->sdr = cpu_to_be64(mfspr(SPRN_SDR1));
+
+       sr = CXL_PSL_SR_An_SC;
+       if (ctx->master)
+               sr |= CXL_PSL_SR_An_MP;
+       if (mfspr(SPRN_LPCR) & LPCR_TC)
+               sr |= CXL_PSL_SR_An_TC;
+       /* HV=0, PR=1, R=1 for userspace
+        * For kernel contexts: this would need to change
+        */
+       sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R;
+       set_endian(sr);
+       sr &= ~(CXL_PSL_SR_An_HV);
+       if (!test_tsk_thread_flag(current, TIF_32BIT))
+               sr |= CXL_PSL_SR_An_SF;
+       ctx->elem->common.pid = cpu_to_be32(current->pid);
+       ctx->elem->common.tid = 0;
+       ctx->elem->sr = cpu_to_be64(sr);
+
+       ctx->elem->common.csrp = 0; /* disable */
+       ctx->elem->common.aurp0 = 0; /* disable */
+       ctx->elem->common.aurp1 = 0; /* disable */
+
+       cxl_prefault(ctx, wed);
+
+       ctx->elem->common.sstp0 = cpu_to_be64(ctx->sstp0);
+       ctx->elem->common.sstp1 = cpu_to_be64(ctx->sstp1);
+
+       for (r = 0; r < CXL_IRQ_RANGES; r++) {
+               ctx->elem->ivte_offsets[r] = cpu_to_be16(ctx->irqs.offset[r]);
+               ctx->elem->ivte_ranges[r] = cpu_to_be16(ctx->irqs.range[r]);
+       }
+
+       ctx->elem->common.amr = cpu_to_be64(amr);
+       ctx->elem->common.wed = cpu_to_be64(wed);
+
+       /* first guy needs to enable */
+       if ((result = afu_check_and_enable(ctx->afu)))
+               return result;
+
+       add_process_element(ctx);
+
+       return 0;
+}
+
+static int deactivate_afu_directed(struct cxl_afu *afu)
+{
+       dev_info(&afu->dev, "Deactivating AFU directed mode\n");
+
+       afu->current_mode = 0;
+       afu->num_procs = 0;
+
+       cxl_sysfs_afu_m_remove(afu);
+       cxl_chardev_afu_remove(afu);
+
+       cxl_afu_reset(afu);
+       cxl_afu_disable(afu);
+       cxl_psl_purge(afu);
+
+       release_spa(afu);
+
+       return 0;
+}
+
+static int activate_dedicated_process(struct cxl_afu *afu)
+{
+       dev_info(&afu->dev, "Activating dedicated process mode\n");
+
+       cxl_p1n_write(afu, CXL_PSL_SCNTL_An, CXL_PSL_SCNTL_An_PM_Process);
+
+       cxl_p1n_write(afu, CXL_PSL_CtxTime_An, 0); /* disable */
+       cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0);    /* disable */
+       cxl_p1n_write(afu, CXL_PSL_AMOR_An, 0xFFFFFFFFFFFFFFFFULL);
+       cxl_p1n_write(afu, CXL_PSL_LPID_An, mfspr(SPRN_LPID));
+       cxl_p1n_write(afu, CXL_HAURP_An, 0);       /* disable */
+       cxl_p1n_write(afu, CXL_PSL_SDR_An, mfspr(SPRN_SDR1));
+
+       cxl_p2n_write(afu, CXL_CSRP_An, 0);        /* disable */
+       cxl_p2n_write(afu, CXL_AURP0_An, 0);       /* disable */
+       cxl_p2n_write(afu, CXL_AURP1_An, 0);       /* disable */
+
+       afu->current_mode = CXL_MODE_DEDICATED;
+       afu->num_procs = 1;
+
+       return cxl_chardev_d_afu_add(afu);
+}
+
+static int attach_dedicated(struct cxl_context *ctx, u64 wed, u64 amr)
+{
+       struct cxl_afu *afu = ctx->afu;
+       u64 sr;
+       int rc;
+
+       sr = CXL_PSL_SR_An_SC;
+       set_endian(sr);
+       if (ctx->master)
+               sr |= CXL_PSL_SR_An_MP;
+       if (mfspr(SPRN_LPCR) & LPCR_TC)
+               sr |= CXL_PSL_SR_An_TC;
+       sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R;
+       if (!test_tsk_thread_flag(current, TIF_32BIT))
+               sr |= CXL_PSL_SR_An_SF;
+       cxl_p2n_write(afu, CXL_PSL_PID_TID_An, (u64)current->pid << 32);
+       cxl_p1n_write(afu, CXL_PSL_SR_An, sr);
+
+       if ((rc = cxl_write_sstp(afu, ctx->sstp0, ctx->sstp1)))
+               return rc;
+
+       cxl_prefault(ctx, wed);
+
+       cxl_p1n_write(afu, CXL_PSL_IVTE_Offset_An,
+                      (((u64)ctx->irqs.offset[0] & 0xffff) << 48) |
+                      (((u64)ctx->irqs.offset[1] & 0xffff) << 32) |
+                      (((u64)ctx->irqs.offset[2] & 0xffff) << 16) |
+                       ((u64)ctx->irqs.offset[3] & 0xffff));
+       cxl_p1n_write(afu, CXL_PSL_IVTE_Limit_An, (u64)
+                      (((u64)ctx->irqs.range[0] & 0xffff) << 48) |
+                      (((u64)ctx->irqs.range[1] & 0xffff) << 32) |
+                      (((u64)ctx->irqs.range[2] & 0xffff) << 16) |
+                       ((u64)ctx->irqs.range[3] & 0xffff));
+
+       cxl_p2n_write(afu, CXL_PSL_AMR_An, amr);
+
+       /* master only context for dedicated */
+       assign_psn_space(ctx);
+
+       if ((rc = cxl_afu_reset(afu)))
+               return rc;
+
+       cxl_p2n_write(afu, CXL_PSL_WED_An, wed);
+
+       return afu_enable(afu);
+}
+
+static int deactivate_dedicated_process(struct cxl_afu *afu)
+{
+       dev_info(&afu->dev, "Deactivating dedicated process mode\n");
+
+       afu->current_mode = 0;
+       afu->num_procs = 0;
+
+       cxl_chardev_afu_remove(afu);
+
+       return 0;
+}
+
+int _cxl_afu_deactivate_mode(struct cxl_afu *afu, int mode)
+{
+       if (mode == CXL_MODE_DIRECTED)
+               return deactivate_afu_directed(afu);
+       if (mode == CXL_MODE_DEDICATED)
+               return deactivate_dedicated_process(afu);
+       return 0;
+}
+
+int cxl_afu_deactivate_mode(struct cxl_afu *afu)
+{
+       return _cxl_afu_deactivate_mode(afu, afu->current_mode);
+}
+
+int cxl_afu_activate_mode(struct cxl_afu *afu, int mode)
+{
+       if (!mode)
+               return 0;
+       if (!(mode & afu->modes_supported))
+               return -EINVAL;
+
+       if (mode == CXL_MODE_DIRECTED)
+               return activate_afu_directed(afu);
+       if (mode == CXL_MODE_DEDICATED)
+               return activate_dedicated_process(afu);
+
+       return -EINVAL;
+}
+
+int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr)
+{
+       ctx->kernel = kernel;
+       if (ctx->afu->current_mode == CXL_MODE_DIRECTED)
+               return attach_afu_directed(ctx, wed, amr);
+
+       if (ctx->afu->current_mode == CXL_MODE_DEDICATED)
+               return attach_dedicated(ctx, wed, amr);
+
+       return -EINVAL;
+}
+
+static inline int detach_process_native_dedicated(struct cxl_context *ctx)
+{
+       cxl_afu_reset(ctx->afu);
+       cxl_afu_disable(ctx->afu);
+       cxl_psl_purge(ctx->afu);
+       return 0;
+}
+
+/*
+ * TODO: handle case when this is called inside a rcu_read_lock() which may
+ * happen when we unbind the driver (ie. cxl_context_detach_all()) .  Terminate
+ * & remove use a mutex lock and schedule which will not good with lock held.
+ * May need to write do_process_element_cmd() that handles outstanding page
+ * faults synchronously.
+ */
+static inline int detach_process_native_afu_directed(struct cxl_context *ctx)
+{
+       if (!ctx->pe_inserted)
+               return 0;
+       if (terminate_process_element(ctx))
+               return -1;
+       if (remove_process_element(ctx))
+               return -1;
+
+       return 0;
+}
+
+int cxl_detach_process(struct cxl_context *ctx)
+{
+       if (ctx->afu->current_mode == CXL_MODE_DEDICATED)
+               return detach_process_native_dedicated(ctx);
+
+       return detach_process_native_afu_directed(ctx);
+}
+
+int cxl_get_irq(struct cxl_context *ctx, struct cxl_irq_info *info)
+{
+       u64 pidtid;
+
+       info->dsisr = cxl_p2n_read(ctx->afu, CXL_PSL_DSISR_An);
+       info->dar = cxl_p2n_read(ctx->afu, CXL_PSL_DAR_An);
+       info->dsr = cxl_p2n_read(ctx->afu, CXL_PSL_DSR_An);
+       pidtid = cxl_p2n_read(ctx->afu, CXL_PSL_PID_TID_An);
+       info->pid = pidtid >> 32;
+       info->tid = pidtid & 0xffffffff;
+       info->afu_err = cxl_p2n_read(ctx->afu, CXL_AFU_ERR_An);
+       info->errstat = cxl_p2n_read(ctx->afu, CXL_PSL_ErrStat_An);
+
+       return 0;
+}
+
+static void recover_psl_err(struct cxl_afu *afu, u64 errstat)
+{
+       u64 dsisr;
+
+       pr_devel("RECOVERING FROM PSL ERROR... (0x%.16llx)\n", errstat);
+
+       /* Clear PSL_DSISR[PE] */
+       dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
+       cxl_p2n_write(afu, CXL_PSL_DSISR_An, dsisr & ~CXL_PSL_DSISR_An_PE);
+
+       /* Write 1s to clear error status bits */
+       cxl_p2n_write(afu, CXL_PSL_ErrStat_An, errstat);
+}
+
+int cxl_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask)
+{
+       if (tfc)
+               cxl_p2n_write(ctx->afu, CXL_PSL_TFC_An, tfc);
+       if (psl_reset_mask)
+               recover_psl_err(ctx->afu, psl_reset_mask);
+
+       return 0;
+}
+
+int cxl_check_error(struct cxl_afu *afu)
+{
+       return (cxl_p1n_read(afu, CXL_PSL_SCNTL_An) == ~0ULL);
+}
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
new file mode 100644 (file)
index 0000000..10c98ab
--- /dev/null
@@ -0,0 +1,1000 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/pci_regs.h>
+#include <linux/pci_ids.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/pci.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <asm/opal.h>
+#include <asm/msi_bitmap.h>
+#include <asm/pci-bridge.h> /* for struct pci_controller */
+#include <asm/pnv-pci.h>
+
+#include "cxl.h"
+
+
+#define CXL_PCI_VSEC_ID        0x1280
+#define CXL_VSEC_MIN_SIZE 0x80
+
+#define CXL_READ_VSEC_LENGTH(dev, vsec, dest)                  \
+       {                                                       \
+               pci_read_config_word(dev, vsec + 0x6, dest);    \
+               *dest >>= 4;                                    \
+       }
+#define CXL_READ_VSEC_NAFUS(dev, vsec, dest) \
+       pci_read_config_byte(dev, vsec + 0x8, dest)
+
+#define CXL_READ_VSEC_STATUS(dev, vsec, dest) \
+       pci_read_config_byte(dev, vsec + 0x9, dest)
+#define CXL_STATUS_SECOND_PORT  0x80
+#define CXL_STATUS_MSI_X_FULL   0x40
+#define CXL_STATUS_MSI_X_SINGLE 0x20
+#define CXL_STATUS_FLASH_RW     0x08
+#define CXL_STATUS_FLASH_RO     0x04
+#define CXL_STATUS_LOADABLE_AFU 0x02
+#define CXL_STATUS_LOADABLE_PSL 0x01
+/* If we see these features we won't try to use the card */
+#define CXL_UNSUPPORTED_FEATURES \
+       (CXL_STATUS_MSI_X_FULL | CXL_STATUS_MSI_X_SINGLE)
+
+#define CXL_READ_VSEC_MODE_CONTROL(dev, vsec, dest) \
+       pci_read_config_byte(dev, vsec + 0xa, dest)
+#define CXL_WRITE_VSEC_MODE_CONTROL(dev, vsec, val) \
+       pci_write_config_byte(dev, vsec + 0xa, val)
+#define CXL_VSEC_PROTOCOL_MASK   0xe0
+#define CXL_VSEC_PROTOCOL_1024TB 0x80
+#define CXL_VSEC_PROTOCOL_512TB  0x40
+#define CXL_VSEC_PROTOCOL_256TB  0x20 /* Power 8 uses this */
+#define CXL_VSEC_PROTOCOL_ENABLE 0x01
+
+#define CXL_READ_VSEC_PSL_REVISION(dev, vsec, dest) \
+       pci_read_config_word(dev, vsec + 0xc, dest)
+#define CXL_READ_VSEC_CAIA_MINOR(dev, vsec, dest) \
+       pci_read_config_byte(dev, vsec + 0xe, dest)
+#define CXL_READ_VSEC_CAIA_MAJOR(dev, vsec, dest) \
+       pci_read_config_byte(dev, vsec + 0xf, dest)
+#define CXL_READ_VSEC_BASE_IMAGE(dev, vsec, dest) \
+       pci_read_config_word(dev, vsec + 0x10, dest)
+
+#define CXL_READ_VSEC_IMAGE_STATE(dev, vsec, dest) \
+       pci_read_config_byte(dev, vsec + 0x13, dest)
+#define CXL_WRITE_VSEC_IMAGE_STATE(dev, vsec, val) \
+       pci_write_config_byte(dev, vsec + 0x13, val)
+#define CXL_VSEC_USER_IMAGE_LOADED 0x80 /* RO */
+#define CXL_VSEC_PERST_LOADS_IMAGE 0x20 /* RW */
+#define CXL_VSEC_PERST_SELECT_USER 0x10 /* RW */
+
+#define CXL_READ_VSEC_AFU_DESC_OFF(dev, vsec, dest) \
+       pci_read_config_dword(dev, vsec + 0x20, dest)
+#define CXL_READ_VSEC_AFU_DESC_SIZE(dev, vsec, dest) \
+       pci_read_config_dword(dev, vsec + 0x24, dest)
+#define CXL_READ_VSEC_PS_OFF(dev, vsec, dest) \
+       pci_read_config_dword(dev, vsec + 0x28, dest)
+#define CXL_READ_VSEC_PS_SIZE(dev, vsec, dest) \
+       pci_read_config_dword(dev, vsec + 0x2c, dest)
+
+
+/* This works a little different than the p1/p2 register accesses to make it
+ * easier to pull out individual fields */
+#define AFUD_READ(afu, off)            in_be64(afu->afu_desc_mmio + off)
+#define EXTRACT_PPC_BIT(val, bit)      (!!(val & PPC_BIT(bit)))
+#define EXTRACT_PPC_BITS(val, bs, be)  ((val & PPC_BITMASK(bs, be)) >> PPC_BITLSHIFT(be))
+
+#define AFUD_READ_INFO(afu)            AFUD_READ(afu, 0x0)
+#define   AFUD_NUM_INTS_PER_PROC(val)  EXTRACT_PPC_BITS(val,  0, 15)
+#define   AFUD_NUM_PROCS(val)          EXTRACT_PPC_BITS(val, 16, 31)
+#define   AFUD_NUM_CRS(val)            EXTRACT_PPC_BITS(val, 32, 47)
+#define   AFUD_MULTIMODE(val)          EXTRACT_PPC_BIT(val, 48)
+#define   AFUD_PUSH_BLOCK_TRANSFER(val)        EXTRACT_PPC_BIT(val, 55)
+#define   AFUD_DEDICATED_PROCESS(val)  EXTRACT_PPC_BIT(val, 59)
+#define   AFUD_AFU_DIRECTED(val)       EXTRACT_PPC_BIT(val, 61)
+#define   AFUD_TIME_SLICED(val)                EXTRACT_PPC_BIT(val, 63)
+#define AFUD_READ_CR(afu)              AFUD_READ(afu, 0x20)
+#define   AFUD_CR_LEN(val)             EXTRACT_PPC_BITS(val, 8, 63)
+#define AFUD_READ_CR_OFF(afu)          AFUD_READ(afu, 0x28)
+#define AFUD_READ_PPPSA(afu)           AFUD_READ(afu, 0x30)
+#define   AFUD_PPPSA_PP(val)           EXTRACT_PPC_BIT(val, 6)
+#define   AFUD_PPPSA_PSA(val)          EXTRACT_PPC_BIT(val, 7)
+#define   AFUD_PPPSA_LEN(val)          EXTRACT_PPC_BITS(val, 8, 63)
+#define AFUD_READ_PPPSA_OFF(afu)       AFUD_READ(afu, 0x38)
+#define AFUD_READ_EB(afu)              AFUD_READ(afu, 0x40)
+#define   AFUD_EB_LEN(val)             EXTRACT_PPC_BITS(val, 8, 63)
+#define AFUD_READ_EB_OFF(afu)          AFUD_READ(afu, 0x48)
+
+static DEFINE_PCI_DEVICE_TABLE(cxl_pci_tbl) = {
+       { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0477), },
+       { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x044b), },
+       { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x04cf), },
+       { PCI_DEVICE_CLASS(0x120000, ~0), },
+
+       { }
+};
+MODULE_DEVICE_TABLE(pci, cxl_pci_tbl);
+
+
+/*
+ * Mostly using these wrappers to avoid confusion:
+ * priv 1 is BAR2, while priv 2 is BAR0
+ */
+static inline resource_size_t p1_base(struct pci_dev *dev)
+{
+       return pci_resource_start(dev, 2);
+}
+
+static inline resource_size_t p1_size(struct pci_dev *dev)
+{
+       return pci_resource_len(dev, 2);
+}
+
+static inline resource_size_t p2_base(struct pci_dev *dev)
+{
+       return pci_resource_start(dev, 0);
+}
+
+static inline resource_size_t p2_size(struct pci_dev *dev)
+{
+       return pci_resource_len(dev, 0);
+}
+
+static int find_cxl_vsec(struct pci_dev *dev)
+{
+       int vsec = 0;
+       u16 val;
+
+       while ((vsec = pci_find_next_ext_capability(dev, vsec, PCI_EXT_CAP_ID_VNDR))) {
+               pci_read_config_word(dev, vsec + 0x4, &val);
+               if (val == CXL_PCI_VSEC_ID)
+                       return vsec;
+       }
+       return 0;
+
+}
+
+static void dump_cxl_config_space(struct pci_dev *dev)
+{
+       int vsec;
+       u32 val;
+
+       dev_info(&dev->dev, "dump_cxl_config_space\n");
+
+       pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &val);
+       dev_info(&dev->dev, "BAR0: %#.8x\n", val);
+       pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &val);
+       dev_info(&dev->dev, "BAR1: %#.8x\n", val);
+       pci_read_config_dword(dev, PCI_BASE_ADDRESS_2, &val);
+       dev_info(&dev->dev, "BAR2: %#.8x\n", val);
+       pci_read_config_dword(dev, PCI_BASE_ADDRESS_3, &val);
+       dev_info(&dev->dev, "BAR3: %#.8x\n", val);
+       pci_read_config_dword(dev, PCI_BASE_ADDRESS_4, &val);
+       dev_info(&dev->dev, "BAR4: %#.8x\n", val);
+       pci_read_config_dword(dev, PCI_BASE_ADDRESS_5, &val);
+       dev_info(&dev->dev, "BAR5: %#.8x\n", val);
+
+       dev_info(&dev->dev, "p1 regs: %#llx, len: %#llx\n",
+               p1_base(dev), p1_size(dev));
+       dev_info(&dev->dev, "p2 regs: %#llx, len: %#llx\n",
+               p1_base(dev), p2_size(dev));
+       dev_info(&dev->dev, "BAR 4/5: %#llx, len: %#llx\n",
+               pci_resource_start(dev, 4), pci_resource_len(dev, 4));
+
+       if (!(vsec = find_cxl_vsec(dev)))
+               return;
+
+#define show_reg(name, what) \
+       dev_info(&dev->dev, "cxl vsec: %30s: %#x\n", name, what)
+
+       pci_read_config_dword(dev, vsec + 0x0, &val);
+       show_reg("Cap ID", (val >> 0) & 0xffff);
+       show_reg("Cap Ver", (val >> 16) & 0xf);
+       show_reg("Next Cap Ptr", (val >> 20) & 0xfff);
+       pci_read_config_dword(dev, vsec + 0x4, &val);
+       show_reg("VSEC ID", (val >> 0) & 0xffff);
+       show_reg("VSEC Rev", (val >> 16) & 0xf);
+       show_reg("VSEC Length", (val >> 20) & 0xfff);
+       pci_read_config_dword(dev, vsec + 0x8, &val);
+       show_reg("Num AFUs", (val >> 0) & 0xff);
+       show_reg("Status", (val >> 8) & 0xff);
+       show_reg("Mode Control", (val >> 16) & 0xff);
+       show_reg("Reserved", (val >> 24) & 0xff);
+       pci_read_config_dword(dev, vsec + 0xc, &val);
+       show_reg("PSL Rev", (val >> 0) & 0xffff);
+       show_reg("CAIA Ver", (val >> 16) & 0xffff);
+       pci_read_config_dword(dev, vsec + 0x10, &val);
+       show_reg("Base Image Rev", (val >> 0) & 0xffff);
+       show_reg("Reserved", (val >> 16) & 0x0fff);
+       show_reg("Image Control", (val >> 28) & 0x3);
+       show_reg("Reserved", (val >> 30) & 0x1);
+       show_reg("Image Loaded", (val >> 31) & 0x1);
+
+       pci_read_config_dword(dev, vsec + 0x14, &val);
+       show_reg("Reserved", val);
+       pci_read_config_dword(dev, vsec + 0x18, &val);
+       show_reg("Reserved", val);
+       pci_read_config_dword(dev, vsec + 0x1c, &val);
+       show_reg("Reserved", val);
+
+       pci_read_config_dword(dev, vsec + 0x20, &val);
+       show_reg("AFU Descriptor Offset", val);
+       pci_read_config_dword(dev, vsec + 0x24, &val);
+       show_reg("AFU Descriptor Size", val);
+       pci_read_config_dword(dev, vsec + 0x28, &val);
+       show_reg("Problem State Offset", val);
+       pci_read_config_dword(dev, vsec + 0x2c, &val);
+       show_reg("Problem State Size", val);
+
+       pci_read_config_dword(dev, vsec + 0x30, &val);
+       show_reg("Reserved", val);
+       pci_read_config_dword(dev, vsec + 0x34, &val);
+       show_reg("Reserved", val);
+       pci_read_config_dword(dev, vsec + 0x38, &val);
+       show_reg("Reserved", val);
+       pci_read_config_dword(dev, vsec + 0x3c, &val);
+       show_reg("Reserved", val);
+
+       pci_read_config_dword(dev, vsec + 0x40, &val);
+       show_reg("PSL Programming Port", val);
+       pci_read_config_dword(dev, vsec + 0x44, &val);
+       show_reg("PSL Programming Control", val);
+
+       pci_read_config_dword(dev, vsec + 0x48, &val);
+       show_reg("Reserved", val);
+       pci_read_config_dword(dev, vsec + 0x4c, &val);
+       show_reg("Reserved", val);
+
+       pci_read_config_dword(dev, vsec + 0x50, &val);
+       show_reg("Flash Address Register", val);
+       pci_read_config_dword(dev, vsec + 0x54, &val);
+       show_reg("Flash Size Register", val);
+       pci_read_config_dword(dev, vsec + 0x58, &val);
+       show_reg("Flash Status/Control Register", val);
+       pci_read_config_dword(dev, vsec + 0x58, &val);
+       show_reg("Flash Data Port", val);
+
+#undef show_reg
+}
+
+static void dump_afu_descriptor(struct cxl_afu *afu)
+{
+       u64 val;
+
+#define show_reg(name, what) \
+       dev_info(&afu->dev, "afu desc: %30s: %#llx\n", name, what)
+
+       val = AFUD_READ_INFO(afu);
+       show_reg("num_ints_per_process", AFUD_NUM_INTS_PER_PROC(val));
+       show_reg("num_of_processes", AFUD_NUM_PROCS(val));
+       show_reg("num_of_afu_CRs", AFUD_NUM_CRS(val));
+       show_reg("req_prog_mode", val & 0xffffULL);
+
+       val = AFUD_READ(afu, 0x8);
+       show_reg("Reserved", val);
+       val = AFUD_READ(afu, 0x10);
+       show_reg("Reserved", val);
+       val = AFUD_READ(afu, 0x18);
+       show_reg("Reserved", val);
+
+       val = AFUD_READ_CR(afu);
+       show_reg("Reserved", (val >> (63-7)) & 0xff);
+       show_reg("AFU_CR_len", AFUD_CR_LEN(val));
+
+       val = AFUD_READ_CR_OFF(afu);
+       show_reg("AFU_CR_offset", val);
+
+       val = AFUD_READ_PPPSA(afu);
+       show_reg("PerProcessPSA_control", (val >> (63-7)) & 0xff);
+       show_reg("PerProcessPSA Length", AFUD_PPPSA_LEN(val));
+
+       val = AFUD_READ_PPPSA_OFF(afu);
+       show_reg("PerProcessPSA_offset", val);
+
+       val = AFUD_READ_EB(afu);
+       show_reg("Reserved", (val >> (63-7)) & 0xff);
+       show_reg("AFU_EB_len", AFUD_EB_LEN(val));
+
+       val = AFUD_READ_EB_OFF(afu);
+       show_reg("AFU_EB_offset", val);
+
+#undef show_reg
+}
+
+static int init_implementation_adapter_regs(struct cxl *adapter, struct pci_dev *dev)
+{
+       struct device_node *np;
+       const __be32 *prop;
+       u64 psl_dsnctl;
+       u64 chipid;
+
+       if (!(np = pnv_pci_to_phb_node(dev)))
+               return -ENODEV;
+
+       while (np && !(prop = of_get_property(np, "ibm,chip-id", NULL)))
+               np = of_get_next_parent(np);
+       if (!np)
+               return -ENODEV;
+       chipid = be32_to_cpup(prop);
+       of_node_put(np);
+
+       /* Tell PSL where to route data to */
+       psl_dsnctl = 0x02E8900002000000ULL | (chipid << (63-5));
+       cxl_p1_write(adapter, CXL_PSL_DSNDCTL, psl_dsnctl);
+       cxl_p1_write(adapter, CXL_PSL_RESLCKTO, 0x20000000200ULL);
+       /* snoop write mask */
+       cxl_p1_write(adapter, CXL_PSL_SNWRALLOC, 0x00000000FFFFFFFFULL);
+       /* set fir_accum */
+       cxl_p1_write(adapter, CXL_PSL_FIR_CNTL, 0x0800000000000000ULL);
+       /* for debugging with trace arrays */
+       cxl_p1_write(adapter, CXL_PSL_TRACE, 0x0000FF7C00000000ULL);
+
+       return 0;
+}
+
+static int init_implementation_afu_regs(struct cxl_afu *afu)
+{
+       /* read/write masks for this slice */
+       cxl_p1n_write(afu, CXL_PSL_APCALLOC_A, 0xFFFFFFFEFEFEFEFEULL);
+       /* APC read/write masks for this slice */
+       cxl_p1n_write(afu, CXL_PSL_COALLOC_A, 0xFF000000FEFEFEFEULL);
+       /* for debugging with trace arrays */
+       cxl_p1n_write(afu, CXL_PSL_SLICE_TRACE, 0x0000FFFF00000000ULL);
+       cxl_p1n_write(afu, CXL_PSL_RXCTL_A, 0xF000000000000000ULL);
+
+       return 0;
+}
+
+int cxl_setup_irq(struct cxl *adapter, unsigned int hwirq,
+                        unsigned int virq)
+{
+       struct pci_dev *dev = to_pci_dev(adapter->dev.parent);
+
+       return pnv_cxl_ioda_msi_setup(dev, hwirq, virq);
+}
+
+int cxl_alloc_one_irq(struct cxl *adapter)
+{
+       struct pci_dev *dev = to_pci_dev(adapter->dev.parent);
+
+       return pnv_cxl_alloc_hwirqs(dev, 1);
+}
+
+void cxl_release_one_irq(struct cxl *adapter, int hwirq)
+{
+       struct pci_dev *dev = to_pci_dev(adapter->dev.parent);
+
+       return pnv_cxl_release_hwirqs(dev, hwirq, 1);
+}
+
+int cxl_alloc_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter, unsigned int num)
+{
+       struct pci_dev *dev = to_pci_dev(adapter->dev.parent);
+
+       return pnv_cxl_alloc_hwirq_ranges(irqs, dev, num);
+}
+
+void cxl_release_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter)
+{
+       struct pci_dev *dev = to_pci_dev(adapter->dev.parent);
+
+       pnv_cxl_release_hwirq_ranges(irqs, dev);
+}
+
+static int setup_cxl_bars(struct pci_dev *dev)
+{
+       /* Safety check in case we get backported to < 3.17 without M64 */
+       if ((p1_base(dev) < 0x100000000ULL) ||
+           (p2_base(dev) < 0x100000000ULL)) {
+               dev_err(&dev->dev, "ABORTING: M32 BAR assignment incompatible with CXL\n");
+               return -ENODEV;
+       }
+
+       /*
+        * BAR 4/5 has a special meaning for CXL and must be programmed with a
+        * special value corresponding to the CXL protocol address range.
+        * For POWER 8 that means bits 48:49 must be set to 10
+        */
+       pci_write_config_dword(dev, PCI_BASE_ADDRESS_4, 0x00000000);
+       pci_write_config_dword(dev, PCI_BASE_ADDRESS_5, 0x00020000);
+
+       return 0;
+}
+
+/* pciex node: ibm,opal-m64-window = <0x3d058 0x0 0x3d058 0x0 0x8 0x0>; */
+static int switch_card_to_cxl(struct pci_dev *dev)
+{
+       int vsec;
+       u8 val;
+       int rc;
+
+       dev_info(&dev->dev, "switch card to CXL\n");
+
+       if (!(vsec = find_cxl_vsec(dev))) {
+               dev_err(&dev->dev, "ABORTING: CXL VSEC not found!\n");
+               return -ENODEV;
+       }
+
+       if ((rc = CXL_READ_VSEC_MODE_CONTROL(dev, vsec, &val))) {
+               dev_err(&dev->dev, "failed to read current mode control: %i", rc);
+               return rc;
+       }
+       val &= ~CXL_VSEC_PROTOCOL_MASK;
+       val |= CXL_VSEC_PROTOCOL_256TB | CXL_VSEC_PROTOCOL_ENABLE;
+       if ((rc = CXL_WRITE_VSEC_MODE_CONTROL(dev, vsec, val))) {
+               dev_err(&dev->dev, "failed to enable CXL protocol: %i", rc);
+               return rc;
+       }
+       /*
+        * The CAIA spec (v0.12 11.6 Bi-modal Device Support) states
+        * we must wait 100ms after this mode switch before touching
+        * PCIe config space.
+        */
+       msleep(100);
+
+       return 0;
+}
+
+static int cxl_map_slice_regs(struct cxl_afu *afu, struct cxl *adapter, struct pci_dev *dev)
+{
+       u64 p1n_base, p2n_base, afu_desc;
+       const u64 p1n_size = 0x100;
+       const u64 p2n_size = 0x1000;
+
+       p1n_base = p1_base(dev) + 0x10000 + (afu->slice * p1n_size);
+       p2n_base = p2_base(dev) + (afu->slice * p2n_size);
+       afu->psn_phys = p2_base(dev) + (adapter->ps_off + (afu->slice * adapter->ps_size));
+       afu_desc = p2_base(dev) + adapter->afu_desc_off + (afu->slice * adapter->afu_desc_size);
+
+       if (!(afu->p1n_mmio = ioremap(p1n_base, p1n_size)))
+               goto err;
+       if (!(afu->p2n_mmio = ioremap(p2n_base, p2n_size)))
+               goto err1;
+       if (afu_desc) {
+               if (!(afu->afu_desc_mmio = ioremap(afu_desc, adapter->afu_desc_size)))
+                       goto err2;
+       }
+
+       return 0;
+err2:
+       iounmap(afu->p2n_mmio);
+err1:
+       iounmap(afu->p1n_mmio);
+err:
+       dev_err(&afu->dev, "Error mapping AFU MMIO regions\n");
+       return -ENOMEM;
+}
+
+static void cxl_unmap_slice_regs(struct cxl_afu *afu)
+{
+       if (afu->p1n_mmio)
+               iounmap(afu->p2n_mmio);
+       if (afu->p1n_mmio)
+               iounmap(afu->p1n_mmio);
+}
+
+static void cxl_release_afu(struct device *dev)
+{
+       struct cxl_afu *afu = to_cxl_afu(dev);
+
+       pr_devel("cxl_release_afu\n");
+
+       kfree(afu);
+}
+
+static struct cxl_afu *cxl_alloc_afu(struct cxl *adapter, int slice)
+{
+       struct cxl_afu *afu;
+
+       if (!(afu = kzalloc(sizeof(struct cxl_afu), GFP_KERNEL)))
+               return NULL;
+
+       afu->adapter = adapter;
+       afu->dev.parent = &adapter->dev;
+       afu->dev.release = cxl_release_afu;
+       afu->slice = slice;
+       idr_init(&afu->contexts_idr);
+       spin_lock_init(&afu->contexts_lock);
+       spin_lock_init(&afu->afu_cntl_lock);
+       mutex_init(&afu->spa_mutex);
+
+       afu->prefault_mode = CXL_PREFAULT_NONE;
+       afu->irqs_max = afu->adapter->user_irqs;
+
+       return afu;
+}
+
+/* Expects AFU struct to have recently been zeroed out */
+static int cxl_read_afu_descriptor(struct cxl_afu *afu)
+{
+       u64 val;
+
+       val = AFUD_READ_INFO(afu);
+       afu->pp_irqs = AFUD_NUM_INTS_PER_PROC(val);
+       afu->max_procs_virtualised = AFUD_NUM_PROCS(val);
+
+       if (AFUD_AFU_DIRECTED(val))
+               afu->modes_supported |= CXL_MODE_DIRECTED;
+       if (AFUD_DEDICATED_PROCESS(val))
+               afu->modes_supported |= CXL_MODE_DEDICATED;
+       if (AFUD_TIME_SLICED(val))
+               afu->modes_supported |= CXL_MODE_TIME_SLICED;
+
+       val = AFUD_READ_PPPSA(afu);
+       afu->pp_size = AFUD_PPPSA_LEN(val) * 4096;
+       afu->psa = AFUD_PPPSA_PSA(val);
+       if ((afu->pp_psa = AFUD_PPPSA_PP(val)))
+               afu->pp_offset = AFUD_READ_PPPSA_OFF(afu);
+
+       return 0;
+}
+
+static int cxl_afu_descriptor_looks_ok(struct cxl_afu *afu)
+{
+       if (afu->psa && afu->adapter->ps_size <
+                       (afu->pp_offset + afu->pp_size*afu->max_procs_virtualised)) {
+               dev_err(&afu->dev, "per-process PSA can't fit inside the PSA!\n");
+               return -ENODEV;
+       }
+
+       if (afu->pp_psa && (afu->pp_size < PAGE_SIZE))
+               dev_warn(&afu->dev, "AFU uses < PAGE_SIZE per-process PSA!");
+
+       return 0;
+}
+
+static int sanitise_afu_regs(struct cxl_afu *afu)
+{
+       u64 reg;
+
+       /*
+        * Clear out any regs that contain either an IVTE or address or may be
+        * waiting on an acknowledgement to try to be a bit safer as we bring
+        * it online
+        */
+       reg = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
+       if ((reg & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) {
+               dev_warn(&afu->dev, "WARNING: AFU was not disabled: %#.16llx\n", reg);
+               if (cxl_afu_reset(afu))
+                       return -EIO;
+               if (cxl_afu_disable(afu))
+                       return -EIO;
+               if (cxl_psl_purge(afu))
+                       return -EIO;
+       }
+       cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0x0000000000000000);
+       cxl_p1n_write(afu, CXL_PSL_IVTE_Limit_An, 0x0000000000000000);
+       cxl_p1n_write(afu, CXL_PSL_IVTE_Offset_An, 0x0000000000000000);
+       cxl_p1n_write(afu, CXL_PSL_AMBAR_An, 0x0000000000000000);
+       cxl_p1n_write(afu, CXL_PSL_SPOffset_An, 0x0000000000000000);
+       cxl_p1n_write(afu, CXL_HAURP_An, 0x0000000000000000);
+       cxl_p2n_write(afu, CXL_CSRP_An, 0x0000000000000000);
+       cxl_p2n_write(afu, CXL_AURP1_An, 0x0000000000000000);
+       cxl_p2n_write(afu, CXL_AURP0_An, 0x0000000000000000);
+       cxl_p2n_write(afu, CXL_SSTP1_An, 0x0000000000000000);
+       cxl_p2n_write(afu, CXL_SSTP0_An, 0x0000000000000000);
+       reg = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
+       if (reg) {
+               dev_warn(&afu->dev, "AFU had pending DSISR: %#.16llx\n", reg);
+               if (reg & CXL_PSL_DSISR_TRANS)
+                       cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE);
+               else
+                       cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A);
+       }
+       reg = cxl_p1n_read(afu, CXL_PSL_SERR_An);
+       if (reg) {
+               if (reg & ~0xffff)
+                       dev_warn(&afu->dev, "AFU had pending SERR: %#.16llx\n", reg);
+               cxl_p1n_write(afu, CXL_PSL_SERR_An, reg & ~0xffff);
+       }
+       reg = cxl_p2n_read(afu, CXL_PSL_ErrStat_An);
+       if (reg) {
+               dev_warn(&afu->dev, "AFU had pending error status: %#.16llx\n", reg);
+               cxl_p2n_write(afu, CXL_PSL_ErrStat_An, reg);
+       }
+
+       return 0;
+}
+
+static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev)
+{
+       struct cxl_afu *afu;
+       bool free = true;
+       int rc;
+
+       if (!(afu = cxl_alloc_afu(adapter, slice)))
+               return -ENOMEM;
+
+       if ((rc = dev_set_name(&afu->dev, "afu%i.%i", adapter->adapter_num, slice)))
+               goto err1;
+
+       if ((rc = cxl_map_slice_regs(afu, adapter, dev)))
+               goto err1;
+
+       if ((rc = sanitise_afu_regs(afu)))
+               goto err2;
+
+       /* We need to reset the AFU before we can read the AFU descriptor */
+       if ((rc = cxl_afu_reset(afu)))
+               goto err2;
+
+       if (cxl_verbose)
+               dump_afu_descriptor(afu);
+
+       if ((rc = cxl_read_afu_descriptor(afu)))
+               goto err2;
+
+       if ((rc = cxl_afu_descriptor_looks_ok(afu)))
+               goto err2;
+
+       if ((rc = init_implementation_afu_regs(afu)))
+               goto err2;
+
+       if ((rc = cxl_register_serr_irq(afu)))
+               goto err2;
+
+       if ((rc = cxl_register_psl_irq(afu)))
+               goto err3;
+
+       /* Don't care if this fails */
+       cxl_debugfs_afu_add(afu);
+
+       /*
+        * After we call this function we must not free the afu directly, even
+        * if it returns an error!
+        */
+       if ((rc = cxl_register_afu(afu)))
+               goto err_put1;
+
+       if ((rc = cxl_sysfs_afu_add(afu)))
+               goto err_put1;
+
+
+       if ((rc = cxl_afu_select_best_mode(afu)))
+               goto err_put2;
+
+       adapter->afu[afu->slice] = afu;
+
+       return 0;
+
+err_put2:
+       cxl_sysfs_afu_remove(afu);
+err_put1:
+       device_unregister(&afu->dev);
+       free = false;
+       cxl_debugfs_afu_remove(afu);
+       cxl_release_psl_irq(afu);
+err3:
+       cxl_release_serr_irq(afu);
+err2:
+       cxl_unmap_slice_regs(afu);
+err1:
+       if (free)
+               kfree(afu);
+       return rc;
+}
+
+static void cxl_remove_afu(struct cxl_afu *afu)
+{
+       pr_devel("cxl_remove_afu\n");
+
+       if (!afu)
+               return;
+
+       cxl_sysfs_afu_remove(afu);
+       cxl_debugfs_afu_remove(afu);
+
+       spin_lock(&afu->adapter->afu_list_lock);
+       afu->adapter->afu[afu->slice] = NULL;
+       spin_unlock(&afu->adapter->afu_list_lock);
+
+       cxl_context_detach_all(afu);
+       cxl_afu_deactivate_mode(afu);
+
+       cxl_release_psl_irq(afu);
+       cxl_release_serr_irq(afu);
+       cxl_unmap_slice_regs(afu);
+
+       device_unregister(&afu->dev);
+}
+
+
+static int cxl_map_adapter_regs(struct cxl *adapter, struct pci_dev *dev)
+{
+       if (pci_request_region(dev, 2, "priv 2 regs"))
+               goto err1;
+       if (pci_request_region(dev, 0, "priv 1 regs"))
+               goto err2;
+
+       pr_devel("cxl_map_adapter_regs: p1: %#.16llx %#llx, p2: %#.16llx %#llx",
+                       p1_base(dev), p1_size(dev), p2_base(dev), p2_size(dev));
+
+       if (!(adapter->p1_mmio = ioremap(p1_base(dev), p1_size(dev))))
+               goto err3;
+
+       if (!(adapter->p2_mmio = ioremap(p2_base(dev), p2_size(dev))))
+               goto err4;
+
+       return 0;
+
+err4:
+       iounmap(adapter->p1_mmio);
+       adapter->p1_mmio = NULL;
+err3:
+       pci_release_region(dev, 0);
+err2:
+       pci_release_region(dev, 2);
+err1:
+       return -ENOMEM;
+}
+
+static void cxl_unmap_adapter_regs(struct cxl *adapter)
+{
+       if (adapter->p1_mmio)
+               iounmap(adapter->p1_mmio);
+       if (adapter->p2_mmio)
+               iounmap(adapter->p2_mmio);
+}
+
+static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev)
+{
+       int vsec;
+       u32 afu_desc_off, afu_desc_size;
+       u32 ps_off, ps_size;
+       u16 vseclen;
+       u8 image_state;
+
+       if (!(vsec = find_cxl_vsec(dev))) {
+               dev_err(&adapter->dev, "ABORTING: CXL VSEC not found!\n");
+               return -ENODEV;
+       }
+
+       CXL_READ_VSEC_LENGTH(dev, vsec, &vseclen);
+       if (vseclen < CXL_VSEC_MIN_SIZE) {
+               pr_err("ABORTING: CXL VSEC too short\n");
+               return -EINVAL;
+       }
+
+       CXL_READ_VSEC_STATUS(dev, vsec, &adapter->vsec_status);
+       CXL_READ_VSEC_PSL_REVISION(dev, vsec, &adapter->psl_rev);
+       CXL_READ_VSEC_CAIA_MAJOR(dev, vsec, &adapter->caia_major);
+       CXL_READ_VSEC_CAIA_MINOR(dev, vsec, &adapter->caia_minor);
+       CXL_READ_VSEC_BASE_IMAGE(dev, vsec, &adapter->base_image);
+       CXL_READ_VSEC_IMAGE_STATE(dev, vsec, &image_state);
+       adapter->user_image_loaded = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED);
+       adapter->perst_loads_image = !!(image_state & CXL_VSEC_PERST_LOADS_IMAGE);
+       adapter->perst_select_user = !!(image_state & CXL_VSEC_PERST_SELECT_USER);
+
+       CXL_READ_VSEC_NAFUS(dev, vsec, &adapter->slices);
+       CXL_READ_VSEC_AFU_DESC_OFF(dev, vsec, &afu_desc_off);
+       CXL_READ_VSEC_AFU_DESC_SIZE(dev, vsec, &afu_desc_size);
+       CXL_READ_VSEC_PS_OFF(dev, vsec, &ps_off);
+       CXL_READ_VSEC_PS_SIZE(dev, vsec, &ps_size);
+
+       /* Convert everything to bytes, because there is NO WAY I'd look at the
+        * code a month later and forget what units these are in ;-) */
+       adapter->ps_off = ps_off * 64 * 1024;
+       adapter->ps_size = ps_size * 64 * 1024;
+       adapter->afu_desc_off = afu_desc_off * 64 * 1024;
+       adapter->afu_desc_size = afu_desc_size *64 * 1024;
+
+       /* Total IRQs - 1 PSL ERROR - #AFU*(1 slice error + 1 DSI) */
+       adapter->user_irqs = pnv_cxl_get_irq_count(dev) - 1 - 2*adapter->slices;
+
+       return 0;
+}
+
+static int cxl_vsec_looks_ok(struct cxl *adapter, struct pci_dev *dev)
+{
+       if (adapter->vsec_status & CXL_STATUS_SECOND_PORT)
+               return -EBUSY;
+
+       if (adapter->vsec_status & CXL_UNSUPPORTED_FEATURES) {
+               dev_err(&adapter->dev, "ABORTING: CXL requires unsupported features\n");
+               return -EINVAL;
+       }
+
+       if (!adapter->slices) {
+               /* Once we support dynamic reprogramming we can use the card if
+                * it supports loadable AFUs */
+               dev_err(&adapter->dev, "ABORTING: Device has no AFUs\n");
+               return -EINVAL;
+       }
+
+       if (!adapter->afu_desc_off || !adapter->afu_desc_size) {
+               dev_err(&adapter->dev, "ABORTING: VSEC shows no AFU descriptors\n");
+               return -EINVAL;
+       }
+
+       if (adapter->ps_size > p2_size(dev) - adapter->ps_off) {
+               dev_err(&adapter->dev, "ABORTING: Problem state size larger than "
+                                  "available in BAR2: 0x%llx > 0x%llx\n",
+                        adapter->ps_size, p2_size(dev) - adapter->ps_off);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void cxl_release_adapter(struct device *dev)
+{
+       struct cxl *adapter = to_cxl_adapter(dev);
+
+       pr_devel("cxl_release_adapter\n");
+
+       kfree(adapter);
+}
+
+static struct cxl *cxl_alloc_adapter(struct pci_dev *dev)
+{
+       struct cxl *adapter;
+
+       if (!(adapter = kzalloc(sizeof(struct cxl), GFP_KERNEL)))
+               return NULL;
+
+       adapter->dev.parent = &dev->dev;
+       adapter->dev.release = cxl_release_adapter;
+       pci_set_drvdata(dev, adapter);
+       spin_lock_init(&adapter->afu_list_lock);
+
+       return adapter;
+}
+
+static int sanitise_adapter_regs(struct cxl *adapter)
+{
+       cxl_p1_write(adapter, CXL_PSL_ErrIVTE, 0x0000000000000000);
+       return cxl_tlb_slb_invalidate(adapter);
+}
+
+static struct cxl *cxl_init_adapter(struct pci_dev *dev)
+{
+       struct cxl *adapter;
+       bool free = true;
+       int rc;
+
+
+       if (!(adapter = cxl_alloc_adapter(dev)))
+               return ERR_PTR(-ENOMEM);
+
+       if ((rc = switch_card_to_cxl(dev)))
+               goto err1;
+
+       if ((rc = cxl_alloc_adapter_nr(adapter)))
+               goto err1;
+
+       if ((rc = dev_set_name(&adapter->dev, "card%i", adapter->adapter_num)))
+               goto err2;
+
+       if ((rc = cxl_read_vsec(adapter, dev)))
+               goto err2;
+
+       if ((rc = cxl_vsec_looks_ok(adapter, dev)))
+               goto err2;
+
+       if ((rc = cxl_map_adapter_regs(adapter, dev)))
+               goto err2;
+
+       if ((rc = sanitise_adapter_regs(adapter)))
+               goto err2;
+
+       if ((rc = init_implementation_adapter_regs(adapter, dev)))
+               goto err3;
+
+       if ((rc = pnv_phb_to_cxl(dev)))
+               goto err3;
+
+       if ((rc = cxl_register_psl_err_irq(adapter)))
+               goto err3;
+
+       /* Don't care if this one fails: */
+       cxl_debugfs_adapter_add(adapter);
+
+       /*
+        * After we call this function we must not free the adapter directly,
+        * even if it returns an error!
+        */
+       if ((rc = cxl_register_adapter(adapter)))
+               goto err_put1;
+
+       if ((rc = cxl_sysfs_adapter_add(adapter)))
+               goto err_put1;
+
+       return adapter;
+
+err_put1:
+       device_unregister(&adapter->dev);
+       free = false;
+       cxl_debugfs_adapter_remove(adapter);
+       cxl_release_psl_err_irq(adapter);
+err3:
+       cxl_unmap_adapter_regs(adapter);
+err2:
+       cxl_remove_adapter_nr(adapter);
+err1:
+       if (free)
+               kfree(adapter);
+       return ERR_PTR(rc);
+}
+
+static void cxl_remove_adapter(struct cxl *adapter)
+{
+       struct pci_dev *pdev = to_pci_dev(adapter->dev.parent);
+
+       pr_devel("cxl_release_adapter\n");
+
+       cxl_sysfs_adapter_remove(adapter);
+       cxl_debugfs_adapter_remove(adapter);
+       cxl_release_psl_err_irq(adapter);
+       cxl_unmap_adapter_regs(adapter);
+       cxl_remove_adapter_nr(adapter);
+
+       device_unregister(&adapter->dev);
+
+       pci_release_region(pdev, 0);
+       pci_release_region(pdev, 2);
+       pci_disable_device(pdev);
+}
+
+static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       struct cxl *adapter;
+       int slice;
+       int rc;
+
+       pci_dev_get(dev);
+
+       if (cxl_verbose)
+               dump_cxl_config_space(dev);
+
+       if ((rc = setup_cxl_bars(dev)))
+               return rc;
+
+       if ((rc = pci_enable_device(dev))) {
+               dev_err(&dev->dev, "pci_enable_device failed: %i\n", rc);
+               return rc;
+       }
+
+       adapter = cxl_init_adapter(dev);
+       if (IS_ERR(adapter)) {
+               dev_err(&dev->dev, "cxl_init_adapter failed: %li\n", PTR_ERR(adapter));
+               return PTR_ERR(adapter);
+       }
+
+       for (slice = 0; slice < adapter->slices; slice++) {
+               if ((rc = cxl_init_afu(adapter, slice, dev)))
+                       dev_err(&dev->dev, "AFU %i failed to initialise: %i\n", slice, rc);
+       }
+
+       return 0;
+}
+
+static void cxl_remove(struct pci_dev *dev)
+{
+       struct cxl *adapter = pci_get_drvdata(dev);
+       int afu;
+
+       dev_warn(&dev->dev, "pci remove\n");
+
+       /*
+        * Lock to prevent someone grabbing a ref through the adapter list as
+        * we are removing it
+        */
+       for (afu = 0; afu < adapter->slices; afu++)
+               cxl_remove_afu(adapter->afu[afu]);
+       cxl_remove_adapter(adapter);
+}
+
+struct pci_driver cxl_pci_driver = {
+       .name = "cxl-pci",
+       .id_table = cxl_pci_tbl,
+       .probe = cxl_probe,
+       .remove = cxl_remove,
+};
diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c
new file mode 100644 (file)
index 0000000..ce7ec06
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+
+#include "cxl.h"
+
+#define to_afu_chardev_m(d) dev_get_drvdata(d)
+
+/*********  Adapter attributes  **********************************************/
+
+static ssize_t caia_version_show(struct device *device,
+                                struct device_attribute *attr,
+                                char *buf)
+{
+       struct cxl *adapter = to_cxl_adapter(device);
+
+       return scnprintf(buf, PAGE_SIZE, "%i.%i\n", adapter->caia_major,
+                        adapter->caia_minor);
+}
+
+static ssize_t psl_revision_show(struct device *device,
+                                struct device_attribute *attr,
+                                char *buf)
+{
+       struct cxl *adapter = to_cxl_adapter(device);
+
+       return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_rev);
+}
+
+static ssize_t base_image_show(struct device *device,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct cxl *adapter = to_cxl_adapter(device);
+
+       return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->base_image);
+}
+
+static ssize_t image_loaded_show(struct device *device,
+                                struct device_attribute *attr,
+                                char *buf)
+{
+       struct cxl *adapter = to_cxl_adapter(device);
+
+       if (adapter->user_image_loaded)
+               return scnprintf(buf, PAGE_SIZE, "user\n");
+       return scnprintf(buf, PAGE_SIZE, "factory\n");
+}
+
+static struct device_attribute adapter_attrs[] = {
+       __ATTR_RO(caia_version),
+       __ATTR_RO(psl_revision),
+       __ATTR_RO(base_image),
+       __ATTR_RO(image_loaded),
+};
+
+
+/*********  AFU master specific attributes  **********************************/
+
+static ssize_t mmio_size_show_master(struct device *device,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       struct cxl_afu *afu = to_afu_chardev_m(device);
+
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size);
+}
+
+static ssize_t pp_mmio_off_show(struct device *device,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct cxl_afu *afu = to_afu_chardev_m(device);
+
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_offset);
+}
+
+static ssize_t pp_mmio_len_show(struct device *device,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct cxl_afu *afu = to_afu_chardev_m(device);
+
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size);
+}
+
+static struct device_attribute afu_master_attrs[] = {
+       __ATTR(mmio_size, S_IRUGO, mmio_size_show_master, NULL),
+       __ATTR_RO(pp_mmio_off),
+       __ATTR_RO(pp_mmio_len),
+};
+
+
+/*********  AFU attributes  **************************************************/
+
+static ssize_t mmio_size_show(struct device *device,
+                             struct device_attribute *attr,
+                             char *buf)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+
+       if (afu->pp_size)
+               return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size);
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size);
+}
+
+static ssize_t reset_store_afu(struct device *device,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+       int rc;
+
+       /* Not safe to reset if it is currently in use */
+       spin_lock(&afu->contexts_lock);
+       if (!idr_is_empty(&afu->contexts_idr)) {
+               rc = -EBUSY;
+               goto err;
+       }
+
+       if ((rc = cxl_afu_reset(afu)))
+               goto err;
+
+       rc = count;
+err:
+       spin_unlock(&afu->contexts_lock);
+       return rc;
+}
+
+static ssize_t irqs_min_show(struct device *device,
+                            struct device_attribute *attr,
+                            char *buf)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+
+       return scnprintf(buf, PAGE_SIZE, "%i\n", afu->pp_irqs);
+}
+
+static ssize_t irqs_max_show(struct device *device,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+
+       return scnprintf(buf, PAGE_SIZE, "%i\n", afu->irqs_max);
+}
+
+static ssize_t irqs_max_store(struct device *device,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+       ssize_t ret;
+       int irqs_max;
+
+       ret = sscanf(buf, "%i", &irqs_max);
+       if (ret != 1)
+               return -EINVAL;
+
+       if (irqs_max < afu->pp_irqs)
+               return -EINVAL;
+
+       if (irqs_max > afu->adapter->user_irqs)
+               return -EINVAL;
+
+       afu->irqs_max = irqs_max;
+       return count;
+}
+
+static ssize_t modes_supported_show(struct device *device,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+       char *p = buf, *end = buf + PAGE_SIZE;
+
+       if (afu->modes_supported & CXL_MODE_DEDICATED)
+               p += scnprintf(p, end - p, "dedicated_process\n");
+       if (afu->modes_supported & CXL_MODE_DIRECTED)
+               p += scnprintf(p, end - p, "afu_directed\n");
+       return (p - buf);
+}
+
+static ssize_t prefault_mode_show(struct device *device,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+
+       switch (afu->prefault_mode) {
+       case CXL_PREFAULT_WED:
+               return scnprintf(buf, PAGE_SIZE, "work_element_descriptor\n");
+       case CXL_PREFAULT_ALL:
+               return scnprintf(buf, PAGE_SIZE, "all\n");
+       default:
+               return scnprintf(buf, PAGE_SIZE, "none\n");
+       }
+}
+
+static ssize_t prefault_mode_store(struct device *device,
+                         struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+       enum prefault_modes mode = -1;
+
+       if (!strncmp(buf, "work_element_descriptor", 23))
+               mode = CXL_PREFAULT_WED;
+       if (!strncmp(buf, "all", 3))
+               mode = CXL_PREFAULT_ALL;
+       if (!strncmp(buf, "none", 4))
+               mode = CXL_PREFAULT_NONE;
+
+       if (mode == -1)
+               return -EINVAL;
+
+       afu->prefault_mode = mode;
+       return count;
+}
+
+static ssize_t mode_show(struct device *device,
+                        struct device_attribute *attr,
+                        char *buf)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+
+       if (afu->current_mode == CXL_MODE_DEDICATED)
+               return scnprintf(buf, PAGE_SIZE, "dedicated_process\n");
+       if (afu->current_mode == CXL_MODE_DIRECTED)
+               return scnprintf(buf, PAGE_SIZE, "afu_directed\n");
+       return scnprintf(buf, PAGE_SIZE, "none\n");
+}
+
+static ssize_t mode_store(struct device *device, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct cxl_afu *afu = to_cxl_afu(device);
+       int old_mode, mode = -1;
+       int rc = -EBUSY;
+
+       /* can't change this if we have a user */
+       spin_lock(&afu->contexts_lock);
+       if (!idr_is_empty(&afu->contexts_idr))
+               goto err;
+
+       if (!strncmp(buf, "dedicated_process", 17))
+               mode = CXL_MODE_DEDICATED;
+       if (!strncmp(buf, "afu_directed", 12))
+               mode = CXL_MODE_DIRECTED;
+       if (!strncmp(buf, "none", 4))
+               mode = 0;
+
+       if (mode == -1) {
+               rc = -EINVAL;
+               goto err;
+       }
+
+       /*
+        * cxl_afu_deactivate_mode needs to be done outside the lock, prevent
+        * other contexts coming in before we are ready:
+        */
+       old_mode = afu->current_mode;
+       afu->current_mode = 0;
+       afu->num_procs = 0;
+
+       spin_unlock(&afu->contexts_lock);
+
+       if ((rc = _cxl_afu_deactivate_mode(afu, old_mode)))
+               return rc;
+       if ((rc = cxl_afu_activate_mode(afu, mode)))
+               return rc;
+
+       return count;
+err:
+       spin_unlock(&afu->contexts_lock);
+       return rc;
+}
+
+static ssize_t api_version_show(struct device *device,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION);
+}
+
+static ssize_t api_version_compatible_show(struct device *device,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE);
+}
+
+static struct device_attribute afu_attrs[] = {
+       __ATTR_RO(mmio_size),
+       __ATTR_RO(irqs_min),
+       __ATTR_RW(irqs_max),
+       __ATTR_RO(modes_supported),
+       __ATTR_RW(mode),
+       __ATTR_RW(prefault_mode),
+       __ATTR_RO(api_version),
+       __ATTR_RO(api_version_compatible),
+       __ATTR(reset, S_IWUSR, NULL, reset_store_afu),
+};
+
+
+
+int cxl_sysfs_adapter_add(struct cxl *adapter)
+{
+       int i, rc;
+
+       for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) {
+               if ((rc = device_create_file(&adapter->dev, &adapter_attrs[i])))
+                       goto err;
+       }
+       return 0;
+err:
+       for (i--; i >= 0; i--)
+               device_remove_file(&adapter->dev, &adapter_attrs[i]);
+       return rc;
+}
+void cxl_sysfs_adapter_remove(struct cxl *adapter)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++)
+               device_remove_file(&adapter->dev, &adapter_attrs[i]);
+}
+
+int cxl_sysfs_afu_add(struct cxl_afu *afu)
+{
+       int i, rc;
+
+       for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) {
+               if ((rc = device_create_file(&afu->dev, &afu_attrs[i])))
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       for (i--; i >= 0; i--)
+               device_remove_file(&afu->dev, &afu_attrs[i]);
+       return rc;
+}
+
+void cxl_sysfs_afu_remove(struct cxl_afu *afu)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(afu_attrs); i++)
+               device_remove_file(&afu->dev, &afu_attrs[i]);
+}
+
+int cxl_sysfs_afu_m_add(struct cxl_afu *afu)
+{
+       int i, rc;
+
+       for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) {
+               if ((rc = device_create_file(afu->chardev_m, &afu_master_attrs[i])))
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       for (i--; i >= 0; i--)
+               device_remove_file(afu->chardev_m, &afu_master_attrs[i]);
+       return rc;
+}
+
+void cxl_sysfs_afu_m_remove(struct cxl_afu *afu)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++)
+               device_remove_file(afu->chardev_m, &afu_master_attrs[i]);
+}
index 248399a881af508f537be12283e999d9e318924a..189b325197488d9ed48f1cecc80ddb06ce05e329 100644 (file)
@@ -35,7 +35,6 @@
 #include "vmci_driver.h"
 #include "vmci_event.h"
 
-#define PCI_VENDOR_ID_VMWARE           0x15AD
 #define PCI_DEVICE_ID_VMWARE_VMCI      0x0740
 
 #define VMCI_UTIL_NUM_RESOURCES 1
index ede41f05c392d499542dac45f9228f37364ecf30..1fa4c80ff88639e894ba8276387cb73cfbd3b93b 100644 (file)
@@ -977,7 +977,7 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
                return ERR_CONTINUE;
 
        /* Now for stop errors.  These aren't fatal to the transfer. */
-       pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n",
+       pr_info("%s: error %d sending stop command, original cmd response %#x, card status %#x\n",
               req->rq_disk->disk_name, brq->stop.error,
               brq->cmd.resp[0], status);
 
@@ -1398,10 +1398,15 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
                if (disable_multi)
                        brq->data.blocks = 1;
 
-               /* Some controllers can't do multiblock reads due to hw bugs */
-               if (card->host->caps2 & MMC_CAP2_NO_MULTI_READ &&
-                   rq_data_dir(req) == READ)
-                       brq->data.blocks = 1;
+               /*
+                * Some controllers have HW issues while operating
+                * in multiple I/O mode
+                */
+               if (card->host->ops->multi_io_quirk)
+                       brq->data.blocks = card->host->ops->multi_io_quirk(card,
+                                               (rq_data_dir(req) == READ) ?
+                                               MMC_DATA_READ : MMC_DATA_WRITE,
+                                               brq->data.blocks);
        }
 
        if (brq->data.blocks > 1 || do_rel_wr) {
@@ -1923,8 +1928,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
                case MMC_BLK_ECC_ERR:
                        if (brq->data.blocks > 1) {
                                /* Redo read one sector at a time */
-                               pr_warning("%s: retrying using single block read\n",
-                                          req->rq_disk->disk_name);
+                               pr_warn("%s: retrying using single block read\n",
+                                       req->rq_disk->disk_name);
                                disable_multi = 1;
                                break;
                        }
@@ -2131,7 +2136,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
        md->disk->queue = md->queue.queue;
        md->disk->driverfs_dev = parent;
        set_disk_ro(md->disk, md->read_only || default_ro);
-       if (area_type & MMC_BLK_DATA_AREA_RPMB)
+       if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT))
                md->disk->flags |= GENHD_FL_NO_PART_SCAN;
 
        /*
index 3e049c13429cfbe730179724053796cc65ba62de..feea926e32f87b7d47610c41a85732dfcf3cc832 100644 (file)
@@ -229,14 +229,12 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
                if (bouncesz > 512) {
                        mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
                        if (!mqrq_cur->bounce_buf) {
-                               pr_warning("%s: unable to "
-                                       "allocate bounce cur buffer\n",
+                               pr_warn("%s: unable to allocate bounce cur buffer\n",
                                        mmc_card_name(card));
                        }
                        mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
                        if (!mqrq_prev->bounce_buf) {
-                               pr_warning("%s: unable to "
-                                       "allocate bounce prev buffer\n",
+                               pr_warn("%s: unable to allocate bounce prev buffer\n",
                                        mmc_card_name(card));
                                kfree(mqrq_cur->bounce_buf);
                                mqrq_cur->bounce_buf = NULL;
index f093cea0d0600797cc1574c6f85a25dac5c128fc..d2de5925b73e6d7cb730b6b687e26d04cc0f92bf 100644 (file)
@@ -1063,8 +1063,8 @@ static int sdio_uart_probe(struct sdio_func *func,
                return -ENOMEM;
 
        if (func->class == SDIO_CLASS_UART) {
-               pr_warning("%s: need info on UART class basic setup\n",
-                      sdio_func_id(func));
+               pr_warn("%s: need info on UART class basic setup\n",
+                       sdio_func_id(func));
                kfree(port);
                return -ENOSYS;
        } else if (func->class == SDIO_CLASS_GPS) {
@@ -1082,9 +1082,8 @@ static int sdio_uart_probe(struct sdio_func *func,
                                break;
                }
                if (!tpl) {
-                       pr_warning(
-       "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
-                              sdio_func_id(func));
+                       pr_warn("%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
+                               sdio_func_id(func));
                        kfree(port);
                        return -EINVAL;
                }
index d03a080fb9cd35ff0b29bf50380f08fdd8818c23..f26a5f1d926dd7e6a79ec1b9f87f7be4fd691994 100644 (file)
@@ -433,8 +433,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
                 */
                if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) {
                        if (!mmc_interrupt_hpi(host->card)) {
-                               pr_warning("%s: %s: Interrupted sanitize\n",
-                                          mmc_hostname(host), __func__);
+                               pr_warn("%s: %s: Interrupted sanitize\n",
+                                       mmc_hostname(host), __func__);
                                cmd->error = 0;
                                break;
                        } else {
@@ -995,7 +995,7 @@ void mmc_set_chip_select(struct mmc_host *host, int mode)
  */
 static void __mmc_set_clock(struct mmc_host *host, unsigned int hz)
 {
-       WARN_ON(hz < host->f_min);
+       WARN_ON(hz && hz < host->f_min);
 
        if (hz > host->f_max)
                hz = host->f_max;
@@ -1221,15 +1221,14 @@ int mmc_regulator_get_ocrmask(struct regulator *supply)
        int                     result = 0;
        int                     count;
        int                     i;
+       int                     vdd_uV;
+       int                     vdd_mV;
 
        count = regulator_count_voltages(supply);
        if (count < 0)
                return count;
 
        for (i = 0; i < count; i++) {
-               int             vdd_uV;
-               int             vdd_mV;
-
                vdd_uV = regulator_list_voltage(supply, i);
                if (vdd_uV <= 0)
                        continue;
@@ -1238,6 +1237,15 @@ int mmc_regulator_get_ocrmask(struct regulator *supply)
                result |= mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV);
        }
 
+       if (!result) {
+               vdd_uV = regulator_get_voltage(supply);
+               if (vdd_uV <= 0)
+                       return vdd_uV;
+
+               vdd_mV = vdd_uV / 1000;
+               result = mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV);
+       }
+
        return result;
 }
 EXPORT_SYMBOL_GPL(mmc_regulator_get_ocrmask);
@@ -1263,7 +1271,6 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
 
        if (vdd_bit) {
                int             tmp;
-               int             voltage;
 
                /*
                 * REVISIT mmc_vddrange_to_ocrmask() may have set some
@@ -1280,22 +1287,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
                        max_uV = min_uV + 100 * 1000;
                }
 
-               /*
-                * If we're using a fixed/static regulator, don't call
-                * regulator_set_voltage; it would fail.
-                */
-               voltage = regulator_get_voltage(supply);
-
-               if (!regulator_can_change_voltage(supply))
-                       min_uV = max_uV = voltage;
-
-               if (voltage < 0)
-                       result = voltage;
-               else if (voltage < min_uV || voltage > max_uV)
-                       result = regulator_set_voltage(supply, min_uV, max_uV);
-               else
-                       result = 0;
-
+               result = regulator_set_voltage(supply, min_uV, max_uV);
                if (result == 0 && !mmc->regulator_enabled) {
                        result = regulator_enable(supply);
                        if (!result)
@@ -1425,8 +1417,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
        if (!host->ops->start_signal_voltage_switch)
                return -EPERM;
        if (!host->ops->card_busy)
-               pr_warning("%s: cannot verify signal voltage switch\n",
-                               mmc_hostname(host));
+               pr_warn("%s: cannot verify signal voltage switch\n",
+                       mmc_hostname(host));
 
        cmd.opcode = SD_SWITCH_VOLTAGE;
        cmd.arg = 0;
@@ -1761,7 +1753,7 @@ void mmc_init_erase(struct mmc_card *card)
                card->erase_shift = ffs(card->ssr.au) - 1;
        } else if (card->ext_csd.hc_erase_size) {
                card->pref_erase = card->ext_csd.hc_erase_size;
-       } else {
+       } else if (card->erase_size) {
                sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11;
                if (sz < 128)
                        card->pref_erase = 512 * 1024 / 512;
@@ -1778,7 +1770,8 @@ void mmc_init_erase(struct mmc_card *card)
                        if (sz)
                                card->pref_erase += card->erase_size - sz;
                }
-       }
+       } else
+               card->pref_erase = 0;
 }
 
 static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card,
@@ -2489,6 +2482,7 @@ void mmc_start_host(struct mmc_host *host)
 {
        host->f_init = max(freqs[0], host->f_min);
        host->rescan_disable = 0;
+       host->ios.power_mode = MMC_POWER_UNDEFINED;
        if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
                mmc_power_off(host);
        else
index 95cceae96944c17c19dbb4e9916119a72fb2bce6..03c53b72a2d6a34bafbea7e1a1bda90299772fa8 100644 (file)
@@ -310,9 +310,8 @@ int mmc_of_parse(struct mmc_host *host)
 {
        struct device_node *np;
        u32 bus_width;
-       bool explicit_inv_wp, gpio_inv_wp = false;
-       enum of_gpio_flags flags;
-       int len, ret, gpio;
+       int len, ret;
+       bool cap_invert, gpio_invert;
 
        if (!host->parent || !host->parent->of_node)
                return 0;
@@ -360,59 +359,62 @@ int mmc_of_parse(struct mmc_host *host)
        if (of_find_property(np, "non-removable", &len)) {
                host->caps |= MMC_CAP_NONREMOVABLE;
        } else {
-               bool explicit_inv_cd, gpio_inv_cd = false;
-
-               explicit_inv_cd = of_property_read_bool(np, "cd-inverted");
+               if (of_property_read_bool(np, "cd-inverted"))
+                       cap_invert = true;
+               else
+                       cap_invert = false;
 
                if (of_find_property(np, "broken-cd", &len))
                        host->caps |= MMC_CAP_NEEDS_POLL;
 
-               gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
-               if (gpio == -EPROBE_DEFER)
-                       return gpio;
-               if (gpio_is_valid(gpio)) {
-                       if (!(flags & OF_GPIO_ACTIVE_LOW))
-                               gpio_inv_cd = true;
-
-                       ret = mmc_gpio_request_cd(host, gpio, 0);
-                       if (ret < 0) {
-                               dev_err(host->parent,
-                                       "Failed to request CD GPIO #%d: %d!\n",
-                                       gpio, ret);
+               ret = mmc_gpiod_request_cd(host, "cd", 0, true,
+                                          0, &gpio_invert);
+               if (ret) {
+                       if (ret == -EPROBE_DEFER)
                                return ret;
-                       } else {
-                               dev_info(host->parent, "Got CD GPIO #%d.\n",
-                                        gpio);
+                       if (ret != -ENOENT) {
+                               dev_err(host->parent,
+                                       "Failed to request CD GPIO: %d\n",
+                                       ret);
                        }
-               }
-
-               if (explicit_inv_cd ^ gpio_inv_cd)
+               } else
+                       dev_info(host->parent, "Got CD GPIO\n");
+
+               /*
+                * There are two ways to flag that the CD line is inverted:
+                * through the cd-inverted flag and by the GPIO line itself
+                * being inverted from the GPIO subsystem. This is a leftover
+                * from the times when the GPIO subsystem did not make it
+                * possible to flag a line as inverted.
+                *
+                * If the capability on the host AND the GPIO line are
+                * both inverted, the end result is that the CD line is
+                * not inverted.
+                */
+               if (cap_invert ^ gpio_invert)
                        host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
        }
 
        /* Parse Write Protection */
-       explicit_inv_wp = of_property_read_bool(np, "wp-inverted");
-
-       gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
-       if (gpio == -EPROBE_DEFER) {
-               ret = -EPROBE_DEFER;
-               goto out;
-       }
-       if (gpio_is_valid(gpio)) {
-               if (!(flags & OF_GPIO_ACTIVE_LOW))
-                       gpio_inv_wp = true;
+       if (of_property_read_bool(np, "wp-inverted"))
+               cap_invert = true;
+       else
+               cap_invert = false;
 
-               ret = mmc_gpio_request_ro(host, gpio);
-               if (ret < 0) {
-                       dev_err(host->parent,
-                               "Failed to request WP GPIO: %d!\n", ret);
+       ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &gpio_invert);
+       if (ret) {
+               if (ret == -EPROBE_DEFER)
                        goto out;
-               } else {
-                               dev_info(host->parent, "Got WP GPIO #%d.\n",
-                                        gpio);
+               if (ret != -ENOENT) {
+                       dev_err(host->parent,
+                               "Failed to request WP GPIO: %d\n",
+                               ret);
                }
-       }
-       if (explicit_inv_wp ^ gpio_inv_wp)
+       } else
+               dev_info(host->parent, "Got WP GPIO\n");
+
+       /* See the comment on CD inversion above */
+       if (cap_invert ^ gpio_invert)
                host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
 
        if (of_find_property(np, "cap-sd-highspeed", &len))
@@ -452,6 +454,14 @@ int mmc_of_parse(struct mmc_host *host)
        if (of_find_property(np, "mmc-hs400-1_2v", &len))
                host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
 
+       host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
+       if (host->dsr_req && (host->dsr & ~0xffff)) {
+               dev_err(host->parent,
+                       "device tree specified broken value for DSR: 0x%x, ignoring\n",
+                       host->dsr);
+               host->dsr_req = 0;
+       }
+
        return 0;
 
 out:
index 1eda8dd8c867228b5643e40f7655b513643eb26f..a301a78a2bd1cdc11eb5f674ab563ac74700ea76 100644 (file)
@@ -162,6 +162,7 @@ static int mmc_decode_csd(struct mmc_card *card)
        csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
        csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
        csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
+       csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1);
        csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
        csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
        csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
@@ -225,9 +226,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
                                "Card will be ignored.\n",
                                mmc_hostname(card->host));
                } else {
-                       pr_warning("%s: unable to read "
-                               "EXT_CSD, performance might "
-                               "suffer.\n",
+                       pr_warn("%s: unable to read EXT_CSD, performance might suffer\n",
                                mmc_hostname(card->host));
                        err = 0;
                }
@@ -298,6 +297,97 @@ static void mmc_select_card_type(struct mmc_card *card)
        card->mmc_avail_type = avail_type;
 }
 
+static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)
+{
+       u8 hc_erase_grp_sz, hc_wp_grp_sz;
+
+       /*
+        * Disable these attributes by default
+        */
+       card->ext_csd.enhanced_area_offset = -EINVAL;
+       card->ext_csd.enhanced_area_size = -EINVAL;
+
+       /*
+        * Enhanced area feature support -- check whether the eMMC
+        * card has the Enhanced area enabled.  If so, export enhanced
+        * area offset and size to user by adding sysfs interface.
+        */
+       if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) &&
+           (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
+               if (card->ext_csd.partition_setting_completed) {
+                       hc_erase_grp_sz =
+                               ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
+                       hc_wp_grp_sz =
+                               ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+
+                       /*
+                        * calculate the enhanced data area offset, in bytes
+                        */
+                       card->ext_csd.enhanced_area_offset =
+                               (ext_csd[139] << 24) + (ext_csd[138] << 16) +
+                               (ext_csd[137] << 8) + ext_csd[136];
+                       if (mmc_card_blockaddr(card))
+                               card->ext_csd.enhanced_area_offset <<= 9;
+                       /*
+                        * calculate the enhanced data area size, in kilobytes
+                        */
+                       card->ext_csd.enhanced_area_size =
+                               (ext_csd[142] << 16) + (ext_csd[141] << 8) +
+                               ext_csd[140];
+                       card->ext_csd.enhanced_area_size *=
+                               (size_t)(hc_erase_grp_sz * hc_wp_grp_sz);
+                       card->ext_csd.enhanced_area_size <<= 9;
+               } else {
+                       pr_warn("%s: defines enhanced area without partition setting complete\n",
+                               mmc_hostname(card->host));
+               }
+       }
+}
+
+static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd)
+{
+       int idx;
+       u8 hc_erase_grp_sz, hc_wp_grp_sz;
+       unsigned int part_size;
+
+       /*
+        * General purpose partition feature support --
+        * If ext_csd has the size of general purpose partitions,
+        * set size, part_cfg, partition name in mmc_part.
+        */
+       if (ext_csd[EXT_CSD_PARTITION_SUPPORT] &
+           EXT_CSD_PART_SUPPORT_PART_EN) {
+               hc_erase_grp_sz =
+                       ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
+               hc_wp_grp_sz =
+                       ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+
+               for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) {
+                       if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] &&
+                           !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] &&
+                           !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2])
+                               continue;
+                       if (card->ext_csd.partition_setting_completed == 0) {
+                               pr_warn("%s: has partition size defined without partition complete\n",
+                                       mmc_hostname(card->host));
+                               break;
+                       }
+                       part_size =
+                               (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]
+                               << 16) +
+                               (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1]
+                               << 8) +
+                               ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3];
+                       part_size *= (size_t)(hc_erase_grp_sz *
+                               hc_wp_grp_sz);
+                       mmc_part_add(card, part_size << 19,
+                               EXT_CSD_PART_CONFIG_ACC_GP0 + idx,
+                               "gp%d", idx, false,
+                               MMC_BLK_DATA_AREA_GP);
+               }
+       }
+}
+
 /*
  * Decode extended CSD.
  */
@@ -305,7 +395,6 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
 {
        int err = 0, idx;
        unsigned int part_size;
-       u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0;
 
        BUG_ON(!card);
 
@@ -402,80 +491,16 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
                ext_csd[EXT_CSD_TRIM_MULT];
        card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
        if (card->ext_csd.rev >= 4) {
-               /*
-                * Enhanced area feature support -- check whether the eMMC
-                * card has the Enhanced area enabled.  If so, export enhanced
-                * area offset and size to user by adding sysfs interface.
-                */
-               if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) &&
-                   (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
-                       hc_erase_grp_sz =
-                               ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
-                       hc_wp_grp_sz =
-                               ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+               if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &
+                   EXT_CSD_PART_SETTING_COMPLETED)
+                       card->ext_csd.partition_setting_completed = 1;
+               else
+                       card->ext_csd.partition_setting_completed = 0;
 
-                       card->ext_csd.enhanced_area_en = 1;
-                       /*
-                        * calculate the enhanced data area offset, in bytes
-                        */
-                       card->ext_csd.enhanced_area_offset =
-                               (ext_csd[139] << 24) + (ext_csd[138] << 16) +
-                               (ext_csd[137] << 8) + ext_csd[136];
-                       if (mmc_card_blockaddr(card))
-                               card->ext_csd.enhanced_area_offset <<= 9;
-                       /*
-                        * calculate the enhanced data area size, in kilobytes
-                        */
-                       card->ext_csd.enhanced_area_size =
-                               (ext_csd[142] << 16) + (ext_csd[141] << 8) +
-                               ext_csd[140];
-                       card->ext_csd.enhanced_area_size *=
-                               (size_t)(hc_erase_grp_sz * hc_wp_grp_sz);
-                       card->ext_csd.enhanced_area_size <<= 9;
-               } else {
-                       /*
-                        * If the enhanced area is not enabled, disable these
-                        * device attributes.
-                        */
-                       card->ext_csd.enhanced_area_offset = -EINVAL;
-                       card->ext_csd.enhanced_area_size = -EINVAL;
-               }
+               mmc_manage_enhanced_area(card, ext_csd);
 
-               /*
-                * General purpose partition feature support --
-                * If ext_csd has the size of general purpose partitions,
-                * set size, part_cfg, partition name in mmc_part.
-                */
-               if (ext_csd[EXT_CSD_PARTITION_SUPPORT] &
-                       EXT_CSD_PART_SUPPORT_PART_EN) {
-                       if (card->ext_csd.enhanced_area_en != 1) {
-                               hc_erase_grp_sz =
-                                       ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
-                               hc_wp_grp_sz =
-                                       ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
-
-                               card->ext_csd.enhanced_area_en = 1;
-                       }
+               mmc_manage_gp_partitions(card, ext_csd);
 
-                       for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) {
-                               if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] &&
-                               !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] &&
-                               !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2])
-                                       continue;
-                               part_size =
-                               (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]
-                                       << 16) +
-                               (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1]
-                                       << 8) +
-                               ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3];
-                               part_size *= (size_t)(hc_erase_grp_sz *
-                                       hc_wp_grp_sz);
-                               mmc_part_add(card, part_size << 19,
-                                       EXT_CSD_PART_CONFIG_ACC_GP0 + idx,
-                                       "gp%d", idx, false,
-                                       MMC_BLK_DATA_AREA_GP);
-                       }
-               }
                card->ext_csd.sec_trim_mult =
                        ext_csd[EXT_CSD_SEC_TRIM_MULT];
                card->ext_csd.sec_erase_mult =
@@ -789,8 +814,8 @@ static int __mmc_select_powerclass(struct mmc_card *card,
                                ext_csd->raw_pwr_cl_200_360;
                break;
        default:
-               pr_warning("%s: Voltage range not supported "
-                          "for power class.\n", mmc_hostname(host));
+               pr_warn("%s: Voltage range not supported for power class\n",
+                       mmc_hostname(host));
                return -EINVAL;
        }
 
@@ -987,19 +1012,35 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
         * 1.8V vccq at 3.3V core voltage (vcc) is not required
         * in the JEDEC spec for DDR.
         *
-        * Do not force change in vccq since we are obviously
-        * working and no change to vccq is needed.
+        * Even (e)MMC card can support 3.3v to 1.2v vccq, but not all
+        * host controller can support this, like some of the SDHCI
+        * controller which connect to an eMMC device. Some of these
+        * host controller still needs to use 1.8v vccq for supporting
+        * DDR mode.
+        *
+        * So the sequence will be:
+        * if (host and device can both support 1.2v IO)
+        *      use 1.2v IO;
+        * else if (host and device can both support 1.8v IO)
+        *      use 1.8v IO;
+        * so if host and device can only support 3.3v IO, this is the
+        * last choice.
         *
         * WARNING: eMMC rules are NOT the same as SD DDR
         */
-       if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) {
-               err = __mmc_set_signal_voltage(host,
-                               MMC_SIGNAL_VOLTAGE_120);
-               if (err)
-                       return err;
-       }
+       err = -EINVAL;
+       if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
+               err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
+
+       if (err && (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_8V))
+               err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
 
-       mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
+       /* make sure vccq is 3.3v after switching disaster */
+       if (err)
+               err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
+
+       if (!err)
+               mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
 
        return err;
 }
@@ -1134,6 +1175,38 @@ bus_speed:
        return err;
 }
 
+const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE] = {
+       0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
+       0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
+       0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
+       0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
+       0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
+       0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
+       0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
+       0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
+};
+EXPORT_SYMBOL(tuning_blk_pattern_4bit);
+
+const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE] = {
+       0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
+       0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
+       0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
+       0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
+       0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
+       0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
+       0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
+       0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
+       0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
+       0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
+       0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
+       0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
+       0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
+       0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
+       0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
+       0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
+};
+EXPORT_SYMBOL(tuning_blk_pattern_8bit);
+
 /*
  * Execute tuning sequence to seek the proper bus operating
  * conditions for HS200 and HS400, which sends CMD21 to the device.
@@ -1271,6 +1344,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                        goto free_card;
        }
 
+       /*
+        * handling only for cards supporting DSR and hosts requesting
+        * DSR configuration
+        */
+       if (card->csd.dsr_imp && host->dsr_req)
+               mmc_set_dsr(host);
+
        /*
         * Select card, as all following commands rely on that.
         */
@@ -1308,7 +1388,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
         * If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF
         * bit.  This bit will be lost every time after a reset or power off.
         */
-       if (card->ext_csd.enhanced_area_en ||
+       if (card->ext_csd.partition_setting_completed ||
            (card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) {
                err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                                 EXT_CSD_ERASE_GROUP_DEF, 1,
@@ -1408,8 +1488,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                if (err && err != -EBADMSG)
                        goto free_card;
                if (err) {
-                       pr_warning("%s: Enabling HPI failed\n",
-                                  mmc_hostname(card->host));
+                       pr_warn("%s: Enabling HPI failed\n",
+                               mmc_hostname(card->host));
                        err = 0;
                } else
                        card->ext_csd.hpi_en = 1;
@@ -1430,9 +1510,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                 * Only if no error, cache is turned on successfully.
                 */
                if (err) {
-                       pr_warning("%s: Cache is supported, "
-                                       "but failed to turn on (%d)\n",
-                                       mmc_hostname(card->host), err);
+                       pr_warn("%s: Cache is supported, but failed to turn on (%d)\n",
+                               mmc_hostname(card->host), err);
                        card->ext_csd.cache_ctrl = 0;
                        err = 0;
                } else {
index f51b5ba3bbea99ce1f2b74e5b3c80a4b882b972c..7911e0510a1d6c2ed131e0fb74c913b2a9cc0162 100644 (file)
@@ -93,6 +93,26 @@ int mmc_deselect_cards(struct mmc_host *host)
        return _mmc_select_card(host, NULL);
 }
 
+/*
+ * Write the value specified in the device tree or board code into the optional
+ * 16 bit Driver Stage Register. This can be used to tune raise/fall times and
+ * drive strength of the DAT and CMD outputs. The actual meaning of a given
+ * value is hardware dependant.
+ * The presence of the DSR register can be determined from the CSD register,
+ * bit 76.
+ */
+int mmc_set_dsr(struct mmc_host *host)
+{
+       struct mmc_command cmd = {0};
+
+       cmd.opcode = MMC_SET_DSR;
+
+       cmd.arg = (host->dsr << 16) | 0xffff;
+       cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
+
+       return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+}
+
 int mmc_go_idle(struct mmc_host *host)
 {
        int err;
@@ -629,8 +649,8 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
        int err;
 
        if (!card->ext_csd.hpi) {
-               pr_warning("%s: Card didn't support HPI command\n",
-                          mmc_hostname(card->host));
+               pr_warn("%s: Card didn't support HPI command\n",
+                       mmc_hostname(card->host));
                return -EINVAL;
        }
 
index 80ae9f4e0293a045ca8e4ebb89023e3c64e062da..390dac665b2a6246309415ca019840bee4661b07 100644 (file)
@@ -14,6 +14,7 @@
 
 int mmc_select_card(struct mmc_card *card);
 int mmc_deselect_cards(struct mmc_host *host);
+int mmc_set_dsr(struct mmc_host *host);
 int mmc_go_idle(struct mmc_host *host);
 int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
 int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
index 0c44510bf717874808a0e1d20de68aa3cf2afda5..d90a6de7901d7514550b3c9ed64d383e528244ad 100644 (file)
@@ -127,6 +127,7 @@ static int mmc_decode_csd(struct mmc_card *card)
                csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
                csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
                csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
+               csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1);
                csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
                csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
                csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
@@ -228,8 +229,8 @@ static int mmc_read_ssr(struct mmc_card *card)
        u32 *ssr;
 
        if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
-               pr_warning("%s: card lacks mandatory SD Status "
-                       "function.\n", mmc_hostname(card->host));
+               pr_warn("%s: card lacks mandatory SD Status function\n",
+                       mmc_hostname(card->host));
                return 0;
        }
 
@@ -239,8 +240,8 @@ static int mmc_read_ssr(struct mmc_card *card)
 
        err = mmc_app_sd_status(card, ssr);
        if (err) {
-               pr_warning("%s: problem reading SD Status "
-                       "register.\n", mmc_hostname(card->host));
+               pr_warn("%s: problem reading SD Status register\n",
+                       mmc_hostname(card->host));
                err = 0;
                goto out;
        }
@@ -264,8 +265,8 @@ static int mmc_read_ssr(struct mmc_card *card)
                                card->ssr.erase_offset = eo * 1000;
                        }
                } else {
-                       pr_warning("%s: SD Status: Invalid Allocation Unit size.\n",
-                                  mmc_hostname(card->host));
+                       pr_warn("%s: SD Status: Invalid Allocation Unit size\n",
+                               mmc_hostname(card->host));
                }
        }
 out:
@@ -285,8 +286,7 @@ static int mmc_read_switch(struct mmc_card *card)
                return 0;
 
        if (!(card->csd.cmdclass & CCC_SWITCH)) {
-               pr_warning("%s: card lacks mandatory switch "
-                       "function, performance might suffer.\n",
+               pr_warn("%s: card lacks mandatory switch function, performance might suffer\n",
                        mmc_hostname(card->host));
                return 0;
        }
@@ -315,7 +315,7 @@ static int mmc_read_switch(struct mmc_card *card)
                if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
                        goto out;
 
-               pr_warning("%s: problem reading Bus Speed modes.\n",
+               pr_warn("%s: problem reading Bus Speed modes\n",
                        mmc_hostname(card->host));
                err = 0;
 
@@ -371,8 +371,7 @@ int mmc_sd_switch_hs(struct mmc_card *card)
                goto out;
 
        if ((status[16] & 0xF) != 1) {
-               pr_warning("%s: Problem switching card "
-                       "into high-speed mode!\n",
+               pr_warn("%s: Problem switching card into high-speed mode!\n",
                        mmc_hostname(card->host));
                err = 0;
        } else {
@@ -439,7 +438,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
                return err;
 
        if ((status[15] & 0xF) != drive_strength) {
-               pr_warning("%s: Problem setting drive strength!\n",
+               pr_warn("%s: Problem setting drive strength!\n",
                        mmc_hostname(card->host));
                return 0;
        }
@@ -517,7 +516,7 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
                return err;
 
        if ((status[16] & 0xF) != card->sd_bus_speed)
-               pr_warning("%s: Problem setting bus speed mode!\n",
+               pr_warn("%s: Problem setting bus speed mode!\n",
                        mmc_hostname(card->host));
        else {
                mmc_set_timing(card->host, timing);
@@ -597,7 +596,7 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
                        return err;
 
                if (((status[15] >> 4) & 0x0F) != current_limit)
-                       pr_warning("%s: Problem setting current limit!\n",
+                       pr_warn("%s: Problem setting current limit!\n",
                                mmc_hostname(card->host));
 
        }
@@ -726,8 +725,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
 try_again:
        if (!retries) {
                ocr &= ~SD_OCR_S18R;
-               pr_warning("%s: Skipping voltage switch\n",
-                       mmc_hostname(host));
+               pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
        }
 
        /*
@@ -871,9 +869,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
                }
 
                if (ro < 0) {
-                       pr_warning("%s: host does not "
-                               "support reading read-only "
-                               "switch. assuming write-enable.\n",
+                       pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n",
                                mmc_hostname(host));
                } else if (ro > 0) {
                        mmc_card_set_readonly(card);
@@ -953,6 +949,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
                mmc_decode_cid(card);
        }
 
+       /*
+        * handling only for cards supporting DSR and hosts requesting
+        * DSR configuration
+        */
+       if (card->csd.dsr_imp && host->dsr_req)
+               mmc_set_dsr(host);
+
        /*
         * Select card, as all following commands rely on that.
         */
index e636d9e99e4ae7faa698bb77a33285b301a33523..2439e717655b132654fb67e9509af8d834940ee1 100644 (file)
@@ -216,8 +216,8 @@ static int sdio_enable_wide(struct mmc_card *card)
                return ret;
 
        if ((ctrl & SDIO_BUS_WIDTH_MASK) == SDIO_BUS_WIDTH_RESERVED)
-               pr_warning("%s: SDIO_CCCR_IF is invalid: 0x%02x\n",
-                          mmc_hostname(card->host), ctrl);
+               pr_warn("%s: SDIO_CCCR_IF is invalid: 0x%02x\n",
+                       mmc_hostname(card->host), ctrl);
 
        /* set as 4-bit bus width */
        ctrl &= ~SDIO_BUS_WIDTH_MASK;
@@ -605,8 +605,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
 
 try_again:
        if (!retries) {
-               pr_warning("%s: Skipping voltage switch\n",
-                               mmc_hostname(host));
+               pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
                ocr &= ~R4_18V_PRESENT;
        }
 
@@ -992,8 +991,16 @@ static int mmc_sdio_resume(struct mmc_host *host)
                }
        }
 
-       if (!err && host->sdio_irqs)
-               wake_up_process(host->sdio_irq_thread);
+       if (!err && host->sdio_irqs) {
+               if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
+                       wake_up_process(host->sdio_irq_thread);
+               } else if (host->caps & MMC_CAP_SDIO_IRQ) {
+                       mmc_host_clk_hold(host);
+                       host->ops->enable_sdio_irq(host, 1);
+                       mmc_host_clk_release(host);
+               }
+       }
+
        mmc_release_host(host);
 
        host->pm_flags &= ~MMC_PM_KEEP_POWER;
index 4fa8fef9147f75ec4e9ff974957093bb28f0ec6a..6da97b170563a8f6bebfba78779d8cb20ffa78bb 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
 #include <linux/acpi.h>
 
 #include <linux/mmc/card.h>
@@ -177,8 +178,8 @@ static int sdio_bus_remove(struct device *dev)
        drv->remove(func);
 
        if (func->irq_handler) {
-               pr_warning("WARNING: driver %s did not remove "
-                       "its interrupt handler!\n", drv->name);
+               pr_warn("WARNING: driver %s did not remove its interrupt handler!\n",
+                       drv->name);
                sdio_claim_host(func);
                sdio_release_irq(func);
                sdio_release_host(func);
@@ -315,7 +316,7 @@ int sdio_add_func(struct sdio_func *func)
        ret = device_add(&func->dev);
        if (ret == 0) {
                sdio_func_set_present(func);
-               acpi_dev_pm_attach(&func->dev, false);
+               dev_pm_domain_attach(&func->dev, false);
        }
 
        return ret;
@@ -332,7 +333,7 @@ void sdio_remove_func(struct sdio_func *func)
        if (!sdio_func_present(func))
                return;
 
-       acpi_dev_pm_detach(&func->dev, false);
+       dev_pm_domain_detach(&func->dev, false);
        device_del(&func->dev);
        put_device(&func->dev);
 }
index 5cc13c8d35bbf3b2c60c8bc68054e05bec0e62fc..09cc67d028f07b76165414625829bdd22f56b7f0 100644 (file)
@@ -69,16 +69,15 @@ static int process_sdio_pending_irqs(struct mmc_host *host)
                if (pending & (1 << i)) {
                        func = card->sdio_func[i - 1];
                        if (!func) {
-                               pr_warning("%s: pending IRQ for "
-                                       "non-existent function\n",
+                               pr_warn("%s: pending IRQ for non-existent function\n",
                                        mmc_card_id(card));
                                ret = -EINVAL;
                        } else if (func->irq_handler) {
                                func->irq_handler(func);
                                count++;
                        } else {
-                               pr_warning("%s: pending IRQ with no handler\n",
-                                      sdio_func_id(func));
+                               pr_warn("%s: pending IRQ with no handler\n",
+                                       sdio_func_id(func));
                                ret = -EINVAL;
                        }
                }
@@ -208,7 +207,7 @@ static int sdio_card_irq_get(struct mmc_card *card)
                                host->sdio_irqs--;
                                return err;
                        }
-               } else {
+               } else if (host->caps & MMC_CAP_SDIO_IRQ) {
                        mmc_host_clk_hold(host);
                        host->ops->enable_sdio_irq(host, 1);
                        mmc_host_clk_release(host);
@@ -229,7 +228,7 @@ static int sdio_card_irq_put(struct mmc_card *card)
                if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
                        atomic_set(&host->sdio_irq_thread_abort, 1);
                        kthread_stop(host->sdio_irq_thread);
-               } else {
+               } else if (host->caps & MMC_CAP_SDIO_IRQ) {
                        mmc_host_clk_hold(host);
                        host->ops->enable_sdio_irq(host, 0);
                        mmc_host_clk_release(host);
index 5f89cb83d5f00008aa3c98f550f9ff962c0306c0..69bbf2adb329db5570b0c0b698152110fb2ccee4 100644 (file)
@@ -221,8 +221,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
        ctx->override_cd_active_level = true;
        ctx->cd_gpio = gpio_to_desc(gpio);
 
-       mmc_gpiod_request_cd_irq(host);
-
        return 0;
 }
 EXPORT_SYMBOL(mmc_gpio_request_cd);
@@ -283,6 +281,8 @@ EXPORT_SYMBOL(mmc_gpio_free_cd);
  * @idx: index of the GPIO to obtain in the consumer
  * @override_active_level: ignore %GPIO_ACTIVE_LOW flag
  * @debounce: debounce time in microseconds
+ * @gpio_invert: will return whether the GPIO line is inverted or not, set
+ * to NULL to ignore
  *
  * Use this function in place of mmc_gpio_request_cd() to use the GPIO
  * descriptor API.  Note that it is paired with mmc_gpiod_free_cd() not
@@ -293,7 +293,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd);
  */
 int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
                         unsigned int idx, bool override_active_level,
-                        unsigned int debounce)
+                        unsigned int debounce, bool *gpio_invert)
 {
        struct mmc_gpio *ctx;
        struct gpio_desc *desc;
@@ -308,20 +308,19 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
        if (!con_id)
                con_id = ctx->cd_label;
 
-       desc = devm_gpiod_get_index(host->parent, con_id, idx);
+       desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
        if (IS_ERR(desc))
                return PTR_ERR(desc);
 
-       ret = gpiod_direction_input(desc);
-       if (ret < 0)
-               return ret;
-
        if (debounce) {
                ret = gpiod_set_debounce(desc, debounce);
                if (ret < 0)
                        return ret;
        }
 
+       if (gpio_invert)
+               *gpio_invert = !gpiod_is_active_low(desc);
+
        ctx->override_cd_active_level = override_active_level;
        ctx->cd_gpio = desc;
 
@@ -329,6 +328,59 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
 }
 EXPORT_SYMBOL(mmc_gpiod_request_cd);
 
+/**
+ * mmc_gpiod_request_ro - request a gpio descriptor for write protection
+ * @host: mmc host
+ * @con_id: function within the GPIO consumer
+ * @idx: index of the GPIO to obtain in the consumer
+ * @override_active_level: ignore %GPIO_ACTIVE_LOW flag
+ * @debounce: debounce time in microseconds
+ * @gpio_invert: will return whether the GPIO line is inverted or not,
+ * set to NULL to ignore
+ *
+ * Use this function in place of mmc_gpio_request_ro() to use the GPIO
+ * descriptor API.  Note that it is paired with mmc_gpiod_free_ro() not
+ * mmc_gpio_free_ro().
+ *
+ * Returns zero on success, else an error.
+ */
+int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
+                        unsigned int idx, bool override_active_level,
+                        unsigned int debounce, bool *gpio_invert)
+{
+       struct mmc_gpio *ctx;
+       struct gpio_desc *desc;
+       int ret;
+
+       ret = mmc_gpio_alloc(host);
+       if (ret < 0)
+               return ret;
+
+       ctx = host->slot.handler_priv;
+
+       if (!con_id)
+               con_id = ctx->ro_label;
+
+       desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
+       if (IS_ERR(desc))
+               return PTR_ERR(desc);
+
+       if (debounce) {
+               ret = gpiod_set_debounce(desc, debounce);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (gpio_invert)
+               *gpio_invert = !gpiod_is_active_low(desc);
+
+       ctx->override_ro_active_level = override_active_level;
+       ctx->ro_gpio = desc;
+
+       return 0;
+}
+EXPORT_SYMBOL(mmc_gpiod_request_ro);
+
 /**
  * mmc_gpiod_free_cd - free the card-detection gpio descriptor
  * @host: mmc host
@@ -348,7 +400,7 @@ void mmc_gpiod_free_cd(struct mmc_host *host)
                host->slot.cd_irq = -EINVAL;
        }
 
-       devm_gpiod_put(&host->class_dev, ctx->cd_gpio);
+       devm_gpiod_put(host->parent, ctx->cd_gpio);
 
        ctx->cd_gpio = NULL;
 }
index 45113582246427eae3f9c6d893cdd6e305a1631b..13860656104b5f58df865ee74501750a3d045c7c 100644 (file)
@@ -14,6 +14,17 @@ config MMC_ARMMMCI
 
          If unsure, say N.
 
+config MMC_QCOM_DML
+       tristate "Qualcomm Data Mover for SD Card Controller"
+       depends on MMC_ARMMMCI && QCOM_BAM_DMA
+       default y
+       help
+         This selects the Qualcomm Data Mover lite/local on SD Card controller.
+         This option will enable the dma to work correctly, if you are using
+         Qcom SOCs and MMC, you would probably need this option to get DMA working.
+
+         if unsure, say N.
+
 config MMC_PXA
        tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
        depends on ARCH_PXA
@@ -568,7 +579,8 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
 
 config MMC_DW
        tristate "Synopsys DesignWare Memory Card Interface"
-       depends on ARC || ARM
+       depends on HAS_DMA
+       depends on ARC || ARM || MIPS || COMPILE_TEST
        help
          This selects support for the Synopsys DesignWare Mobile Storage IP
          block, this provides host support for SD and MMC interfaces, in both
@@ -626,6 +638,15 @@ config MMC_DW_PCI
 
          If unsure, say N.
 
+config MMC_DW_ROCKCHIP
+       tristate "Rockchip specific extensions for Synopsys DW Memory Card Interface"
+       depends on MMC_DW && ARCH_ROCKCHIP
+       select MMC_DW_PLTFM
+       help
+         This selects support for Rockchip SoC specific extensions to the
+         Synopsys DesignWare Memory Card Interface driver. Select this option
+         for platforms based on RK3066, RK3188 and RK3288 SoC's.
+
 config MMC_SH_MMCIF
        tristate "SuperH Internal MMCIF support"
        depends on MMC_BLOCK && HAS_DMA
index f211eede8db58d48887d5619d96c4cca6bdbdbab..b09ecfb88269da9f3d1b5796a2b84137e3e10dd5 100644 (file)
@@ -3,6 +3,7 @@
 #
 
 obj-$(CONFIG_MMC_ARMMMCI)      += mmci.o
+obj-$(CONFIG_MMC_QCOM_DML)     += mmci_qcom_dml.o
 obj-$(CONFIG_MMC_PXA)          += pxamci.o
 obj-$(CONFIG_MMC_MXC)          += mxcmmc.o
 obj-$(CONFIG_MMC_MXS)          += mxs-mmc.o
@@ -45,6 +46,7 @@ obj-$(CONFIG_MMC_DW_PLTFM)    += dw_mmc-pltfm.o
 obj-$(CONFIG_MMC_DW_EXYNOS)    += dw_mmc-exynos.o
 obj-$(CONFIG_MMC_DW_K3)                += dw_mmc-k3.o
 obj-$(CONFIG_MMC_DW_PCI)       += dw_mmc-pci.o
+obj-$(CONFIG_MMC_DW_ROCKCHIP)  += dw_mmc-rockchip.o
 obj-$(CONFIG_MMC_SH_MMCIF)     += sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)       += jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)       += vub300.o
index bb585d9409014e8bead8879acf5f7145e9e5790c..77250d4b197923124f9389b811623ea3a6359c28 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/gpio.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -2195,7 +2196,8 @@ static int __init atmci_init_slot(struct atmel_mci *host,
        /* Assume card is present initially */
        set_bit(ATMCI_CARD_PRESENT, &slot->flags);
        if (gpio_is_valid(slot->detect_pin)) {
-               if (gpio_request(slot->detect_pin, "mmc_detect")) {
+               if (devm_gpio_request(&host->pdev->dev, slot->detect_pin,
+                                     "mmc_detect")) {
                        dev_dbg(&mmc->class_dev, "no detect pin available\n");
                        slot->detect_pin = -EBUSY;
                } else if (gpio_get_value(slot->detect_pin) ^
@@ -2208,7 +2210,8 @@ static int __init atmci_init_slot(struct atmel_mci *host,
                mmc->caps |= MMC_CAP_NEEDS_POLL;
 
        if (gpio_is_valid(slot->wp_pin)) {
-               if (gpio_request(slot->wp_pin, "mmc_wp")) {
+               if (devm_gpio_request(&host->pdev->dev, slot->wp_pin,
+                                     "mmc_wp")) {
                        dev_dbg(&mmc->class_dev, "no WP pin available\n");
                        slot->wp_pin = -EBUSY;
                }
@@ -2232,7 +2235,6 @@ static int __init atmci_init_slot(struct atmel_mci *host,
                        dev_dbg(&mmc->class_dev,
                                "could not request IRQ %d for detect pin\n",
                                gpio_to_irq(slot->detect_pin));
-                       gpio_free(slot->detect_pin);
                        slot->detect_pin = -EBUSY;
                }
        }
@@ -2242,7 +2244,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
        return 0;
 }
 
-static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
+static void atmci_cleanup_slot(struct atmel_mci_slot *slot,
                unsigned int id)
 {
        /* Debugfs stuff is cleaned up by mmc core */
@@ -2257,10 +2259,7 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
 
                free_irq(gpio_to_irq(pin), slot);
                del_timer_sync(&slot->detect_timer);
-               gpio_free(pin);
        }
-       if (gpio_is_valid(slot->wp_pin))
-               gpio_free(slot->wp_pin);
 
        slot->host->slot[id] = NULL;
        mmc_free_host(slot->mmc);
@@ -2344,6 +2343,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
 
        /* keep only major version number */
        switch (version & 0xf00) {
+       case 0x600:
        case 0x500:
                host->caps.has_odd_clk_div = 1;
        case 0x400:
@@ -2377,7 +2377,7 @@ static int __init atmci_probe(struct platform_device *pdev)
        struct resource                 *regs;
        unsigned int                    nr_slots;
        int                             irq;
-       int                             ret;
+       int                             ret, i;
 
        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!regs)
@@ -2395,7 +2395,7 @@ static int __init atmci_probe(struct platform_device *pdev)
        if (irq < 0)
                return irq;
 
-       host = kzalloc(sizeof(struct atmel_mci), GFP_KERNEL);
+       host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
        if (!host)
                return -ENOMEM;
 
@@ -2403,20 +2403,18 @@ static int __init atmci_probe(struct platform_device *pdev)
        spin_lock_init(&host->lock);
        INIT_LIST_HEAD(&host->queue);
 
-       host->mck = clk_get(&pdev->dev, "mci_clk");
-       if (IS_ERR(host->mck)) {
-               ret = PTR_ERR(host->mck);
-               goto err_clk_get;
-       }
+       host->mck = devm_clk_get(&pdev->dev, "mci_clk");
+       if (IS_ERR(host->mck))
+               return PTR_ERR(host->mck);
 
-       ret = -ENOMEM;
-       host->regs = ioremap(regs->start, resource_size(regs));
+       host->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
        if (!host->regs)
-               goto err_ioremap;
+               return -ENOMEM;
 
        ret = clk_prepare_enable(host->mck);
        if (ret)
-               goto err_request_irq;
+               return ret;
+
        atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
        host->bus_hz = clk_get_rate(host->mck);
        clk_disable_unprepare(host->mck);
@@ -2427,7 +2425,7 @@ static int __init atmci_probe(struct platform_device *pdev)
 
        ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
        if (ret)
-               goto err_request_irq;
+               return ret;
 
        /* Get MCI capabilities and set operations according to it */
        atmci_get_cap(host);
@@ -2485,7 +2483,7 @@ static int __init atmci_probe(struct platform_device *pdev)
                if (!host->buffer) {
                        ret = -ENOMEM;
                        dev_err(&pdev->dev, "buffer allocation failed\n");
-                       goto err_init_slot;
+                       goto err_dma_alloc;
                }
        }
 
@@ -2495,16 +2493,16 @@ static int __init atmci_probe(struct platform_device *pdev)
 
        return 0;
 
+err_dma_alloc:
+       for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
+               if (host->slot[i])
+                       atmci_cleanup_slot(host->slot[i], i);
+       }
 err_init_slot:
+       del_timer_sync(&host->timer);
        if (host->dma.chan)
                dma_release_channel(host->dma.chan);
        free_irq(irq, host);
-err_request_irq:
-       iounmap(host->regs);
-err_ioremap:
-       clk_put(host->mck);
-err_clk_get:
-       kfree(host);
        return ret;
 }
 
@@ -2528,14 +2526,11 @@ static int __exit atmci_remove(struct platform_device *pdev)
        atmci_readl(host, ATMCI_SR);
        clk_disable_unprepare(host->mck);
 
+       del_timer_sync(&host->timer);
        if (host->dma.chan)
                dma_release_channel(host->dma.chan);
 
        free_irq(platform_get_irq(pdev, 0), host);
-       iounmap(host->regs);
-
-       clk_put(host->mck);
-       kfree(host);
 
        return 0;
 }
index 9c9f6af29251c6f6be68b533f22b29584104c889..725f6a6fd89b425b83cffb9e093d55d819a5078f 100644 (file)
@@ -1028,9 +1028,12 @@ static int au1xmmc_probe(struct platform_device *pdev)
        host->clk = clk_get(&pdev->dev, ALCHEMY_PERIPH_CLK);
        if (IS_ERR(host->clk)) {
                dev_err(&pdev->dev, "cannot find clock\n");
+               ret = PTR_ERR(host->clk);
                goto out_irq;
        }
-       if (clk_prepare_enable(host->clk)) {
+
+       ret = clk_prepare_enable(host->clk);
+       if (ret) {
                dev_err(&pdev->dev, "cannot enable clock\n");
                goto out_clk;
        }
index 6ada1b36685b0b20f471cfdd1318590046464c4d..4c69fbd2981190734d4ef1ff9861df73c1ab5806 100644 (file)
@@ -95,9 +95,6 @@ static int dw_mci_pci_resume(struct device *dev)
 
        return dw_mci_resume(host);
 }
-#else
-#define dw_mci_pci_suspend     NULL
-#define dw_mci_pci_resume      NULL
 #endif /* CONFIG_PM_SLEEP */
 
 static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume);
index d4a47a9f55848cbc40f383c3378a9c20304667d6..8b6572162ed960ed9d8c0ead021dda6a574c664e 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/dw_mmc.h>
 #include <linux/of.h>
+#include <linux/clk.h>
 
 #include "dw_mmc.h"
 #include "dw_mmc-pltfm.h"
@@ -30,10 +31,6 @@ static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr)
        *cmdr |= SDMMC_CMD_USE_HOLD_REG;
 }
 
-static const struct dw_mci_drv_data rockchip_drv_data = {
-       .prepare_command        = dw_mci_pltfm_prepare_command,
-};
-
 static const struct dw_mci_drv_data socfpga_drv_data = {
        .prepare_command        = dw_mci_pltfm_prepare_command,
 };
@@ -84,9 +81,6 @@ static int dw_mci_pltfm_resume(struct device *dev)
 
        return dw_mci_resume(host);
 }
-#else
-#define dw_mci_pltfm_suspend   NULL
-#define dw_mci_pltfm_resume    NULL
 #endif /* CONFIG_PM_SLEEP */
 
 SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
@@ -94,8 +88,6 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
 
 static const struct of_device_id dw_mci_pltfm_match[] = {
        { .compatible = "snps,dw-mshc", },
-       { .compatible = "rockchip,rk2928-dw-mshc",
-               .data = &rockchip_drv_data },
        { .compatible = "altr,socfpga-dw-mshc",
                .data = &socfpga_drv_data },
        {},
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
new file mode 100644 (file)
index 0000000..f0c2cb1
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/of_address.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define RK3288_CLKGEN_DIV       2
+
+static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
+{
+       *cmdr |= SDMMC_CMD_USE_HOLD_REG;
+}
+
+static int dw_mci_rk3288_setup_clock(struct dw_mci *host)
+{
+       host->bus_hz /= RK3288_CLKGEN_DIV;
+
+       return 0;
+}
+
+static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+       int ret;
+       unsigned int cclkin;
+       u32 bus_hz;
+
+       /*
+        * cclkin: source clock of mmc controller
+        * bus_hz: card interface clock generated by CLKGEN
+        * bus_hz = cclkin / RK3288_CLKGEN_DIV
+        * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div))
+        *
+        * Note: div can only be 0 or 1
+        *       if DDR50 8bit mode(only emmc work in 8bit mode),
+        *       div must be set 1
+        */
+       if (ios->bus_width == MMC_BUS_WIDTH_8 &&
+           ios->timing == MMC_TIMING_MMC_DDR52)
+               cclkin = 2 * ios->clock * RK3288_CLKGEN_DIV;
+       else
+               cclkin = ios->clock * RK3288_CLKGEN_DIV;
+
+       ret = clk_set_rate(host->ciu_clk, cclkin);
+       if (ret)
+               dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock);
+
+       bus_hz = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV;
+       if (bus_hz != host->bus_hz) {
+               host->bus_hz = bus_hz;
+               /* force dw_mci_setup_bus() */
+               host->current_speed = 0;
+       }
+}
+
+static const struct dw_mci_drv_data rk2928_drv_data = {
+       .prepare_command        = dw_mci_rockchip_prepare_command,
+};
+
+static const struct dw_mci_drv_data rk3288_drv_data = {
+       .prepare_command        = dw_mci_rockchip_prepare_command,
+       .set_ios                = dw_mci_rk3288_set_ios,
+       .setup_clock    = dw_mci_rk3288_setup_clock,
+};
+
+static const struct of_device_id dw_mci_rockchip_match[] = {
+       { .compatible = "rockchip,rk2928-dw-mshc",
+               .data = &rk2928_drv_data },
+       { .compatible = "rockchip,rk3288-dw-mshc",
+               .data = &rk3288_drv_data },
+       {},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match);
+
+static int dw_mci_rockchip_probe(struct platform_device *pdev)
+{
+       const struct dw_mci_drv_data *drv_data;
+       const struct of_device_id *match;
+
+       if (!pdev->dev.of_node)
+               return -ENODEV;
+
+       match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
+       drv_data = match->data;
+
+       return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dw_mci_rockchip_suspend(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+
+       return dw_mci_suspend(host);
+}
+
+static int dw_mci_rockchip_resume(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+
+       return dw_mci_resume(host);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops,
+                        dw_mci_rockchip_suspend,
+                        dw_mci_rockchip_resume);
+
+static struct platform_driver dw_mci_rockchip_pltfm_driver = {
+       .probe          = dw_mci_rockchip_probe,
+       .remove         = __exit_p(dw_mci_pltfm_remove),
+       .driver         = {
+               .name           = "dwmmc_rockchip",
+               .of_match_table = dw_mci_rockchip_match,
+               .pm             = &dw_mci_rockchip_pmops,
+       },
+};
+
+module_platform_driver(dw_mci_rockchip_pltfm_driver);
+
+MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension");
+MODULE_ALIAS("platform:dwmmc-rockchip");
+MODULE_LICENSE("GPL v2");
index 8f216edbdf080d0c51e3eedcf83a75a1886fc9e3..69f0cc68d5b2727412c3b430d0f5eaee99072941 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/irq.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/dw_mmc.h>
 #include <linux/bitops.h>
@@ -81,36 +82,6 @@ struct idmac_desc {
 };
 #endif /* CONFIG_MMC_DW_IDMAC */
 
-static const u8 tuning_blk_pattern_4bit[] = {
-       0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
-       0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
-       0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
-       0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
-       0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
-       0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
-       0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
-       0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
-};
-
-static const u8 tuning_blk_pattern_8bit[] = {
-       0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
-       0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
-       0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
-       0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
-       0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
-       0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
-       0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
-       0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
-       0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
-       0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
-       0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
-       0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
-       0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
-       0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
-       0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
-       0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
-};
-
 static bool dw_mci_reset(struct dw_mci *host);
 
 #if defined(CONFIG_DEBUG_FS)
@@ -234,10 +205,13 @@ err:
 }
 #endif /* defined(CONFIG_DEBUG_FS) */
 
+static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg);
+
 static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 {
        struct mmc_data *data;
        struct dw_mci_slot *slot = mmc_priv(mmc);
+       struct dw_mci *host = slot->host;
        const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
        u32 cmdr;
        cmd->error = -EINPROGRESS;
@@ -253,6 +227,34 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
        else if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
                cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
 
+       if (cmd->opcode == SD_SWITCH_VOLTAGE) {
+               u32 clk_en_a;
+
+               /* Special bit makes CMD11 not die */
+               cmdr |= SDMMC_CMD_VOLT_SWITCH;
+
+               /* Change state to continue to handle CMD11 weirdness */
+               WARN_ON(slot->host->state != STATE_SENDING_CMD);
+               slot->host->state = STATE_SENDING_CMD11;
+
+               /*
+                * We need to disable low power mode (automatic clock stop)
+                * while doing voltage switch so we don't confuse the card,
+                * since stopping the clock is a specific part of the UHS
+                * voltage change dance.
+                *
+                * Note that low power mode (SDMMC_CLKEN_LOW_PWR) will be
+                * unconditionally turned back on in dw_mci_setup_bus() if it's
+                * ever called with a non-zero clock.  That shouldn't happen
+                * until the voltage change is all done.
+                */
+               clk_en_a = mci_readl(host, CLKENA);
+               clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id);
+               mci_writel(host, CLKENA, clk_en_a);
+               mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
+                            SDMMC_CMD_PRV_DAT_WAIT, 0);
+       }
+
        if (cmd->flags & MMC_RSP_PRESENT) {
                /* We expect a response, so set this bit */
                cmdr |= SDMMC_CMD_RESP_EXP;
@@ -775,11 +777,15 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
        unsigned int clock = slot->clock;
        u32 div;
        u32 clk_en_a;
+       u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT;
+
+       /* We must continue to set bit 28 in CMD until the change is complete */
+       if (host->state == STATE_WAITING_CMD11_DONE)
+               sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH;
 
        if (!clock) {
                mci_writel(host, CLKENA, 0);
-               mci_send_cmd(slot,
-                            SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+               mci_send_cmd(slot, sdmmc_cmd_bits, 0);
        } else if (clock != host->current_speed || force_clkinit) {
                div = host->bus_hz / clock;
                if (host->bus_hz % clock && host->bus_hz > clock)
@@ -803,15 +809,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
                mci_writel(host, CLKSRC, 0);
 
                /* inform CIU */
-               mci_send_cmd(slot,
-                            SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+               mci_send_cmd(slot, sdmmc_cmd_bits, 0);
 
                /* set clock to desired speed */
                mci_writel(host, CLKDIV, div);
 
                /* inform CIU */
-               mci_send_cmd(slot,
-                            SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+               mci_send_cmd(slot, sdmmc_cmd_bits, 0);
 
                /* enable clock; only low power if no SDIO */
                clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
@@ -820,8 +824,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
                mci_writel(host, CLKENA, clk_en_a);
 
                /* inform CIU */
-               mci_send_cmd(slot,
-                            SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+               mci_send_cmd(slot, sdmmc_cmd_bits, 0);
 
                /* keep the clock with reflecting clock dividor */
                slot->__clk_old = clock << div;
@@ -897,6 +900,17 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
 
        slot->mrq = mrq;
 
+       if (host->state == STATE_WAITING_CMD11_DONE) {
+               dev_warn(&slot->mmc->class_dev,
+                        "Voltage change didn't complete\n");
+               /*
+                * this case isn't expected to happen, so we can
+                * either crash here or just try to continue on
+                * in the closest possible state
+                */
+               host->state = STATE_IDLE;
+       }
+
        if (host->state == STATE_IDLE) {
                host->state = STATE_SENDING_CMD;
                dw_mci_start_request(host, slot);
@@ -936,6 +950,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        struct dw_mci_slot *slot = mmc_priv(mmc);
        const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
        u32 regs;
+       int ret;
 
        switch (ios->bus_width) {
        case MMC_BUS_WIDTH_4:
@@ -972,14 +987,43 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        /* Slot specific timing and width adjustment */
        dw_mci_setup_bus(slot, false);
 
+       if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
+               slot->host->state = STATE_IDLE;
+
        switch (ios->power_mode) {
        case MMC_POWER_UP:
+               if (!IS_ERR(mmc->supply.vmmc)) {
+                       ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
+                                       ios->vdd);
+                       if (ret) {
+                               dev_err(slot->host->dev,
+                                       "failed to enable vmmc regulator\n");
+                               /*return, if failed turn on vmmc*/
+                               return;
+                       }
+               }
+               if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) {
+                       ret = regulator_enable(mmc->supply.vqmmc);
+                       if (ret < 0)
+                               dev_err(slot->host->dev,
+                                       "failed to enable vqmmc regulator\n");
+                       else
+                               slot->host->vqmmc_enabled = true;
+               }
                set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
                regs = mci_readl(slot->host, PWREN);
                regs |= (1 << slot->id);
                mci_writel(slot->host, PWREN, regs);
                break;
        case MMC_POWER_OFF:
+               if (!IS_ERR(mmc->supply.vmmc))
+                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+
+               if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) {
+                       regulator_disable(mmc->supply.vqmmc);
+                       slot->host->vqmmc_enabled = false;
+               }
+
                regs = mci_readl(slot->host, PWREN);
                regs &= ~(1 << slot->id);
                mci_writel(slot->host, PWREN, regs);
@@ -989,6 +1033,59 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        }
 }
 
+static int dw_mci_card_busy(struct mmc_host *mmc)
+{
+       struct dw_mci_slot *slot = mmc_priv(mmc);
+       u32 status;
+
+       /*
+        * Check the busy bit which is low when DAT[3:0]
+        * (the data lines) are 0000
+        */
+       status = mci_readl(slot->host, STATUS);
+
+       return !!(status & SDMMC_STATUS_BUSY);
+}
+
+static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct dw_mci_slot *slot = mmc_priv(mmc);
+       struct dw_mci *host = slot->host;
+       u32 uhs;
+       u32 v18 = SDMMC_UHS_18V << slot->id;
+       int min_uv, max_uv;
+       int ret;
+
+       /*
+        * Program the voltage.  Note that some instances of dw_mmc may use
+        * the UHS_REG for this.  For other instances (like exynos) the UHS_REG
+        * does no harm but you need to set the regulator directly.  Try both.
+        */
+       uhs = mci_readl(host, UHS_REG);
+       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+               min_uv = 2700000;
+               max_uv = 3600000;
+               uhs &= ~v18;
+       } else {
+               min_uv = 1700000;
+               max_uv = 1950000;
+               uhs |= v18;
+       }
+       if (!IS_ERR(mmc->supply.vqmmc)) {
+               ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
+
+               if (ret) {
+                       dev_err(&mmc->class_dev,
+                                        "Regulator set error %d: %d - %d\n",
+                                        ret, min_uv, max_uv);
+                       return ret;
+               }
+       }
+       mci_writel(host, UHS_REG, uhs);
+
+       return 0;
+}
+
 static int dw_mci_get_ro(struct mmc_host *mmc)
 {
        int read_only;
@@ -1131,6 +1228,9 @@ static const struct mmc_host_ops dw_mci_ops = {
        .get_cd                 = dw_mci_get_cd,
        .enable_sdio_irq        = dw_mci_enable_sdio_irq,
        .execute_tuning         = dw_mci_execute_tuning,
+       .card_busy              = dw_mci_card_busy,
+       .start_signal_voltage_switch = dw_mci_switch_voltage,
+
 };
 
 static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
@@ -1154,7 +1254,11 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
                dw_mci_start_request(host, slot);
        } else {
                dev_vdbg(host->dev, "list empty\n");
-               host->state = STATE_IDLE;
+
+               if (host->state == STATE_SENDING_CMD11)
+                       host->state = STATE_WAITING_CMD11_DONE;
+               else
+                       host->state = STATE_IDLE;
        }
 
        spin_unlock(&host->lock);
@@ -1265,8 +1369,10 @@ static void dw_mci_tasklet_func(unsigned long priv)
 
                switch (state) {
                case STATE_IDLE:
+               case STATE_WAITING_CMD11_DONE:
                        break;
 
+               case STATE_SENDING_CMD11:
                case STATE_SENDING_CMD:
                        if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
                                                &host->pending_events))
@@ -1299,6 +1405,14 @@ static void dw_mci_tasklet_func(unsigned long priv)
                        /* fall through */
 
                case STATE_SENDING_DATA:
+                       /*
+                        * We could get a data error and never a transfer
+                        * complete so we'd better check for it here.
+                        *
+                        * Note that we don't really care if we also got a
+                        * transfer complete; stopping the DMA and sending an
+                        * abort won't hurt.
+                        */
                        if (test_and_clear_bit(EVENT_DATA_ERROR,
                                               &host->pending_events)) {
                                dw_mci_stop_dma(host);
@@ -1312,7 +1426,29 @@ static void dw_mci_tasklet_func(unsigned long priv)
                                break;
 
                        set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
+
+                       /*
+                        * Handle an EVENT_DATA_ERROR that might have shown up
+                        * before the transfer completed.  This might not have
+                        * been caught by the check above because the interrupt
+                        * could have gone off between the previous check and
+                        * the check for transfer complete.
+                        *
+                        * Technically this ought not be needed assuming we
+                        * get a DATA_COMPLETE eventually (we'll notice the
+                        * error and end the request), but it shouldn't hurt.
+                        *
+                        * This has the advantage of sending the stop command.
+                        */
+                       if (test_and_clear_bit(EVENT_DATA_ERROR,
+                                              &host->pending_events)) {
+                               dw_mci_stop_dma(host);
+                               send_stop_abort(host, data);
+                               state = STATE_DATA_ERROR;
+                               break;
+                       }
                        prev_state = state = STATE_DATA_BUSY;
+
                        /* fall through */
 
                case STATE_DATA_BUSY:
@@ -1335,6 +1471,22 @@ static void dw_mci_tasklet_func(unsigned long priv)
                                /* stop command for open-ended transfer*/
                                if (data->stop)
                                        send_stop_abort(host, data);
+                       } else {
+                               /*
+                                * If we don't have a command complete now we'll
+                                * never get one since we just reset everything;
+                                * better end the request.
+                                *
+                                * If we do have a command complete we'll fall
+                                * through to the SENDING_STOP command and
+                                * everything will be peachy keen.
+                                */
+                               if (!test_bit(EVENT_CMD_COMPLETE,
+                                             &host->pending_events)) {
+                                       host->cmd = NULL;
+                                       dw_mci_request_end(host, mrq);
+                                       goto unlock;
+                               }
                        }
 
                        /*
@@ -1821,6 +1973,14 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
        }
 
        if (pending) {
+               /* Check volt switch first, since it can look like an error */
+               if ((host->state == STATE_SENDING_CMD11) &&
+                   (pending & SDMMC_INT_VOLT_SWITCH)) {
+                       mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH);
+                       pending &= ~SDMMC_INT_VOLT_SWITCH;
+                       dw_mci_cmd_interrupt(host, pending);
+               }
+
                if (pending & DW_MCI_CMD_ERROR_FLAGS) {
                        mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
                        host->cmd_status = pending;
@@ -1926,7 +2086,9 @@ static void dw_mci_work_routine_card(struct work_struct *work)
 
                                        switch (host->state) {
                                        case STATE_IDLE:
+                                       case STATE_WAITING_CMD11_DONE:
                                                break;
+                                       case STATE_SENDING_CMD11:
                                        case STATE_SENDING_CMD:
                                                mrq->cmd->error = -ENOMEDIUM;
                                                if (!mrq->data)
@@ -2028,10 +2190,6 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
 {
        return 0;
 }
-static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
-{
-       return NULL;
-}
 #endif /* CONFIG_OF */
 
 static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
@@ -2064,7 +2222,13 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
                mmc->f_max = freq[1];
        }
 
-       mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+       /*if there are external regulators, get them*/
+       ret = mmc_regulator_get_supply(mmc);
+       if (ret == -EPROBE_DEFER)
+               goto err_host_allocated;
+
+       if (!mmc->ocr_avail)
+               mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
 
        if (host->pdata->caps)
                mmc->caps = host->pdata->caps;
@@ -2085,7 +2249,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        if (host->pdata->caps2)
                mmc->caps2 = host->pdata->caps2;
 
-       mmc_of_parse(mmc);
+       ret = mmc_of_parse(mmc);
+       if (ret)
+               goto err_host_allocated;
 
        if (host->pdata->blk_settings) {
                mmc->max_segs = host->pdata->blk_settings->max_segs;
@@ -2117,7 +2283,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 
        ret = mmc_add_host(mmc);
        if (ret)
-               goto err_setup_bus;
+               goto err_host_allocated;
 
 #if defined(CONFIG_DEBUG_FS)
        dw_mci_init_debugfs(slot);
@@ -2128,9 +2294,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 
        return 0;
 
-err_setup_bus:
+err_host_allocated:
        mmc_free_host(mmc);
-       return -EINVAL;
+       return ret;
 }
 
 static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
@@ -2423,24 +2589,6 @@ int dw_mci_probe(struct dw_mci *host)
                }
        }
 
-       host->vmmc = devm_regulator_get_optional(host->dev, "vmmc");
-       if (IS_ERR(host->vmmc)) {
-               ret = PTR_ERR(host->vmmc);
-               if (ret == -EPROBE_DEFER)
-                       goto err_clk_ciu;
-
-               dev_info(host->dev, "no vmmc regulator found: %d\n", ret);
-               host->vmmc = NULL;
-       } else {
-               ret = regulator_enable(host->vmmc);
-               if (ret) {
-                       if (ret != -EPROBE_DEFER)
-                               dev_err(host->dev,
-                                       "regulator_enable fail: %d\n", ret);
-                       goto err_clk_ciu;
-               }
-       }
-
        host->quirks = host->pdata->quirks;
 
        spin_lock_init(&host->lock);
@@ -2584,8 +2732,6 @@ err_workqueue:
 err_dmaunmap:
        if (host->use_dma && host->dma_ops->exit)
                host->dma_ops->exit(host);
-       if (host->vmmc)
-               regulator_disable(host->vmmc);
 
 err_clk_ciu:
        if (!IS_ERR(host->ciu_clk))
@@ -2621,9 +2767,6 @@ void dw_mci_remove(struct dw_mci *host)
        if (host->use_dma && host->dma_ops->exit)
                host->dma_ops->exit(host);
 
-       if (host->vmmc)
-               regulator_disable(host->vmmc);
-
        if (!IS_ERR(host->ciu_clk))
                clk_disable_unprepare(host->ciu_clk);
 
@@ -2640,9 +2783,6 @@ EXPORT_SYMBOL(dw_mci_remove);
  */
 int dw_mci_suspend(struct dw_mci *host)
 {
-       if (host->vmmc)
-               regulator_disable(host->vmmc);
-
        return 0;
 }
 EXPORT_SYMBOL(dw_mci_suspend);
@@ -2651,15 +2791,6 @@ int dw_mci_resume(struct dw_mci *host)
 {
        int i, ret;
 
-       if (host->vmmc) {
-               ret = regulator_enable(host->vmmc);
-               if (ret) {
-                       dev_err(host->dev,
-                               "failed to enable regulator: %d\n", ret);
-                       return ret;
-               }
-       }
-
        if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) {
                ret = -ENODEV;
                return ret;
index 08fd956d81f3bc687162e333cb612c37f06ad025..01b99e8a919073b10dace3daea7b8444f70b0e9b 100644 (file)
@@ -99,6 +99,7 @@
 #define SDMMC_INT_HLE                  BIT(12)
 #define SDMMC_INT_FRUN                 BIT(11)
 #define SDMMC_INT_HTO                  BIT(10)
+#define SDMMC_INT_VOLT_SWITCH          BIT(10) /* overloads bit 10! */
 #define SDMMC_INT_DRTO                 BIT(9)
 #define SDMMC_INT_RTO                  BIT(8)
 #define SDMMC_INT_DCRC                 BIT(7)
 /* Command register defines */
 #define SDMMC_CMD_START                        BIT(31)
 #define SDMMC_CMD_USE_HOLD_REG BIT(29)
+#define SDMMC_CMD_VOLT_SWITCH          BIT(28)
 #define SDMMC_CMD_CCS_EXP              BIT(23)
 #define SDMMC_CMD_CEATA_RD             BIT(22)
 #define SDMMC_CMD_UPD_CLK              BIT(21)
 /* Status register defines */
 #define SDMMC_GET_FCNT(x)              (((x)>>17) & 0x1FFF)
 #define SDMMC_STATUS_DMA_REQ           BIT(31)
+#define SDMMC_STATUS_BUSY              BIT(9)
 /* FIFOTH register defines */
 #define SDMMC_SET_FIFOTH(m, r, t)      (((m) & 0x7) << 28 | \
                                         ((r) & 0xFFF) << 16 | \
 #define SDMMC_GET_VERID(x)             ((x) & 0xFFFF)
 /* Card read threshold */
 #define SDMMC_SET_RD_THLD(v, x)                (((v) & 0x1FFF) << 16 | (x))
-
+#define SDMMC_UHS_18V                  BIT(0)
 /* All ctrl reset bits */
 #define SDMMC_CTRL_ALL_RESET_FLAGS \
        (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
index 537d6c7a5ae48a9458dd3674cf56a975a8331996..76e8bce6f46e7015bb6ae9e3dfe082d2cf58db35 100644 (file)
@@ -30,7 +30,9 @@
 #include <asm/mach-jz4740/gpio.h>
 #include <asm/cacheflush.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 
+#include <asm/mach-jz4740/dma.h>
 #include <asm/mach-jz4740/jz4740_mmc.h>
 
 #define JZ_REG_MMC_STRPCL      0x00
@@ -112,6 +114,11 @@ enum jz4740_mmc_state {
        JZ4740_MMC_STATE_DONE,
 };
 
+struct jz4740_mmc_host_next {
+       int sg_len;
+       s32 cookie;
+};
+
 struct jz4740_mmc_host {
        struct mmc_host *mmc;
        struct platform_device *pdev;
@@ -122,6 +129,7 @@ struct jz4740_mmc_host {
        int card_detect_irq;
 
        void __iomem *base;
+       struct resource *mem_res;
        struct mmc_request *req;
        struct mmc_command *cmd;
 
@@ -136,8 +144,220 @@ struct jz4740_mmc_host {
        struct timer_list timeout_timer;
        struct sg_mapping_iter miter;
        enum jz4740_mmc_state state;
+
+       /* DMA support */
+       struct dma_chan *dma_rx;
+       struct dma_chan *dma_tx;
+       struct jz4740_mmc_host_next next_data;
+       bool use_dma;
+       int sg_len;
+
+/* The DMA trigger level is 8 words, that is to say, the DMA read
+ * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write
+ * trigger is when data words in MSC_TXFIFO is < 8.
+ */
+#define JZ4740_MMC_FIFO_HALF_SIZE 8
 };
 
+/*----------------------------------------------------------------------------*/
+/* DMA infrastructure */
+
+static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host)
+{
+       if (!host->use_dma)
+               return;
+
+       dma_release_channel(host->dma_tx);
+       dma_release_channel(host->dma_rx);
+}
+
+static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
+{
+       dma_cap_mask_t mask;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       host->dma_tx = dma_request_channel(mask, NULL, host);
+       if (!host->dma_tx) {
+               dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n");
+               return -ENODEV;
+       }
+
+       host->dma_rx = dma_request_channel(mask, NULL, host);
+       if (!host->dma_rx) {
+               dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n");
+               goto free_master_write;
+       }
+
+       /* Initialize DMA pre request cookie */
+       host->next_data.cookie = 1;
+
+       return 0;
+
+free_master_write:
+       dma_release_channel(host->dma_tx);
+       return -ENODEV;
+}
+
+static inline int jz4740_mmc_get_dma_dir(struct mmc_data *data)
+{
+       return (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+}
+
+static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host,
+                                                      struct mmc_data *data)
+{
+       return (data->flags & MMC_DATA_READ) ? host->dma_rx : host->dma_tx;
+}
+
+static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host,
+                                struct mmc_data *data)
+{
+       struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
+       enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data);
+
+       dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
+}
+
+/* Prepares DMA data for current/next transfer, returns non-zero on failure */
+static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host,
+                                      struct mmc_data *data,
+                                      struct jz4740_mmc_host_next *next,
+                                      struct dma_chan *chan)
+{
+       struct jz4740_mmc_host_next *next_data = &host->next_data;
+       enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data);
+       int sg_len;
+
+       if (!next && data->host_cookie &&
+           data->host_cookie != host->next_data.cookie) {
+               dev_warn(mmc_dev(host->mmc),
+                        "[%s] invalid cookie: data->host_cookie %d host->next_data.cookie %d\n",
+                        __func__,
+                        data->host_cookie,
+                        host->next_data.cookie);
+               data->host_cookie = 0;
+       }
+
+       /* Check if next job is already prepared */
+       if (next || data->host_cookie != host->next_data.cookie) {
+               sg_len = dma_map_sg(chan->device->dev,
+                                   data->sg,
+                                   data->sg_len,
+                                   dir);
+
+       } else {
+               sg_len = next_data->sg_len;
+               next_data->sg_len = 0;
+       }
+
+       if (sg_len <= 0) {
+               dev_err(mmc_dev(host->mmc),
+                       "Failed to map scatterlist for DMA operation\n");
+               return -EINVAL;
+       }
+
+       if (next) {
+               next->sg_len = sg_len;
+               data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
+       } else
+               host->sg_len = sg_len;
+
+       return 0;
+}
+
+static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
+                                        struct mmc_data *data)
+{
+       int ret;
+       struct dma_chan *chan;
+       struct dma_async_tx_descriptor *desc;
+       struct dma_slave_config conf = {
+               .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+               .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+               .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
+               .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
+       };
+
+       if (data->flags & MMC_DATA_WRITE) {
+               conf.direction = DMA_MEM_TO_DEV;
+               conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
+               conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT;
+               chan = host->dma_tx;
+       } else {
+               conf.direction = DMA_DEV_TO_MEM;
+               conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
+               conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE;
+               chan = host->dma_rx;
+       }
+
+       ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan);
+       if (ret)
+               return ret;
+
+       dmaengine_slave_config(chan, &conf);
+       desc = dmaengine_prep_slave_sg(chan,
+                                      data->sg,
+                                      host->sg_len,
+                                      conf.direction,
+                                      DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!desc) {
+               dev_err(mmc_dev(host->mmc),
+                       "Failed to allocate DMA %s descriptor",
+                        conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX");
+               goto dma_unmap;
+       }
+
+       dmaengine_submit(desc);
+       dma_async_issue_pending(chan);
+
+       return 0;
+
+dma_unmap:
+       jz4740_mmc_dma_unmap(host, data);
+       return -ENOMEM;
+}
+
+static void jz4740_mmc_pre_request(struct mmc_host *mmc,
+                                  struct mmc_request *mrq,
+                                  bool is_first_req)
+{
+       struct jz4740_mmc_host *host = mmc_priv(mmc);
+       struct mmc_data *data = mrq->data;
+       struct jz4740_mmc_host_next *next_data = &host->next_data;
+
+       BUG_ON(data->host_cookie);
+
+       if (host->use_dma) {
+               struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
+
+               if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan))
+                       data->host_cookie = 0;
+       }
+}
+
+static void jz4740_mmc_post_request(struct mmc_host *mmc,
+                                   struct mmc_request *mrq,
+                                   int err)
+{
+       struct jz4740_mmc_host *host = mmc_priv(mmc);
+       struct mmc_data *data = mrq->data;
+
+       if (host->use_dma && data->host_cookie) {
+               jz4740_mmc_dma_unmap(host, data);
+               data->host_cookie = 0;
+       }
+
+       if (err) {
+               struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
+
+               dmaengine_terminate_all(chan);
+       }
+}
+
+/*----------------------------------------------------------------------------*/
+
 static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host,
        unsigned int irq, bool enabled)
 {
@@ -442,6 +662,8 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host,
                        cmdat |= JZ_MMC_CMDAT_WRITE;
                if (cmd->data->flags & MMC_DATA_STREAM)
                        cmdat |= JZ_MMC_CMDAT_STREAM;
+               if (host->use_dma)
+                       cmdat |= JZ_MMC_CMDAT_DMA_EN;
 
                writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
                writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
@@ -474,6 +696,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
        struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid;
        struct mmc_command *cmd = host->req->cmd;
        struct mmc_request *req = host->req;
+       struct mmc_data *data = cmd->data;
        bool timeout = false;
 
        if (cmd->error)
@@ -484,23 +707,37 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
                if (cmd->flags & MMC_RSP_PRESENT)
                        jz4740_mmc_read_response(host, cmd);
 
-               if (!cmd->data)
+               if (!data)
                        break;
 
                jz_mmc_prepare_data_transfer(host);
 
        case JZ4740_MMC_STATE_TRANSFER_DATA:
-               if (cmd->data->flags & MMC_DATA_READ)
-                       timeout = jz4740_mmc_read_data(host, cmd->data);
+               if (host->use_dma) {
+                       /* Use DMA if enabled.
+                        * Data transfer direction is defined later by
+                        * relying on data flags in
+                        * jz4740_mmc_prepare_dma_data() and
+                        * jz4740_mmc_start_dma_transfer().
+                        */
+                       timeout = jz4740_mmc_start_dma_transfer(host, data);
+                       data->bytes_xfered = data->blocks * data->blksz;
+               } else if (data->flags & MMC_DATA_READ)
+                       /* Use PIO if DMA is not enabled.
+                        * Data transfer direction was defined before
+                        * by relying on data flags in
+                        * jz_mmc_prepare_data_transfer().
+                        */
+                       timeout = jz4740_mmc_read_data(host, data);
                else
-                       timeout = jz4740_mmc_write_data(host, cmd->data);
+                       timeout = jz4740_mmc_write_data(host, data);
 
                if (unlikely(timeout)) {
                        host->state = JZ4740_MMC_STATE_TRANSFER_DATA;
                        break;
                }
 
-               jz4740_mmc_transfer_check_state(host, cmd->data);
+               jz4740_mmc_transfer_check_state(host, data);
 
                timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
                if (unlikely(timeout)) {
@@ -664,6 +901,8 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
 
 static const struct mmc_host_ops jz4740_mmc_ops = {
        .request        = jz4740_mmc_request,
+       .pre_req        = jz4740_mmc_pre_request,
+       .post_req       = jz4740_mmc_post_request,
        .set_ios        = jz4740_mmc_set_ios,
        .get_ro         = mmc_gpio_get_ro,
        .get_cd         = mmc_gpio_get_cd,
@@ -757,7 +996,6 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
        struct mmc_host *mmc;
        struct jz4740_mmc_host *host;
        struct jz4740_mmc_platform_data *pdata;
-       struct resource *res;
 
        pdata = pdev->dev.platform_data;
 
@@ -784,10 +1022,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
                goto err_free_host;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       host->base = devm_ioremap_resource(&pdev->dev, res);
+       host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       host->base = devm_ioremap_resource(&pdev->dev, host->mem_res);
        if (IS_ERR(host->base)) {
                ret = PTR_ERR(host->base);
+               dev_err(&pdev->dev, "Failed to ioremap base memory\n");
                goto err_free_host;
        }
 
@@ -834,6 +1073,10 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
        /* It is not important when it times out, it just needs to timeout. */
        set_timer_slack(&host->timeout_timer, HZ);
 
+       host->use_dma = true;
+       if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0)
+               host->use_dma = false;
+
        platform_set_drvdata(pdev, host);
        ret = mmc_add_host(mmc);
 
@@ -843,6 +1086,10 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
        }
        dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
 
+       dev_info(&pdev->dev, "Using %s, %d-bit mode\n",
+                host->use_dma ? "DMA" : "PIO",
+                (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1);
+
        return 0;
 
 err_free_irq:
@@ -850,6 +1097,8 @@ err_free_irq:
 err_free_gpios:
        jz4740_mmc_free_gpios(pdev);
 err_gpio_bulk_free:
+       if (host->use_dma)
+               jz4740_mmc_release_dma_channels(host);
        jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
 err_free_host:
        mmc_free_host(mmc);
@@ -872,6 +1121,9 @@ static int jz4740_mmc_remove(struct platform_device *pdev)
        jz4740_mmc_free_gpios(pdev);
        jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
 
+       if (host->use_dma)
+               jz4740_mmc_release_dma_channels(host);
+
        mmc_free_host(host->mmc);
 
        return 0;
@@ -909,7 +1161,6 @@ static struct platform_driver jz4740_mmc_driver = {
        .remove = jz4740_mmc_remove,
        .driver = {
                .name = "jz4740-mmc",
-               .owner = THIS_MODULE,
                .pm = JZ4740_MMC_PM_OPS,
        },
 };
index cc8d4a6099cdc602071af7616e1451896e7f6959..e4a07546f8b631d4c905dbe483449bf381960279 100644 (file)
@@ -1436,6 +1436,7 @@ static int mmc_spi_probe(struct spi_device *spi)
                                             host->pdata->cd_debounce);
                if (status != 0)
                        goto fail_add_host;
+               mmc_gpiod_request_cd_irq(mmc);
        }
 
        if (host->pdata && host->pdata->flags & MMC_SPI_USE_RO_GPIO) {
index e4d470704150c257bdf0e535cefa05f4bc41a059..43af791e2e45e1b29a94fc364978c99a315f2ffa 100644 (file)
@@ -43,6 +43,7 @@
 #include <asm/sizes.h>
 
 #include "mmci.h"
+#include "mmci_qcom_dml.h"
 
 #define DRIVER_NAME "mmci-pl18x"
 
@@ -60,12 +61,13 @@ static unsigned int fmax = 515633;
  * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
  *               is asserted (likewise for RX)
  * @data_cmd_enable: enable value for data commands.
- * @sdio: variant supports SDIO
+ * @st_sdio: enable ST specific SDIO logic
  * @st_clkdiv: true if using a ST-specific clock divider algorithm
  * @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
  * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
  * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
  *                  register
+ * @datactrl_mask_sdio: SDIO enable mask in datactrl register
  * @pwrreg_powerup: power up value for MMCIPOWER register
  * @f_max: maximum clk frequency supported by the controller.
  * @signal_direction: input/out direction of bus signals can be indicated
@@ -74,6 +76,7 @@ static unsigned int fmax = 515633;
  * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
  * @explicit_mclk_control: enable explicit mclk control in driver.
  * @qcom_fifo: enables qcom specific fifo pio read logic.
+ * @qcom_dml: enables qcom specific dma glue for dma transfers.
  * @reversed_irq_handling: handle data irq before cmd irq.
  */
 struct variant_data {
@@ -86,7 +89,8 @@ struct variant_data {
        unsigned int            fifohalfsize;
        unsigned int            data_cmd_enable;
        unsigned int            datactrl_mask_ddrmode;
-       bool                    sdio;
+       unsigned int            datactrl_mask_sdio;
+       bool                    st_sdio;
        bool                    st_clkdiv;
        bool                    blksz_datactrl16;
        bool                    blksz_datactrl4;
@@ -98,6 +102,7 @@ struct variant_data {
        bool                    pwrreg_nopower;
        bool                    explicit_mclk_control;
        bool                    qcom_fifo;
+       bool                    qcom_dml;
        bool                    reversed_irq_handling;
 };
 
@@ -133,7 +138,8 @@ static struct variant_data variant_u300 = {
        .clkreg_enable          = MCI_ST_U300_HWFCEN,
        .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
        .datalength_bits        = 16,
-       .sdio                   = true,
+       .datactrl_mask_sdio     = MCI_ST_DPSM_SDIOEN,
+       .st_sdio                        = true,
        .pwrreg_powerup         = MCI_PWR_ON,
        .f_max                  = 100000000,
        .signal_direction       = true,
@@ -146,7 +152,8 @@ static struct variant_data variant_nomadik = {
        .fifohalfsize           = 8 * 4,
        .clkreg                 = MCI_CLK_ENABLE,
        .datalength_bits        = 24,
-       .sdio                   = true,
+       .datactrl_mask_sdio     = MCI_ST_DPSM_SDIOEN,
+       .st_sdio                = true,
        .st_clkdiv              = true,
        .pwrreg_powerup         = MCI_PWR_ON,
        .f_max                  = 100000000,
@@ -163,7 +170,8 @@ static struct variant_data variant_ux500 = {
        .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
        .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
        .datalength_bits        = 24,
-       .sdio                   = true,
+       .datactrl_mask_sdio     = MCI_ST_DPSM_SDIOEN,
+       .st_sdio                = true,
        .st_clkdiv              = true,
        .pwrreg_powerup         = MCI_PWR_ON,
        .f_max                  = 100000000,
@@ -182,7 +190,8 @@ static struct variant_data variant_ux500v2 = {
        .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
        .datactrl_mask_ddrmode  = MCI_ST_DPSM_DDRMODE,
        .datalength_bits        = 24,
-       .sdio                   = true,
+       .datactrl_mask_sdio     = MCI_ST_DPSM_SDIOEN,
+       .st_sdio                = true,
        .st_clkdiv              = true,
        .blksz_datactrl16       = true,
        .pwrreg_powerup         = MCI_PWR_ON,
@@ -208,6 +217,7 @@ static struct variant_data variant_qcom = {
        .f_max                  = 208000000,
        .explicit_mclk_control  = true,
        .qcom_fifo              = true,
+       .qcom_dml               = true,
 };
 
 static int mmci_card_busy(struct mmc_host *mmc)
@@ -421,6 +431,7 @@ static void mmci_dma_setup(struct mmci_host *host)
 {
        const char *rxname, *txname;
        dma_cap_mask_t mask;
+       struct variant_data *variant = host->variant;
 
        host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
        host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
@@ -471,6 +482,10 @@ static void mmci_dma_setup(struct mmci_host *host)
                if (max_seg_size < host->mmc->max_seg_size)
                        host->mmc->max_seg_size = max_seg_size;
        }
+
+       if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel)
+               if (dml_hw_init(host, host->mmc->parent->of_node))
+                       variant->qcom_dml = false;
 }
 
 /*
@@ -572,6 +587,7 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
        struct dma_async_tx_descriptor *desc;
        enum dma_data_direction buffer_dirn;
        int nr_sg;
+       unsigned long flags = DMA_CTRL_ACK;
 
        if (data->flags & MMC_DATA_READ) {
                conf.direction = DMA_DEV_TO_MEM;
@@ -596,9 +612,12 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
        if (nr_sg == 0)
                return -EINVAL;
 
+       if (host->variant->qcom_dml)
+               flags |= DMA_PREP_INTERRUPT;
+
        dmaengine_slave_config(chan, &conf);
        desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
-                                           conf.direction, DMA_CTRL_ACK);
+                                           conf.direction, flags);
        if (!desc)
                goto unmap_exit;
 
@@ -647,6 +666,9 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
        dmaengine_submit(host->dma_desc_current);
        dma_async_issue_pending(host->dma_current);
 
+       if (host->variant->qcom_dml)
+               dml_start_xfer(host, data);
+
        datactrl |= MCI_DPSM_DMAENABLE;
 
        /* Trigger the DMA transfer */
@@ -792,32 +814,26 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
        if (data->flags & MMC_DATA_READ)
                datactrl |= MCI_DPSM_DIRECTION;
 
-       /* The ST Micro variants has a special bit to enable SDIO */
-       if (variant->sdio && host->mmc->card)
-               if (mmc_card_sdio(host->mmc->card)) {
-                       /*
-                        * The ST Micro variants has a special bit
-                        * to enable SDIO.
-                        */
-                       u32 clk;
+       if (host->mmc->card && mmc_card_sdio(host->mmc->card)) {
+               u32 clk;
 
-                       datactrl |= MCI_ST_DPSM_SDIOEN;
+               datactrl |= variant->datactrl_mask_sdio;
 
-                       /*
-                        * The ST Micro variant for SDIO small write transfers
-                        * needs to have clock H/W flow control disabled,
-                        * otherwise the transfer will not start. The threshold
-                        * depends on the rate of MCLK.
-                        */
-                       if (data->flags & MMC_DATA_WRITE &&
-                           (host->size < 8 ||
-                            (host->size <= 8 && host->mclk > 50000000)))
-                               clk = host->clk_reg & ~variant->clkreg_enable;
-                       else
-                               clk = host->clk_reg | variant->clkreg_enable;
+               /*
+                * The ST Micro variant for SDIO small write transfers
+                * needs to have clock H/W flow control disabled,
+                * otherwise the transfer will not start. The threshold
+                * depends on the rate of MCLK.
+                */
+               if (variant->st_sdio && data->flags & MMC_DATA_WRITE &&
+                   (host->size < 8 ||
+                    (host->size <= 8 && host->mclk > 50000000)))
+                       clk = host->clk_reg & ~variant->clkreg_enable;
+               else
+                       clk = host->clk_reg | variant->clkreg_enable;
 
-                       mmci_write_clkreg(host, clk);
-               }
+               mmci_write_clkreg(host, clk);
+       }
 
        if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 ||
            host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)
@@ -1658,16 +1674,35 @@ static int mmci_probe(struct amba_device *dev,
        writel(0, host->base + MMCIMASK1);
        writel(0xfff, host->base + MMCICLEAR);
 
-       /* If DT, cd/wp gpios must be supplied through it. */
-       if (!np && gpio_is_valid(plat->gpio_cd)) {
-               ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0);
-               if (ret)
-                       goto clk_disable;
-       }
-       if (!np && gpio_is_valid(plat->gpio_wp)) {
-               ret = mmc_gpio_request_ro(mmc, plat->gpio_wp);
-               if (ret)
-                       goto clk_disable;
+       /*
+        * If:
+        * - not using DT but using a descriptor table, or
+        * - using a table of descriptors ALONGSIDE DT, or
+        * look up these descriptors named "cd" and "wp" right here, fail
+        * silently of these do not exist and proceed to try platform data
+        */
+       if (!np) {
+               ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
+               if (ret < 0) {
+                       if (ret == -EPROBE_DEFER)
+                               goto clk_disable;
+                       else if (gpio_is_valid(plat->gpio_cd)) {
+                               ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0);
+                               if (ret)
+                                       goto clk_disable;
+                       }
+               }
+
+               ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL);
+               if (ret < 0) {
+                       if (ret == -EPROBE_DEFER)
+                               goto clk_disable;
+                       else if (gpio_is_valid(plat->gpio_wp)) {
+                               ret = mmc_gpio_request_ro(mmc, plat->gpio_wp);
+                               if (ret)
+                                       goto clk_disable;
+                       }
+               }
        }
 
        ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED,
diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c
new file mode 100644 (file)
index 0000000..2b7fc37
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ *
+ * Copyright (c) 2011, The Linux Foundation. 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 version 2 and
+ * only 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.
+ *
+ */
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/bitops.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include "mmci.h"
+
+/* Registers */
+#define DML_CONFIG                     0x00
+#define PRODUCER_CRCI_MSK              GENMASK(1, 0)
+#define PRODUCER_CRCI_DISABLE          0
+#define PRODUCER_CRCI_X_SEL            BIT(0)
+#define PRODUCER_CRCI_Y_SEL            BIT(1)
+#define CONSUMER_CRCI_MSK              GENMASK(3, 2)
+#define CONSUMER_CRCI_DISABLE          0
+#define CONSUMER_CRCI_X_SEL            BIT(2)
+#define CONSUMER_CRCI_Y_SEL            BIT(3)
+#define PRODUCER_TRANS_END_EN          BIT(4)
+#define BYPASS                         BIT(16)
+#define DIRECT_MODE                    BIT(17)
+#define INFINITE_CONS_TRANS            BIT(18)
+
+#define DML_SW_RESET                   0x08
+#define DML_PRODUCER_START             0x0c
+#define DML_CONSUMER_START             0x10
+#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14
+#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18
+#define DML_PIPE_ID                    0x1c
+#define PRODUCER_PIPE_ID_SHFT          0
+#define PRODUCER_PIPE_ID_MSK           GENMASK(4, 0)
+#define CONSUMER_PIPE_ID_SHFT          16
+#define CONSUMER_PIPE_ID_MSK           GENMASK(20, 16)
+
+#define DML_PRODUCER_BAM_BLOCK_SIZE    0x24
+#define DML_PRODUCER_BAM_TRANS_SIZE    0x28
+
+/* other definitions */
+#define PRODUCER_PIPE_LOGICAL_SIZE     4096
+#define CONSUMER_PIPE_LOGICAL_SIZE     4096
+
+#define DML_OFFSET                     0x800
+
+void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
+{
+       u32 config;
+       void __iomem *base = host->base + DML_OFFSET;
+
+       if (data->flags & MMC_DATA_READ) {
+               /* Read operation: configure DML for producer operation */
+               /* Set producer CRCI-x and disable consumer CRCI */
+               config = readl_relaxed(base + DML_CONFIG);
+               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
+               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE;
+               writel_relaxed(config, base + DML_CONFIG);
+
+               /* Set the Producer BAM block size */
+               writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE);
+
+               /* Set Producer BAM Transaction size */
+               writel_relaxed(data->blocks * data->blksz,
+                              base + DML_PRODUCER_BAM_TRANS_SIZE);
+               /* Set Producer Transaction End bit */
+               config = readl_relaxed(base + DML_CONFIG);
+               config |= PRODUCER_TRANS_END_EN;
+               writel_relaxed(config, base + DML_CONFIG);
+               /* Trigger producer */
+               writel_relaxed(1, base + DML_PRODUCER_START);
+       } else {
+               /* Write operation: configure DML for consumer operation */
+               /* Set consumer CRCI-x and disable producer CRCI*/
+               config = readl_relaxed(base + DML_CONFIG);
+               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
+               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE;
+               writel_relaxed(config, base + DML_CONFIG);
+               /* Clear Producer Transaction End bit */
+               config = readl_relaxed(base + DML_CONFIG);
+               config &= ~PRODUCER_TRANS_END_EN;
+               writel_relaxed(config, base + DML_CONFIG);
+               /* Trigger consumer */
+               writel_relaxed(1, base + DML_CONSUMER_START);
+       }
+
+       /* make sure the dml is configured before dma is triggered */
+       wmb();
+}
+
+static int of_get_dml_pipe_index(struct device_node *np, const char *name)
+{
+       int index;
+       struct of_phandle_args  dma_spec;
+
+       index = of_property_match_string(np, "dma-names", name);
+
+       if (index < 0)
+               return -ENODEV;
+
+       if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
+                                      &dma_spec))
+               return -ENODEV;
+
+       if (dma_spec.args_count)
+               return dma_spec.args[0];
+
+       return -ENODEV;
+}
+
+/* Initialize the dml hardware connected to SD Card controller */
+int dml_hw_init(struct mmci_host *host, struct device_node *np)
+{
+       u32 config;
+       void __iomem *base;
+       int consumer_id, producer_id;
+
+       consumer_id = of_get_dml_pipe_index(np, "tx");
+       producer_id = of_get_dml_pipe_index(np, "rx");
+
+       if (producer_id < 0 || consumer_id < 0)
+               return -ENODEV;
+
+       base = host->base + DML_OFFSET;
+
+       /* Reset the DML block */
+       writel_relaxed(1, base + DML_SW_RESET);
+
+       /* Disable the producer and consumer CRCI */
+       config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE);
+       /*
+        * Disable the bypass mode. Bypass mode will only be used
+        * if data transfer is to happen in PIO mode and don't
+        * want the BAM interface to connect with SDCC-DML.
+        */
+       config &= ~BYPASS;
+       /*
+        * Disable direct mode as we don't DML to MASTER the AHB bus.
+        * BAM connected with DML should MASTER the AHB bus.
+        */
+       config &= ~DIRECT_MODE;
+       /*
+        * Disable infinite mode transfer as we won't be doing any
+        * infinite size data transfers. All data transfer will be
+        * of finite data size.
+        */
+       config &= ~INFINITE_CONS_TRANS;
+       writel_relaxed(config, base + DML_CONFIG);
+
+       /*
+        * Initialize the logical BAM pipe size for producer
+        * and consumer.
+        */
+       writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
+                      base + DML_PRODUCER_PIPE_LOGICAL_SIZE);
+       writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
+                      base + DML_CONSUMER_PIPE_LOGICAL_SIZE);
+
+       /* Initialize Producer/consumer pipe id */
+       writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT),
+                      base + DML_PIPE_ID);
+
+       /* Make sure dml intialization is finished */
+       mb();
+
+       return 0;
+}
diff --git a/drivers/mmc/host/mmci_qcom_dml.h b/drivers/mmc/host/mmci_qcom_dml.h
new file mode 100644 (file)
index 0000000..6e405d0
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright (c) 2011, The Linux Foundation. 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 version 2 and
+ * only 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.
+ *
+ */
+#ifndef __MMC_QCOM_DML_H__
+#define __MMC_QCOM_DML_H__
+
+#ifdef CONFIG_MMC_QCOM_DML
+int dml_hw_init(struct mmci_host *host, struct device_node *np);
+void dml_start_xfer(struct mmci_host *host, struct mmc_data *data);
+#else
+static inline int dml_hw_init(struct mmci_host *host, struct device_node *np)
+{
+       return -ENOSYS;
+}
+static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
+{
+}
+#endif /* CONFIG_MMC_QCOM_DML */
+
+#endif /* __MMC_QCOM_DML_H__ */
index b4b1efbf6c165c21aa1d8f1fe13e6bc9efb57f09..f3e18d08e8529e05e443395bfbf3cc73b1c3496a 100644 (file)
@@ -717,7 +717,6 @@ static struct platform_driver moxart_mmc_driver = {
        .remove     = moxart_remove,
        .driver     = {
                .name           = "mmc-moxart",
-               .owner          = THIS_MODULE,
                .of_match_table = moxart_mmc_match,
        },
 };
index ed1cb93c3784b54a62916e9aeb0540db2bf303e6..ad111422ad55b9dbbf3b83dacd0aaf33c286a204 100644 (file)
@@ -1238,7 +1238,6 @@ static struct platform_driver mxcmci_driver = {
        .id_table       = mxcmci_devtype,
        .driver         = {
                .name           = DRIVER_NAME,
-               .owner          = THIS_MODULE,
                .pm     = &mxcmci_pm_ops,
                .of_match_table = mxcmci_of_match,
        }
index 140885a5a4e734bbcb3806e39e7601503e1cf599..cd74e5143c36a2d2c90641e45723920364ca08b1 100644 (file)
@@ -735,7 +735,6 @@ static struct platform_driver mxs_mmc_driver = {
        .id_table       = mxs_ssp_ids,
        .driver         = {
                .name   = DRIVER_NAME,
-               .owner  = THIS_MODULE,
 #ifdef CONFIG_PM
                .pm     = &mxs_mmc_pm_ops,
 #endif
index 81974ecdfcbcb7a1649eee6eeab7a14dd8f49600..68dd6c79c378c8a355a36e23300eb996bfcbc720 100644 (file)
@@ -1494,7 +1494,6 @@ static struct platform_driver mmc_omap_driver = {
        .remove         = mmc_omap_remove,
        .driver         = {
                .name   = DRIVER_NAME,
-               .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(mmc_omap_match),
        },
 };
index 965672663ef066a3b2e58c5f1a2478c5becdfdda..df27bb4fc098b218a298e70493aa5c22131c7b52 100644 (file)
@@ -1829,7 +1829,17 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc)
        return 0;
 }
 
-static const struct mmc_host_ops omap_hsmmc_ops = {
+static int omap_hsmmc_multi_io_quirk(struct mmc_card *card,
+                                    unsigned int direction, int blk_size)
+{
+       /* This controller can't do multiblock reads due to hw bugs */
+       if (direction == MMC_DATA_READ)
+               return 1;
+
+       return blk_size;
+}
+
+static struct mmc_host_ops omap_hsmmc_ops = {
        .enable = omap_hsmmc_enable_fclk,
        .disable = omap_hsmmc_disable_fclk,
        .post_req = omap_hsmmc_post_req,
@@ -2101,7 +2111,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 
        if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
                dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
-               mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
+               omap_hsmmc_ops.multi_io_quirk = omap_hsmmc_multi_io_quirk;
        }
 
        pm_runtime_enable(host->dev);
@@ -2489,7 +2499,6 @@ static struct platform_driver omap_hsmmc_driver = {
        .remove         = omap_hsmmc_remove,
        .driver         = {
                .name = DRIVER_NAME,
-               .owner = THIS_MODULE,
                .pm = &omap_hsmmc_dev_pm_ops,
                .of_match_table = of_match_ptr(omap_mmc_of_match),
        },
index 32fe11323f39e10bee30cac93daa0ba94e771939..1b6d0bfe35f53c2d474d884feda7c7b1271911c7 100644 (file)
@@ -474,7 +474,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                unsigned int clk = rate / ios->clock;
 
                if (host->clkrt == CLKRT_OFF)
-                       clk_enable(host->clk);
+                       clk_prepare_enable(host->clk);
 
                if (ios->clock == 26000000) {
                        /* to support 26MHz */
@@ -501,7 +501,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                pxamci_stop_clock(host);
                if (host->clkrt != CLKRT_OFF) {
                        host->clkrt = CLKRT_OFF;
-                       clk_disable(host->clk);
+                       clk_disable_unprepare(host->clk);
                }
        }
 
@@ -885,7 +885,6 @@ static struct platform_driver pxamci_driver = {
        .remove         = pxamci_remove,
        .driver         = {
                .name   = DRIVER_NAME,
-               .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(pxa_mmc_dt_ids),
        },
 };
index dfde4a21023864ae26aa68336001772d51e41209..c70b602f8f1ef435c9d7dcf1e31c9f3f570dc997 100644 (file)
@@ -412,6 +412,13 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
        }
 
        if (rsp_type == SD_RSP_TYPE_R2) {
+               /*
+                * The controller offloads the last byte {CRC-7, end bit 1'b1}
+                * of response type R2. Assign dummy CRC, 0, and end bit to the
+                * byte(ptr[16], goes into the LSB of resp[3] later).
+                */
+               ptr[16] = 1;
+
                for (i = 0; i < 4; i++) {
                        cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4);
                        dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n",
@@ -1292,6 +1299,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
        mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
                MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
                MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+       mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
        mmc->max_current_330 = 400;
        mmc->max_current_180 = 800;
        mmc->ops = &realtek_pci_sdmmc_ops;
@@ -1416,7 +1424,6 @@ static struct platform_driver rtsx_pci_sdmmc_driver = {
        .remove         = rtsx_pci_sdmmc_drv_remove,
        .id_table       = rtsx_pci_sdmmc_ids,
        .driver         = {
-               .owner  = THIS_MODULE,
                .name   = DRV_NAME_RTSX_PCI_SDMMC,
        },
 };
index 5d3766e792f0855cd117b42fab7068e3c90a7726..88af827e086b9f66b5acbfe663cb8837b1d62d24 100644 (file)
@@ -435,6 +435,13 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host,
        }
 
        if (rsp_type == SD_RSP_TYPE_R2) {
+               /*
+                * The controller offloads the last byte {CRC-7, end bit 1'b1}
+                * of response type R2. Assign dummy CRC, 0, and end bit to the
+                * byte(ptr[16], goes into the LSB of resp[3] later).
+                */
+               ptr[16] = 1;
+
                for (i = 0; i < 4; i++) {
                        cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4);
                        dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n",
@@ -1329,6 +1336,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
                MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
                MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
                MMC_CAP_NEEDS_POLL;
+       mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
 
        mmc->max_current_330 = 400;
        mmc->max_current_180 = 800;
@@ -1445,7 +1453,6 @@ static struct platform_driver rtsx_usb_sdmmc_driver = {
        .remove         = rtsx_usb_sdmmc_drv_remove,
        .id_table       = rtsx_usb_sdmmc_ids,
        .driver         = {
-               .owner  = THIS_MODULE,
                .name   = "rtsx_usb_sdmmc",
        },
 };
index e5516a226362dc0c4e98c39215bfe2c55f87b992..94cddf381ba3d72d1e9359afffb694f06dce6118 100644 (file)
@@ -985,7 +985,8 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
                 * one block being transferred. */
 
                if (data->blocks > 1) {
-                       pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);
+                       pr_warn("%s: can't do non-word sized block transfers (blksz %d)\n",
+                               __func__, data->blksz);
                        return -EINVAL;
                }
        }
@@ -1874,7 +1875,6 @@ MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
 static struct platform_driver s3cmci_driver = {
        .driver = {
                .name   = "s3c-sdi",
-               .owner  = THIS_MODULE,
        },
        .id_table       = s3cmci_driver_ids,
        .probe          = s3cmci_probe,
index 8c5337002c5137ec5658cc79f929c25f378ab0f8..9cccc0e89b0435928ca375d8b1640c5008cc6551 100644 (file)
@@ -67,6 +67,8 @@ struct sdhci_acpi_slot {
        unsigned int    caps2;
        mmc_pm_flag_t   pm_caps;
        unsigned int    flags;
+       int (*probe_slot)(struct platform_device *, const char *, const char *);
+       int (*remove_slot)(struct platform_device *);
 };
 
 struct sdhci_acpi_host {
@@ -122,13 +124,67 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = {
        .ops = &sdhci_acpi_ops_int,
 };
 
+static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev,
+                                     const char *hid, const char *uid)
+{
+       struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
+       struct sdhci_host *host;
+
+       if (!c || !c->host)
+               return 0;
+
+       host = c->host;
+
+       /* Platform specific code during emmc proble slot goes here */
+
+       if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") &&
+           sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 &&
+           sdhci_readl(host, SDHCI_CAPABILITIES_1) == 0x00000807)
+               host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
+
+       return 0;
+}
+
+static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev,
+                                     const char *hid, const char *uid)
+{
+       struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
+       struct sdhci_host *host;
+
+       if (!c || !c->host)
+               return 0;
+
+       host = c->host;
+
+       /* Platform specific code during emmc proble slot goes here */
+
+       return 0;
+}
+
+static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev,
+                                   const char *hid, const char *uid)
+{
+       struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
+       struct sdhci_host *host;
+
+       if (!c || !c->host || !c->slot)
+               return 0;
+
+       host = c->host;
+
+       /* Platform specific code during emmc proble slot goes here */
+
+       return 0;
+}
+
 static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
        .chip    = &sdhci_acpi_chip_int,
        .caps    = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
                   MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR,
        .caps2   = MMC_CAP2_HC_ERASE_SZ,
        .flags   = SDHCI_ACPI_RUNTIME_PM,
-       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC,
+       .probe_slot     = sdhci_acpi_emmc_probe_slot,
 };
 
 static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
@@ -137,12 +193,15 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
        .caps    = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
        .flags   = SDHCI_ACPI_RUNTIME_PM,
        .pm_caps = MMC_PM_KEEP_POWER,
+       .probe_slot     = sdhci_acpi_sdio_probe_slot,
 };
 
 static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
        .flags   = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
                   SDHCI_ACPI_RUNTIME_PM,
-       .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
+       .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
+                  SDHCI_QUIRK2_STOP_WITH_TC,
+       .probe_slot     = sdhci_acpi_sd_probe_slot,
 };
 
 struct sdhci_acpi_uid_slot {
@@ -156,6 +215,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
        { "80860F14" , "3" , &sdhci_acpi_slot_int_sd   },
        { "80860F16" , NULL, &sdhci_acpi_slot_int_sd   },
        { "INT33BB"  , "2" , &sdhci_acpi_slot_int_sdio },
+       { "INT33BB"  , "3" , &sdhci_acpi_slot_int_sd },
        { "INT33C6"  , NULL, &sdhci_acpi_slot_int_sdio },
        { "INT3436"  , NULL, &sdhci_acpi_slot_int_sdio },
        { "PNP0D40"  },
@@ -173,8 +233,8 @@ static const struct acpi_device_id sdhci_acpi_ids[] = {
 };
 MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
 
-static const struct sdhci_acpi_slot *sdhci_acpi_get_slot_by_ids(const char *hid,
-                                                               const char *uid)
+static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid,
+                                                        const char *uid)
 {
        const struct sdhci_acpi_uid_slot *u;
 
@@ -189,24 +249,6 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot_by_ids(const char *hid,
        return NULL;
 }
 
-static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,
-                                                        const char *hid)
-{
-       const struct sdhci_acpi_slot *slot;
-       struct acpi_device_info *info;
-       const char *uid = NULL;
-       acpi_status status;
-
-       status = acpi_get_object_info(handle, &info);
-       if (!ACPI_FAILURE(status) && (info->valid & ACPI_VALID_UID))
-               uid = info->unique_id.string;
-
-       slot = sdhci_acpi_get_slot_by_ids(hid, uid);
-
-       kfree(info);
-       return slot;
-}
-
 static int sdhci_acpi_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -217,6 +259,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
        struct resource *iomem;
        resource_size_t len;
        const char *hid;
+       const char *uid;
        int err;
 
        if (acpi_bus_get_device(handle, &device))
@@ -226,6 +269,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
                return -ENODEV;
 
        hid = acpi_device_hid(device);
+       uid = device->pnp.unique_id;
 
        iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!iomem)
@@ -244,7 +288,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
 
        c = sdhci_priv(host);
        c->host = host;
-       c->slot = sdhci_acpi_get_slot(handle, hid);
+       c->slot = sdhci_acpi_get_slot(hid, uid);
        c->pdev = pdev;
        c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM);
 
@@ -277,6 +321,11 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
        }
 
        if (c->slot) {
+               if (c->slot->probe_slot) {
+                       err = c->slot->probe_slot(pdev, hid, uid);
+                       if (err)
+                               goto err_free;
+               }
                if (c->slot->chip) {
                        host->ops            = c->slot->chip->ops;
                        host->quirks        |= c->slot->chip->quirks;
@@ -297,7 +346,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
        if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
                bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
 
-               if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0)) {
+               if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL)) {
                        dev_warn(dev, "failed to setup card detect gpio\n");
                        c->use_runtime_pm = false;
                }
@@ -334,6 +383,9 @@ static int sdhci_acpi_remove(struct platform_device *pdev)
                pm_runtime_put_noidle(dev);
        }
 
+       if (c->slot && c->slot->remove_slot)
+               c->slot->remove_slot(pdev);
+
        dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0);
        sdhci_remove_host(c->host, dead);
        sdhci_free_host(c->host);
@@ -385,20 +437,13 @@ static int sdhci_acpi_runtime_idle(struct device *dev)
        return 0;
 }
 
-#else
-
-#define sdhci_acpi_runtime_suspend     NULL
-#define sdhci_acpi_runtime_resume      NULL
-#define sdhci_acpi_runtime_idle                NULL
-
 #endif
 
 static const struct dev_pm_ops sdhci_acpi_pm_ops = {
        .suspend                = sdhci_acpi_suspend,
        .resume                 = sdhci_acpi_resume,
-       .runtime_suspend        = sdhci_acpi_runtime_suspend,
-       .runtime_resume         = sdhci_acpi_runtime_resume,
-       .runtime_idle           = sdhci_acpi_runtime_idle,
+       SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
+                       sdhci_acpi_runtime_resume, sdhci_acpi_runtime_idle)
 };
 
 static struct platform_driver sdhci_acpi_driver = {
index dd780c315a638edbfb90dbdae818472f927a0c5e..e7e4fbdcbfe021e248e91b25fce4eb0a3cf2a1ed 100644 (file)
@@ -225,7 +225,7 @@ static struct sdhci_pltfm_data sdhci_pltfm_data_kona = {
                SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
 };
 
-static struct __initconst of_device_id sdhci_bcm_kona_of_match[] = {
+static const struct of_device_id sdhci_bcm_kona_of_match[] = {
        { .compatible = "brcm,kona-sdhci"},
        { .compatible = "bcm,kona-sdhci"}, /* deprecated name */
        {}
@@ -359,7 +359,6 @@ static int sdhci_bcm_kona_remove(struct platform_device *pdev)
 static struct platform_driver sdhci_bcm_kona_driver = {
        .driver         = {
                .name   = "sdhci-kona",
-               .owner  = THIS_MODULE,
                .pm     = SDHCI_PLTFM_PMOPS,
                .of_match_table = sdhci_bcm_kona_of_match,
        },
index 46af9a439d7b48c772f68352253ba23641d756ea..439d259fdf1d571336fe86a3b3de483143430517 100644 (file)
@@ -194,7 +194,6 @@ MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match);
 static struct platform_driver bcm2835_sdhci_driver = {
        .driver = {
                .name = "sdhci-bcm2835",
-               .owner = THIS_MODULE,
                .of_match_table = bcm2835_sdhci_of_match,
                .pm = SDHCI_PLTFM_PMOPS,
        },
index 14b74075589afd5b24ce594bb0b42adf7f5ae1cc..a7935a8d0922218f0874deea4e7f303a35dbb685 100644 (file)
@@ -106,7 +106,6 @@ static int sdhci_cns3xxx_remove(struct platform_device *pdev)
 static struct platform_driver sdhci_cns3xxx_driver = {
        .driver         = {
                .name   = "sdhci-cns3xxx",
-               .owner  = THIS_MODULE,
                .pm     = SDHCI_PLTFM_PMOPS,
        },
        .probe          = sdhci_cns3xxx_probe,
index e6278ec007d775c576346102e358426e08c3edbe..ca969d271a270bfb0bcb6e56fc3626f7398218a5 100644 (file)
@@ -146,7 +146,6 @@ MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table);
 static struct platform_driver sdhci_dove_driver = {
        .driver         = {
                .name   = "sdhci-dove",
-               .owner  = THIS_MODULE,
                .pm     = SDHCI_PLTFM_PMOPS,
                .of_match_table = sdhci_dove_of_match_table,
        },
index ccec0e32590f6b5f9336f0fc77df279e429756a2..587ee0edeb576b8ad2a25c0762d059afff8a1f2c 100644 (file)
@@ -880,6 +880,24 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
        sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 }
 
+static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+
+       return esdhc_is_usdhc(imx_data) ? 1 << 28 : 1 << 27;
+}
+
+static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+
+       /* use maximum timeout counter */
+       sdhci_writeb(host, esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
+                       SDHCI_TIMEOUT_CONTROL);
+}
+
 static struct sdhci_ops sdhci_esdhc_ops = {
        .read_l = esdhc_readl_le,
        .read_w = esdhc_readw_le,
@@ -889,7 +907,9 @@ static struct sdhci_ops sdhci_esdhc_ops = {
        .set_clock = esdhc_pltfm_set_clock,
        .get_max_clock = esdhc_pltfm_get_max_clock,
        .get_min_clock = esdhc_pltfm_get_min_clock,
+       .get_max_timeout_count = esdhc_get_max_timeout_count,
        .get_ro = esdhc_pltfm_get_ro,
+       .set_timeout = esdhc_set_timeout,
        .set_bus_width = esdhc_pltfm_set_bus_width,
        .set_uhs_signaling = esdhc_set_uhs_signaling,
        .reset = esdhc_reset,
@@ -1207,7 +1227,6 @@ static const struct dev_pm_ops sdhci_esdhc_pmops = {
 static struct platform_driver sdhci_esdhc_imx_driver = {
        .driver         = {
                .name   = "sdhci-esdhc-imx",
-               .owner  = THIS_MODULE,
                .of_match_table = imx_esdhc_dt_ids,
                .pm     = &sdhci_esdhc_pmops,
        },
index 1a6661ed62050fda39cea0a57428abd2bb09e9e0..30804385af6dd02106045d7ef0109319ec5318a1 100644 (file)
 #define CMUX_SHIFT_PHASE_SHIFT 24
 #define CMUX_SHIFT_PHASE_MASK  (7 << CMUX_SHIFT_PHASE_SHIFT)
 
-static const u32 tuning_block_64[] = {
-       0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe,
-       0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777,
-       0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff,
-       0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7
-};
-
-static const u32 tuning_block_128[] = {
-       0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc,
-       0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff,
-       0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff,
-       0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb,
-       0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc,
-       0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff,
-       0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb,
-       0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77
-};
-
 struct sdhci_msm_host {
        struct platform_device *pdev;
        void __iomem *core_mem; /* MSM SDCC mapped address */
@@ -358,8 +340,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
 {
        int tuning_seq_cnt = 3;
        u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
-       const u32 *tuning_block_pattern = tuning_block_64;
-       int size = sizeof(tuning_block_64);     /* Pattern size in bytes */
+       const u8 *tuning_block_pattern = tuning_blk_pattern_4bit;
+       int size = sizeof(tuning_blk_pattern_4bit);
        int rc;
        struct mmc_host *mmc = host->mmc;
        struct mmc_ios ios = host->mmc->ios;
@@ -375,8 +357,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
 
        if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
            (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
-               tuning_block_pattern = tuning_block_128;
-               size = sizeof(tuning_block_128);
+               tuning_block_pattern = tuning_blk_pattern_8bit;
+               size = sizeof(tuning_blk_pattern_8bit);
        }
 
        data_buf = kmalloc(size, GFP_KERNEL);
@@ -610,7 +592,6 @@ static struct platform_driver sdhci_msm_driver = {
        .remove = sdhci_msm_remove,
        .driver = {
                   .name = "sdhci_msm",
-                  .owner = THIS_MODULE,
                   .of_match_table = sdhci_msm_dt_match,
        },
 };
index 5bd1092310f2e6c1b742ce11e3b590119be66435..981d66e5c023147d99ae35a6e9e985e453800058 100644 (file)
@@ -213,7 +213,6 @@ MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
 static struct platform_driver sdhci_arasan_driver = {
        .driver = {
                .name = "sdhci-arasan",
-               .owner = THIS_MODULE,
                .of_match_table = sdhci_arasan_of_match,
                .pm = &sdhci_arasan_dev_pm_ops,
        },
index 8be4dcfb49a05f12c2f2711323e321af8f529625..8872c85c63d4a9dc7e9bfddaefe28f7ab8e55fb2 100644 (file)
@@ -388,7 +388,6 @@ MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match);
 static struct platform_driver sdhci_esdhc_driver = {
        .driver = {
                .name = "sdhci-esdhc",
-               .owner = THIS_MODULE,
                .of_match_table = sdhci_esdhc_of_match,
                .pm = ESDHC_PMOPS,
        },
index b341661369a20a645ea3356e2d8a71cfb42a9ee0..be479279a1d55479bad67654e855fa71781678ea 100644 (file)
@@ -89,7 +89,6 @@ MODULE_DEVICE_TABLE(of, sdhci_hlwd_of_match);
 static struct platform_driver sdhci_hlwd_driver = {
        .driver = {
                .name = "sdhci-hlwd",
-               .owner = THIS_MODULE,
                .of_match_table = sdhci_hlwd_of_match,
                .pm = SDHCI_PLTFM_PMOPS,
        },
index c3a1debc9289860755a207cf1315ef544bdda21c..61192973e7cbaad1305c2ebea324f43e2506665a 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/pm_runtime.h>
+#include <linux/mmc/slot-gpio.h>
 #include <linux/mmc/sdhci-pci-data.h>
 
 #include "sdhci.h"
@@ -271,6 +272,8 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
                                 MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR;
        slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
        slot->hw_reset = sdhci_pci_int_hw_reset;
+       if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
+               slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
        return 0;
 }
 
@@ -280,22 +283,35 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
        return 0;
 }
 
+static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
+{
+       slot->cd_con_id = NULL;
+       slot->cd_idx = 0;
+       slot->cd_override_level = true;
+       return 0;
+}
+
 static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
        .allow_runtime_pm = true,
        .probe_slot     = byt_emmc_probe_slot,
-       .quirks2        = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+       .quirks2        = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+                         SDHCI_QUIRK2_STOP_WITH_TC,
 };
 
 static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
-       .quirks2        = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
+       .quirks2        = SDHCI_QUIRK2_HOST_OFF_CARD_ON |
+                       SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
        .allow_runtime_pm = true,
        .probe_slot     = byt_sdio_probe_slot,
 };
 
 static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
-       .quirks2        = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
+       .quirks2        = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
+                         SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+                         SDHCI_QUIRK2_STOP_WITH_TC,
        .allow_runtime_pm = true,
        .own_cd_for_runtime_pm = true,
+       .probe_slot     = byt_sd_probe_slot,
 };
 
 /* Define Host controllers for Intel Merrifield platform */
@@ -317,7 +333,9 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
 
 static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
        .quirks         = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
-       .quirks2        = SDHCI_QUIRK2_BROKEN_HS200,
+       .quirks2        = SDHCI_QUIRK2_BROKEN_HS200 |
+                       SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+       .allow_runtime_pm = true,
        .probe_slot     = intel_mrfl_mmc_probe_slot,
 };
 
@@ -876,6 +894,29 @@ static const struct pci_device_id pci_ids[] = {
                .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_emmc,
        },
 
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BSW_EMMC,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_emmc,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BSW_SDIO,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_sdio,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_BSW_SD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_sd,
+       },
 
        {
                .vendor         = PCI_VENDOR_ID_INTEL,
@@ -1269,20 +1310,13 @@ static int sdhci_pci_runtime_idle(struct device *dev)
        return 0;
 }
 
-#else
-
-#define sdhci_pci_runtime_suspend      NULL
-#define sdhci_pci_runtime_resume       NULL
-#define sdhci_pci_runtime_idle         NULL
-
 #endif
 
 static const struct dev_pm_ops sdhci_pci_pm_ops = {
        .suspend = sdhci_pci_suspend,
        .resume = sdhci_pci_resume,
-       .runtime_suspend = sdhci_pci_runtime_suspend,
-       .runtime_resume = sdhci_pci_runtime_resume,
-       .runtime_idle = sdhci_pci_runtime_idle,
+       SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend,
+                       sdhci_pci_runtime_resume, sdhci_pci_runtime_idle)
 };
 
 /*****************************************************************************\
@@ -1332,6 +1366,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
        slot->pci_bar = bar;
        slot->rst_n_gpio = -EINVAL;
        slot->cd_gpio = -EINVAL;
+       slot->cd_idx = -1;
 
        /* Retrieve platform data if there is any */
        if (*sdhci_pci_get_data)
@@ -1390,6 +1425,13 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
        host->mmc->slotno = slotno;
        host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
 
+       if (slot->cd_idx >= 0 &&
+           mmc_gpiod_request_cd(host->mmc, slot->cd_con_id, slot->cd_idx,
+                                slot->cd_override_level, 0, NULL)) {
+               dev_warn(&pdev->dev, "failed to setup card detect gpio\n");
+               slot->cd_idx = -1;
+       }
+
        ret = sdhci_add_host(host);
        if (ret)
                goto remove;
@@ -1402,7 +1444,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
         * Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure.
         */
        if (chip->fixes && chip->fixes->own_cd_for_runtime_pm &&
-           !gpio_is_valid(slot->cd_gpio))
+           !gpio_is_valid(slot->cd_gpio) && slot->cd_idx < 0)
                chip->allow_runtime_pm = false;
 
        return slot;
index c101477ef3be28364b31b50c92c88dd72909e3d6..d57c3d169914e94e716b64b90e8b8da2b86afa87 100644 (file)
@@ -11,6 +11,9 @@
 #define PCI_DEVICE_ID_INTEL_BYT_SDIO   0x0f15
 #define PCI_DEVICE_ID_INTEL_BYT_SD     0x0f16
 #define PCI_DEVICE_ID_INTEL_BYT_EMMC2  0x0f50
+#define PCI_DEVICE_ID_INTEL_BSW_EMMC   0x2294
+#define PCI_DEVICE_ID_INTEL_BSW_SDIO   0x2295
+#define PCI_DEVICE_ID_INTEL_BSW_SD     0x2296
 #define PCI_DEVICE_ID_INTEL_MRFL_MMC   0x1190
 #define PCI_DEVICE_ID_INTEL_CLV_SDIO0  0x08f9
 #define PCI_DEVICE_ID_INTEL_CLV_SDIO1  0x08fa
@@ -61,6 +64,10 @@ struct sdhci_pci_slot {
        int                     cd_gpio;
        int                     cd_irq;
 
+       char                    *cd_con_id;
+       int                     cd_idx;
+       bool                    cd_override_level;
+
        void (*hw_reset)(struct sdhci_host *host);
 };
 
index 7e834fb78f427d2c085dce62956337d126e7e4f3..c5b01d6bb85d41fdf236473b4f2b25cae9ee68f5 100644 (file)
@@ -123,7 +123,6 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
                                    size_t priv_size)
 {
        struct sdhci_host *host;
-       struct device_node *np = pdev->dev.of_node;
        struct resource *iomem;
        int ret;
 
@@ -136,13 +135,8 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
        if (resource_size(iomem) < 0x100)
                dev_err(&pdev->dev, "Invalid iomem size!\n");
 
-       /* Some PCI-based MFD need the parent here */
-       if (pdev->dev.parent != &platform_bus && !np)
-               host = sdhci_alloc_host(pdev->dev.parent,
-                       sizeof(struct sdhci_pltfm_host) + priv_size);
-       else
-               host = sdhci_alloc_host(&pdev->dev,
-                       sizeof(struct sdhci_pltfm_host) + priv_size);
+       host = sdhci_alloc_host(&pdev->dev,
+               sizeof(struct sdhci_pltfm_host) + priv_size);
 
        if (IS_ERR(host)) {
                ret = PTR_ERR(host);
index 3c0f3c0a1cc86969f5f72dfaa27d6fdc2f083b58..b4c23e983baf221c62d81ba0f957d2674e3296a4 100644 (file)
@@ -261,7 +261,6 @@ static int sdhci_pxav2_remove(struct platform_device *pdev)
 static struct platform_driver sdhci_pxav2_driver = {
        .driver         = {
                .name   = "sdhci-pxav2",
-               .owner  = THIS_MODULE,
 #ifdef CONFIG_OF
                .of_match_table = sdhci_pxav2_of_match,
 #endif
index 6f842fb8e6b81834de13f0ffef3e19af2b9cb3a0..5036d7d39529a949d982e5814a9f4d010683a9a3 100644 (file)
@@ -224,12 +224,11 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
 
 static const struct sdhci_ops pxav3_sdhci_ops = {
        .set_clock = sdhci_set_clock,
-       .set_uhs_signaling = pxav3_set_uhs_signaling,
        .platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
        .get_max_clock = sdhci_pltfm_clk_get_max_clock,
        .set_bus_width = sdhci_set_bus_width,
        .reset = pxav3_reset,
-       .set_uhs_signaling = sdhci_set_uhs_signaling,
+       .set_uhs_signaling = pxav3_set_uhs_signaling,
 };
 
 static struct sdhci_pltfm_data sdhci_pxav3_pdata = {
@@ -381,11 +380,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
 
        return 0;
 
-err_of_parse:
-err_cd_req:
 err_add_host:
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
+err_of_parse:
+err_cd_req:
        clk_disable_unprepare(clk);
 err_clk_get:
 err_mbus_win:
@@ -492,7 +491,6 @@ static struct platform_driver sdhci_pxav3_driver = {
 #ifdef CONFIG_OF
                .of_match_table = sdhci_pxav3_of_match,
 #endif
-               .owner  = THIS_MODULE,
                .pm     = SDHCI_PXAV3_PMOPS,
        },
        .probe          = sdhci_pxav3_probe,
index fa5954a05449f9ae0116c1db1d486458674e8d0b..0ce6eb17deaf5b6361199ed2e8583f7bd72c5b56 100644 (file)
@@ -606,8 +606,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
        ret = sdhci_add_host(host);
        if (ret) {
                dev_err(dev, "sdhci_add_host() failed\n");
-               pm_runtime_forbid(&pdev->dev);
-               pm_runtime_get_noresume(&pdev->dev);
                goto err_req_regs;
        }
 
@@ -618,6 +616,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
        return 0;
 
  err_req_regs:
+       pm_runtime_disable(&pdev->dev);
+
  err_no_busclks:
        clk_disable_unprepare(sc->clk_io);
 
@@ -747,7 +747,6 @@ static struct platform_driver sdhci_s3c_driver = {
        .remove         = sdhci_s3c_remove,
        .id_table       = sdhci_s3c_driver_ids,
        .driver         = {
-               .owner  = THIS_MODULE,
                .name   = "s3c-sdhci",
                .of_match_table = of_match_ptr(sdhci_s3c_dt_match),
                .pm     = SDHCI_S3C_PMOPS,
index 17004531d089e6c2bce4fc33d319f6fdf3cb377f..dd29d47c07aa8168dec853de07bdc599508112ab 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/mmc/slot-gpio.h>
 #include "sdhci-pltfm.h"
 
+#define SDHCI_SIRF_8BITBUS BIT(3)
+
 struct sdhci_sirf_priv {
        struct clk *clk;
        int gpio_cd;
@@ -27,10 +29,30 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
        return clk_get_rate(priv->clk);
 }
 
+static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
+{
+       u8 ctrl;
+
+       ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+       ctrl &= ~(SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS);
+
+       /*
+        * CSR atlas7 and prima2 SD host version is not 3.0
+        * 8bit-width enable bit of CSR SD hosts is 3,
+        * while stardard hosts use bit 5
+        */
+       if (width == MMC_BUS_WIDTH_8)
+               ctrl |= SDHCI_SIRF_8BITBUS;
+       else if (width == MMC_BUS_WIDTH_4)
+               ctrl |= SDHCI_CTRL_4BITBUS;
+
+       sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+}
+
 static struct sdhci_ops sdhci_sirf_ops = {
        .set_clock = sdhci_set_clock,
        .get_max_clock  = sdhci_sirf_get_max_clk,
-       .set_bus_width = sdhci_set_bus_width,
+       .set_bus_width = sdhci_sirf_set_bus_width,
        .reset = sdhci_reset,
        .set_uhs_signaling = sdhci_set_uhs_signaling,
 };
@@ -94,6 +116,7 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
                                ret);
                        goto err_request_cd;
                }
+               mmc_gpiod_request_cd_irq(host->mmc);
        }
 
        return 0;
@@ -167,7 +190,6 @@ MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
 static struct platform_driver sdhci_sirf_driver = {
        .driver         = {
                .name   = "sdhci-sirf",
-               .owner  = THIS_MODULE,
                .of_match_table = sdhci_sirf_of_match,
 #ifdef CONFIG_PM_SLEEP
                .pm     = &sdhci_sirf_pm_ops,
index 9d535c7336ef8425ab40bc6d6abea91553f17294..22e58268545f0b028ee3cd82d4fa7e50a49a4558 100644 (file)
@@ -230,7 +230,6 @@ MODULE_DEVICE_TABLE(of, sdhci_spear_id_table);
 static struct platform_driver sdhci_driver = {
        .driver = {
                .name   = "sdhci",
-               .owner  = THIS_MODULE,
                .pm     = &sdhci_pm_ops,
                .of_match_table = of_match_ptr(sdhci_spear_id_table),
        },
index 33100d10d17685b732279dc5c2d42b3f8950ee97..59797106af930c874eb0558ea368a5225ba2a461 100644 (file)
@@ -318,7 +318,6 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
 static struct platform_driver sdhci_tegra_driver = {
        .driver         = {
                .name   = "sdhci-tegra",
-               .owner  = THIS_MODULE,
                .of_match_table = sdhci_tegra_dt_match,
                .pm     = SDHCI_PLTFM_PMOPS,
        },
index 37b2a9ae52eff16cd44649f42fb4822ff05c89db..ada1a3ea3a87122b7b6ca52c7397e338bc81128f 100644 (file)
@@ -707,19 +707,28 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
        sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 }
 
-static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
+static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
 {
        u8 count;
+
+       if (host->ops->set_timeout) {
+               host->ops->set_timeout(host, cmd);
+       } else {
+               count = sdhci_calc_timeout(host, cmd);
+               sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
+       }
+}
+
+static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
+{
        u8 ctrl;
        struct mmc_data *data = cmd->data;
        int ret;
 
        WARN_ON(host->data);
 
-       if (data || (cmd->flags & MMC_RSP_BUSY)) {
-               count = sdhci_calc_timeout(host, cmd);
-               sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
-       }
+       if (data || (cmd->flags & MMC_RSP_BUSY))
+               sdhci_set_timeout(host, cmd);
 
        if (!data)
                return;
@@ -1007,6 +1016,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        mod_timer(&host->timer, timeout);
 
        host->cmd = cmd;
+       host->busy_handle = 0;
 
        sdhci_prepare_data(host, cmd);
 
@@ -1194,7 +1204,6 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 clock_set:
        if (real_div)
                host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div;
-
        clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
        clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
                << SDHCI_DIVIDER_HI_SHIFT;
@@ -1357,11 +1366,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
                present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
                /*
                 * Check if the re-tuning timer has already expired and there
-                * is no on-going data transfer. If so, we need to execute
-                * tuning procedure before sending command.
+                * is no on-going data transfer and DAT0 is not busy. If so,
+                * we need to execute tuning procedure before sending command.
                 */
                if ((host->flags & SDHCI_NEEDS_RETUNING) &&
-                   !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
+                   !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ)) &&
+                   (present_state & SDHCI_DATA_0_LVL_MASK)) {
                        if (mmc->card) {
                                /* eMMC uses cmd21 but sd and sdio use cmd19 */
                                tuning_opcode =
@@ -1471,6 +1481,18 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
        if (!ios->clock || ios->clock != host->clock) {
                host->ops->set_clock(host, ios->clock);
                host->clock = ios->clock;
+
+               if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK &&
+                   host->clock) {
+                       host->timeout_clk = host->mmc->actual_clock ?
+                                               host->mmc->actual_clock / 1000 :
+                                               host->clock / 1000;
+                       host->mmc->max_busy_timeout =
+                               host->ops->get_max_timeout_count ?
+                               host->ops->get_max_timeout_count(host) :
+                               1 << 27;
+                       host->mmc->max_busy_timeout /= host->timeout_clk;
+               }
        }
 
        sdhci_set_power(host, ios->power_mode, ios->vdd);
@@ -1733,8 +1755,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
                        ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000,
                                                    3600000);
                        if (ret) {
-                               pr_warning("%s: Switching to 3.3V signalling voltage "
-                                               " failed\n", mmc_hostname(mmc));
+                               pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
+                                       mmc_hostname(mmc));
                                return -EIO;
                        }
                }
@@ -1746,8 +1768,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
                if (!(ctrl & SDHCI_CTRL_VDD_180))
                        return 0;
 
-               pr_warning("%s: 3.3V regulator output did not became stable\n",
-                               mmc_hostname(mmc));
+               pr_warn("%s: 3.3V regulator output did not became stable\n",
+                       mmc_hostname(mmc));
 
                return -EAGAIN;
        case MMC_SIGNAL_VOLTAGE_180:
@@ -1755,8 +1777,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
                        ret = regulator_set_voltage(mmc->supply.vqmmc,
                                        1700000, 1950000);
                        if (ret) {
-                               pr_warning("%s: Switching to 1.8V signalling voltage "
-                                               " failed\n", mmc_hostname(mmc));
+                               pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
+                                       mmc_hostname(mmc));
                                return -EIO;
                        }
                }
@@ -1773,8 +1795,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
                if (ctrl & SDHCI_CTRL_VDD_180)
                        return 0;
 
-               pr_warning("%s: 1.8V regulator output did not became stable\n",
-                               mmc_hostname(mmc));
+               pr_warn("%s: 1.8V regulator output did not became stable\n",
+                       mmc_hostname(mmc));
 
                return -EAGAIN;
        case MMC_SIGNAL_VOLTAGE_120:
@@ -1782,8 +1804,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
                        ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000,
                                                    1300000);
                        if (ret) {
-                               pr_warning("%s: Switching to 1.2V signalling voltage "
-                                               " failed\n", mmc_hostname(mmc));
+                               pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
+                                       mmc_hostname(mmc));
                                return -EIO;
                        }
                }
@@ -2203,7 +2225,7 @@ static void sdhci_tuning_timer(unsigned long data)
  *                                                                           *
 \*****************************************************************************/
 
-static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
+static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
 {
        BUG_ON(intmask == 0);
 
@@ -2241,11 +2263,18 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
                if (host->cmd->data)
                        DBG("Cannot wait for busy signal when also "
                                "doing a data transfer");
-               else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ))
+               else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ)
+                               && !host->busy_handle) {
+                       /* Mark that command complete before busy is ended */
+                       host->busy_handle = 1;
                        return;
+               }
 
                /* The controller does not support the end-of-busy IRQ,
                 * fall through and take the SDHCI_INT_RESPONSE */
+       } else if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
+                  host->cmd->opcode == MMC_STOP_TRANSMISSION && !host->data) {
+               *mask &= ~SDHCI_INT_DATA_END;
        }
 
        if (intmask & SDHCI_INT_RESPONSE)
@@ -2304,8 +2333,21 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
                 * above in sdhci_cmd_irq().
                 */
                if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
+                       if (intmask & SDHCI_INT_DATA_TIMEOUT) {
+                               host->cmd->error = -ETIMEDOUT;
+                               tasklet_schedule(&host->finish_tasklet);
+                               return;
+                       }
                        if (intmask & SDHCI_INT_DATA_END) {
-                               sdhci_finish_command(host);
+                               /*
+                                * Some cards handle busy-end interrupt
+                                * before the command completed, so make
+                                * sure we do things in the proper order.
+                                */
+                               if (host->busy_handle)
+                                       sdhci_finish_command(host);
+                               else
+                                       host->busy_handle = 1;
                                return;
                        }
                }
@@ -2442,7 +2484,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
                }
 
                if (intmask & SDHCI_INT_CMD_MASK)
-                       sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
+                       sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK,
+                                     &intmask);
 
                if (intmask & SDHCI_INT_DATA_MASK)
                        sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
@@ -2534,7 +2577,7 @@ void sdhci_enable_irq_wakeups(struct sdhci_host *host)
 }
 EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
 
-void sdhci_disable_irq_wakeups(struct sdhci_host *host)
+static void sdhci_disable_irq_wakeups(struct sdhci_host *host)
 {
        u8 val;
        u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
@@ -2544,7 +2587,6 @@ void sdhci_disable_irq_wakeups(struct sdhci_host *host)
        val &= ~mask;
        sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL);
 }
-EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
 
 int sdhci_suspend_host(struct sdhci_host *host)
 {
@@ -2749,6 +2791,7 @@ int sdhci_add_host(struct sdhci_host *host)
        u32 caps[2] = {0, 0};
        u32 max_current_caps;
        unsigned int ocr_avail;
+       unsigned int override_timeout_clk;
        int ret;
 
        WARN_ON(host == NULL);
@@ -2762,6 +2805,8 @@ int sdhci_add_host(struct sdhci_host *host)
        if (debug_quirks2)
                host->quirks2 = debug_quirks2;
 
+       override_timeout_clk = host->timeout_clk;
+
        sdhci_do_reset(host, SDHCI_RESET_ALL);
 
        host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
@@ -2807,8 +2852,7 @@ int sdhci_add_host(struct sdhci_host *host)
        if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
                if (host->ops->enable_dma) {
                        if (host->ops->enable_dma(host)) {
-                               pr_warning("%s: No suitable DMA "
-                                       "available. Falling back to PIO.\n",
+                               pr_warn("%s: No suitable DMA available - falling back to PIO\n",
                                        mmc_hostname(mmc));
                                host->flags &=
                                        ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA);
@@ -2830,15 +2874,14 @@ int sdhci_add_host(struct sdhci_host *host)
                        dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
                                          host->adma_desc, host->adma_addr);
                        kfree(host->align_buffer);
-                       pr_warning("%s: Unable to allocate ADMA "
-                               "buffers. Falling back to standard DMA.\n",
+                       pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n",
                                mmc_hostname(mmc));
                        host->flags &= ~SDHCI_USE_ADMA;
                        host->adma_desc = NULL;
                        host->align_buffer = NULL;
                } else if (host->adma_addr & 3) {
-                       pr_warning("%s: unable to allocate aligned ADMA descriptor\n",
-                                  mmc_hostname(mmc));
+                       pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
+                               mmc_hostname(mmc));
                        host->flags &= ~SDHCI_USE_ADMA;
                        dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
                                          host->adma_desc, host->adma_addr);
@@ -2908,25 +2951,30 @@ int sdhci_add_host(struct sdhci_host *host)
        } else
                mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
 
-       host->timeout_clk =
-               (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
-       if (host->timeout_clk == 0) {
-               if (host->ops->get_timeout_clock) {
-                       host->timeout_clk = host->ops->get_timeout_clock(host);
-               } else if (!(host->quirks &
-                               SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
-                       pr_err("%s: Hardware doesn't specify timeout clock "
-                              "frequency.\n", mmc_hostname(mmc));
-                       return -ENODEV;
+       if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
+               host->timeout_clk = (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >>
+                                       SDHCI_TIMEOUT_CLK_SHIFT;
+               if (host->timeout_clk == 0) {
+                       if (host->ops->get_timeout_clock) {
+                               host->timeout_clk =
+                                       host->ops->get_timeout_clock(host);
+                       } else {
+                               pr_err("%s: Hardware doesn't specify timeout clock frequency.\n",
+                                       mmc_hostname(mmc));
+                               return -ENODEV;
+                       }
                }
-       }
-       if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
-               host->timeout_clk *= 1000;
 
-       if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
-               host->timeout_clk = mmc->f_max / 1000;
+               if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
+                       host->timeout_clk *= 1000;
 
-       mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
+               mmc->max_busy_timeout = host->ops->get_max_timeout_count ?
+                       host->ops->get_max_timeout_count(host) : 1 << 27;
+               mmc->max_busy_timeout /= host->timeout_clk;
+       }
+
+       if (override_timeout_clk)
+               host->timeout_clk = override_timeout_clk;
 
        mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
        mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
@@ -2998,8 +3046,13 @@ int sdhci_add_host(struct sdhci_host *host)
                /* SD3.0: SDR104 is supported so (for eMMC) the caps2
                 * field can be promoted to support HS200.
                 */
-               if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
+               if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) {
                        mmc->caps2 |= MMC_CAP2_HS200;
+                       if (IS_ERR(mmc->supply.vqmmc) ||
+                                       !regulator_is_supported_voltage
+                                       (mmc->supply.vqmmc, 1100000, 1300000))
+                               mmc->caps2 &= ~MMC_CAP2_HS200_1_2V_SDR;
+               }
        } else if (caps[1] & SDHCI_SUPPORT_SDR50)
                mmc->caps |= MMC_CAP_UHS_SDR50;
 
@@ -3049,7 +3102,7 @@ int sdhci_add_host(struct sdhci_host *host)
         */
        max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
        if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) {
-               u32 curr = regulator_get_current_limit(mmc->supply.vmmc);
+               int curr = regulator_get_current_limit(mmc->supply.vmmc);
                if (curr > 0) {
 
                        /* convert to SDHCI_MAX_CURRENT format */
@@ -3158,8 +3211,8 @@ int sdhci_add_host(struct sdhci_host *host)
                mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
                                SDHCI_MAX_BLOCK_SHIFT;
                if (mmc->max_blk_size >= 3) {
-                       pr_warning("%s: Invalid maximum block size, "
-                               "assuming 512 bytes\n", mmc_hostname(mmc));
+                       pr_warn("%s: Invalid maximum block size, assuming 512 bytes\n",
+                               mmc_hostname(mmc));
                        mmc->max_blk_size = 0;
                }
        }
index 4a5cd5e3fa3eeb7a0992976c902b99bb29316584..31896a779d4ed07834026743ac9d87ad917bebb8 100644 (file)
@@ -72,6 +72,7 @@
 #define  SDHCI_WRITE_PROTECT   0x00080000
 #define  SDHCI_DATA_LVL_MASK   0x00F00000
 #define   SDHCI_DATA_LVL_SHIFT 20
+#define   SDHCI_DATA_0_LVL_MASK        0x00100000
 
 #define SDHCI_HOST_CONTROL     0x28
 #define  SDHCI_CTRL_LED                0x01
@@ -281,6 +282,9 @@ struct sdhci_ops {
        unsigned int    (*get_max_clock)(struct sdhci_host *host);
        unsigned int    (*get_min_clock)(struct sdhci_host *host);
        unsigned int    (*get_timeout_clock)(struct sdhci_host *host);
+       unsigned int    (*get_max_timeout_count)(struct sdhci_host *host);
+       void            (*set_timeout)(struct sdhci_host *host,
+                                      struct mmc_command *cmd);
        void            (*set_bus_width)(struct sdhci_host *host, int width);
        void (*platform_send_init_74_clocks)(struct sdhci_host *host,
                                             u8 power_mode);
index d11708c815d721ba7dd9626ff21a523331ac4392..7d9d6a3215210f537b1b6e46070b610320f80ff5 100644 (file)
@@ -1553,7 +1553,6 @@ static struct platform_driver sh_mmcif_driver = {
        .driver         = {
                .name   = DRIVER_NAME,
                .pm     = &sh_mmcif_dev_pm_ops,
-               .owner  = THIS_MODULE,
                .of_match_table = mmcif_of_match,
        },
 };
index 91058dabd11afdbe443633140f2732462709d024..a2e81a1ea6af19ddae959327d9503189ff228659 100644 (file)
@@ -39,6 +39,7 @@ struct sh_mobile_sdhi_of_data {
        unsigned long tmio_flags;
        unsigned long capabilities;
        unsigned long capabilities2;
+       dma_addr_t dma_rx_offset;
 };
 
 static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
@@ -48,14 +49,16 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
 };
 
 static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = {
-       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE,
+       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
+                         TMIO_MMC_CLK_ACTUAL,
        .capabilities   = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
 };
 
 static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
-       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE,
+       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
+                         TMIO_MMC_CLK_ACTUAL,
        .capabilities   = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
-       .capabilities2  = MMC_CAP2_NO_MULTI_READ,
+       .dma_rx_offset  = 0x2000,
 };
 
 static const struct of_device_id sh_mobile_sdhi_of_match[] = {
@@ -68,6 +71,9 @@ static const struct of_device_id sh_mobile_sdhi_of_match[] = {
        { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, },
        { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, },
        { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, },
+       { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, },
+       { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, },
+       { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, },
        {},
 };
 MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
@@ -132,6 +138,24 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
        return 0;
 }
 
+static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card,
+                                        unsigned int direction, int blk_size)
+{
+       /*
+        * In Renesas controllers, when performing a
+        * multiple block read of one or two blocks,
+        * depending on the timing with which the
+        * response register is read, the response
+        * value may not be read properly.
+        * Use single block read for this HW bug
+        */
+       if ((direction == MMC_DATA_READ) &&
+           blk_size == 2)
+               return 1;
+
+       return blk_size;
+}
+
 static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
 {
        mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100));
@@ -187,6 +211,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
        mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
        mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
        mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
+       mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk;
        if (p) {
                mmc_data->flags = p->tmio_flags;
                mmc_data->ocr_mask = p->tmio_ocr_mask;
@@ -223,11 +248,27 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
         */
        mmc_data->flags |= TMIO_MMC_SDIO_IRQ;
 
+       /*
+        * All SDHI have CMD12 controll bit
+        */
+       mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL;
+
+       /*
+        * All SDHI need SDIO_INFO1 reserved bit
+        */
+       mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK;
+
+       /*
+        * All SDHI have DMA control register
+        */
+       mmc_data->flags |= TMIO_MMC_HAVE_CTL_DMA_REG;
+
        if (of_id && of_id->data) {
                const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
                mmc_data->flags |= of_data->tmio_flags;
                mmc_data->capabilities |= of_data->capabilities;
                mmc_data->capabilities2 |= of_data->capabilities2;
+               dma_priv->dma_rx_offset = of_data->dma_rx_offset;
        }
 
        /* SD control register space size is 0x100, 0x200 for bus_shift=1 */
@@ -332,8 +373,9 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
 }
 
 static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume)
-       SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                       pm_runtime_force_resume)
+       SET_PM_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
                        tmio_mmc_host_runtime_resume,
                        NULL)
 };
@@ -341,7 +383,6 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
 static struct platform_driver sh_mobile_sdhi_driver = {
        .driver         = {
                .name   = "sh_mobile_sdhi",
-               .owner  = THIS_MODULE,
                .pm     = &tmio_mmc_dev_pm_ops,
                .of_match_table = sh_mobile_sdhi_of_match,
        },
index 024f67c98cdcad3b741e3b5ec39ad8b17a670d8c..d1663b3c41436fc4cb4a7f172364644c08cbaad5 100644 (file)
@@ -990,7 +990,8 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
        /* 400kHz ~ 50MHz */
        mmc->f_min              =   400000;
        mmc->f_max              = 50000000;
-       mmc->caps              |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+       mmc->caps              |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
+                                 MMC_CAP_ERASE;
 
        ret = mmc_of_parse(mmc);
        if (ret)
@@ -1035,7 +1036,6 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
 static struct platform_driver sunxi_mmc_driver = {
        .driver = {
                .name   = "sunxi-mmc",
-               .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(sunxi_mmc_of_match),
        },
        .probe          = sunxi_mmc_probe,
index d1760ebcac0359e6e25273ac849f4c89f60f6b2b..93c4b40df90a78bfcb16375d3669436112fa77a7 100644 (file)
@@ -952,8 +952,8 @@ static int tifm_sd_probe(struct tifm_dev *sock)
 
        if (!(TIFM_SOCK_STATE_OCCUPIED
              & readl(sock->addr + SOCK_PRESENT_STATE))) {
-               pr_warning("%s : card gone, unexpectedly\n",
-                      dev_name(&sock->dev));
+               pr_warn("%s : card gone, unexpectedly\n",
+                       dev_name(&sock->dev));
                return rc;
        }
 
index cfad844730d80195ff63172c3f69aed3e3fe1155..659028ddb8b17e052721b0c8e9791ffbf598cd19 100644 (file)
@@ -30,7 +30,7 @@ static int tmio_mmc_suspend(struct device *dev)
        const struct mfd_cell *cell = mfd_get_cell(pdev);
        int ret;
 
-       ret = tmio_mmc_host_suspend(dev);
+       ret = pm_runtime_force_suspend(dev);
 
        /* Tell MFD core it can disable us now.*/
        if (!ret && cell->disable)
@@ -50,7 +50,7 @@ static int tmio_mmc_resume(struct device *dev)
                ret = cell->resume(pdev);
 
        if (!ret)
-               ret = tmio_mmc_host_resume(dev);
+               ret = pm_runtime_force_resume(dev);
 
        return ret;
 }
@@ -135,6 +135,9 @@ static int tmio_mmc_remove(struct platform_device *pdev)
 
 static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume)
+       SET_PM_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
+                       tmio_mmc_host_runtime_resume,
+                       NULL)
 };
 
 static struct platform_driver tmio_mmc_driver = {
index 100ffe0b2faf932399f3e47af4e61167b7a706ad..a34ecbe1c1ad62c0d8c4b3d5ed7010a3df770020 100644 (file)
 
 struct tmio_mmc_data;
 
-/*
- * We differentiate between the following 3 power states:
- * 1. card slot powered off, controller stopped. This is used, when either there
- *    is no card in the slot, or the card really has to be powered down.
- * 2. card slot powered on, controller stopped. This is used, when a card is in
- *    the slot, but no activity is currently taking place. This is a power-
- *    saving mode with card-state preserved. This state can be entered, e.g.
- *    when MMC clock-gating is used.
- * 3. card slot powered on, controller running. This is the actual active state.
- */
-enum tmio_mmc_power {
-       TMIO_MMC_OFF_STOP,      /* card power off, controller stopped */
-       TMIO_MMC_ON_STOP,       /* card power on, controller stopped */
-       TMIO_MMC_ON_RUN,        /* card power on, controller running */
-};
-
 struct tmio_mmc_host {
        void __iomem *ctl;
        struct mmc_command      *cmd;
@@ -63,9 +47,6 @@ struct tmio_mmc_host {
        struct mmc_data         *data;
        struct mmc_host         *mmc;
 
-       /* Controller and card power state */
-       enum tmio_mmc_power     power;
-
        /* Callbacks for clock / power control */
        void (*set_pwr)(struct platform_device *host, int state);
        void (*set_clk_div)(struct platform_device *host, int state);
@@ -92,15 +73,16 @@ struct tmio_mmc_host {
        struct delayed_work     delayed_reset_work;
        struct work_struct      done;
 
-       /* Cache IRQ mask */
+       /* Cache */
        u32                     sdcard_irq_mask;
        u32                     sdio_irq_mask;
+       unsigned int            clk_cache;
 
        spinlock_t              lock;           /* protect host private data */
        unsigned long           last_req_ts;
        struct mutex            ios_lock;       /* protect set_ios() context */
        bool                    native_hotplug;
-       bool                    resuming;
+       bool                    sdio_irq_enabled;
 };
 
 int tmio_mmc_host_probe(struct tmio_mmc_host **host,
@@ -162,12 +144,7 @@ static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
 }
 #endif
 
-#ifdef CONFIG_PM_SLEEP
-int tmio_mmc_host_suspend(struct device *dev);
-int tmio_mmc_host_resume(struct device *dev);
-#endif
-
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 int tmio_mmc_host_runtime_suspend(struct device *dev);
 int tmio_mmc_host_runtime_resume(struct device *dev);
 #endif
index eb8f1d5c34b157f51c01210a273bf2c81257ac2d..7d077388b9eb48010bafd28a171addc461f163b6 100644 (file)
@@ -28,10 +28,8 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
        if (!host->chan_tx || !host->chan_rx)
                return;
 
-#if defined(CONFIG_SUPERH) || defined(CONFIG_ARCH_SHMOBILE)
-       /* Switch DMA mode on or off - SuperH specific? */
-       sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
-#endif
+       if (host->pdata->flags & TMIO_MMC_HAVE_CTL_DMA_REG)
+               sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
 }
 
 void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
@@ -312,7 +310,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
                if (pdata->dma->chan_priv_rx)
                        cfg.slave_id = pdata->dma->slave_id_rx;
                cfg.direction = DMA_DEV_TO_MEM;
-               cfg.src_addr = cfg.dst_addr;
+               cfg.src_addr = cfg.dst_addr + pdata->dma->dma_rx_offset;
                cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
                cfg.dst_addr = 0;
                ret = dmaengine_slave_config(host->chan_rx, &cfg);
index faf0924e71cb1d08a1fbe10645f713f8524a2bf8..250bf8c9f9986e47f498f6e127e32b02c9835f21 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/pm_qos.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
+#include <linux/mmc/sdio.h>
 #include <linux/scatterlist.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
@@ -129,19 +130,28 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
 {
        struct tmio_mmc_host *host = mmc_priv(mmc);
 
-       if (enable) {
+       if (enable && !host->sdio_irq_enabled) {
+               /* Keep device active while SDIO irq is enabled */
+               pm_runtime_get_sync(mmc_dev(mmc));
+               host->sdio_irq_enabled = true;
+
                host->sdio_irq_mask = TMIO_SDIO_MASK_ALL &
                                        ~TMIO_SDIO_STAT_IOIRQ;
                sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
                sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
-       } else {
+       } else if (!enable && host->sdio_irq_enabled) {
                host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
                sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
                sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
+
+               host->sdio_irq_enabled = false;
+               pm_runtime_mark_last_busy(mmc_dev(mmc));
+               pm_runtime_put_autosuspend(mmc_dev(mmc));
        }
 }
 
-static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
+static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
+                               unsigned int new_clock)
 {
        u32 clk = 0, clock;
 
@@ -149,7 +159,11 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
                for (clock = host->mmc->f_min, clk = 0x80000080;
                        new_clock >= (clock<<1); clk >>= 1)
                        clock <<= 1;
-               clk |= 0x100;
+
+               /* 1/1 clock is option */
+               if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) &&
+                   ((clk >> 22) & 0x1))
+                       clk |= 0xff;
        }
 
        if (host->set_clk_div)
@@ -245,6 +259,9 @@ static void tmio_mmc_reset_work(struct work_struct *work)
 
        tmio_mmc_abort_dma(host);
        mmc_request_done(host->mmc, mrq);
+
+       pm_runtime_mark_last_busy(mmc_dev(host->mmc));
+       pm_runtime_put_autosuspend(mmc_dev(host->mmc));
 }
 
 /* called with host->lock held, interrupts disabled */
@@ -274,6 +291,9 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
                tmio_mmc_abort_dma(host);
 
        mmc_request_done(host->mmc, mrq);
+
+       pm_runtime_mark_last_busy(mmc_dev(host->mmc));
+       pm_runtime_put_autosuspend(mmc_dev(host->mmc));
 }
 
 static void tmio_mmc_done_work(struct work_struct *work)
@@ -295,6 +315,7 @@ static void tmio_mmc_done_work(struct work_struct *work)
 #define TRANSFER_READ  0x1000
 #define TRANSFER_MULTI 0x2000
 #define SECURITY_CMD   0x4000
+#define NO_CMD12_ISSUE 0x4000 /* TMIO_MMC_HAVE_CMD12_CTRL */
 
 static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd)
 {
@@ -331,6 +352,14 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
                if (data->blocks > 1) {
                        sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x100);
                        c |= TRANSFER_MULTI;
+
+                       /*
+                        * Disable auto CMD12 at IO_RW_EXTENDED when
+                        * multiple block transfer
+                        */
+                       if ((host->pdata->flags & TMIO_MMC_HAVE_CMD12_CTRL) &&
+                           (cmd->opcode == SD_IO_RW_EXTENDED))
+                               c |= NO_CMD12_ISSUE;
                }
                if (data->flags & MMC_DATA_READ)
                        c |= TRANSFER_READ;
@@ -347,6 +376,40 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
        return 0;
 }
 
+static void tmio_mmc_transfer_data(struct tmio_mmc_host *host,
+                                  unsigned short *buf,
+                                  unsigned int count)
+{
+       int is_read = host->data->flags & MMC_DATA_READ;
+       u8  *buf8;
+
+       /*
+        * Transfer the data
+        */
+       if (is_read)
+               sd_ctrl_read16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
+       else
+               sd_ctrl_write16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
+
+       /* if count was even number */
+       if (!(count & 0x1))
+               return;
+
+       /* if count was odd number */
+       buf8 = (u8 *)(buf + (count >> 1));
+
+       /*
+        * FIXME
+        *
+        * driver and this function are assuming that
+        * it is used as little endian
+        */
+       if (is_read)
+               *buf8 = sd_ctrl_read16(host, CTL_SD_DATA_PORT) & 0xff;
+       else
+               sd_ctrl_write16(host, CTL_SD_DATA_PORT, *buf8);
+}
+
 /*
  * This chip always returns (at least?) as much data as you ask for.
  * I'm unsure what happens if you ask for less than a block. This should be
@@ -379,10 +442,7 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
                 count, host->sg_off, data->flags);
 
        /* Transfer the data */
-       if (data->flags & MMC_DATA_READ)
-               sd_ctrl_read16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
-       else
-               sd_ctrl_write16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
+       tmio_mmc_transfer_data(host, buf, count);
 
        host->sg_off += count;
 
@@ -465,6 +525,9 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
                goto out;
 
        if (host->chan_tx && (data->flags & MMC_DATA_WRITE) && !host->force_pio) {
+               u32 status = sd_ctrl_read32(host, CTL_STATUS);
+               bool done = false;
+
                /*
                 * Has all data been written out yet? Testing on SuperH showed,
                 * that in most cases the first interrupt comes already with the
@@ -473,7 +536,15 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
                 * DATAEND interrupt with the BUSY bit set, in this cases
                 * waiting for one more interrupt fixes the problem.
                 */
-               if (!(sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_CMD_BUSY)) {
+               if (host->pdata->flags & TMIO_MMC_HAS_IDLE_WAIT) {
+                       if (status & TMIO_STAT_ILL_FUNC)
+                               done = true;
+               } else {
+                       if (!(status & TMIO_STAT_CMD_BUSY))
+                               done = true;
+               }
+
+               if (done) {
                        tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_DATAEND);
                        tasklet_schedule(&host->dma_complete);
                }
@@ -557,6 +628,9 @@ static void tmio_mmc_card_irq_status(struct tmio_mmc_host *host,
 
        pr_debug_status(*status);
        pr_debug_status(*ireg);
+
+       /* Clear the status except the interrupt status */
+       sd_ctrl_write32(host, CTL_STATUS, TMIO_MASK_IRQ);
 }
 
 static bool __tmio_mmc_card_detect_irq(struct tmio_mmc_host *host,
@@ -637,6 +711,7 @@ irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid)
        struct mmc_host *mmc = host->mmc;
        struct tmio_mmc_data *pdata = host->pdata;
        unsigned int ireg, status;
+       unsigned int sdio_status;
 
        if (!(pdata->flags & TMIO_MMC_SDIO_IRQ))
                return IRQ_HANDLED;
@@ -644,7 +719,11 @@ irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid)
        status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
        ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdcard_irq_mask;
 
-       sd_ctrl_write16(host, CTL_SDIO_STATUS, status & ~TMIO_SDIO_MASK_ALL);
+       sdio_status = status & ~TMIO_SDIO_MASK_ALL;
+       if (pdata->flags & TMIO_MMC_SDIO_STATUS_QUIRK)
+               sdio_status |= 6;
+
+       sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status);
 
        if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ)
                mmc_signal_sdio_irq(mmc);
@@ -728,6 +807,8 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
        spin_unlock_irqrestore(&host->lock, flags);
 
+       pm_runtime_get_sync(mmc_dev(mmc));
+
        if (mrq->data) {
                ret = tmio_mmc_start_data(host, mrq->data);
                if (ret)
@@ -746,11 +827,14 @@ fail:
        host->mrq = NULL;
        mrq->cmd->error = ret;
        mmc_request_done(mmc, mrq);
+
+       pm_runtime_mark_last_busy(mmc_dev(mmc));
+       pm_runtime_put_autosuspend(mmc_dev(mmc));
 }
 
-static int tmio_mmc_clk_update(struct mmc_host *mmc)
+static int tmio_mmc_clk_update(struct tmio_mmc_host *host)
 {
-       struct tmio_mmc_host *host = mmc_priv(mmc);
+       struct mmc_host *mmc = host->mmc;
        struct tmio_mmc_data *pdata = host->pdata;
        int ret;
 
@@ -812,6 +896,19 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host)
                host->set_pwr(host->pdev, 0);
 }
 
+static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host,
+                               unsigned char bus_width)
+{
+       switch (bus_width) {
+       case MMC_BUS_WIDTH_1:
+               sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
+               break;
+       case MMC_BUS_WIDTH_4:
+               sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0);
+               break;
+       }
+}
+
 /* Set MMC clock / power.
  * Note: This controller uses a simple divider scheme therefore it cannot
  * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as
@@ -824,6 +921,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        struct device *dev = &host->pdev->dev;
        unsigned long flags;
 
+       pm_runtime_get_sync(mmc_dev(mmc));
+
        mutex_lock(&host->ios_lock);
 
        spin_lock_irqsave(&host->lock, flags);
@@ -850,60 +949,22 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
        spin_unlock_irqrestore(&host->lock, flags);
 
-       /*
-        * host->power toggles between false and true in both cases - either
-        * or not the controller can be runtime-suspended during inactivity.
-        * But if the controller has to be kept on, the runtime-pm usage_count
-        * is kept positive, so no suspending actually takes place.
-        */
-       if (ios->power_mode == MMC_POWER_ON && ios->clock) {
-               if (host->power != TMIO_MMC_ON_RUN) {
-                       tmio_mmc_clk_update(mmc);
-                       pm_runtime_get_sync(dev);
-                       if (host->resuming) {
-                               tmio_mmc_reset(host);
-                               host->resuming = false;
-                       }
-               }
-               if (host->power == TMIO_MMC_OFF_STOP)
-                       tmio_mmc_reset(host);
+       switch (ios->power_mode) {
+       case MMC_POWER_OFF:
+               tmio_mmc_power_off(host);
+               tmio_mmc_clk_stop(host);
+               break;
+       case MMC_POWER_UP:
                tmio_mmc_set_clock(host, ios->clock);
-               if (host->power == TMIO_MMC_OFF_STOP)
-                       /* power up SD card and the bus */
-                       tmio_mmc_power_on(host, ios->vdd);
-               host->power = TMIO_MMC_ON_RUN;
-               /* start bus clock */
+               tmio_mmc_power_on(host, ios->vdd);
                tmio_mmc_clk_start(host);
-       } else if (ios->power_mode != MMC_POWER_UP) {
-               struct tmio_mmc_data *pdata = host->pdata;
-               unsigned int old_power = host->power;
-
-               if (old_power != TMIO_MMC_OFF_STOP) {
-                       if (ios->power_mode == MMC_POWER_OFF) {
-                               tmio_mmc_power_off(host);
-                               host->power = TMIO_MMC_OFF_STOP;
-                       } else {
-                               host->power = TMIO_MMC_ON_STOP;
-                       }
-               }
-
-               if (old_power == TMIO_MMC_ON_RUN) {
-                       tmio_mmc_clk_stop(host);
-                       pm_runtime_put(dev);
-                       if (pdata->clk_disable)
-                               pdata->clk_disable(host->pdev);
-               }
-       }
-
-       if (host->power != TMIO_MMC_OFF_STOP) {
-               switch (ios->bus_width) {
-               case MMC_BUS_WIDTH_1:
-                       sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
+               tmio_mmc_set_bus_width(host, ios->bus_width);
                break;
-               case MMC_BUS_WIDTH_4:
-                       sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0);
+       case MMC_POWER_ON:
+               tmio_mmc_set_clock(host, ios->clock);
+               tmio_mmc_clk_start(host);
+               tmio_mmc_set_bus_width(host, ios->bus_width);
                break;
-               }
        }
 
        /* Let things settle. delay taken from winCE driver */
@@ -915,7 +976,12 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                        ios->clock, ios->power_mode);
        host->mrq = NULL;
 
+       host->clk_cache = ios->clock;
+
        mutex_unlock(&host->ios_lock);
+
+       pm_runtime_mark_last_busy(mmc_dev(mmc));
+       pm_runtime_put_autosuspend(mmc_dev(mmc));
 }
 
 static int tmio_mmc_get_ro(struct mmc_host *mmc)
@@ -926,8 +992,25 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc)
        if (ret >= 0)
                return ret;
 
-       return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) ||
-                (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT));
+       pm_runtime_get_sync(mmc_dev(mmc));
+       ret = !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) ||
+               (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT));
+       pm_runtime_mark_last_busy(mmc_dev(mmc));
+       pm_runtime_put_autosuspend(mmc_dev(mmc));
+
+       return ret;
+}
+
+static int tmio_multi_io_quirk(struct mmc_card *card,
+                              unsigned int direction, int blk_size)
+{
+       struct tmio_mmc_host *host = mmc_priv(card->host);
+       struct tmio_mmc_data *pdata = host->pdata;
+
+       if (pdata->multi_io_quirk)
+               return pdata->multi_io_quirk(card, direction, blk_size);
+
+       return blk_size;
 }
 
 static const struct mmc_host_ops tmio_mmc_ops = {
@@ -936,6 +1019,7 @@ static const struct mmc_host_ops tmio_mmc_ops = {
        .get_ro         = tmio_mmc_get_ro,
        .get_cd         = mmc_gpio_get_cd,
        .enable_sdio_irq = tmio_mmc_enable_sdio_irq,
+       .multi_io_quirk = tmio_multi_io_quirk,
 };
 
 static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
@@ -1032,28 +1116,23 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
                                  mmc->caps & MMC_CAP_NONREMOVABLE ||
                                  mmc->slot.cd_irq >= 0);
 
-       _host->power = TMIO_MMC_OFF_STOP;
-       pm_runtime_enable(&pdev->dev);
-       ret = pm_runtime_resume(&pdev->dev);
-       if (ret < 0)
-               goto pm_disable;
-
-       if (tmio_mmc_clk_update(mmc) < 0) {
+       if (tmio_mmc_clk_update(_host) < 0) {
                mmc->f_max = pdata->hclk;
                mmc->f_min = mmc->f_max / 512;
        }
 
        /*
-        * There are 4 different scenarios for the card detection:
-        *  1) an external gpio irq handles the cd (best for power savings)
-        *  2) internal sdhi irq handles the cd
-        *  3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL
-        *  4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE
-        *
-        *  While we increment the runtime PM counter for all scenarios when
-        *  the mmc core activates us by calling an appropriate set_ios(), we
-        *  must additionally ensure that in case 2) the tmio mmc hardware stays
-        *  powered on during runtime for the card detection to work.
+        * Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from
+        * looping forever...
+        */
+       if (mmc->f_min == 0) {
+               ret = -EINVAL;
+               goto host_free;
+       }
+
+       /*
+        * While using internal tmio hardware logic for card detection, we need
+        * to ensure it stays powered for it to work.
         */
        if (_host->native_hotplug)
                pm_runtime_get_noresume(&pdev->dev);
@@ -1074,8 +1153,12 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
 
        _host->sdcard_irq_mask &= ~irq_mask;
 
-       if (pdata->flags & TMIO_MMC_SDIO_IRQ)
-               tmio_mmc_enable_sdio_irq(mmc, 0);
+       _host->sdio_irq_enabled = false;
+       if (pdata->flags & TMIO_MMC_SDIO_IRQ) {
+               _host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
+               sd_ctrl_write16(_host, CTL_SDIO_IRQ_MASK, _host->sdio_irq_mask);
+               sd_ctrl_write16(_host, CTL_TRANSACTION_CTL, 0x0000);
+       }
 
        spin_lock_init(&_host->lock);
        mutex_init(&_host->ios_lock);
@@ -1087,9 +1170,12 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
        /* See if we also get DMA */
        tmio_mmc_request_dma(_host, pdata);
 
+       pm_runtime_set_active(&pdev->dev);
+       pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+       pm_runtime_use_autosuspend(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+
        ret = mmc_add_host(mmc);
-       if (pdata->clk_disable)
-               pdata->clk_disable(pdev);
        if (ret < 0) {
                tmio_mmc_host_remove(_host);
                return ret;
@@ -1103,15 +1189,13 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
                        tmio_mmc_host_remove(_host);
                        return ret;
                }
+               mmc_gpiod_request_cd_irq(mmc);
        }
 
        *host = _host;
 
        return 0;
 
-pm_disable:
-       pm_runtime_disable(&pdev->dev);
-       iounmap(_host->ctl);
 host_free:
        mmc_free_host(mmc);
 
@@ -1142,34 +1226,20 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
 }
 EXPORT_SYMBOL(tmio_mmc_host_remove);
 
-#ifdef CONFIG_PM_SLEEP
-int tmio_mmc_host_suspend(struct device *dev)
+#ifdef CONFIG_PM
+int tmio_mmc_host_runtime_suspend(struct device *dev)
 {
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct tmio_mmc_host *host = mmc_priv(mmc);
 
        tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
-       return 0;
-}
-EXPORT_SYMBOL(tmio_mmc_host_suspend);
-
-int tmio_mmc_host_resume(struct device *dev)
-{
-       struct mmc_host *mmc = dev_get_drvdata(dev);
-       struct tmio_mmc_host *host = mmc_priv(mmc);
 
-       tmio_mmc_enable_dma(host, true);
+       if (host->clk_cache)
+               tmio_mmc_clk_stop(host);
 
-       /* The MMC core will perform the complete set up */
-       host->resuming = true;
-       return 0;
-}
-EXPORT_SYMBOL(tmio_mmc_host_resume);
-#endif
+       if (host->pdata->clk_disable)
+               host->pdata->clk_disable(host->pdev);
 
-#ifdef CONFIG_PM_RUNTIME
-int tmio_mmc_host_runtime_suspend(struct device *dev)
-{
        return 0;
 }
 EXPORT_SYMBOL(tmio_mmc_host_runtime_suspend);
@@ -1179,6 +1249,14 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct tmio_mmc_host *host = mmc_priv(mmc);
 
+       tmio_mmc_reset(host);
+       tmio_mmc_clk_update(host);
+
+       if (host->clk_cache) {
+               tmio_mmc_set_clock(host, host->clk_cache);
+               tmio_mmc_clk_start(host);
+       }
+
        tmio_mmc_enable_dma(host, true);
 
        return 0;
index 1defd5ed323668780846c6fe85041dec6205c20a..9a6dfb0c4eccb54f2afa8514f6e5ded1dab9f202 100644 (file)
@@ -803,8 +803,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
                default:
 #ifdef CONFIG_MMC_DEBUG
-                       pr_warning("%s: Data command %d is not "
-                               "supported by this controller.\n",
+                       pr_warn("%s: Data command %d is not supported by this controller\n",
                                mmc_hostname(host->mmc), cmd->opcode);
 #endif
                        cmd->error = -EINVAL;
@@ -1429,8 +1428,8 @@ free:
        free_dma(dma);
 
 err:
-       pr_warning(DRIVER_NAME ": Unable to allocate DMA %d. "
-               "Falling back on FIFO.\n", dma);
+       pr_warn(DRIVER_NAME ": Unable to allocate DMA %d - falling back on FIFO\n",
+               dma);
 }
 
 static void wbsd_release_dma(struct wbsd_host *host)
@@ -1664,9 +1663,7 @@ static int wbsd_init(struct device *dev, int base, int irq, int dma,
        ret = wbsd_scan(host);
        if (ret) {
                if (pnp && (ret == -ENODEV)) {
-                       pr_warning(DRIVER_NAME
-                               ": Unable to confirm device presence. You may "
-                               "experience lock-ups.\n");
+                       pr_warn(DRIVER_NAME ": Unable to confirm device presence - you may experience lock-ups\n");
                } else {
                        wbsd_free_mmc(dev);
                        return ret;
@@ -1688,10 +1685,7 @@ static int wbsd_init(struct device *dev, int base, int irq, int dma,
         */
        if (pnp) {
                if ((host->config != 0) && !wbsd_chip_validate(host)) {
-                       pr_warning(DRIVER_NAME
-                               ": PnP active but chip not configured! "
-                               "You probably have a buggy BIOS. "
-                               "Configuring chip manually.\n");
+                       pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n");
                        wbsd_chip_config(host);
                }
        } else
@@ -1884,10 +1878,7 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
         */
        if (host->config != 0) {
                if (!wbsd_chip_validate(host)) {
-                       pr_warning(DRIVER_NAME
-                               ": PnP active but chip not configured! "
-                               "You probably have a buggy BIOS. "
-                               "Configuring chip manually.\n");
+                       pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n");
                        wbsd_chip_config(host);
                }
        }
index 15396720f489a802338eda997b26dbff2de66103..3652afd3ec78d9e395a7902d9fe3cc6da570b43a 100644 (file)
@@ -954,7 +954,7 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        spin_lock_irqsave(&port->vio.lock, flags);
 
        dr = &port->vio.drings[VIO_DRIVER_TX_RING];
-       if (unlikely(vnet_tx_dring_avail(dr) < 2)) {
+       if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
                if (!netif_queue_stopped(dev)) {
                        netif_stop_queue(dev);
 
@@ -1049,7 +1049,7 @@ ldc_start_done:
        dev->stats.tx_bytes += port->tx_bufs[txi].skb->len;
 
        dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1);
-       if (unlikely(vnet_tx_dring_avail(dr) < 2)) {
+       if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
                netif_stop_queue(dev);
                if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr))
                        netif_wake_queue(dev);
index acaaf6784179b04bf227de6fefb770f3e744a231..186ce541c65762f8ee1720aae7f573f145982406 100644 (file)
@@ -2152,9 +2152,7 @@ static int tun_chr_fasync(int fd, struct file *file, int on)
                goto out;
 
        if (on) {
-               ret = __f_setown(file, task_pid(current), PIDTYPE_PID, 0);
-               if (ret)
-                       goto out;
+               __f_setown(file, task_pid(current), PIDTYPE_PID, 0);
                tfile->flags |= TUN_FASYNC;
        } else
                tfile->flags &= ~TUN_FASYNC;
index 3759479f959a43b86654e7b493a5d1beb5b6034b..5f0199f6c31e6688c1a7e931fc90414c97907802 100644 (file)
@@ -117,7 +117,6 @@ enum {
 /*
  * PCI vendor and device IDs.
  */
-#define PCI_VENDOR_ID_VMWARE            0x15AD
 #define PCI_DEVICE_ID_VMWARE_VMXNET3    0x07B0
 #define MAX_ETHERNET_CARDS             10
 #define MAX_PCI_PASSTHRU_DEVICE                6
index 9c47b897b6d212c3de827ddde941acb325111546..8079c31ac5e64372fe978405c8faa72981998320 100644 (file)
@@ -937,22 +937,18 @@ static int read_xenbus_vif_flags(struct backend_info *be)
        return 0;
 }
 
-
-/* ** Driver Registration ** */
-
-
 static const struct xenbus_device_id netback_ids[] = {
        { "vif" },
        { "" }
 };
 
-
-static DEFINE_XENBUS_DRIVER(netback, ,
+static struct xenbus_driver netback_driver = {
+       .ids = netback_ids,
        .probe = netback_probe,
        .remove = netback_remove,
        .uevent = netback_uevent,
        .otherend_changed = frontend_changed,
-);
+};
 
 int xenvif_xenbus_init(void)
 {
index ca82f545ec2ca12678ca4f4ce6669c0b564f8bb2..fa671442f420806224bc85ee5f6b5200f7b5478c 100644 (file)
@@ -2300,12 +2300,6 @@ static void xennet_sysfs_delif(struct net_device *netdev)
 
 #endif /* CONFIG_SYSFS */
 
-static const struct xenbus_device_id netfront_ids[] = {
-       { "vif" },
-       { "" }
-};
-
-
 static int xennet_remove(struct xenbus_device *dev)
 {
        struct netfront_info *info = dev_get_drvdata(&dev->dev);
@@ -2338,12 +2332,18 @@ static int xennet_remove(struct xenbus_device *dev)
        return 0;
 }
 
-static DEFINE_XENBUS_DRIVER(netfront, ,
+static const struct xenbus_device_id netfront_ids[] = {
+       { "vif" },
+       { "" }
+};
+
+static struct xenbus_driver netfront_driver = {
+       .ids = netfront_ids,
        .probe = netfront_probe,
        .remove = xennet_remove,
        .resume = netfront_resume,
        .otherend_changed = netback_changed,
-);
+};
 
 static int __init netif_init(void)
 {
index 5160c4eb73c2e3788cd310693be3969b88642ea9..1a13f5b722c57e4d2d523c6be9b289084dba115a 100644 (file)
@@ -11,6 +11,7 @@ config OF_SELFTEST
        bool "Device Tree Runtime self tests"
        depends on OF_IRQ && OF_EARLY_FLATTREE
        select OF_DYNAMIC
+       select OF_RESOLVE
        help
          This option builds in test cases for the device tree infrastructure
          that are executed once at boot time, and the results dumped to the
@@ -79,4 +80,7 @@ config OF_RESERVED_MEM
        help
          Helpers to allow for reservation of memory regions
 
+config OF_RESOLVE
+       bool
+
 endmenu # OF
index 2b6a7b129d10b2092cb9e891158908c86c82fcfa..ca9209ce50cd74b85c599e16482c0e38078e03e0 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_OF_PCI)  += of_pci.o
 obj-$(CONFIG_OF_PCI_IRQ)  += of_pci_irq.o
 obj-$(CONFIG_OF_MTD)   += of_mtd.o
 obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
+obj-$(CONFIG_OF_RESOLVE)  += resolver.o
 
 CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
 CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt
index e3718250d66e4f2b5fcb13c9936b7e28615440ec..afdb78299f61f8d5465ec1d203d482d0674fe31d 100644 (file)
@@ -5,6 +5,8 @@
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/pci_regs.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
 #include <linux/string.h>
 
 /* Max address size we deal with */
@@ -293,6 +295,51 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
 }
 EXPORT_SYMBOL_GPL(of_pci_range_parser_one);
 
+/*
+ * of_pci_range_to_resource - Create a resource from an of_pci_range
+ * @range:     the PCI range that describes the resource
+ * @np:                device node where the range belongs to
+ * @res:       pointer to a valid resource that will be updated to
+ *              reflect the values contained in the range.
+ *
+ * Returns EINVAL if the range cannot be converted to resource.
+ *
+ * Note that if the range is an IO range, the resource will be converted
+ * using pci_address_to_pio() which can fail if it is called too early or
+ * if the range cannot be matched to any host bridge IO space (our case here).
+ * To guard against that we try to register the IO range first.
+ * If that fails we know that pci_address_to_pio() will do too.
+ */
+int of_pci_range_to_resource(struct of_pci_range *range,
+                            struct device_node *np, struct resource *res)
+{
+       int err;
+       res->flags = range->flags;
+       res->parent = res->child = res->sibling = NULL;
+       res->name = np->full_name;
+
+       if (res->flags & IORESOURCE_IO) {
+               unsigned long port;
+               err = pci_register_io_range(range->cpu_addr, range->size);
+               if (err)
+                       goto invalid_range;
+               port = pci_address_to_pio(range->cpu_addr);
+               if (port == (unsigned long)-1) {
+                       err = -EINVAL;
+                       goto invalid_range;
+               }
+               res->start = port;
+       } else {
+               res->start = range->cpu_addr;
+       }
+       res->end = res->start + range->size - 1;
+       return 0;
+
+invalid_range:
+       res->start = (resource_size_t)OF_BAD_ADDR;
+       res->end = (resource_size_t)OF_BAD_ADDR;
+       return err;
+}
 #endif /* CONFIG_PCI */
 
 /*
@@ -601,12 +648,119 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
 }
 EXPORT_SYMBOL(of_get_address);
 
+#ifdef PCI_IOBASE
+struct io_range {
+       struct list_head list;
+       phys_addr_t start;
+       resource_size_t size;
+};
+
+static LIST_HEAD(io_range_list);
+static DEFINE_SPINLOCK(io_range_lock);
+#endif
+
+/*
+ * Record the PCI IO range (expressed as CPU physical address + size).
+ * Return a negative value if an error has occured, zero otherwise
+ */
+int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
+{
+       int err = 0;
+
+#ifdef PCI_IOBASE
+       struct io_range *range;
+       resource_size_t allocated_size = 0;
+
+       /* check if the range hasn't been previously recorded */
+       spin_lock(&io_range_lock);
+       list_for_each_entry(range, &io_range_list, list) {
+               if (addr >= range->start && addr + size <= range->start + size) {
+                       /* range already registered, bail out */
+                       goto end_register;
+               }
+               allocated_size += range->size;
+       }
+
+       /* range not registed yet, check for available space */
+       if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
+               /* if it's too big check if 64K space can be reserved */
+               if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
+                       err = -E2BIG;
+                       goto end_register;
+               }
+
+               size = SZ_64K;
+               pr_warn("Requested IO range too big, new size set to 64K\n");
+       }
+
+       /* add the range to the list */
+       range = kzalloc(sizeof(*range), GFP_KERNEL);
+       if (!range) {
+               err = -ENOMEM;
+               goto end_register;
+       }
+
+       range->start = addr;
+       range->size = size;
+
+       list_add_tail(&range->list, &io_range_list);
+
+end_register:
+       spin_unlock(&io_range_lock);
+#endif
+
+       return err;
+}
+
+phys_addr_t pci_pio_to_address(unsigned long pio)
+{
+       phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
+
+#ifdef PCI_IOBASE
+       struct io_range *range;
+       resource_size_t allocated_size = 0;
+
+       if (pio > IO_SPACE_LIMIT)
+               return address;
+
+       spin_lock(&io_range_lock);
+       list_for_each_entry(range, &io_range_list, list) {
+               if (pio >= allocated_size && pio < allocated_size + range->size) {
+                       address = range->start + pio - allocated_size;
+                       break;
+               }
+               allocated_size += range->size;
+       }
+       spin_unlock(&io_range_lock);
+#endif
+
+       return address;
+}
+
 unsigned long __weak pci_address_to_pio(phys_addr_t address)
 {
+#ifdef PCI_IOBASE
+       struct io_range *res;
+       resource_size_t offset = 0;
+       unsigned long addr = -1;
+
+       spin_lock(&io_range_lock);
+       list_for_each_entry(res, &io_range_list, list) {
+               if (address >= res->start && address < res->start + res->size) {
+                       addr = res->start - address + offset;
+                       break;
+               }
+               offset += res->size;
+       }
+       spin_unlock(&io_range_lock);
+
+       return addr;
+#else
        if (address > IO_SPACE_LIMIT)
                return (unsigned long)-1;
 
        return (unsigned long) address;
+#endif
 }
 
 static int __of_address_to_resource(struct device_node *dev,
index 293ed4b687ba7265889002edcb4f01b7d7edfe21..2305dc0382bca0a5c86bc89f5335253de46ff109 100644 (file)
@@ -1021,6 +1021,9 @@ struct device_node *of_find_node_by_phandle(phandle handle)
        struct device_node *np;
        unsigned long flags;
 
+       if (!handle)
+               return NULL;
+
        raw_spin_lock_irqsave(&devtree_lock, flags);
        for (np = of_allnodes; np; np = np->allnext)
                if (np->phandle == handle)
index 848199633798cd635030b398c54f93e173de057d..8882b467be95d1099f8e11c96ac27200727ee515 100644 (file)
@@ -1,7 +1,9 @@
 #include <linux/kernel.h>
 #include <linux/export.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_pci.h>
+#include <linux/slab.h>
 
 static inline int __of_pci_pci_compare(struct device_node *node,
                                       unsigned int data)
@@ -89,6 +91,146 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res)
 }
 EXPORT_SYMBOL_GPL(of_pci_parse_bus_range);
 
+/**
+ * This function will try to obtain the host bridge domain number by
+ * finding a property called "linux,pci-domain" of the given device node.
+ *
+ * @node: device tree node with the domain information
+ *
+ * Returns the associated domain number from DT in the range [0-0xffff], or
+ * a negative value if the required property is not found.
+ */
+int of_get_pci_domain_nr(struct device_node *node)
+{
+       const __be32 *value;
+       int len;
+       u16 domain;
+
+       value = of_get_property(node, "linux,pci-domain", &len);
+       if (!value || len < sizeof(*value))
+               return -EINVAL;
+
+       domain = (u16)be32_to_cpup(value);
+
+       return domain;
+}
+EXPORT_SYMBOL_GPL(of_get_pci_domain_nr);
+
+#if defined(CONFIG_OF_ADDRESS)
+/**
+ * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT
+ * @dev: device node of the host bridge having the range property
+ * @busno: bus number associated with the bridge root bus
+ * @bus_max: maximum number of buses for this bridge
+ * @resources: list where the range of resources will be added after DT parsing
+ * @io_base: pointer to a variable that will contain on return the physical
+ * address for the start of the I/O range. Can be NULL if the caller doesn't
+ * expect IO ranges to be present in the device tree.
+ *
+ * It is the caller's job to free the @resources list.
+ *
+ * This function will parse the "ranges" property of a PCI host bridge device
+ * node and setup the resource mapping based on its content. It is expected
+ * that the property conforms with the Power ePAPR document.
+ *
+ * It returns zero if the range parsing has been successful or a standard error
+ * value if it failed.
+ */
+int of_pci_get_host_bridge_resources(struct device_node *dev,
+                       unsigned char busno, unsigned char bus_max,
+                       struct list_head *resources, resource_size_t *io_base)
+{
+       struct resource *res;
+       struct resource *bus_range;
+       struct of_pci_range range;
+       struct of_pci_range_parser parser;
+       char range_type[4];
+       int err;
+
+       if (io_base)
+               *io_base = (resource_size_t)OF_BAD_ADDR;
+
+       bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
+       if (!bus_range)
+               return -ENOMEM;
+
+       pr_info("PCI host bridge %s ranges:\n", dev->full_name);
+
+       err = of_pci_parse_bus_range(dev, bus_range);
+       if (err) {
+               bus_range->start = busno;
+               bus_range->end = bus_max;
+               bus_range->flags = IORESOURCE_BUS;
+               pr_info("  No bus range found for %s, using %pR\n",
+                       dev->full_name, bus_range);
+       } else {
+               if (bus_range->end > bus_range->start + bus_max)
+                       bus_range->end = bus_range->start + bus_max;
+       }
+       pci_add_resource(resources, bus_range);
+
+       /* Check for ranges property */
+       err = of_pci_range_parser_init(&parser, dev);
+       if (err)
+               goto parse_failed;
+
+       pr_debug("Parsing ranges property...\n");
+       for_each_of_pci_range(&parser, &range) {
+               /* Read next ranges element */
+               if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
+                       snprintf(range_type, 4, " IO");
+               else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
+                       snprintf(range_type, 4, "MEM");
+               else
+                       snprintf(range_type, 4, "err");
+               pr_info("  %s %#010llx..%#010llx -> %#010llx\n", range_type,
+                       range.cpu_addr, range.cpu_addr + range.size - 1,
+                       range.pci_addr);
+
+               /*
+                * If we failed translation or got a zero-sized region
+                * then skip this range
+                */
+               if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
+                       continue;
+
+               res = kzalloc(sizeof(struct resource), GFP_KERNEL);
+               if (!res) {
+                       err = -ENOMEM;
+                       goto parse_failed;
+               }
+
+               err = of_pci_range_to_resource(&range, dev, res);
+               if (err)
+                       goto conversion_failed;
+
+               if (resource_type(res) == IORESOURCE_IO) {
+                       if (!io_base) {
+                               pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n",
+                                       dev->full_name);
+                               err = -EINVAL;
+                               goto conversion_failed;
+                       }
+                       if (*io_base != (resource_size_t)OF_BAD_ADDR)
+                               pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n",
+                                       dev->full_name);
+                       *io_base = range.cpu_addr;
+               }
+
+               pci_add_resource_offset(resources, res, res->start - range.pci_addr);
+       }
+
+       return 0;
+
+conversion_failed:
+       kfree(res);
+parse_failed:
+       pci_free_resource_list(resources);
+       return err;
+}
+EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources);
+#endif /* CONFIG_OF_ADDRESS */
+
 #ifdef CONFIG_PCI_MSI
 
 static LIST_HEAD(of_pci_msi_chip_list);
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
new file mode 100644 (file)
index 0000000..aed7959
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * Functions for dealing with DT resolution
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+/* illegal phandle value (set when unresolved) */
+#define OF_PHANDLE_ILLEGAL     0xdeadbeef
+
+/**
+ * Find a node with the give full name by recursively following any of
+ * the child node links.
+ */
+static struct device_node *__of_find_node_by_full_name(struct device_node *node,
+               const char *full_name)
+{
+       struct device_node *child, *found;
+
+       if (node == NULL)
+               return NULL;
+
+       /* check */
+       if (of_node_cmp(node->full_name, full_name) == 0)
+               return node;
+
+       for_each_child_of_node(node, child) {
+               found = __of_find_node_by_full_name(child, full_name);
+               if (found != NULL)
+                       return found;
+       }
+
+       return NULL;
+}
+
+/*
+ * Find live tree's maximum phandle value.
+ */
+static phandle of_get_tree_max_phandle(void)
+{
+       struct device_node *node;
+       phandle phandle;
+       unsigned long flags;
+
+       /* now search recursively */
+       raw_spin_lock_irqsave(&devtree_lock, flags);
+       phandle = 0;
+       for_each_of_allnodes(node) {
+               if (node->phandle != OF_PHANDLE_ILLEGAL &&
+                               node->phandle > phandle)
+                       phandle = node->phandle;
+       }
+       raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+       return phandle;
+}
+
+/*
+ * Adjust a subtree's phandle values by a given delta.
+ * Makes sure not to just adjust the device node's phandle value,
+ * but modify the phandle properties values as well.
+ */
+static void __of_adjust_tree_phandles(struct device_node *node,
+               int phandle_delta)
+{
+       struct device_node *child;
+       struct property *prop;
+       phandle phandle;
+
+       /* first adjust the node's phandle direct value */
+       if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
+               node->phandle += phandle_delta;
+
+       /* now adjust phandle & linux,phandle values */
+       for_each_property_of_node(node, prop) {
+
+               /* only look for these two */
+               if (of_prop_cmp(prop->name, "phandle") != 0 &&
+                   of_prop_cmp(prop->name, "linux,phandle") != 0)
+                       continue;
+
+               /* must be big enough */
+               if (prop->length < 4)
+                       continue;
+
+               /* read phandle value */
+               phandle = be32_to_cpup(prop->value);
+               if (phandle == OF_PHANDLE_ILLEGAL)      /* unresolved */
+                       continue;
+
+               /* adjust */
+               *(uint32_t *)prop->value = cpu_to_be32(node->phandle);
+       }
+
+       /* now do the children recursively */
+       for_each_child_of_node(node, child)
+               __of_adjust_tree_phandles(child, phandle_delta);
+}
+
+static int __of_adjust_phandle_ref(struct device_node *node, struct property *rprop, int value, bool is_delta)
+{
+       phandle phandle;
+       struct device_node *refnode;
+       struct property *sprop;
+       char *propval, *propcur, *propend, *nodestr, *propstr, *s;
+       int offset, propcurlen;
+       int err = 0;
+
+       /* make a copy */
+       propval = kmalloc(rprop->length, GFP_KERNEL);
+       if (!propval) {
+               pr_err("%s: Could not copy value of '%s'\n",
+                               __func__, rprop->name);
+               return -ENOMEM;
+       }
+       memcpy(propval, rprop->value, rprop->length);
+
+       propend = propval + rprop->length;
+       for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
+               propcurlen = strlen(propcur);
+
+               nodestr = propcur;
+               s = strchr(propcur, ':');
+               if (!s) {
+                       pr_err("%s: Illegal symbol entry '%s' (1)\n",
+                               __func__, propcur);
+                       err = -EINVAL;
+                       goto err_fail;
+               }
+               *s++ = '\0';
+
+               propstr = s;
+               s = strchr(s, ':');
+               if (!s) {
+                       pr_err("%s: Illegal symbol entry '%s' (2)\n",
+                               __func__, (char *)rprop->value);
+                       err = -EINVAL;
+                       goto err_fail;
+               }
+
+               *s++ = '\0';
+               err = kstrtoint(s, 10, &offset);
+               if (err != 0) {
+                       pr_err("%s: Could get offset '%s'\n",
+                               __func__, (char *)rprop->value);
+                       goto err_fail;
+               }
+
+               /* look into the resolve node for the full path */
+               refnode = __of_find_node_by_full_name(node, nodestr);
+               if (!refnode) {
+                       pr_warn("%s: Could not find refnode '%s'\n",
+                               __func__, (char *)rprop->value);
+                       continue;
+               }
+
+               /* now find the property */
+               for_each_property_of_node(refnode, sprop) {
+                       if (of_prop_cmp(sprop->name, propstr) == 0)
+                               break;
+               }
+
+               if (!sprop) {
+                       pr_err("%s: Could not find property '%s'\n",
+                               __func__, (char *)rprop->value);
+                       err = -ENOENT;
+                       goto err_fail;
+               }
+
+               phandle = is_delta ? be32_to_cpup(sprop->value + offset) + value : value;
+               *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
+       }
+
+err_fail:
+       kfree(propval);
+       return err;
+}
+
+/*
+ * Adjust the local phandle references by the given phandle delta.
+ * Assumes the existances of a __local_fixups__ node at the root
+ * of the tree. Does not take any devtree locks so make sure you
+ * call this on a tree which is at the detached state.
+ */
+static int __of_adjust_tree_phandle_references(struct device_node *node,
+               int phandle_delta)
+{
+       struct device_node *child;
+       struct property *rprop;
+       int err;
+
+       /* locate the symbols & fixups nodes on resolve */
+       for_each_child_of_node(node, child)
+               if (of_node_cmp(child->name, "__local_fixups__") == 0)
+                       break;
+
+       /* no local fixups */
+       if (!child)
+               return 0;
+
+       /* find the local fixups property */
+       for_each_property_of_node(child, rprop) {
+               /* skip properties added automatically */
+               if (of_prop_cmp(rprop->name, "name") == 0)
+                       continue;
+
+               err = __of_adjust_phandle_ref(node, rprop, phandle_delta, true);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+/**
+ * of_resolve  - Resolve the given node against the live tree.
+ *
+ * @resolve:   Node to resolve
+ *
+ * Perform dynamic Device Tree resolution against the live tree
+ * to the given node to resolve. This depends on the live tree
+ * having a __symbols__ node, and the resolve node the __fixups__ &
+ * __local_fixups__ nodes (if needed).
+ * The result of the operation is a resolve node that it's contents
+ * are fit to be inserted or operate upon the live tree.
+ * Returns 0 on success or a negative error value on error.
+ */
+int of_resolve_phandles(struct device_node *resolve)
+{
+       struct device_node *child, *refnode;
+       struct device_node *root_sym, *resolve_sym, *resolve_fix;
+       struct property *rprop;
+       const char *refpath;
+       phandle phandle, phandle_delta;
+       int err;
+
+       /* the resolve node must exist, and be detached */
+       if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
+               return -EINVAL;
+
+       /* first we need to adjust the phandles */
+       phandle_delta = of_get_tree_max_phandle() + 1;
+       __of_adjust_tree_phandles(resolve, phandle_delta);
+       err = __of_adjust_tree_phandle_references(resolve, phandle_delta);
+       if (err != 0)
+               return err;
+
+       root_sym = NULL;
+       resolve_sym = NULL;
+       resolve_fix = NULL;
+
+       /* this may fail (if no fixups are required) */
+       root_sym = of_find_node_by_path("/__symbols__");
+
+       /* locate the symbols & fixups nodes on resolve */
+       for_each_child_of_node(resolve, child) {
+
+               if (!resolve_sym &&
+                               of_node_cmp(child->name, "__symbols__") == 0)
+                       resolve_sym = child;
+
+               if (!resolve_fix &&
+                               of_node_cmp(child->name, "__fixups__") == 0)
+                       resolve_fix = child;
+
+               /* both found, don't bother anymore */
+               if (resolve_sym && resolve_fix)
+                       break;
+       }
+
+       /* we do allow for the case where no fixups are needed */
+       if (!resolve_fix) {
+               err = 0;        /* no error */
+               goto out;
+       }
+
+       /* we need to fixup, but no root symbols... */
+       if (!root_sym) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       for_each_property_of_node(resolve_fix, rprop) {
+
+               /* skip properties added automatically */
+               if (of_prop_cmp(rprop->name, "name") == 0)
+                       continue;
+
+               err = of_property_read_string(root_sym,
+                               rprop->name, &refpath);
+               if (err != 0) {
+                       pr_err("%s: Could not find symbol '%s'\n",
+                                       __func__, rprop->name);
+                       goto out;
+               }
+
+               refnode = of_find_node_by_path(refpath);
+               if (!refnode) {
+                       pr_err("%s: Could not find node by path '%s'\n",
+                                       __func__, refpath);
+                       err = -ENOENT;
+                       goto out;
+               }
+
+               phandle = refnode->phandle;
+               of_node_put(refnode);
+
+               pr_debug("%s: %s phandle is 0x%08x\n",
+                               __func__, rprop->name, phandle);
+
+               err = __of_adjust_phandle_ref(resolve, rprop, phandle, false);
+               if (err)
+                       break;
+       }
+
+out:
+       /* NULL is handled by of_node_put as NOP */
+       of_node_put(root_sym);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(of_resolve_phandles);
index a737cb5974deae12aed1987d5250326ffa350445..78001270a5980bc7342a5241c8930297cf92f883 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/errno.h>
+#include <linux/hashtable.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_fdt.h>
@@ -24,7 +25,7 @@ static struct selftest_results {
        int failed;
 } selftest_results;
 
-#define NO_OF_NODES 2
+#define NO_OF_NODES 3
 static struct device_node *nodes[NO_OF_NODES];
 static int last_node_index;
 static bool selftest_live_tree;
@@ -145,6 +146,97 @@ static void __init of_selftest_dynamic(void)
                         "Adding a large property should have passed\n");
 }
 
+static int __init of_selftest_check_node_linkage(struct device_node *np)
+{
+       struct device_node *child, *allnext_index = np;
+       int count = 0, rc;
+
+       for_each_child_of_node(np, child) {
+               if (child->parent != np) {
+                       pr_err("Child node %s links to wrong parent %s\n",
+                                child->name, np->name);
+                       return -EINVAL;
+               }
+
+               while (allnext_index && allnext_index != child)
+                       allnext_index = allnext_index->allnext;
+               if (allnext_index != child) {
+                       pr_err("Node %s is ordered differently in sibling and allnode lists\n",
+                                child->name);
+                       return -EINVAL;
+               }
+
+               rc = of_selftest_check_node_linkage(child);
+               if (rc < 0)
+                       return rc;
+               count += rc;
+       }
+
+       return count + 1;
+}
+
+static void __init of_selftest_check_tree_linkage(void)
+{
+       struct device_node *np;
+       int allnode_count = 0, child_count;
+
+       if (!of_allnodes)
+               return;
+
+       for_each_of_allnodes(np)
+               allnode_count++;
+       child_count = of_selftest_check_node_linkage(of_allnodes);
+
+       selftest(child_count > 0, "Device node data structure is corrupted\n");
+       selftest(child_count == allnode_count, "allnodes list size (%i) doesn't match"
+                "sibling lists size (%i)\n", allnode_count, child_count);
+       pr_debug("allnodes list size (%i); sibling lists size (%i)\n", allnode_count, child_count);
+}
+
+struct node_hash {
+       struct hlist_node node;
+       struct device_node *np;
+};
+
+static DEFINE_HASHTABLE(phandle_ht, 8);
+static void __init of_selftest_check_phandles(void)
+{
+       struct device_node *np;
+       struct node_hash *nh;
+       struct hlist_node *tmp;
+       int i, dup_count = 0, phandle_count = 0;
+
+       for_each_of_allnodes(np) {
+               if (!np->phandle)
+                       continue;
+
+               hash_for_each_possible(phandle_ht, nh, node, np->phandle) {
+                       if (nh->np->phandle == np->phandle) {
+                               pr_info("Duplicate phandle! %i used by %s and %s\n",
+                                       np->phandle, nh->np->full_name, np->full_name);
+                               dup_count++;
+                               break;
+                       }
+               }
+
+               nh = kzalloc(sizeof(*nh), GFP_KERNEL);
+               if (WARN_ON(!nh))
+                       return;
+
+               nh->np = np;
+               hash_add(phandle_ht, &nh->node, np->phandle);
+               phandle_count++;
+       }
+       selftest(dup_count == 0, "Found %i duplicates in %i phandles\n",
+                dup_count, phandle_count);
+
+       /* Clean up */
+       hash_for_each_safe(phandle_ht, i, tmp, nh, node) {
+               hash_del(&nh->node);
+               kfree(nh);
+       }
+}
+
 static void __init of_selftest_parse_phandle_with_args(void)
 {
        struct device_node *np;
@@ -637,6 +729,8 @@ static int attach_node_and_children(struct device_node *np)
        dup = np;
 
        while (dup) {
+               if (WARN_ON(last_node_index >= NO_OF_NODES))
+                       return -EINVAL;
                nodes[last_node_index++] = dup;
                dup = dup->sibling;
        }
@@ -670,6 +764,7 @@ static int __init selftest_data_add(void)
        extern uint8_t __dtb_testcases_begin[];
        extern uint8_t __dtb_testcases_end[];
        const int size = __dtb_testcases_end - __dtb_testcases_begin;
+       int rc;
 
        if (!size) {
                pr_warn("%s: No testcase data to attach; not running tests\n",
@@ -690,6 +785,12 @@ static int __init selftest_data_add(void)
                pr_warn("%s: No tree to attach; not running tests\n", __func__);
                return -ENODATA;
        }
+       of_node_set_flag(selftest_data_node, OF_DETACHED);
+       rc = of_resolve_phandles(selftest_data_node);
+       if (rc) {
+               pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc);
+               return -EINVAL;
+       }
 
        if (!of_allnodes) {
                /* enabling flag for removing nodes */
@@ -717,10 +818,6 @@ static void detach_node_and_children(struct device_node *np)
 {
        while (np->child)
                detach_node_and_children(np->child);
-
-       while (np->sibling)
-               detach_node_and_children(np->sibling);
-
        of_detach_node(np);
 }
 
@@ -749,8 +846,7 @@ static void selftest_data_remove(void)
                if (nodes[last_node_index]) {
                        np = of_find_node_by_path(nodes[last_node_index]->full_name);
                        if (strcmp(np->full_name, "/aliases") != 0) {
-                               detach_node_and_children(np->child);
-                               of_detach_node(np);
+                               detach_node_and_children(np);
                        } else {
                                for_each_property_of_node(np, prop) {
                                        if (strcmp(prop->name, "testcase-alias") == 0)
@@ -780,6 +876,8 @@ static int __init of_selftest(void)
        of_node_put(np);
 
        pr_info("start of selftest - you will see error messages\n");
+       of_selftest_check_tree_linkage();
+       of_selftest_check_phandles();
        of_selftest_find_node_by_name();
        of_selftest_dynamic();
        of_selftest_parse_phandle_with_args();
@@ -790,12 +888,16 @@ static int __init of_selftest(void)
        of_selftest_parse_interrupts_extended();
        of_selftest_match_node();
        of_selftest_platform_populate();
-       pr_info("end of selftest - %i passed, %i failed\n",
-               selftest_results.passed, selftest_results.failed);
 
        /* removing selftest data from live tree */
        selftest_data_remove();
 
+       /* Double check linkage after removing testcase data */
+       of_selftest_check_tree_linkage();
+
+       pr_info("end of selftest - %i passed, %i failed\n",
+               selftest_results.passed, selftest_results.failed);
+
        return 0;
 }
 late_initcall(of_selftest);
index 219ef9324e9c6b8e270f064b97a1d34f11e6c884..6994e15c24bfe25d322ec9fe499daef23084a3bb 100644 (file)
 #include "tests-interrupts.dtsi"
 #include "tests-match.dtsi"
 #include "tests-platform.dtsi"
+
+/*
+ * phandle fixup data - generated by dtc patches that aren't upstream.
+ * This data must be regenerated whenever phandle references are modified in
+ * the testdata tree.
+ *
+ * The format of this data may be subject to change. For the time being consider
+ * this a kernel-internal data format.
+ */
+/ { __local_fixups__ {
+       fixup = "/testcase-data/testcase-device2:interrupt-parent:0",
+               "/testcase-data/testcase-device1:interrupt-parent:0",
+               "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:60",
+               "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:52",
+               "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:44",
+               "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:36",
+               "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:24",
+               "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:8",
+               "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:0",
+               "/testcase-data/interrupts/interrupts1:interrupt-parent:0",
+               "/testcase-data/interrupts/interrupts0:interrupt-parent:0",
+               "/testcase-data/interrupts/intmap1:interrupt-map:12",
+               "/testcase-data/interrupts/intmap0:interrupt-map:52",
+               "/testcase-data/interrupts/intmap0:interrupt-map:36",
+               "/testcase-data/interrupts/intmap0:interrupt-map:16",
+               "/testcase-data/interrupts/intmap0:interrupt-map:4",
+               "/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:12",
+               "/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:0",
+               "/testcase-data/phandle-tests/consumer-a:phandle-list:56",
+               "/testcase-data/phandle-tests/consumer-a:phandle-list:52",
+               "/testcase-data/phandle-tests/consumer-a:phandle-list:40",
+               "/testcase-data/phandle-tests/consumer-a:phandle-list:24",
+               "/testcase-data/phandle-tests/consumer-a:phandle-list:8",
+               "/testcase-data/phandle-tests/consumer-a:phandle-list:0";
+}; };
index 90f5ccacce4ba819786506054deec71f05473026..3dc25fad490c84564f6143219fa94c17eac71b48 100644 (file)
@@ -63,4 +63,32 @@ config PCIE_SPEAR13XX
        help
          Say Y here if you want PCIe support on SPEAr13XX SoCs.
 
+config PCI_KEYSTONE
+       bool "TI Keystone PCIe controller"
+       depends on ARCH_KEYSTONE
+       select PCIE_DW
+       select PCIEPORTBUS
+       help
+         Say Y here if you want to enable PCI controller support on Keystone
+         SoCs. The PCI controller on Keystone is based on Designware hardware
+         and therefore the driver re-uses the Designware core functions to
+         implement the driver.
+
+config PCIE_XILINX
+       bool "Xilinx AXI PCIe host bridge support"
+       depends on ARCH_ZYNQ
+       help
+         Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
+         Host Bridge driver.
+
+config PCI_XGENE
+       bool "X-Gene PCIe controller"
+       depends on ARCH_XGENE
+       depends on OF
+       select PCIEPORTBUS
+       help
+         Say Y here if you want internal PCI support on APM X-Gene SoC.
+         There are 5 internal PCIe ports available. Each port is GEN3 capable
+         and have varied lanes from x1 to x8.
+
 endmenu
index d0e88f114ff93b5e016f7993f012b17ce4ff2631..26b3461d68d7b1055abec2a83c30c2f20b96713c 100644 (file)
@@ -8,3 +8,6 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
 obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
 obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
 obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
+obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
+obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
+obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
index 35fc73a8d0b3d0cff77f13c280d17549a57c1128..233fe8a882649dfc4197b8ac58ff1dd0bdaed6e9 100644 (file)
@@ -257,11 +257,6 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
        struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
        int ret;
 
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
-                       IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
-                       IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
-
        ret = clk_prepare_enable(imx6_pcie->pcie_phy);
        if (ret) {
                dev_err(pp->dev, "unable to enable pcie_phy clock\n");
@@ -283,6 +278,12 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
        /* allow the clocks to stabilize */
        usleep_range(200, 500);
 
+       /* power up core phy and enable ref clock */
+       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                       IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
+       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                       IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
+
        /* Some boards don't have PCIe reset GPIO. */
        if (gpio_is_valid(imx6_pcie->reset_gpio)) {
                gpio_set_value(imx6_pcie->reset_gpio, 0);
@@ -647,7 +648,7 @@ static int __init imx6_pcie_init(void)
 {
        return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe);
 }
-fs_initcall(imx6_pcie_init);
+module_init(imx6_pcie_init);
 
 MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
 MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver");
diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/host/pci-keystone-dw.c
new file mode 100644 (file)
index 0000000..34086ce
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * Designware application register space functions for Keystone PCI controller
+ *
+ * Copyright (C) 2013-2014 Texas Instruments., Ltd.
+ *             http://www.ti.com
+ *
+ * Author: Murali Karicheri <m-karicheri2@ti.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include "pcie-designware.h"
+#include "pci-keystone.h"
+
+/* Application register defines */
+#define LTSSM_EN_VAL                   1
+#define LTSSM_STATE_MASK               0x1f
+#define LTSSM_STATE_L0                 0x11
+#define DBI_CS2_EN_VAL                 0x20
+#define OB_XLAT_EN_VAL                 2
+
+/* Application registers */
+#define CMD_STATUS                     0x004
+#define CFG_SETUP                      0x008
+#define OB_SIZE                                0x030
+#define CFG_PCIM_WIN_SZ_IDX            3
+#define CFG_PCIM_WIN_CNT               32
+#define SPACE0_REMOTE_CFG_OFFSET       0x1000
+#define OB_OFFSET_INDEX(n)             (0x200 + (8 * n))
+#define OB_OFFSET_HI(n)                        (0x204 + (8 * n))
+
+/* IRQ register defines */
+#define IRQ_EOI                                0x050
+#define IRQ_STATUS                     0x184
+#define IRQ_ENABLE_SET                 0x188
+#define IRQ_ENABLE_CLR                 0x18c
+
+#define MSI_IRQ                                0x054
+#define MSI0_IRQ_STATUS                        0x104
+#define MSI0_IRQ_ENABLE_SET            0x108
+#define MSI0_IRQ_ENABLE_CLR            0x10c
+#define IRQ_STATUS                     0x184
+#define MSI_IRQ_OFFSET                 4
+
+/* Config space registers */
+#define DEBUG0                         0x728
+
+#define to_keystone_pcie(x)    container_of(x, struct keystone_pcie, pp)
+
+static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
+{
+       return sys->private_data;
+}
+
+static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset,
+                                            u32 *bit_pos)
+{
+       *reg_offset = offset % 8;
+       *bit_pos = offset >> 3;
+}
+
+u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
+{
+       struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+
+       return ks_pcie->app.start + MSI_IRQ;
+}
+
+void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset)
+{
+       struct pcie_port *pp = &ks_pcie->pp;
+       u32 pending, vector;
+       int src, virq;
+
+       pending = readl(ks_pcie->va_app_base + MSI0_IRQ_STATUS + (offset << 4));
+
+       /*
+        * MSI0 status bit 0-3 shows vectors 0, 8, 16, 24, MSI1 status bit
+        * shows 1, 9, 17, 25 and so forth
+        */
+       for (src = 0; src < 4; src++) {
+               if (BIT(src) & pending) {
+                       vector = offset + (src << 3);
+                       virq = irq_linear_revmap(pp->irq_domain, vector);
+                       dev_dbg(pp->dev, "irq: bit %d, vector %d, virq %d\n",
+                               src, vector, virq);
+                       generic_handle_irq(virq);
+               }
+       }
+}
+
+static void ks_dw_pcie_msi_irq_ack(struct irq_data *d)
+{
+       u32 offset, reg_offset, bit_pos;
+       struct keystone_pcie *ks_pcie;
+       unsigned int irq = d->irq;
+       struct msi_desc *msi;
+       struct pcie_port *pp;
+
+       msi = irq_get_msi_desc(irq);
+       pp = sys_to_pcie(msi->dev->bus->sysdata);
+       ks_pcie = to_keystone_pcie(pp);
+       offset = irq - irq_linear_revmap(pp->irq_domain, 0);
+       update_reg_offset_bit_pos(offset, &reg_offset, &bit_pos);
+
+       writel(BIT(bit_pos),
+              ks_pcie->va_app_base + MSI0_IRQ_STATUS + (reg_offset << 4));
+       writel(reg_offset + MSI_IRQ_OFFSET, ks_pcie->va_app_base + IRQ_EOI);
+}
+
+void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
+{
+       u32 reg_offset, bit_pos;
+       struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+
+       update_reg_offset_bit_pos(irq, &reg_offset, &bit_pos);
+       writel(BIT(bit_pos),
+              ks_pcie->va_app_base + MSI0_IRQ_ENABLE_SET + (reg_offset << 4));
+}
+
+void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
+{
+       u32 reg_offset, bit_pos;
+       struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+
+       update_reg_offset_bit_pos(irq, &reg_offset, &bit_pos);
+       writel(BIT(bit_pos),
+              ks_pcie->va_app_base + MSI0_IRQ_ENABLE_CLR + (reg_offset << 4));
+}
+
+static void ks_dw_pcie_msi_irq_mask(struct irq_data *d)
+{
+       struct keystone_pcie *ks_pcie;
+       unsigned int irq = d->irq;
+       struct msi_desc *msi;
+       struct pcie_port *pp;
+       u32 offset;
+
+       msi = irq_get_msi_desc(irq);
+       pp = sys_to_pcie(msi->dev->bus->sysdata);
+       ks_pcie = to_keystone_pcie(pp);
+       offset = irq - irq_linear_revmap(pp->irq_domain, 0);
+
+       /* Mask the end point if PVM implemented */
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               if (msi->msi_attrib.maskbit)
+                       mask_msi_irq(d);
+       }
+
+       ks_dw_pcie_msi_clear_irq(pp, offset);
+}
+
+static void ks_dw_pcie_msi_irq_unmask(struct irq_data *d)
+{
+       struct keystone_pcie *ks_pcie;
+       unsigned int irq = d->irq;
+       struct msi_desc *msi;
+       struct pcie_port *pp;
+       u32 offset;
+
+       msi = irq_get_msi_desc(irq);
+       pp = sys_to_pcie(msi->dev->bus->sysdata);
+       ks_pcie = to_keystone_pcie(pp);
+       offset = irq - irq_linear_revmap(pp->irq_domain, 0);
+
+       /* Mask the end point if PVM implemented */
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               if (msi->msi_attrib.maskbit)
+                       unmask_msi_irq(d);
+       }
+
+       ks_dw_pcie_msi_set_irq(pp, offset);
+}
+
+static struct irq_chip ks_dw_pcie_msi_irq_chip = {
+       .name = "Keystone-PCIe-MSI-IRQ",
+       .irq_ack = ks_dw_pcie_msi_irq_ack,
+       .irq_mask = ks_dw_pcie_msi_irq_mask,
+       .irq_unmask = ks_dw_pcie_msi_irq_unmask,
+};
+
+static int ks_dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
+                             irq_hw_number_t hwirq)
+{
+       irq_set_chip_and_handler(irq, &ks_dw_pcie_msi_irq_chip,
+                                handle_level_irq);
+       irq_set_chip_data(irq, domain->host_data);
+       set_irq_flags(irq, IRQF_VALID);
+
+       return 0;
+}
+
+const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = {
+       .map = ks_dw_pcie_msi_map,
+};
+
+int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_chip *chip)
+{
+       struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+       int i;
+
+       pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np,
+                                       MAX_MSI_IRQS,
+                                       &ks_dw_pcie_msi_domain_ops,
+                                       chip);
+       if (!pp->irq_domain) {
+               dev_err(pp->dev, "irq domain init failed\n");
+               return -ENXIO;
+       }
+
+       for (i = 0; i < MAX_MSI_IRQS; i++)
+               irq_create_mapping(pp->irq_domain, i);
+
+       return 0;
+}
+
+void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie)
+{
+       int i;
+
+       for (i = 0; i < MAX_LEGACY_IRQS; i++)
+               writel(0x1, ks_pcie->va_app_base + IRQ_ENABLE_SET + (i << 4));
+}
+
+void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset)
+{
+       struct pcie_port *pp = &ks_pcie->pp;
+       u32 pending;
+       int virq;
+
+       pending = readl(ks_pcie->va_app_base + IRQ_STATUS + (offset << 4));
+
+       if (BIT(0) & pending) {
+               virq = irq_linear_revmap(ks_pcie->legacy_irq_domain, offset);
+               dev_dbg(pp->dev, ": irq: irq_offset %d, virq %d\n", offset,
+                       virq);
+               generic_handle_irq(virq);
+       }
+
+       /* EOI the INTx interrupt */
+       writel(offset, ks_pcie->va_app_base + IRQ_EOI);
+}
+
+static void ks_dw_pcie_ack_legacy_irq(struct irq_data *d)
+{
+}
+
+static void ks_dw_pcie_mask_legacy_irq(struct irq_data *d)
+{
+}
+
+static void ks_dw_pcie_unmask_legacy_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip ks_dw_pcie_legacy_irq_chip = {
+       .name = "Keystone-PCI-Legacy-IRQ",
+       .irq_ack = ks_dw_pcie_ack_legacy_irq,
+       .irq_mask = ks_dw_pcie_mask_legacy_irq,
+       .irq_unmask = ks_dw_pcie_unmask_legacy_irq,
+};
+
+static int ks_dw_pcie_init_legacy_irq_map(struct irq_domain *d,
+                               unsigned int irq, irq_hw_number_t hw_irq)
+{
+       irq_set_chip_and_handler(irq, &ks_dw_pcie_legacy_irq_chip,
+                                handle_level_irq);
+       irq_set_chip_data(irq, d->host_data);
+       set_irq_flags(irq, IRQF_VALID);
+
+       return 0;
+}
+
+static const struct irq_domain_ops ks_dw_pcie_legacy_irq_domain_ops = {
+       .map = ks_dw_pcie_init_legacy_irq_map,
+       .xlate = irq_domain_xlate_onetwocell,
+};
+
+/**
+ * ks_dw_pcie_set_dbi_mode() - Set DBI mode to access overlaid BAR mask
+ * registers
+ *
+ * Since modification of dbi_cs2 involves different clock domain, read the
+ * status back to ensure the transition is complete.
+ */
+static void ks_dw_pcie_set_dbi_mode(void __iomem *reg_virt)
+{
+       u32 val;
+
+       writel(DBI_CS2_EN_VAL | readl(reg_virt + CMD_STATUS),
+              reg_virt + CMD_STATUS);
+
+       do {
+               val = readl(reg_virt + CMD_STATUS);
+       } while (!(val & DBI_CS2_EN_VAL));
+}
+
+/**
+ * ks_dw_pcie_clear_dbi_mode() - Disable DBI mode
+ *
+ * Since modification of dbi_cs2 involves different clock domain, read the
+ * status back to ensure the transition is complete.
+ */
+static void ks_dw_pcie_clear_dbi_mode(void __iomem *reg_virt)
+{
+       u32 val;
+
+       writel(~DBI_CS2_EN_VAL & readl(reg_virt + CMD_STATUS),
+                    reg_virt + CMD_STATUS);
+
+       do {
+               val = readl(reg_virt + CMD_STATUS);
+       } while (val & DBI_CS2_EN_VAL);
+}
+
+void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
+{
+       struct pcie_port *pp = &ks_pcie->pp;
+       u32 start = pp->mem.start, end = pp->mem.end;
+       int i, tr_size;
+
+       /* Disable BARs for inbound access */
+       ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base);
+       writel(0, pp->dbi_base + PCI_BASE_ADDRESS_0);
+       writel(0, pp->dbi_base + PCI_BASE_ADDRESS_1);
+       ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base);
+
+       /* Set outbound translation size per window division */
+       writel(CFG_PCIM_WIN_SZ_IDX & 0x7, ks_pcie->va_app_base + OB_SIZE);
+
+       tr_size = (1 << (CFG_PCIM_WIN_SZ_IDX & 0x7)) * SZ_1M;
+
+       /* Using Direct 1:1 mapping of RC <-> PCI memory space */
+       for (i = 0; (i < CFG_PCIM_WIN_CNT) && (start < end); i++) {
+               writel(start | 1, ks_pcie->va_app_base + OB_OFFSET_INDEX(i));
+               writel(0, ks_pcie->va_app_base + OB_OFFSET_HI(i));
+               start += tr_size;
+       }
+
+       /* Enable OB translation */
+       writel(OB_XLAT_EN_VAL | readl(ks_pcie->va_app_base + CMD_STATUS),
+              ks_pcie->va_app_base + CMD_STATUS);
+}
+
+/**
+ * ks_pcie_cfg_setup() - Set up configuration space address for a device
+ *
+ * @ks_pcie: ptr to keystone_pcie structure
+ * @bus: Bus number the device is residing on
+ * @devfn: device, function number info
+ *
+ * Forms and returns the address of configuration space mapped in PCIESS
+ * address space 0.  Also configures CFG_SETUP for remote configuration space
+ * access.
+ *
+ * The address space has two regions to access configuration - local and remote.
+ * We access local region for bus 0 (as RC is attached on bus 0) and remote
+ * region for others with TYPE 1 access when bus > 1.  As for device on bus = 1,
+ * we will do TYPE 0 access as it will be on our secondary bus (logical).
+ * CFG_SETUP is needed only for remote configuration access.
+ */
+static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus,
+                                      unsigned int devfn)
+{
+       u8 device = PCI_SLOT(devfn), function = PCI_FUNC(devfn);
+       struct pcie_port *pp = &ks_pcie->pp;
+       u32 regval;
+
+       if (bus == 0)
+               return pp->dbi_base;
+
+       regval = (bus << 16) | (device << 8) | function;
+
+       /*
+        * Since Bus#1 will be a virtual bus, we need to have TYPE0
+        * access only.
+        * TYPE 1
+        */
+       if (bus != 1)
+               regval |= BIT(24);
+
+       writel(regval, ks_pcie->va_app_base + CFG_SETUP);
+       return pp->va_cfg0_base;
+}
+
+int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+                            unsigned int devfn, int where, int size, u32 *val)
+{
+       struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+       u8 bus_num = bus->number;
+       void __iomem *addr;
+
+       addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
+
+       return dw_pcie_cfg_read(addr + (where & ~0x3), where, size, val);
+}
+
+int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+                            unsigned int devfn, int where, int size, u32 val)
+{
+       struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+       u8 bus_num = bus->number;
+       void __iomem *addr;
+
+       addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
+
+       return dw_pcie_cfg_write(addr + (where & ~0x3), where, size, val);
+}
+
+/**
+ * ks_dw_pcie_v3_65_scan_bus() - keystone scan_bus post initialization
+ *
+ * This sets BAR0 to enable inbound access for MSI_IRQ register
+ */
+void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
+{
+       struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+
+       /* Configure and set up BAR0 */
+       ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base);
+
+       /* Enable BAR0 */
+       writel(1, pp->dbi_base + PCI_BASE_ADDRESS_0);
+       writel(SZ_4K - 1, pp->dbi_base + PCI_BASE_ADDRESS_0);
+
+       ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base);
+
+        /*
+         * For BAR0, just setting bus address for inbound writes (MSI) should
+         * be sufficient.  Use physical address to avoid any conflicts.
+         */
+       writel(ks_pcie->app.start, pp->dbi_base + PCI_BASE_ADDRESS_0);
+}
+
+/**
+ * ks_dw_pcie_link_up() - Check if link up
+ */
+int ks_dw_pcie_link_up(struct pcie_port *pp)
+{
+       u32 val = readl(pp->dbi_base + DEBUG0);
+
+       return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0;
+}
+
+void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie)
+{
+       u32 val;
+
+       /* Disable Link training */
+       val = readl(ks_pcie->va_app_base + CMD_STATUS);
+       val &= ~LTSSM_EN_VAL;
+       writel(LTSSM_EN_VAL | val,  ks_pcie->va_app_base + CMD_STATUS);
+
+       /* Initiate Link Training */
+       val = readl(ks_pcie->va_app_base + CMD_STATUS);
+       writel(LTSSM_EN_VAL | val,  ks_pcie->va_app_base + CMD_STATUS);
+}
+
+/**
+ * ks_dw_pcie_host_init() - initialize host for v3_65 dw hardware
+ *
+ * Ioremap the register resources, initialize legacy irq domain
+ * and call dw_pcie_v3_65_host_init() API to initialize the Keystone
+ * PCI host controller.
+ */
+int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
+                               struct device_node *msi_intc_np)
+{
+       struct pcie_port *pp = &ks_pcie->pp;
+       struct platform_device *pdev = to_platform_device(pp->dev);
+       struct resource *res;
+
+       /* Index 0 is the config reg. space address */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       pp->dbi_base = devm_ioremap_resource(pp->dev, res);
+       if (IS_ERR(pp->dbi_base))
+               return PTR_ERR(pp->dbi_base);
+
+       /*
+        * We set these same and is used in pcie rd/wr_other_conf
+        * functions
+        */
+       pp->va_cfg0_base = pp->dbi_base + SPACE0_REMOTE_CFG_OFFSET;
+       pp->va_cfg1_base = pp->va_cfg0_base;
+
+       /* Index 1 is the application reg. space address */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       ks_pcie->app = *res;
+       ks_pcie->va_app_base = devm_ioremap_resource(pp->dev, res);
+       if (IS_ERR(ks_pcie->va_app_base))
+               return PTR_ERR(ks_pcie->va_app_base);
+
+       /* Create legacy IRQ domain */
+       ks_pcie->legacy_irq_domain =
+                       irq_domain_add_linear(ks_pcie->legacy_intc_np,
+                                       MAX_LEGACY_IRQS,
+                                       &ks_dw_pcie_legacy_irq_domain_ops,
+                                       NULL);
+       if (!ks_pcie->legacy_irq_domain) {
+               dev_err(pp->dev, "Failed to add irq domain for legacy irqs\n");
+               return -EINVAL;
+       }
+
+       return dw_pcie_host_init(pp);
+}
diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/host/pci-keystone.c
new file mode 100644 (file)
index 0000000..1b893bc
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * PCIe host controller driver for Texas Instruments Keystone SoCs
+ *
+ * Copyright (C) 2013-2014 Texas Instruments., Ltd.
+ *             http://www.ti.com
+ *
+ * Author: Murali Karicheri <m-karicheri2@ti.com>
+ * Implementation based on pci-exynos.c and pcie-designware.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/irqchip/chained_irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/of_pci.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+
+#include "pcie-designware.h"
+#include "pci-keystone.h"
+
+#define DRIVER_NAME    "keystone-pcie"
+
+/* driver specific constants */
+#define MAX_MSI_HOST_IRQS              8
+#define MAX_LEGACY_HOST_IRQS           4
+
+/* DEV_STAT_CTRL */
+#define PCIE_CAP_BASE          0x70
+
+/* PCIE controller device IDs */
+#define PCIE_RC_K2HK           0xb008
+#define PCIE_RC_K2E            0xb009
+#define PCIE_RC_K2L            0xb00a
+
+#define to_keystone_pcie(x)    container_of(x, struct keystone_pcie, pp)
+
+static void quirk_limit_mrrs(struct pci_dev *dev)
+{
+       struct pci_bus *bus = dev->bus;
+       struct pci_dev *bridge = bus->self;
+       static const struct pci_device_id rc_pci_devids[] = {
+               { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2HK),
+                .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
+               { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2E),
+                .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
+               { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2L),
+                .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
+               { 0, },
+       };
+
+       if (pci_is_root_bus(bus))
+               return;
+
+       /* look for the host bridge */
+       while (!pci_is_root_bus(bus)) {
+               bridge = bus->self;
+               bus = bus->parent;
+       }
+
+       if (bridge) {
+               /*
+                * Keystone PCI controller has a h/w limitation of
+                * 256 bytes maximum read request size.  It can't handle
+                * anything higher than this.  So force this limit on
+                * all downstream devices.
+                */
+               if (pci_match_id(rc_pci_devids, bridge)) {
+                       if (pcie_get_readrq(dev) > 256) {
+                               dev_info(&dev->dev, "limiting MRRS to 256\n");
+                               pcie_set_readrq(dev, 256);
+                       }
+               }
+       }
+}
+DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs);
+
+static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
+{
+       struct pcie_port *pp = &ks_pcie->pp;
+       int count = 200;
+
+       dw_pcie_setup_rc(pp);
+
+       if (dw_pcie_link_up(pp)) {
+               dev_err(pp->dev, "Link already up\n");
+               return 0;
+       }
+
+       ks_dw_pcie_initiate_link_train(ks_pcie);
+       /* check if the link is up or not */
+       while (!dw_pcie_link_up(pp)) {
+               usleep_range(100, 1000);
+               if (--count) {
+                       ks_dw_pcie_initiate_link_train(ks_pcie);
+                       continue;
+               }
+               dev_err(pp->dev, "phy link never came up\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void ks_pcie_msi_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+       struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
+       u32 offset = irq - ks_pcie->msi_host_irqs[0];
+       struct pcie_port *pp = &ks_pcie->pp;
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+
+       dev_dbg(pp->dev, "ks_pci_msi_irq_handler, irq %d\n", irq);
+
+       /*
+        * The chained irq handler installation would have replaced normal
+        * interrupt driver handler so we need to take care of mask/unmask and
+        * ack operation.
+        */
+       chained_irq_enter(chip, desc);
+       ks_dw_pcie_handle_msi_irq(ks_pcie, offset);
+       chained_irq_exit(chip, desc);
+}
+
+/**
+ * ks_pcie_legacy_irq_handler() - Handle legacy interrupt
+ * @irq: IRQ line for legacy interrupts
+ * @desc: Pointer to irq descriptor
+ *
+ * Traverse through pending legacy interrupts and invoke handler for each. Also
+ * takes care of interrupt controller level mask/ack operation.
+ */
+static void ks_pcie_legacy_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+       struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
+       struct pcie_port *pp = &ks_pcie->pp;
+       u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0];
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+
+       dev_dbg(pp->dev, ": Handling legacy irq %d\n", irq);
+
+       /*
+        * The chained irq handler installation would have replaced normal
+        * interrupt driver handler so we need to take care of mask/unmask and
+        * ack operation.
+        */
+       chained_irq_enter(chip, desc);
+       ks_dw_pcie_handle_legacy_irq(ks_pcie, irq_offset);
+       chained_irq_exit(chip, desc);
+}
+
+static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
+                                          char *controller, int *num_irqs)
+{
+       int temp, max_host_irqs, legacy = 1, *host_irqs, ret = -EINVAL;
+       struct device *dev = ks_pcie->pp.dev;
+       struct device_node *np_pcie = dev->of_node, **np_temp;
+
+       if (!strcmp(controller, "msi-interrupt-controller"))
+               legacy = 0;
+
+       if (legacy) {
+               np_temp = &ks_pcie->legacy_intc_np;
+               max_host_irqs = MAX_LEGACY_HOST_IRQS;
+               host_irqs = &ks_pcie->legacy_host_irqs[0];
+       } else {
+               np_temp = &ks_pcie->msi_intc_np;
+               max_host_irqs = MAX_MSI_HOST_IRQS;
+               host_irqs =  &ks_pcie->msi_host_irqs[0];
+       }
+
+       /* interrupt controller is in a child node */
+       *np_temp = of_find_node_by_name(np_pcie, controller);
+       if (!(*np_temp)) {
+               dev_err(dev, "Node for %s is absent\n", controller);
+               goto out;
+       }
+       temp = of_irq_count(*np_temp);
+       if (!temp)
+               goto out;
+       if (temp > max_host_irqs)
+               dev_warn(dev, "Too many %s interrupts defined %u\n",
+                       (legacy ? "legacy" : "MSI"), temp);
+
+       /*
+        * support upto max_host_irqs. In dt from index 0 to 3 (legacy) or 0 to
+        * 7 (MSI)
+        */
+       for (temp = 0; temp < max_host_irqs; temp++) {
+               host_irqs[temp] = irq_of_parse_and_map(*np_temp, temp);
+               if (host_irqs[temp] < 0)
+                       break;
+       }
+       if (temp) {
+               *num_irqs = temp;
+               ret = 0;
+       }
+out:
+       return ret;
+}
+
+static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie)
+{
+       int i;
+
+       /* Legacy IRQ */
+       for (i = 0; i < ks_pcie->num_legacy_host_irqs; i++) {
+               irq_set_handler_data(ks_pcie->legacy_host_irqs[i], ks_pcie);
+               irq_set_chained_handler(ks_pcie->legacy_host_irqs[i],
+                                       ks_pcie_legacy_irq_handler);
+       }
+       ks_dw_pcie_enable_legacy_irqs(ks_pcie);
+
+       /* MSI IRQ */
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               for (i = 0; i < ks_pcie->num_msi_host_irqs; i++) {
+                       irq_set_chained_handler(ks_pcie->msi_host_irqs[i],
+                                               ks_pcie_msi_irq_handler);
+                       irq_set_handler_data(ks_pcie->msi_host_irqs[i],
+                                            ks_pcie);
+               }
+       }
+}
+
+/*
+ * When a PCI device does not exist during config cycles, keystone host gets a
+ * bus error instead of returning 0xffffffff. This handler always returns 0
+ * for this kind of faults.
+ */
+static int keystone_pcie_fault(unsigned long addr, unsigned int fsr,
+                               struct pt_regs *regs)
+{
+       unsigned long instr = *(unsigned long *) instruction_pointer(regs);
+
+       if ((instr & 0x0e100090) == 0x00100090) {
+               int reg = (instr >> 12) & 15;
+
+               regs->uregs[reg] = -1;
+               regs->ARM_pc += 4;
+       }
+
+       return 0;
+}
+
+static void __init ks_pcie_host_init(struct pcie_port *pp)
+{
+       struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+       u32 val;
+
+       ks_pcie_establish_link(ks_pcie);
+       ks_dw_pcie_setup_rc_app_regs(ks_pcie);
+       ks_pcie_setup_interrupts(ks_pcie);
+       writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8),
+                       pp->dbi_base + PCI_IO_BASE);
+
+       /* update the Vendor ID */
+       writew(ks_pcie->device_id, pp->dbi_base + PCI_DEVICE_ID);
+
+       /* update the DEV_STAT_CTRL to publish right mrrs */
+       val = readl(pp->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL);
+       val &= ~PCI_EXP_DEVCTL_READRQ;
+       /* set the mrrs to 256 bytes */
+       val |= BIT(12);
+       writel(val, pp->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL);
+
+       /*
+        * PCIe access errors that result into OCP errors are caught by ARM as
+        * "External aborts"
+        */
+       hook_fault_code(17, keystone_pcie_fault, SIGBUS, 0,
+                       "Asynchronous external abort");
+}
+
+static struct pcie_host_ops keystone_pcie_host_ops = {
+       .rd_other_conf = ks_dw_pcie_rd_other_conf,
+       .wr_other_conf = ks_dw_pcie_wr_other_conf,
+       .link_up = ks_dw_pcie_link_up,
+       .host_init = ks_pcie_host_init,
+       .msi_set_irq = ks_dw_pcie_msi_set_irq,
+       .msi_clear_irq = ks_dw_pcie_msi_clear_irq,
+       .get_msi_addr = ks_dw_pcie_get_msi_addr,
+       .msi_host_init = ks_dw_pcie_msi_host_init,
+       .scan_bus = ks_dw_pcie_v3_65_scan_bus,
+};
+
+static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
+                        struct platform_device *pdev)
+{
+       struct pcie_port *pp = &ks_pcie->pp;
+       int ret;
+
+       ret = ks_pcie_get_irq_controller_info(ks_pcie,
+                                       "legacy-interrupt-controller",
+                                       &ks_pcie->num_legacy_host_irqs);
+       if (ret)
+               return ret;
+
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               ret = ks_pcie_get_irq_controller_info(ks_pcie,
+                                               "msi-interrupt-controller",
+                                               &ks_pcie->num_msi_host_irqs);
+               if (ret)
+                       return ret;
+       }
+
+       pp->root_bus_nr = -1;
+       pp->ops = &keystone_pcie_host_ops;
+       ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to initialize host\n");
+               return ret;
+       }
+
+       return ret;
+}
+
+static const struct of_device_id ks_pcie_of_match[] = {
+       {
+               .type = "pci",
+               .compatible = "ti,keystone-pcie",
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, ks_pcie_of_match);
+
+static int __exit ks_pcie_remove(struct platform_device *pdev)
+{
+       struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(ks_pcie->clk);
+
+       return 0;
+}
+
+static int __init ks_pcie_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct keystone_pcie *ks_pcie;
+       struct pcie_port *pp;
+       struct resource *res;
+       void __iomem *reg_p;
+       struct phy *phy;
+       int ret = 0;
+
+       ks_pcie = devm_kzalloc(&pdev->dev, sizeof(*ks_pcie),
+                               GFP_KERNEL);
+       if (!ks_pcie) {
+               dev_err(dev, "no memory for keystone pcie\n");
+               return -ENOMEM;
+       }
+       pp = &ks_pcie->pp;
+
+       /* initialize SerDes Phy if present */
+       phy = devm_phy_get(dev, "pcie-phy");
+       if (!IS_ERR_OR_NULL(phy)) {
+               ret = phy_init(phy);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* index 2 is to read PCI DEVICE_ID */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+       reg_p = devm_ioremap_resource(dev, res);
+       if (IS_ERR(reg_p))
+               return PTR_ERR(reg_p);
+       ks_pcie->device_id = readl(reg_p) >> 16;
+       devm_iounmap(dev, reg_p);
+       devm_release_mem_region(dev, res->start, resource_size(res));
+
+       pp->dev = dev;
+       platform_set_drvdata(pdev, ks_pcie);
+       ks_pcie->clk = devm_clk_get(dev, "pcie");
+       if (IS_ERR(ks_pcie->clk)) {
+               dev_err(dev, "Failed to get pcie rc clock\n");
+               return PTR_ERR(ks_pcie->clk);
+       }
+       ret = clk_prepare_enable(ks_pcie->clk);
+       if (ret)
+               return ret;
+
+       ret = ks_add_pcie_port(ks_pcie, pdev);
+       if (ret < 0)
+               goto fail_clk;
+
+       return 0;
+fail_clk:
+       clk_disable_unprepare(ks_pcie->clk);
+
+       return ret;
+}
+
+static struct platform_driver ks_pcie_driver __refdata = {
+       .probe  = ks_pcie_probe,
+       .remove = __exit_p(ks_pcie_remove),
+       .driver = {
+               .name   = "keystone-pcie",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(ks_pcie_of_match),
+       },
+};
+
+module_platform_driver(ks_pcie_driver);
+
+MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>");
+MODULE_DESCRIPTION("Keystone PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-keystone.h b/drivers/pci/host/pci-keystone.h
new file mode 100644 (file)
index 0000000..1fc1fce
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Keystone PCI Controller's common includes
+ *
+ * Copyright (C) 2013-2014 Texas Instruments., Ltd.
+ *             http://www.ti.com
+ *
+ * Author: Murali Karicheri <m-karicheri2@ti.com>
+ *
+ *
+ * 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 MAX_LEGACY_IRQS                        4
+#define MAX_MSI_HOST_IRQS              8
+#define MAX_LEGACY_HOST_IRQS           4
+
+struct keystone_pcie {
+       struct  clk             *clk;
+       struct  pcie_port       pp;
+       /* PCI Device ID */
+       u32                     device_id;
+       int                     num_legacy_host_irqs;
+       int                     legacy_host_irqs[MAX_LEGACY_HOST_IRQS];
+       struct                  device_node *legacy_intc_np;
+
+       int                     num_msi_host_irqs;
+       int                     msi_host_irqs[MAX_MSI_HOST_IRQS];
+       struct                  device_node *msi_intc_np;
+       struct irq_domain       *legacy_irq_domain;
+
+       /* Application register space */
+       void __iomem            *va_app_base;
+       struct resource         app;
+};
+
+/* Keystone DW specific MSI controller APIs/definitions */
+void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset);
+u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
+
+/* Keystone specific PCI controller APIs */
+void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie);
+void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset);
+int  ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
+                       struct device_node *msi_intc_np);
+int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+               unsigned int devfn, int where, int size, u32 val);
+int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+               unsigned int devfn, int where, int size, u32 *val);
+void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie);
+int ks_dw_pcie_link_up(struct pcie_port *pp);
+void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie);
+void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq);
+void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq);
+void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp);
+int ks_dw_pcie_msi_host_init(struct pcie_port *pp,
+               struct msi_chip *chip);
index a8c6f1a92e0f39435780cc9b3bfb96842ebb531b..b1315e197ffba3701fee63a68828895174152b8e 100644 (file)
@@ -873,7 +873,7 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn,
        rangesz = pna + na + ns;
        nranges = rlen / sizeof(__be32) / rangesz;
 
-       for (i = 0; i < nranges; i++) {
+       for (i = 0; i < nranges; i++, range += rangesz) {
                u32 flags = of_read_number(range, 1);
                u32 slot = of_read_number(range + 1, 1);
                u64 cpuaddr = of_read_number(range + na, pna);
@@ -883,14 +883,14 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn,
                        rtype = IORESOURCE_IO;
                else if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_MEM32)
                        rtype = IORESOURCE_MEM;
+               else
+                       continue;
 
                if (slot == PCI_SLOT(devfn) && type == rtype) {
                        *tgt = DT_CPUADDR_TO_TARGET(cpuaddr);
                        *attr = DT_CPUADDR_TO_ATTR(cpuaddr);
                        return 0;
                }
-
-               range += rangesz;
        }
 
        return -ENOENT;
index 0fb0fdb223d5174d6a2e10e750a74194d27a4db6..3d43874319bebb13999889522f8d85e61b7251a2 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/of_pci.h>
 #include <linux/of_platform.h>
 #include <linux/pci.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/sizes.h>
 
 #define AFI_INTR_CODE                  0xb8
 #define  AFI_INTR_CODE_MASK            0xf
-#define  AFI_INTR_AXI_SLAVE_ERROR      1
-#define  AFI_INTR_AXI_DECODE_ERROR     2
+#define  AFI_INTR_INI_SLAVE_ERROR      1
+#define  AFI_INTR_INI_DECODE_ERROR     2
 #define  AFI_INTR_TARGET_ABORT         3
 #define  AFI_INTR_MASTER_ABORT         4
 #define  AFI_INTR_INVALID_WRITE                5
 #define  AFI_INTR_LEGACY               6
 #define  AFI_INTR_FPCI_DECODE_ERROR    7
+#define  AFI_INTR_AXI_DECODE_ERROR     8
+#define  AFI_INTR_FPCI_TIMEOUT         9
+#define  AFI_INTR_PE_PRSNT_SENSE       10
+#define  AFI_INTR_PE_CLKREQ_SENSE      11
+#define  AFI_INTR_CLKCLAMP_SENSE       12
+#define  AFI_INTR_RDY4PD_SENSE         13
+#define  AFI_INTR_P2P_ERROR            14
 
 #define AFI_INTR_SIGNATURE     0xbc
 #define AFI_UPPER_FPCI_ADDRESS 0xc0
 #define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK      (0xf << 20)
 #define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE    (0x0 << 20)
 #define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420       (0x0 << 20)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1     (0x0 << 20)
 #define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL      (0x1 << 20)
 #define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222       (0x1 << 20)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1     (0x1 << 20)
 #define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411       (0x2 << 20)
 
 #define AFI_FUSE                       0x104
 #define  AFI_PEX_CTRL_RST              (1 << 0)
 #define  AFI_PEX_CTRL_CLKREQ_EN                (1 << 1)
 #define  AFI_PEX_CTRL_REFCLK_EN                (1 << 3)
+#define  AFI_PEX_CTRL_OVERRIDE_EN      (1 << 4)
+
+#define AFI_PLLE_CONTROL               0x160
+#define  AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9)
+#define  AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1)
 
 #define AFI_PEXBIAS_CTRL_0             0x168
 
 #define RP_VEND_XP     0x00000F00
 #define  RP_VEND_XP_DL_UP      (1 << 30)
 
+#define RP_PRIV_MISC   0x00000FE0
+#define  RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0)
+#define  RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0)
+
 #define RP_LINK_CONTROL_STATUS                 0x00000090
 #define  RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000
 #define  RP_LINK_CONTROL_STATUS_LINKSTAT_MASK  0x3fff0000
 
 #define PADS_REFCLK_CFG0                       0x000000C8
 #define PADS_REFCLK_CFG1                       0x000000CC
+#define PADS_REFCLK_BIAS                       0x000000D0
 
 /*
  * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit
@@ -236,6 +256,7 @@ struct tegra_pcie_soc_data {
        bool has_pex_bias_ctrl;
        bool has_intr_prsnt_sense;
        bool has_cml_clk;
+       bool has_gen2;
 };
 
 static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip)
@@ -253,6 +274,7 @@ struct tegra_pcie {
        struct list_head buses;
        struct resource *cs;
 
+       struct resource all;
        struct resource io;
        struct resource mem;
        struct resource prefetch;
@@ -267,6 +289,8 @@ struct tegra_pcie {
        struct reset_control *afi_rst;
        struct reset_control *pcie_xrst;
 
+       struct phy *phy;
+
        struct tegra_msi msi;
 
        struct list_head ports;
@@ -382,7 +406,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
        for (i = 0; i < 16; i++) {
                unsigned long virt = (unsigned long)bus->area->addr +
                                     i * SZ_64K;
-               phys_addr_t phys = cs + i * SZ_1M + busnr * SZ_64K;
+               phys_addr_t phys = cs + i * SZ_16M + busnr * SZ_64K;
 
                err = ioremap_page_range(virt, virt + SZ_64K, phys, prot);
                if (err < 0) {
@@ -561,6 +585,8 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
        if (soc->has_pex_clkreq_en)
                value |= AFI_PEX_CTRL_CLKREQ_EN;
 
+       value |= AFI_PEX_CTRL_OVERRIDE_EN;
+
        afi_writel(port->pcie, value, ctrl);
 
        tegra_pcie_port_reset(port);
@@ -568,6 +594,7 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
 
 static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
 {
+       const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
        unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
        unsigned long value;
 
@@ -578,6 +605,10 @@ static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
 
        /* disable reference clock */
        value = afi_readl(port->pcie, ctrl);
+
+       if (soc->has_pex_clkreq_en)
+               value &= ~AFI_PEX_CTRL_CLKREQ_EN;
+
        value &= ~AFI_PEX_CTRL_REFCLK_EN;
        afi_writel(port->pcie, value, ctrl);
 }
@@ -626,13 +657,25 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
 static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
 {
        struct tegra_pcie *pcie = sys_to_pcie(sys);
+       int err;
+       phys_addr_t io_start;
+
+       err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem);
+       if (err < 0)
+               return err;
+
+       err = devm_request_resource(pcie->dev, &pcie->all, &pcie->prefetch);
+       if (err)
+               return err;
+
+       io_start = pci_pio_to_address(pcie->io.start);
 
        pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
        pci_add_resource_offset(&sys->resources, &pcie->prefetch,
                                sys->mem_offset);
        pci_add_resource(&sys->resources, &pcie->busn);
 
-       pci_ioremap_io(nr * SZ_64K, pcie->io.start);
+       pci_ioremap_io(nr * SZ_64K, io_start);
 
        return 1;
 }
@@ -684,9 +727,15 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
                "Target abort",
                "Master abort",
                "Invalid write",
+               "Legacy interrupt",
                "Response decoding error",
                "AXI response decoding error",
                "Transaction timeout",
+               "Slot present pin change",
+               "Slot clock request change",
+               "TMS clock ramp change",
+               "TMS ready for power down",
+               "Peer2Peer error",
        };
        struct tegra_pcie *pcie = arg;
        u32 code, signature;
@@ -737,6 +786,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
 static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
 {
        u32 fpci_bar, size, axi_address;
+       phys_addr_t io_start = pci_pio_to_address(pcie->io.start);
 
        /* Bar 0: type 1 extended configuration space */
        fpci_bar = 0xfe100000;
@@ -749,7 +799,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
        /* Bar 1: downstream IO bar */
        fpci_bar = 0xfdfc0000;
        size = resource_size(&pcie->io);
-       axi_address = pcie->io.start;
+       axi_address = io_start;
        afi_writel(pcie, axi_address, AFI_AXI_BAR1_START);
        afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ);
        afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1);
@@ -792,30 +842,27 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
        afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
 }
 
-static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
+static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout)
 {
        const struct tegra_pcie_soc_data *soc = pcie->soc_data;
-       struct tegra_pcie_port *port;
-       unsigned int timeout;
-       unsigned long value;
+       u32 value;
 
-       /* power down PCIe slot clock bias pad */
-       if (soc->has_pex_bias_ctrl)
-               afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
+       timeout = jiffies + msecs_to_jiffies(timeout);
 
-       /* configure mode and disable all ports */
-       value = afi_readl(pcie, AFI_PCIE_CONFIG);
-       value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
-       value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
-
-       list_for_each_entry(port, &pcie->ports, list)
-               value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
+       while (time_before(jiffies, timeout)) {
+               value = pads_readl(pcie, soc->pads_pll_ctl);
+               if (value & PADS_PLL_CTL_LOCKDET)
+                       return 0;
+       }
 
-       afi_writel(pcie, value, AFI_PCIE_CONFIG);
+       return -ETIMEDOUT;
+}
 
-       value = afi_readl(pcie, AFI_FUSE);
-       value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
-       afi_writel(pcie, value, AFI_FUSE);
+static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
+{
+       const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+       u32 value;
+       int err;
 
        /* initialize internal PHY, enable up to 16 PCIE lanes */
        pads_writel(pcie, 0x0, PADS_CTL_SEL);
@@ -834,6 +881,13 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
        value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel;
        pads_writel(pcie, value, soc->pads_pll_ctl);
 
+       /* reset PLL */
+       value = pads_readl(pcie, soc->pads_pll_ctl);
+       value &= ~PADS_PLL_CTL_RST_B4SM;
+       pads_writel(pcie, value, soc->pads_pll_ctl);
+
+       usleep_range(20, 100);
+
        /* take PLL out of reset  */
        value = pads_readl(pcie, soc->pads_pll_ctl);
        value |= PADS_PLL_CTL_RST_B4SM;
@@ -846,15 +900,11 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
                pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1);
 
        /* wait for the PLL to lock */
-       timeout = 300;
-       do {
-               value = pads_readl(pcie, soc->pads_pll_ctl);
-               usleep_range(1000, 2000);
-               if (--timeout == 0) {
-                       pr_err("Tegra PCIe error: timeout waiting for PLL\n");
-                       return -EBUSY;
-               }
-       } while (!(value & PADS_PLL_CTL_LOCKDET));
+       err = tegra_pcie_pll_wait(pcie, 500);
+       if (err < 0) {
+               dev_err(pcie->dev, "PLL failed to lock: %d\n", err);
+               return err;
+       }
 
        /* turn off IDDQ override */
        value = pads_readl(pcie, PADS_CTL);
@@ -866,6 +916,58 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
        value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L;
        pads_writel(pcie, value, PADS_CTL);
 
+       return 0;
+}
+
+static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
+{
+       const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+       struct tegra_pcie_port *port;
+       unsigned long value;
+       int err;
+
+       /* enable PLL power down */
+       if (pcie->phy) {
+               value = afi_readl(pcie, AFI_PLLE_CONTROL);
+               value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL;
+               value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN;
+               afi_writel(pcie, value, AFI_PLLE_CONTROL);
+       }
+
+       /* power down PCIe slot clock bias pad */
+       if (soc->has_pex_bias_ctrl)
+               afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
+
+       /* configure mode and disable all ports */
+       value = afi_readl(pcie, AFI_PCIE_CONFIG);
+       value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
+       value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
+
+       list_for_each_entry(port, &pcie->ports, list)
+               value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
+
+       afi_writel(pcie, value, AFI_PCIE_CONFIG);
+
+       if (soc->has_gen2) {
+               value = afi_readl(pcie, AFI_FUSE);
+               value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS;
+               afi_writel(pcie, value, AFI_FUSE);
+       } else {
+               value = afi_readl(pcie, AFI_FUSE);
+               value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
+               afi_writel(pcie, value, AFI_FUSE);
+       }
+
+       if (!pcie->phy)
+               err = tegra_pcie_phy_enable(pcie);
+       else
+               err = phy_power_on(pcie->phy);
+
+       if (err < 0) {
+               dev_err(pcie->dev, "failed to power on PHY: %d\n", err);
+               return err;
+       }
+
        /* take the PCIe interface module out of reset */
        reset_control_deassert(pcie->pcie_xrst);
 
@@ -899,6 +1001,10 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
 
        /* TODO: disable and unprepare clocks? */
 
+       err = phy_power_off(pcie->phy);
+       if (err < 0)
+               dev_warn(pcie->dev, "failed to power off PHY: %d\n", err);
+
        reset_control_assert(pcie->pcie_xrst);
        reset_control_assert(pcie->afi_rst);
        reset_control_assert(pcie->pex_rst);
@@ -1020,6 +1126,19 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
                return err;
        }
 
+       pcie->phy = devm_phy_optional_get(pcie->dev, "pcie");
+       if (IS_ERR(pcie->phy)) {
+               err = PTR_ERR(pcie->phy);
+               dev_err(&pdev->dev, "failed to get PHY: %d\n", err);
+               return err;
+       }
+
+       err = phy_init(pcie->phy);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to initialize PHY: %d\n", err);
+               return err;
+       }
+
        err = tegra_pcie_power_on(pcie);
        if (err) {
                dev_err(&pdev->dev, "failed to power up: %d\n", err);
@@ -1078,10 +1197,17 @@ poweroff:
 
 static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
 {
+       int err;
+
        if (pcie->irq > 0)
                free_irq(pcie->irq, pcie);
 
        tegra_pcie_power_off(pcie);
+
+       err = phy_exit(pcie->phy);
+       if (err < 0)
+               dev_err(pcie->dev, "failed to teardown PHY: %d\n", err);
+
        return 0;
 }
 
@@ -1170,8 +1296,10 @@ static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
                return hwirq;
 
        irq = irq_create_mapping(msi->domain, hwirq);
-       if (!irq)
+       if (!irq) {
+               tegra_msi_free(msi, hwirq);
                return -EINVAL;
+       }
 
        irq_set_msi_desc(irq, desc);
 
@@ -1189,8 +1317,10 @@ static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
 {
        struct tegra_msi *msi = to_tegra_msi(chip);
        struct irq_data *d = irq_get_irq_data(irq);
+       irq_hw_number_t hwirq = irqd_to_hwirq(d);
 
-       tegra_msi_free(msi, d->hwirq);
+       irq_dispose_mapping(irq);
+       tegra_msi_free(msi, hwirq);
 }
 
 static struct irq_chip tegra_msi_irq_chip = {
@@ -1327,7 +1457,19 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
 {
        struct device_node *np = pcie->dev->of_node;
 
-       if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
+       if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
+               switch (lanes) {
+               case 0x0000104:
+                       dev_info(pcie->dev, "4x1, 1x1 configuration\n");
+                       *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1;
+                       return 0;
+
+               case 0x0000102:
+                       dev_info(pcie->dev, "2x1, 1x1 configuration\n");
+                       *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1;
+                       return 0;
+               }
+       } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
                switch (lanes) {
                case 0x00000204:
                        dev_info(pcie->dev, "4x1, 2x1 configuration\n");
@@ -1435,7 +1577,23 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
        struct device_node *np = pcie->dev->of_node;
        unsigned int i = 0;
 
-       if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
+       if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
+               pcie->num_supplies = 7;
+
+               pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+                                             sizeof(*pcie->supplies),
+                                             GFP_KERNEL);
+               if (!pcie->supplies)
+                       return -ENOMEM;
+
+               pcie->supplies[i++].supply = "avddio-pex";
+               pcie->supplies[i++].supply = "dvddio-pex";
+               pcie->supplies[i++].supply = "avdd-pex-pll";
+               pcie->supplies[i++].supply = "hvdd-pex";
+               pcie->supplies[i++].supply = "hvdd-pex-pll-e";
+               pcie->supplies[i++].supply = "vddio-pex-ctl";
+               pcie->supplies[i++].supply = "avdd-pll-erefe";
+       } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
                bool need_pexa = false, need_pexb = false;
 
                /* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */
@@ -1514,32 +1672,50 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
        struct resource res;
        int err;
 
+       memset(&pcie->all, 0, sizeof(pcie->all));
+       pcie->all.flags = IORESOURCE_MEM;
+       pcie->all.name = np->full_name;
+       pcie->all.start = ~0;
+       pcie->all.end = 0;
+
        if (of_pci_range_parser_init(&parser, np)) {
                dev_err(pcie->dev, "missing \"ranges\" property\n");
                return -EINVAL;
        }
 
        for_each_of_pci_range(&parser, &range) {
-               of_pci_range_to_resource(&range, np, &res);
+               err = of_pci_range_to_resource(&range, np, &res);
+               if (err < 0)
+                       return err;
 
                switch (res.flags & IORESOURCE_TYPE_BITS) {
                case IORESOURCE_IO:
                        memcpy(&pcie->io, &res, sizeof(res));
-                       pcie->io.name = "I/O";
+                       pcie->io.name = np->full_name;
                        break;
 
                case IORESOURCE_MEM:
                        if (res.flags & IORESOURCE_PREFETCH) {
                                memcpy(&pcie->prefetch, &res, sizeof(res));
-                               pcie->prefetch.name = "PREFETCH";
+                               pcie->prefetch.name = "prefetchable";
                        } else {
                                memcpy(&pcie->mem, &res, sizeof(res));
-                               pcie->mem.name = "MEM";
+                               pcie->mem.name = "non-prefetchable";
                        }
                        break;
                }
+
+               if (res.start <= pcie->all.start)
+                       pcie->all.start = res.start;
+
+               if (res.end >= pcie->all.end)
+                       pcie->all.end = res.end;
        }
 
+       err = devm_request_resource(pcie->dev, &iomem_resource, &pcie->all);
+       if (err < 0)
+               return err;
+
        err = of_pci_parse_bus_range(np, &pcie->busn);
        if (err < 0) {
                dev_err(pcie->dev, "failed to parse ranges property: %d\n",
@@ -1641,6 +1817,12 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
        unsigned int retries = 3;
        unsigned long value;
 
+       /* override presence detection */
+       value = readl(port->base + RP_PRIV_MISC);
+       value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT;
+       value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT;
+       writel(value, port->base + RP_PRIV_MISC);
+
        do {
                unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
 
@@ -1721,6 +1903,7 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = {
        .has_pex_bias_ctrl = false,
        .has_intr_prsnt_sense = false,
        .has_cml_clk = false,
+       .has_gen2 = false,
 };
 
 static const struct tegra_pcie_soc_data tegra30_pcie_data = {
@@ -1732,9 +1915,23 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = {
        .has_pex_bias_ctrl = true,
        .has_intr_prsnt_sense = true,
        .has_cml_clk = true,
+       .has_gen2 = false,
+};
+
+static const struct tegra_pcie_soc_data tegra124_pcie_data = {
+       .num_ports = 2,
+       .msi_base_shift = 8,
+       .pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
+       .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
+       .has_pex_clkreq_en = true,
+       .has_pex_bias_ctrl = true,
+       .has_intr_prsnt_sense = true,
+       .has_cml_clk = true,
+       .has_gen2 = true,
 };
 
 static const struct of_device_id tegra_pcie_of_match[] = {
+       { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie_data },
        { .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data },
        { .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data },
        { },
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
new file mode 100644 (file)
index 0000000..9ecabfa
--- /dev/null
@@ -0,0 +1,659 @@
+/**
+ * APM X-Gene PCIe Driver
+ *
+ * Copyright (c) 2014 Applied Micro Circuits Corporation.
+ *
+ * Author: Tanmay Inamdar <tinamdar@apm.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.
+ *
+ */
+#include <linux/clk-private.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PCIECORE_CTLANDSTATUS          0x50
+#define PIM1_1L                                0x80
+#define IBAR2                          0x98
+#define IR2MSK                         0x9c
+#define PIM2_1L                                0xa0
+#define IBAR3L                         0xb4
+#define IR3MSKL                                0xbc
+#define PIM3_1L                                0xc4
+#define OMR1BARL                       0x100
+#define OMR2BARL                       0x118
+#define OMR3BARL                       0x130
+#define CFGBARL                                0x154
+#define CFGBARH                                0x158
+#define CFGCTL                         0x15c
+#define RTDID                          0x160
+#define BRIDGE_CFG_0                   0x2000
+#define BRIDGE_CFG_4                   0x2010
+#define BRIDGE_STATUS_0                        0x2600
+
+#define LINK_UP_MASK                   0x00000100
+#define AXI_EP_CFG_ACCESS              0x10000
+#define EN_COHERENCY                   0xF0000000
+#define EN_REG                         0x00000001
+#define OB_LO_IO                       0x00000002
+#define XGENE_PCIE_VENDORID            0x10E8
+#define XGENE_PCIE_DEVICEID            0xE004
+#define SZ_1T                          (SZ_1G*1024ULL)
+#define PIPE_PHY_RATE_RD(src)          ((0xc000 & (u32)(src)) >> 0xe)
+
+struct xgene_pcie_port {
+       struct device_node      *node;
+       struct device           *dev;
+       struct clk              *clk;
+       void __iomem            *csr_base;
+       void __iomem            *cfg_base;
+       unsigned long           cfg_addr;
+       bool                    link_up;
+};
+
+static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
+{
+       return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags;
+}
+
+/* PCIe Configuration Out/In */
+static inline void xgene_pcie_cfg_out32(void __iomem *addr, int offset, u32 val)
+{
+       writel(val, addr + offset);
+}
+
+static inline void xgene_pcie_cfg_out16(void __iomem *addr, int offset, u16 val)
+{
+       u32 val32 = readl(addr + (offset & ~0x3));
+
+       switch (offset & 0x3) {
+       case 2:
+               val32 &= ~0xFFFF0000;
+               val32 |= (u32)val << 16;
+               break;
+       case 0:
+       default:
+               val32 &= ~0xFFFF;
+               val32 |= val;
+               break;
+       }
+       writel(val32, addr + (offset & ~0x3));
+}
+
+static inline void xgene_pcie_cfg_out8(void __iomem *addr, int offset, u8 val)
+{
+       u32 val32 = readl(addr + (offset & ~0x3));
+
+       switch (offset & 0x3) {
+       case 0:
+               val32 &= ~0xFF;
+               val32 |= val;
+               break;
+       case 1:
+               val32 &= ~0xFF00;
+               val32 |= (u32)val << 8;
+               break;
+       case 2:
+               val32 &= ~0xFF0000;
+               val32 |= (u32)val << 16;
+               break;
+       case 3:
+       default:
+               val32 &= ~0xFF000000;
+               val32 |= (u32)val << 24;
+               break;
+       }
+       writel(val32, addr + (offset & ~0x3));
+}
+
+static inline void xgene_pcie_cfg_in32(void __iomem *addr, int offset, u32 *val)
+{
+       *val = readl(addr + offset);
+}
+
+static inline void xgene_pcie_cfg_in16(void __iomem *addr, int offset, u32 *val)
+{
+       *val = readl(addr + (offset & ~0x3));
+
+       switch (offset & 0x3) {
+       case 2:
+               *val >>= 16;
+               break;
+       }
+
+       *val &= 0xFFFF;
+}
+
+static inline void xgene_pcie_cfg_in8(void __iomem *addr, int offset, u32 *val)
+{
+       *val = readl(addr + (offset & ~0x3));
+
+       switch (offset & 0x3) {
+       case 3:
+               *val = *val >> 24;
+               break;
+       case 2:
+               *val = *val >> 16;
+               break;
+       case 1:
+               *val = *val >> 8;
+               break;
+       }
+       *val &= 0xFF;
+}
+
+/*
+ * When the address bit [17:16] is 2'b01, the Configuration access will be
+ * treated as Type 1 and it will be forwarded to external PCIe device.
+ */
+static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
+{
+       struct xgene_pcie_port *port = bus->sysdata;
+
+       if (bus->number >= (bus->primary + 1))
+               return port->cfg_base + AXI_EP_CFG_ACCESS;
+
+       return port->cfg_base;
+}
+
+/*
+ * For Configuration request, RTDID register is used as Bus Number,
+ * Device Number and Function number of the header fields.
+ */
+static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
+{
+       struct xgene_pcie_port *port = bus->sysdata;
+       unsigned int b, d, f;
+       u32 rtdid_val = 0;
+
+       b = bus->number;
+       d = PCI_SLOT(devfn);
+       f = PCI_FUNC(devfn);
+
+       if (!pci_is_root_bus(bus))
+               rtdid_val = (b << 8) | (d << 3) | f;
+
+       writel(rtdid_val, port->csr_base + RTDID);
+       /* read the register back to ensure flush */
+       readl(port->csr_base + RTDID);
+}
+
+/*
+ * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as
+ * the translation from PCI bus to native BUS.  Entire DDR region
+ * is mapped into PCIe space using these registers, so it can be
+ * reached by DMA from EP devices.  The BAR0/1 of bridge should be
+ * hidden during enumeration to avoid the sizing and resource allocation
+ * by PCIe core.
+ */
+static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
+{
+       if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) ||
+                                    (offset == PCI_BASE_ADDRESS_1)))
+               return true;
+
+       return false;
+}
+
+static int xgene_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
+                                 int offset, int len, u32 *val)
+{
+       struct xgene_pcie_port *port = bus->sysdata;
+       void __iomem *addr;
+
+       if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       if (xgene_pcie_hide_rc_bars(bus, offset)) {
+               *val = 0;
+               return PCIBIOS_SUCCESSFUL;
+       }
+
+       xgene_pcie_set_rtdid_reg(bus, devfn);
+       addr = xgene_pcie_get_cfg_base(bus);
+       switch (len) {
+       case 1:
+               xgene_pcie_cfg_in8(addr, offset, val);
+               break;
+       case 2:
+               xgene_pcie_cfg_in16(addr, offset, val);
+               break;
+       default:
+               xgene_pcie_cfg_in32(addr, offset, val);
+               break;
+       }
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int xgene_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
+                                  int offset, int len, u32 val)
+{
+       struct xgene_pcie_port *port = bus->sysdata;
+       void __iomem *addr;
+
+       if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       if (xgene_pcie_hide_rc_bars(bus, offset))
+               return PCIBIOS_SUCCESSFUL;
+
+       xgene_pcie_set_rtdid_reg(bus, devfn);
+       addr = xgene_pcie_get_cfg_base(bus);
+       switch (len) {
+       case 1:
+               xgene_pcie_cfg_out8(addr, offset, (u8)val);
+               break;
+       case 2:
+               xgene_pcie_cfg_out16(addr, offset, (u16)val);
+               break;
+       default:
+               xgene_pcie_cfg_out32(addr, offset, val);
+               break;
+       }
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops xgene_pcie_ops = {
+       .read = xgene_pcie_read_config,
+       .write = xgene_pcie_write_config
+};
+
+static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr,
+                                 u32 flags, u64 size)
+{
+       u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags;
+       u32 val32 = 0;
+       u32 val;
+
+       val32 = readl(csr_base + addr);
+       val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16);
+       writel(val, csr_base + addr);
+
+       val32 = readl(csr_base + addr + 0x04);
+       val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16);
+       writel(val, csr_base + addr + 0x04);
+
+       val32 = readl(csr_base + addr + 0x04);
+       val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16);
+       writel(val, csr_base + addr + 0x04);
+
+       val32 = readl(csr_base + addr + 0x08);
+       val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16);
+       writel(val, csr_base + addr + 0x08);
+
+       return mask;
+}
+
+static void xgene_pcie_linkup(struct xgene_pcie_port *port,
+                                  u32 *lanes, u32 *speed)
+{
+       void __iomem *csr_base = port->csr_base;
+       u32 val32;
+
+       port->link_up = false;
+       val32 = readl(csr_base + PCIECORE_CTLANDSTATUS);
+       if (val32 & LINK_UP_MASK) {
+               port->link_up = true;
+               *speed = PIPE_PHY_RATE_RD(val32);
+               val32 = readl(csr_base + BRIDGE_STATUS_0);
+               *lanes = val32 >> 26;
+       }
+}
+
+static int xgene_pcie_init_port(struct xgene_pcie_port *port)
+{
+       int rc;
+
+       port->clk = clk_get(port->dev, NULL);
+       if (IS_ERR(port->clk)) {
+               dev_err(port->dev, "clock not available\n");
+               return -ENODEV;
+       }
+
+       rc = clk_prepare_enable(port->clk);
+       if (rc) {
+               dev_err(port->dev, "clock enable failed\n");
+               return rc;
+       }
+
+       return 0;
+}
+
+static int xgene_pcie_map_reg(struct xgene_pcie_port *port,
+                             struct platform_device *pdev)
+{
+       struct resource *res;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
+       port->csr_base = devm_ioremap_resource(port->dev, res);
+       if (IS_ERR(port->csr_base))
+               return PTR_ERR(port->csr_base);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+       port->cfg_base = devm_ioremap_resource(port->dev, res);
+       if (IS_ERR(port->cfg_base))
+               return PTR_ERR(port->cfg_base);
+       port->cfg_addr = res->start;
+
+       return 0;
+}
+
+static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port,
+                                   struct resource *res, u32 offset,
+                                   u64 cpu_addr, u64 pci_addr)
+{
+       void __iomem *base = port->csr_base + offset;
+       resource_size_t size = resource_size(res);
+       u64 restype = resource_type(res);
+       u64 mask = 0;
+       u32 min_size;
+       u32 flag = EN_REG;
+
+       if (restype == IORESOURCE_MEM) {
+               min_size = SZ_128M;
+       } else {
+               min_size = 128;
+               flag |= OB_LO_IO;
+       }
+
+       if (size >= min_size)
+               mask = ~(size - 1) | flag;
+       else
+               dev_warn(port->dev, "res size 0x%llx less than minimum 0x%x\n",
+                        (u64)size, min_size);
+
+       writel(lower_32_bits(cpu_addr), base);
+       writel(upper_32_bits(cpu_addr), base + 0x04);
+       writel(lower_32_bits(mask), base + 0x08);
+       writel(upper_32_bits(mask), base + 0x0c);
+       writel(lower_32_bits(pci_addr), base + 0x10);
+       writel(upper_32_bits(pci_addr), base + 0x14);
+}
+
+static void xgene_pcie_setup_cfg_reg(void __iomem *csr_base, u64 addr)
+{
+       writel(lower_32_bits(addr), csr_base + CFGBARL);
+       writel(upper_32_bits(addr), csr_base + CFGBARH);
+       writel(EN_REG, csr_base + CFGCTL);
+}
+
+static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
+                                struct list_head *res,
+                                resource_size_t io_base)
+{
+       struct pci_host_bridge_window *window;
+       struct device *dev = port->dev;
+       int ret;
+
+       list_for_each_entry(window, res, list) {
+               struct resource *res = window->res;
+               u64 restype = resource_type(res);
+
+               dev_dbg(port->dev, "%pR\n", res);
+
+               switch (restype) {
+               case IORESOURCE_IO:
+                       xgene_pcie_setup_ob_reg(port, res, OMR3BARL, io_base,
+                                               res->start - window->offset);
+                       ret = pci_remap_iospace(res, io_base);
+                       if (ret < 0)
+                               return ret;
+                       break;
+               case IORESOURCE_MEM:
+                       xgene_pcie_setup_ob_reg(port, res, OMR1BARL, res->start,
+                                               res->start - window->offset);
+                       break;
+               case IORESOURCE_BUS:
+                       break;
+               default:
+                       dev_err(dev, "invalid resource %pR\n", res);
+                       return -EINVAL;
+               }
+       }
+       xgene_pcie_setup_cfg_reg(port->csr_base, port->cfg_addr);
+
+       return 0;
+}
+
+static void xgene_pcie_setup_pims(void *addr, u64 pim, u64 size)
+{
+       writel(lower_32_bits(pim), addr);
+       writel(upper_32_bits(pim) | EN_COHERENCY, addr + 0x04);
+       writel(lower_32_bits(size), addr + 0x10);
+       writel(upper_32_bits(size), addr + 0x14);
+}
+
+/*
+ * X-Gene PCIe support maximum 3 inbound memory regions
+ * This function helps to select a region based on size of region
+ */
+static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size)
+{
+       if ((size > 4) && (size < SZ_16M) && !(*ib_reg_mask & (1 << 1))) {
+               *ib_reg_mask |= (1 << 1);
+               return 1;
+       }
+
+       if ((size > SZ_1K) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 0))) {
+               *ib_reg_mask |= (1 << 0);
+               return 0;
+       }
+
+       if ((size > SZ_1M) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 2))) {
+               *ib_reg_mask |= (1 << 2);
+               return 2;
+       }
+
+       return -EINVAL;
+}
+
+static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
+                                   struct of_pci_range *range, u8 *ib_reg_mask)
+{
+       void __iomem *csr_base = port->csr_base;
+       void __iomem *cfg_base = port->cfg_base;
+       void *bar_addr;
+       void *pim_addr;
+       u64 cpu_addr = range->cpu_addr;
+       u64 pci_addr = range->pci_addr;
+       u64 size = range->size;
+       u64 mask = ~(size - 1) | EN_REG;
+       u32 flags = PCI_BASE_ADDRESS_MEM_TYPE_64;
+       u32 bar_low;
+       int region;
+
+       region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size);
+       if (region < 0) {
+               dev_warn(port->dev, "invalid pcie dma-range config\n");
+               return;
+       }
+
+       if (range->flags & IORESOURCE_PREFETCH)
+               flags |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+       bar_low = pcie_bar_low_val((u32)cpu_addr, flags);
+       switch (region) {
+       case 0:
+               xgene_pcie_set_ib_mask(csr_base, BRIDGE_CFG_4, flags, size);
+               bar_addr = cfg_base + PCI_BASE_ADDRESS_0;
+               writel(bar_low, bar_addr);
+               writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
+               pim_addr = csr_base + PIM1_1L;
+               break;
+       case 1:
+               bar_addr = csr_base + IBAR2;
+               writel(bar_low, bar_addr);
+               writel(lower_32_bits(mask), csr_base + IR2MSK);
+               pim_addr = csr_base + PIM2_1L;
+               break;
+       case 2:
+               bar_addr = csr_base + IBAR3L;
+               writel(bar_low, bar_addr);
+               writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
+               writel(lower_32_bits(mask), csr_base + IR3MSKL);
+               writel(upper_32_bits(mask), csr_base + IR3MSKL + 0x4);
+               pim_addr = csr_base + PIM3_1L;
+               break;
+       }
+
+       xgene_pcie_setup_pims(pim_addr, pci_addr, ~(size - 1));
+}
+
+static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
+                                    struct device_node *node)
+{
+       const int na = 3, ns = 2;
+       int rlen;
+
+       parser->node = node;
+       parser->pna = of_n_addr_cells(node);
+       parser->np = parser->pna + na + ns;
+
+       parser->range = of_get_property(node, "dma-ranges", &rlen);
+       if (!parser->range)
+               return -ENOENT;
+       parser->end = parser->range + rlen / sizeof(__be32);
+
+       return 0;
+}
+
+static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port)
+{
+       struct device_node *np = port->node;
+       struct of_pci_range range;
+       struct of_pci_range_parser parser;
+       struct device *dev = port->dev;
+       u8 ib_reg_mask = 0;
+
+       if (pci_dma_range_parser_init(&parser, np)) {
+               dev_err(dev, "missing dma-ranges property\n");
+               return -EINVAL;
+       }
+
+       /* Get the dma-ranges from DT */
+       for_each_of_pci_range(&parser, &range) {
+               u64 end = range.cpu_addr + range.size - 1;
+
+               dev_dbg(port->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
+                       range.flags, range.cpu_addr, end, range.pci_addr);
+               xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask);
+       }
+       return 0;
+}
+
+/* clear BAR configuration which was done by firmware */
+static void xgene_pcie_clear_config(struct xgene_pcie_port *port)
+{
+       int i;
+
+       for (i = PIM1_1L; i <= CFGCTL; i += 4)
+               writel(0x0, port->csr_base + i);
+}
+
+static int xgene_pcie_setup(struct xgene_pcie_port *port,
+                           struct list_head *res,
+                           resource_size_t io_base)
+{
+       u32 val, lanes = 0, speed = 0;
+       int ret;
+
+       xgene_pcie_clear_config(port);
+
+       /* setup the vendor and device IDs correctly */
+       val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID;
+       writel(val, port->csr_base + BRIDGE_CFG_0);
+
+       ret = xgene_pcie_map_ranges(port, res, io_base);
+       if (ret)
+               return ret;
+
+       ret = xgene_pcie_parse_map_dma_ranges(port);
+       if (ret)
+               return ret;
+
+       xgene_pcie_linkup(port, &lanes, &speed);
+       if (!port->link_up)
+               dev_info(port->dev, "(rc) link down\n");
+       else
+               dev_info(port->dev, "(rc) x%d gen-%d link up\n",
+                               lanes, speed + 1);
+       return 0;
+}
+
+static int xgene_pcie_probe_bridge(struct platform_device *pdev)
+{
+       struct device_node *dn = pdev->dev.of_node;
+       struct xgene_pcie_port *port;
+       resource_size_t iobase = 0;
+       struct pci_bus *bus;
+       int ret;
+       LIST_HEAD(res);
+
+       port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
+       if (!port)
+               return -ENOMEM;
+       port->node = of_node_get(pdev->dev.of_node);
+       port->dev = &pdev->dev;
+
+       ret = xgene_pcie_map_reg(port, pdev);
+       if (ret)
+               return ret;
+
+       ret = xgene_pcie_init_port(port);
+       if (ret)
+               return ret;
+
+       ret = of_pci_get_host_bridge_resources(dn, 0, 0xff, &res, &iobase);
+       if (ret)
+               return ret;
+
+       ret = xgene_pcie_setup(port, &res, iobase);
+       if (ret)
+               return ret;
+
+       bus = pci_scan_root_bus(&pdev->dev, 0, &xgene_pcie_ops, port, &res);
+       if (!bus)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, port);
+       return 0;
+}
+
+static const struct of_device_id xgene_pcie_match_table[] = {
+       {.compatible = "apm,xgene-pcie",},
+       {},
+};
+
+static struct platform_driver xgene_pcie_driver = {
+       .driver = {
+                  .name = "xgene-pcie",
+                  .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(xgene_pcie_match_table),
+       },
+       .probe = xgene_pcie_probe_bridge,
+};
+module_platform_driver(xgene_pcie_driver);
+
+MODULE_AUTHOR("Tanmay Inamdar <tinamdar@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene PCIe driver");
+MODULE_LICENSE("GPL v2");
index 52bd3a14356310195af1219e74b7e65091da6d88..dfed00aa3ac039c76548bba9eea7eb0a2e6c8fbc 100644 (file)
@@ -73,6 +73,8 @@ static unsigned long global_io_offset;
 
 static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
 {
+       BUG_ON(!sys->private_data);
+
        return sys->private_data;
 }
 
@@ -194,30 +196,6 @@ void dw_pcie_msi_init(struct pcie_port *pp)
        dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
 }
 
-static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0)
-{
-       int flag = 1;
-
-       do {
-               pos = find_next_zero_bit(pp->msi_irq_in_use,
-                               MAX_MSI_IRQS, pos);
-               /*if you have reached to the end then get out from here.*/
-               if (pos == MAX_MSI_IRQS)
-                       return -ENOSPC;
-               /*
-                * Check if this position is at correct offset.nvec is always a
-                * power of two. pos0 must be nvec bit aligned.
-                */
-               if (pos % msgvec)
-                       pos += msgvec - (pos % msgvec);
-               else
-                       flag = 0;
-       } while (flag);
-
-       *pos0 = pos;
-       return 0;
-}
-
 static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
 {
        unsigned int res, bit, val;
@@ -236,13 +214,14 @@ static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base,
 
        for (i = 0; i < nvec; i++) {
                irq_set_msi_desc_off(irq_base, i, NULL);
-               clear_bit(pos + i, pp->msi_irq_in_use);
                /* Disable corresponding interrupt on MSI controller */
                if (pp->ops->msi_clear_irq)
                        pp->ops->msi_clear_irq(pp, pos + i);
                else
                        dw_pcie_msi_clear_irq(pp, pos + i);
        }
+
+       bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec));
 }
 
 static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
@@ -258,31 +237,13 @@ static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
 
 static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
 {
-       int irq, pos0, pos1, i;
+       int irq, pos0, i;
        struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata);
 
-       if (!pp) {
-               BUG();
-               return -EINVAL;
-       }
-
-       pos0 = find_first_zero_bit(pp->msi_irq_in_use,
-                       MAX_MSI_IRQS);
-       if (pos0 % no_irqs) {
-               if (find_valid_pos0(pp, no_irqs, pos0, &pos0))
-                       goto no_valid_irq;
-       }
-       if (no_irqs > 1) {
-               pos1 = find_next_bit(pp->msi_irq_in_use,
-                               MAX_MSI_IRQS, pos0);
-               /* there must be nvec number of consecutive free bits */
-               while ((pos1 - pos0) < no_irqs) {
-                       if (find_valid_pos0(pp, no_irqs, pos1, &pos0))
-                               goto no_valid_irq;
-                       pos1 = find_next_bit(pp->msi_irq_in_use,
-                                       MAX_MSI_IRQS, pos0);
-               }
-       }
+       pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS,
+                                      order_base_2(no_irqs));
+       if (pos0 < 0)
+               goto no_valid_irq;
 
        irq = irq_find_mapping(pp->irq_domain, pos0);
        if (!irq)
@@ -300,7 +261,6 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
                        clear_irq_range(pp, irq, i, pos0);
                        goto no_valid_irq;
                }
-               set_bit(pos0 + i, pp->msi_irq_in_use);
                /*Enable corresponding interrupt in MSI interrupt controller */
                if (pp->ops->msi_set_irq)
                        pp->ops->msi_set_irq(pp, pos0 + i);
@@ -316,69 +276,28 @@ no_valid_irq:
        return -ENOSPC;
 }
 
-static void clear_irq(unsigned int irq)
-{
-       unsigned int pos, nvec;
-       struct msi_desc *msi;
-       struct pcie_port *pp;
-       struct irq_data *data = irq_get_irq_data(irq);
-
-       /* get the port structure */
-       msi = irq_data_get_msi(data);
-       pp = sys_to_pcie(msi->dev->bus->sysdata);
-       if (!pp) {
-               BUG();
-               return;
-       }
-
-       /* undo what was done in assign_irq */
-       pos = data->hwirq;
-       nvec = 1 << msi->msi_attrib.multiple;
-
-       clear_irq_range(pp, irq, nvec, pos);
-
-       /* all irqs cleared; reset attributes */
-       msi->irq = 0;
-       msi->msi_attrib.multiple = 0;
-}
-
 static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
                        struct msi_desc *desc)
 {
-       int irq, pos, msgvec;
-       u16 msg_ctr;
+       int irq, pos;
        struct msi_msg msg;
        struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
 
-       if (!pp) {
-               BUG();
-               return -EINVAL;
-       }
-
-       pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS,
-                               &msg_ctr);
-       msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4;
-       if (msgvec == 0)
-               msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1;
-       if (msgvec > 5)
-               msgvec = 0;
-
-       irq = assign_irq((1 << msgvec), desc, &pos);
+       irq = assign_irq(1, desc, &pos);
        if (irq < 0)
                return irq;
 
-       /*
-        * write_msi_msg() will update PCI_MSI_FLAGS so there is
-        * no need to explicitly call pci_write_config_word().
-        */
-       desc->msi_attrib.multiple = msgvec;
-
-       if (pp->ops->get_msi_data)
-               msg.address_lo = pp->ops->get_msi_data(pp);
+       if (pp->ops->get_msi_addr)
+               msg.address_lo = pp->ops->get_msi_addr(pp);
        else
                msg.address_lo = virt_to_phys((void *)pp->msi_data);
        msg.address_hi = 0x0;
-       msg.data = pos;
+
+       if (pp->ops->get_msi_data)
+               msg.data = pp->ops->get_msi_data(pp, pos);
+       else
+               msg.data = pos;
+
        write_msi_msg(irq, &msg);
 
        return 0;
@@ -386,7 +305,11 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
 
 static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
 {
-       clear_irq(irq);
+       struct irq_data *data = irq_get_irq_data(irq);
+       struct msi_desc *msi = irq_data_get_msi(data);
+       struct pcie_port *pp = sys_to_pcie(msi->dev->bus->sysdata);
+
+       clear_irq_range(pp, irq, 1, data->hwirq);
 }
 
 static struct msi_chip dw_pcie_msi_chip = {
@@ -425,7 +348,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
        struct resource *cfg_res;
        u32 val, na, ns;
        const __be32 *addrp;
-       int i, index;
+       int i, index, ret;
 
        /* Find the address cell size and the number of cells in order to get
         * the untranslated address.
@@ -435,16 +358,16 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
 
        cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
        if (cfg_res) {
-               pp->config.cfg0_size = resource_size(cfg_res)/2;
-               pp->config.cfg1_size = resource_size(cfg_res)/2;
+               pp->cfg0_size = resource_size(cfg_res)/2;
+               pp->cfg1_size = resource_size(cfg_res)/2;
                pp->cfg0_base = cfg_res->start;
-               pp->cfg1_base = cfg_res->start + pp->config.cfg0_size;
+               pp->cfg1_base = cfg_res->start + pp->cfg0_size;
 
                /* Find the untranslated configuration space address */
                index = of_property_match_string(np, "reg-names", "config");
-               addrp = of_get_address(np, index, false, false);
+               addrp = of_get_address(np, index, NULL, NULL);
                pp->cfg0_mod_base = of_read_number(addrp, ns);
-               pp->cfg1_mod_base = pp->cfg0_mod_base + pp->config.cfg0_size;
+               pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size;
        } else {
                dev_err(pp->dev, "missing *config* reg space\n");
        }
@@ -466,9 +389,9 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
                        pp->io.end = min_t(resource_size_t,
                                           IO_SPACE_LIMIT,
                                           range.pci_addr + range.size
-                                          + global_io_offset);
-                       pp->config.io_size = resource_size(&pp->io);
-                       pp->config.io_bus_addr = range.pci_addr;
+                                          + global_io_offset - 1);
+                       pp->io_size = resource_size(&pp->io);
+                       pp->io_bus_addr = range.pci_addr;
                        pp->io_base = range.cpu_addr;
 
                        /* Find the untranslated IO space address */
@@ -478,8 +401,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
                if (restype == IORESOURCE_MEM) {
                        of_pci_range_to_resource(&range, np, &pp->mem);
                        pp->mem.name = "MEM";
-                       pp->config.mem_size = resource_size(&pp->mem);
-                       pp->config.mem_bus_addr = range.pci_addr;
+                       pp->mem_size = resource_size(&pp->mem);
+                       pp->mem_bus_addr = range.pci_addr;
 
                        /* Find the untranslated MEM space address */
                        pp->mem_mod_base = of_read_number(parser.range -
@@ -487,19 +410,29 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
                }
                if (restype == 0) {
                        of_pci_range_to_resource(&range, np, &pp->cfg);
-                       pp->config.cfg0_size = resource_size(&pp->cfg)/2;
-                       pp->config.cfg1_size = resource_size(&pp->cfg)/2;
+                       pp->cfg0_size = resource_size(&pp->cfg)/2;
+                       pp->cfg1_size = resource_size(&pp->cfg)/2;
                        pp->cfg0_base = pp->cfg.start;
-                       pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size;
+                       pp->cfg1_base = pp->cfg.start + pp->cfg0_size;
 
                        /* Find the untranslated configuration space address */
                        pp->cfg0_mod_base = of_read_number(parser.range -
                                                           parser.np + na, ns);
                        pp->cfg1_mod_base = pp->cfg0_mod_base +
-                                           pp->config.cfg0_size;
+                                           pp->cfg0_size;
                }
        }
 
+       ret = of_pci_parse_bus_range(np, &pp->busn);
+       if (ret < 0) {
+               pp->busn.name = np->name;
+               pp->busn.start = 0;
+               pp->busn.end = 0xff;
+               pp->busn.flags = IORESOURCE_BUS;
+               dev_dbg(pp->dev, "failed to parse bus-range property: %d, using default %pR\n",
+                       ret, &pp->busn);
+       }
+
        if (!pp->dbi_base) {
                pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start,
                                        resource_size(&pp->cfg));
@@ -511,17 +444,22 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
 
        pp->mem_base = pp->mem.start;
 
-       pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
-                                       pp->config.cfg0_size);
        if (!pp->va_cfg0_base) {
-               dev_err(pp->dev, "error with ioremap in function\n");
-               return -ENOMEM;
+               pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
+                                               pp->cfg0_size);
+               if (!pp->va_cfg0_base) {
+                       dev_err(pp->dev, "error with ioremap in function\n");
+                       return -ENOMEM;
+               }
        }
-       pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
-                                       pp->config.cfg1_size);
+
        if (!pp->va_cfg1_base) {
-               dev_err(pp->dev, "error with ioremap\n");
-               return -ENOMEM;
+               pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
+                                               pp->cfg1_size);
+               if (!pp->va_cfg1_base) {
+                       dev_err(pp->dev, "error with ioremap\n");
+                       return -ENOMEM;
+               }
        }
 
        if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
@@ -530,16 +468,22 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
        }
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
-               pp->irq_domain = irq_domain_add_linear(pp->dev->of_node,
-                                       MAX_MSI_IRQS, &msi_domain_ops,
-                                       &dw_pcie_msi_chip);
-               if (!pp->irq_domain) {
-                       dev_err(pp->dev, "irq domain init failed\n");
-                       return -ENXIO;
-               }
+               if (!pp->ops->msi_host_init) {
+                       pp->irq_domain = irq_domain_add_linear(pp->dev->of_node,
+                                               MAX_MSI_IRQS, &msi_domain_ops,
+                                               &dw_pcie_msi_chip);
+                       if (!pp->irq_domain) {
+                               dev_err(pp->dev, "irq domain init failed\n");
+                               return -ENXIO;
+                       }
 
-               for (i = 0; i < MAX_MSI_IRQS; i++)
-                       irq_create_mapping(pp->irq_domain, i);
+                       for (i = 0; i < MAX_MSI_IRQS; i++)
+                               irq_create_mapping(pp->irq_domain, i);
+               } else {
+                       ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        if (pp->ops->host_init)
@@ -558,7 +502,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
        dw_pci.private_data = (void **)&pp;
 
        pci_common_init_dev(pp->dev, &dw_pci);
-       pci_assign_unassigned_resources();
 #ifdef CONFIG_PCI_DOMAINS
        dw_pci.domain++;
 #endif
@@ -573,7 +516,7 @@ static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev)
                          PCIE_ATU_VIEWPORT);
        dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE);
        dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE);
-       dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->config.cfg0_size - 1,
+       dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1,
                          PCIE_ATU_LIMIT);
        dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
        dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
@@ -589,7 +532,7 @@ static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev)
        dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1);
        dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE);
        dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE);
-       dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->config.cfg1_size - 1,
+       dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1,
                          PCIE_ATU_LIMIT);
        dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
        dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
@@ -604,10 +547,10 @@ static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp)
        dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1);
        dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE);
        dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE);
-       dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->config.mem_size - 1,
+       dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1,
                          PCIE_ATU_LIMIT);
-       dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET);
-       dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr),
+       dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET);
+       dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr),
                          PCIE_ATU_UPPER_TARGET);
        dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
 }
@@ -620,10 +563,10 @@ static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp)
        dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1);
        dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE);
        dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE);
-       dw_pcie_writel_rc(pp, pp->io_mod_base + pp->config.io_size - 1,
+       dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1,
                          PCIE_ATU_LIMIT);
-       dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET);
-       dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr),
+       dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET);
+       dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr),
                          PCIE_ATU_UPPER_TARGET);
        dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
 }
@@ -707,11 +650,6 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
        struct pcie_port *pp = sys_to_pcie(bus->sysdata);
        int ret;
 
-       if (!pp) {
-               BUG();
-               return -EINVAL;
-       }
-
        if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
                *val = 0xffffffff;
                return PCIBIOS_DEVICE_NOT_FOUND;
@@ -736,11 +674,6 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
        struct pcie_port *pp = sys_to_pcie(bus->sysdata);
        int ret;
 
-       if (!pp) {
-               BUG();
-               return -EINVAL;
-       }
-
        if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
                return PCIBIOS_DEVICE_NOT_FOUND;
 
@@ -768,19 +701,17 @@ static int dw_pcie_setup(int nr, struct pci_sys_data *sys)
 
        pp = sys_to_pcie(sys);
 
-       if (!pp)
-               return 0;
-
-       if (global_io_offset < SZ_1M && pp->config.io_size > 0) {
-               sys->io_offset = global_io_offset - pp->config.io_bus_addr;
+       if (global_io_offset < SZ_1M && pp->io_size > 0) {
+               sys->io_offset = global_io_offset - pp->io_bus_addr;
                pci_ioremap_io(global_io_offset, pp->io_base);
                global_io_offset += SZ_64K;
                pci_add_resource_offset(&sys->resources, &pp->io,
                                        sys->io_offset);
        }
 
-       sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr;
+       sys->mem_offset = pp->mem.start - pp->mem_bus_addr;
        pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset);
+       pci_add_resource(&sys->resources, &pp->busn);
 
        return 1;
 }
@@ -790,14 +721,16 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys)
        struct pci_bus *bus;
        struct pcie_port *pp = sys_to_pcie(sys);
 
-       if (pp) {
-               pp->root_bus_nr = sys->busnr;
-               bus = pci_scan_root_bus(pp->dev, sys->busnr, &dw_pcie_ops,
-                                       sys, &sys->resources);
-       } else {
-               bus = NULL;
-               BUG();
-       }
+       pp->root_bus_nr = sys->busnr;
+       bus = pci_create_root_bus(pp->dev, sys->busnr,
+                                 &dw_pcie_ops, sys, &sys->resources);
+       if (!bus)
+               return NULL;
+
+       pci_scan_child_bus(bus);
+
+       if (bus && pp->ops->scan_bus)
+               pp->ops->scan_bus(pp);
 
        return bus;
 }
@@ -833,7 +766,6 @@ static struct hw_pci dw_pci = {
 
 void dw_pcie_setup_rc(struct pcie_port *pp)
 {
-       struct pcie_port_info *config = &pp->config;
        u32 val;
        u32 membase;
        u32 memlimit;
@@ -888,7 +820,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
 
        /* setup memory base, memory limit */
        membase = ((u32)pp->mem_base & 0xfff00000) >> 16;
-       memlimit = (config->mem_size + (u32)pp->mem_base) & 0xfff00000;
+       memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000;
        val = memlimit | membase;
        dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);
 
index daf81f922cda34e472f4be5c16d5a967f70528da..c6256751daffb89eb939ee53840d53f864ccde3a 100644 (file)
 #ifndef _PCIE_DESIGNWARE_H
 #define _PCIE_DESIGNWARE_H
 
-struct pcie_port_info {
-       u32             cfg0_size;
-       u32             cfg1_size;
-       u32             io_size;
-       u32             mem_size;
-       phys_addr_t     io_bus_addr;
-       phys_addr_t     mem_bus_addr;
-};
-
 /*
  * Maximum number of MSI IRQs can be 256 per controller. But keep
  * it 32 as of now. Probably we will never need more than 32. If needed,
@@ -38,17 +29,23 @@ struct pcie_port {
        u64                     cfg0_base;
        u64                     cfg0_mod_base;
        void __iomem            *va_cfg0_base;
+       u32                     cfg0_size;
        u64                     cfg1_base;
        u64                     cfg1_mod_base;
        void __iomem            *va_cfg1_base;
+       u32                     cfg1_size;
        u64                     io_base;
        u64                     io_mod_base;
+       phys_addr_t             io_bus_addr;
+       u32                     io_size;
        u64                     mem_base;
        u64                     mem_mod_base;
+       phys_addr_t             mem_bus_addr;
+       u32                     mem_size;
        struct resource         cfg;
        struct resource         io;
        struct resource         mem;
-       struct pcie_port_info   config;
+       struct resource         busn;
        int                     irq;
        u32                     lanes;
        struct pcie_host_ops    *ops;
@@ -73,7 +70,10 @@ struct pcie_host_ops {
        void (*host_init)(struct pcie_port *pp);
        void (*msi_set_irq)(struct pcie_port *pp, int irq);
        void (*msi_clear_irq)(struct pcie_port *pp, int irq);
-       u32 (*get_msi_data)(struct pcie_port *pp);
+       u32 (*get_msi_addr)(struct pcie_port *pp);
+       u32 (*get_msi_data)(struct pcie_port *pp, int pos);
+       void (*scan_bus)(struct pcie_port *pp);
+       int (*msi_host_init)(struct pcie_port *pp, struct msi_chip *chip);
 };
 
 int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val);
index 4884ee5e07d461477afc55308cd5f055da78b35c..61158e03ab5f8437e2f28d9564a2f70b5cd78961 100644 (file)
@@ -323,6 +323,7 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
 
        /* Setup PCIe address space mappings for each resource */
        resource_size_t size;
+       resource_size_t res_start;
        u32 mask;
 
        rcar_pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win));
@@ -335,8 +336,13 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
        mask = (roundup_pow_of_two(size) / SZ_128) - 1;
        rcar_pci_write_reg(pcie, mask << 7, PCIEPAMR(win));
 
-       rcar_pci_write_reg(pcie, upper_32_bits(res->start), PCIEPARH(win));
-       rcar_pci_write_reg(pcie, lower_32_bits(res->start), PCIEPARL(win));
+       if (res->flags & IORESOURCE_IO)
+               res_start = pci_pio_to_address(res->start);
+       else
+               res_start = res->start;
+
+       rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPARH(win));
+       rcar_pci_write_reg(pcie, lower_32_bits(res_start), PCIEPARL(win));
 
        /* First resource is for IO */
        mask = PAR_ENABLE;
@@ -363,9 +369,10 @@ static int rcar_pcie_setup(int nr, struct pci_sys_data *sys)
 
                rcar_pcie_setup_window(i, pcie);
 
-               if (res->flags & IORESOURCE_IO)
-                       pci_ioremap_io(nr * SZ_64K, res->start);
-               else
+               if (res->flags & IORESOURCE_IO) {
+                       phys_addr_t io_start = pci_pio_to_address(res->start);
+                       pci_ioremap_io(nr * SZ_64K, io_start);
+               } else
                        pci_add_resource(&sys->resources, res);
        }
        pci_add_resource(&sys->resources, &pcie->busn);
@@ -935,8 +942,10 @@ static int rcar_pcie_probe(struct platform_device *pdev)
        }
 
        for_each_of_pci_range(&parser, &range) {
-               of_pci_range_to_resource(&range, pdev->dev.of_node,
+               err = of_pci_range_to_resource(&range, pdev->dev.of_node,
                                                &pcie->res[win++]);
+               if (err < 0)
+                       return err;
 
                if (win > RCAR_PCI_MAX_RESOURCES)
                        break;
index 6dea9e43a75c1dcc866c2836f3b64cae282c0944..85f594e1708fbc9191864273cc7f093217f7e463 100644 (file)
@@ -340,7 +340,7 @@ static int __init spear13xx_pcie_probe(struct platform_device *pdev)
 
        pp->dev = dev;
 
-       dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
        pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
        if (IS_ERR(pp->dbi_base)) {
                dev_err(dev, "couldn't remap dbi base %p\n", dbi_base);
diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c
new file mode 100644 (file)
index 0000000..ccc496b
--- /dev/null
@@ -0,0 +1,970 @@
+/*
+ * PCIe host controller driver for Xilinx AXI PCIe Bridge
+ *
+ * Copyright (c) 2012 - 2014 Xilinx, Inc.
+ *
+ * Based on the Tegra PCIe driver
+ *
+ * Bits taken from Synopsys Designware Host controller driver and
+ * ARM PCI Host generic 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+/* Register definitions */
+#define XILINX_PCIE_REG_BIR            0x00000130
+#define XILINX_PCIE_REG_IDR            0x00000138
+#define XILINX_PCIE_REG_IMR            0x0000013c
+#define XILINX_PCIE_REG_PSCR           0x00000144
+#define XILINX_PCIE_REG_RPSC           0x00000148
+#define XILINX_PCIE_REG_MSIBASE1       0x0000014c
+#define XILINX_PCIE_REG_MSIBASE2       0x00000150
+#define XILINX_PCIE_REG_RPEFR          0x00000154
+#define XILINX_PCIE_REG_RPIFR1         0x00000158
+#define XILINX_PCIE_REG_RPIFR2         0x0000015c
+
+/* Interrupt registers definitions */
+#define XILINX_PCIE_INTR_LINK_DOWN     BIT(0)
+#define XILINX_PCIE_INTR_ECRC_ERR      BIT(1)
+#define XILINX_PCIE_INTR_STR_ERR       BIT(2)
+#define XILINX_PCIE_INTR_HOT_RESET     BIT(3)
+#define XILINX_PCIE_INTR_CFG_TIMEOUT   BIT(8)
+#define XILINX_PCIE_INTR_CORRECTABLE   BIT(9)
+#define XILINX_PCIE_INTR_NONFATAL      BIT(10)
+#define XILINX_PCIE_INTR_FATAL         BIT(11)
+#define XILINX_PCIE_INTR_INTX          BIT(16)
+#define XILINX_PCIE_INTR_MSI           BIT(17)
+#define XILINX_PCIE_INTR_SLV_UNSUPP    BIT(20)
+#define XILINX_PCIE_INTR_SLV_UNEXP     BIT(21)
+#define XILINX_PCIE_INTR_SLV_COMPL     BIT(22)
+#define XILINX_PCIE_INTR_SLV_ERRP      BIT(23)
+#define XILINX_PCIE_INTR_SLV_CMPABT    BIT(24)
+#define XILINX_PCIE_INTR_SLV_ILLBUR    BIT(25)
+#define XILINX_PCIE_INTR_MST_DECERR    BIT(26)
+#define XILINX_PCIE_INTR_MST_SLVERR    BIT(27)
+#define XILINX_PCIE_INTR_MST_ERRP      BIT(28)
+#define XILINX_PCIE_IMR_ALL_MASK       0x1FF30FED
+#define XILINX_PCIE_IDR_ALL_MASK       0xFFFFFFFF
+
+/* Root Port Error FIFO Read Register definitions */
+#define XILINX_PCIE_RPEFR_ERR_VALID    BIT(18)
+#define XILINX_PCIE_RPEFR_REQ_ID       GENMASK(15, 0)
+#define XILINX_PCIE_RPEFR_ALL_MASK     0xFFFFFFFF
+
+/* Root Port Interrupt FIFO Read Register 1 definitions */
+#define XILINX_PCIE_RPIFR1_INTR_VALID  BIT(31)
+#define XILINX_PCIE_RPIFR1_MSI_INTR    BIT(30)
+#define XILINX_PCIE_RPIFR1_INTR_MASK   GENMASK(28, 27)
+#define XILINX_PCIE_RPIFR1_ALL_MASK    0xFFFFFFFF
+#define XILINX_PCIE_RPIFR1_INTR_SHIFT  27
+
+/* Bridge Info Register definitions */
+#define XILINX_PCIE_BIR_ECAM_SZ_MASK   GENMASK(18, 16)
+#define XILINX_PCIE_BIR_ECAM_SZ_SHIFT  16
+
+/* Root Port Interrupt FIFO Read Register 2 definitions */
+#define XILINX_PCIE_RPIFR2_MSG_DATA    GENMASK(15, 0)
+
+/* Root Port Status/control Register definitions */
+#define XILINX_PCIE_REG_RPSC_BEN       BIT(0)
+
+/* Phy Status/Control Register definitions */
+#define XILINX_PCIE_REG_PSCR_LNKUP     BIT(11)
+
+/* ECAM definitions */
+#define ECAM_BUS_NUM_SHIFT             20
+#define ECAM_DEV_NUM_SHIFT             12
+
+/* Number of MSI IRQs */
+#define XILINX_NUM_MSI_IRQS            128
+
+/* Number of Memory Resources */
+#define XILINX_MAX_NUM_RESOURCES       3
+
+/**
+ * struct xilinx_pcie_port - PCIe port information
+ * @reg_base: IO Mapped Register Base
+ * @irq: Interrupt number
+ * @msi_pages: MSI pages
+ * @root_busno: Root Bus number
+ * @dev: Device pointer
+ * @irq_domain: IRQ domain pointer
+ * @bus_range: Bus range
+ * @resources: Bus Resources
+ */
+struct xilinx_pcie_port {
+       void __iomem *reg_base;
+       u32 irq;
+       unsigned long msi_pages;
+       u8 root_busno;
+       struct device *dev;
+       struct irq_domain *irq_domain;
+       struct resource bus_range;
+       struct list_head resources;
+};
+
+static DECLARE_BITMAP(msi_irq_in_use, XILINX_NUM_MSI_IRQS);
+
+static inline struct xilinx_pcie_port *sys_to_pcie(struct pci_sys_data *sys)
+{
+       return sys->private_data;
+}
+
+static inline u32 pcie_read(struct xilinx_pcie_port *port, u32 reg)
+{
+       return readl(port->reg_base + reg);
+}
+
+static inline void pcie_write(struct xilinx_pcie_port *port, u32 val, u32 reg)
+{
+       writel(val, port->reg_base + reg);
+}
+
+static inline bool xilinx_pcie_link_is_up(struct xilinx_pcie_port *port)
+{
+       return (pcie_read(port, XILINX_PCIE_REG_PSCR) &
+               XILINX_PCIE_REG_PSCR_LNKUP) ? 1 : 0;
+}
+
+/**
+ * xilinx_pcie_clear_err_interrupts - Clear Error Interrupts
+ * @port: PCIe port information
+ */
+static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port)
+{
+       u32 val = pcie_read(port, XILINX_PCIE_REG_RPEFR);
+
+       if (val & XILINX_PCIE_RPEFR_ERR_VALID) {
+               dev_dbg(port->dev, "Requester ID %d\n",
+                       val & XILINX_PCIE_RPEFR_REQ_ID);
+               pcie_write(port, XILINX_PCIE_RPEFR_ALL_MASK,
+                          XILINX_PCIE_REG_RPEFR);
+       }
+}
+
+/**
+ * xilinx_pcie_valid_device - Check if a valid device is present on bus
+ * @bus: PCI Bus structure
+ * @devfn: device/function
+ *
+ * Return: 'true' on success and 'false' if invalid device is found
+ */
+static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
+{
+       struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
+
+       /* Check if link is up when trying to access downstream ports */
+       if (bus->number != port->root_busno)
+               if (!xilinx_pcie_link_is_up(port))
+                       return false;
+
+       /* Only one device down on each root port */
+       if (bus->number == port->root_busno && devfn > 0)
+               return false;
+
+       /*
+        * Do not read more than one device on the bus directly attached
+        * to RC.
+        */
+       if (bus->primary == port->root_busno && devfn > 0)
+               return false;
+
+       return true;
+}
+
+/**
+ * xilinx_pcie_config_base - Get configuration base
+ * @bus: PCI Bus structure
+ * @devfn: Device/function
+ * @where: Offset from base
+ *
+ * Return: Base address of the configuration space needed to be
+ *        accessed.
+ */
+static void __iomem *xilinx_pcie_config_base(struct pci_bus *bus,
+                                            unsigned int devfn, int where)
+{
+       struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
+       int relbus;
+
+       relbus = (bus->number << ECAM_BUS_NUM_SHIFT) |
+                (devfn << ECAM_DEV_NUM_SHIFT);
+
+       return port->reg_base + relbus + where;
+}
+
+/**
+ * xilinx_pcie_read_config - Read configuration space
+ * @bus: PCI Bus structure
+ * @devfn: Device/function
+ * @where: Offset from base
+ * @size: Byte/word/dword
+ * @val: Value to be read
+ *
+ * Return: PCIBIOS_SUCCESSFUL on success
+ *        PCIBIOS_DEVICE_NOT_FOUND on failure
+ */
+static int xilinx_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
+                                  int where, int size, u32 *val)
+{
+       void __iomem *addr;
+
+       if (!xilinx_pcie_valid_device(bus, devfn)) {
+               *val = 0xFFFFFFFF;
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       addr = xilinx_pcie_config_base(bus, devfn, where);
+
+       switch (size) {
+       case 1:
+               *val = readb(addr);
+               break;
+       case 2:
+               *val = readw(addr);
+               break;
+       default:
+               *val = readl(addr);
+               break;
+       }
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+/**
+ * xilinx_pcie_write_config - Write configuration space
+ * @bus: PCI Bus structure
+ * @devfn: Device/function
+ * @where: Offset from base
+ * @size: Byte/word/dword
+ * @val: Value to be written to device
+ *
+ * Return: PCIBIOS_SUCCESSFUL on success
+ *        PCIBIOS_DEVICE_NOT_FOUND on failure
+ */
+static int xilinx_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
+                                   int where, int size, u32 val)
+{
+       void __iomem *addr;
+
+       if (!xilinx_pcie_valid_device(bus, devfn))
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       addr = xilinx_pcie_config_base(bus, devfn, where);
+
+       switch (size) {
+       case 1:
+               writeb(val, addr);
+               break;
+       case 2:
+               writew(val, addr);
+               break;
+       default:
+               writel(val, addr);
+               break;
+       }
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+/* PCIe operations */
+static struct pci_ops xilinx_pcie_ops = {
+       .read  = xilinx_pcie_read_config,
+       .write = xilinx_pcie_write_config,
+};
+
+/* MSI functions */
+
+/**
+ * xilinx_pcie_destroy_msi - Free MSI number
+ * @irq: IRQ to be freed
+ */
+static void xilinx_pcie_destroy_msi(unsigned int irq)
+{
+       struct irq_desc *desc;
+       struct msi_desc *msi;
+       struct xilinx_pcie_port *port;
+
+       desc = irq_to_desc(irq);
+       msi = irq_desc_get_msi_desc(desc);
+       port = sys_to_pcie(msi->dev->bus->sysdata);
+
+       if (!test_bit(irq, msi_irq_in_use))
+               dev_err(port->dev, "Trying to free unused MSI#%d\n", irq);
+       else
+               clear_bit(irq, msi_irq_in_use);
+}
+
+/**
+ * xilinx_pcie_assign_msi - Allocate MSI number
+ * @port: PCIe port structure
+ *
+ * Return: A valid IRQ on success and error value on failure.
+ */
+static int xilinx_pcie_assign_msi(struct xilinx_pcie_port *port)
+{
+       int pos;
+
+       pos = find_first_zero_bit(msi_irq_in_use, XILINX_NUM_MSI_IRQS);
+       if (pos < XILINX_NUM_MSI_IRQS)
+               set_bit(pos, msi_irq_in_use);
+       else
+               return -ENOSPC;
+
+       return pos;
+}
+
+/**
+ * xilinx_msi_teardown_irq - Destroy the MSI
+ * @chip: MSI Chip descriptor
+ * @irq: MSI IRQ to destroy
+ */
+static void xilinx_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
+{
+       xilinx_pcie_destroy_msi(irq);
+}
+
+/**
+ * xilinx_pcie_msi_setup_irq - Setup MSI request
+ * @chip: MSI chip pointer
+ * @pdev: PCIe device pointer
+ * @desc: MSI descriptor pointer
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int xilinx_pcie_msi_setup_irq(struct msi_chip *chip,
+                                    struct pci_dev *pdev,
+                                    struct msi_desc *desc)
+{
+       struct xilinx_pcie_port *port = sys_to_pcie(pdev->bus->sysdata);
+       unsigned int irq;
+       int hwirq;
+       struct msi_msg msg;
+       phys_addr_t msg_addr;
+
+       hwirq = xilinx_pcie_assign_msi(port);
+       if (hwirq < 0)
+               return hwirq;
+
+       irq = irq_create_mapping(port->irq_domain, hwirq);
+       if (!irq)
+               return -EINVAL;
+
+       irq_set_msi_desc(irq, desc);
+
+       msg_addr = virt_to_phys((void *)port->msi_pages);
+
+       msg.address_hi = 0;
+       msg.address_lo = msg_addr;
+       msg.data = irq;
+
+       write_msi_msg(irq, &msg);
+
+       return 0;
+}
+
+/* MSI Chip Descriptor */
+static struct msi_chip xilinx_pcie_msi_chip = {
+       .setup_irq = xilinx_pcie_msi_setup_irq,
+       .teardown_irq = xilinx_msi_teardown_irq,
+};
+
+/* HW Interrupt Chip Descriptor */
+static struct irq_chip xilinx_msi_irq_chip = {
+       .name = "Xilinx PCIe MSI",
+       .irq_enable = unmask_msi_irq,
+       .irq_disable = mask_msi_irq,
+       .irq_mask = mask_msi_irq,
+       .irq_unmask = unmask_msi_irq,
+};
+
+/**
+ * xilinx_pcie_msi_map - Set the handler for the MSI and mark IRQ as valid
+ * @domain: IRQ domain
+ * @irq: Virtual IRQ number
+ * @hwirq: HW interrupt number
+ *
+ * Return: Always returns 0.
+ */
+static int xilinx_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
+                              irq_hw_number_t hwirq)
+{
+       irq_set_chip_and_handler(irq, &xilinx_msi_irq_chip, handle_simple_irq);
+       irq_set_chip_data(irq, domain->host_data);
+       set_irq_flags(irq, IRQF_VALID);
+
+       return 0;
+}
+
+/* IRQ Domain operations */
+static const struct irq_domain_ops msi_domain_ops = {
+       .map = xilinx_pcie_msi_map,
+};
+
+/**
+ * xilinx_pcie_enable_msi - Enable MSI support
+ * @port: PCIe port information
+ */
+static void xilinx_pcie_enable_msi(struct xilinx_pcie_port *port)
+{
+       phys_addr_t msg_addr;
+
+       port->msi_pages = __get_free_pages(GFP_KERNEL, 0);
+       msg_addr = virt_to_phys((void *)port->msi_pages);
+       pcie_write(port, 0x0, XILINX_PCIE_REG_MSIBASE1);
+       pcie_write(port, msg_addr, XILINX_PCIE_REG_MSIBASE2);
+}
+
+/**
+ * xilinx_pcie_add_bus - Add MSI chip info to PCIe bus
+ * @bus: PCIe bus
+ */
+static void xilinx_pcie_add_bus(struct pci_bus *bus)
+{
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
+
+               xilinx_pcie_msi_chip.dev = port->dev;
+               bus->msi = &xilinx_pcie_msi_chip;
+       }
+}
+
+/* INTx Functions */
+
+/**
+ * xilinx_pcie_intx_map - Set the handler for the INTx and mark IRQ as valid
+ * @domain: IRQ domain
+ * @irq: Virtual IRQ number
+ * @hwirq: HW interrupt number
+ *
+ * Return: Always returns 0.
+ */
+static int xilinx_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+                               irq_hw_number_t hwirq)
+{
+       irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+       irq_set_chip_data(irq, domain->host_data);
+       set_irq_flags(irq, IRQF_VALID);
+
+       return 0;
+}
+
+/* INTx IRQ Domain operations */
+static const struct irq_domain_ops intx_domain_ops = {
+       .map = xilinx_pcie_intx_map,
+};
+
+/* PCIe HW Functions */
+
+/**
+ * xilinx_pcie_intr_handler - Interrupt Service Handler
+ * @irq: IRQ number
+ * @data: PCIe port information
+ *
+ * Return: IRQ_HANDLED on success and IRQ_NONE on failure
+ */
+static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
+{
+       struct xilinx_pcie_port *port = (struct xilinx_pcie_port *)data;
+       u32 val, mask, status, msi_data;
+
+       /* Read interrupt decode and mask registers */
+       val = pcie_read(port, XILINX_PCIE_REG_IDR);
+       mask = pcie_read(port, XILINX_PCIE_REG_IMR);
+
+       status = val & mask;
+       if (!status)
+               return IRQ_NONE;
+
+       if (status & XILINX_PCIE_INTR_LINK_DOWN)
+               dev_warn(port->dev, "Link Down\n");
+
+       if (status & XILINX_PCIE_INTR_ECRC_ERR)
+               dev_warn(port->dev, "ECRC failed\n");
+
+       if (status & XILINX_PCIE_INTR_STR_ERR)
+               dev_warn(port->dev, "Streaming error\n");
+
+       if (status & XILINX_PCIE_INTR_HOT_RESET)
+               dev_info(port->dev, "Hot reset\n");
+
+       if (status & XILINX_PCIE_INTR_CFG_TIMEOUT)
+               dev_warn(port->dev, "ECAM access timeout\n");
+
+       if (status & XILINX_PCIE_INTR_CORRECTABLE) {
+               dev_warn(port->dev, "Correctable error message\n");
+               xilinx_pcie_clear_err_interrupts(port);
+       }
+
+       if (status & XILINX_PCIE_INTR_NONFATAL) {
+               dev_warn(port->dev, "Non fatal error message\n");
+               xilinx_pcie_clear_err_interrupts(port);
+       }
+
+       if (status & XILINX_PCIE_INTR_FATAL) {
+               dev_warn(port->dev, "Fatal error message\n");
+               xilinx_pcie_clear_err_interrupts(port);
+       }
+
+       if (status & XILINX_PCIE_INTR_INTX) {
+               /* INTx interrupt received */
+               val = pcie_read(port, XILINX_PCIE_REG_RPIFR1);
+
+               /* Check whether interrupt valid */
+               if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
+                       dev_warn(port->dev, "RP Intr FIFO1 read error\n");
+                       return IRQ_HANDLED;
+               }
+
+               /* Clear interrupt FIFO register 1 */
+               pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK,
+                          XILINX_PCIE_REG_RPIFR1);
+
+               /* Handle INTx Interrupt */
+               val = ((val & XILINX_PCIE_RPIFR1_INTR_MASK) >>
+                       XILINX_PCIE_RPIFR1_INTR_SHIFT) + 1;
+               generic_handle_irq(irq_find_mapping(port->irq_domain, val));
+       }
+
+       if (status & XILINX_PCIE_INTR_MSI) {
+               /* MSI Interrupt */
+               val = pcie_read(port, XILINX_PCIE_REG_RPIFR1);
+
+               if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
+                       dev_warn(port->dev, "RP Intr FIFO1 read error\n");
+                       return IRQ_HANDLED;
+               }
+
+               if (val & XILINX_PCIE_RPIFR1_MSI_INTR) {
+                       msi_data = pcie_read(port, XILINX_PCIE_REG_RPIFR2) &
+                                  XILINX_PCIE_RPIFR2_MSG_DATA;
+
+                       /* Clear interrupt FIFO register 1 */
+                       pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK,
+                                  XILINX_PCIE_REG_RPIFR1);
+
+                       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+                               /* Handle MSI Interrupt */
+                               generic_handle_irq(msi_data);
+                       }
+               }
+       }
+
+       if (status & XILINX_PCIE_INTR_SLV_UNSUPP)
+               dev_warn(port->dev, "Slave unsupported request\n");
+
+       if (status & XILINX_PCIE_INTR_SLV_UNEXP)
+               dev_warn(port->dev, "Slave unexpected completion\n");
+
+       if (status & XILINX_PCIE_INTR_SLV_COMPL)
+               dev_warn(port->dev, "Slave completion timeout\n");
+
+       if (status & XILINX_PCIE_INTR_SLV_ERRP)
+               dev_warn(port->dev, "Slave Error Poison\n");
+
+       if (status & XILINX_PCIE_INTR_SLV_CMPABT)
+               dev_warn(port->dev, "Slave Completer Abort\n");
+
+       if (status & XILINX_PCIE_INTR_SLV_ILLBUR)
+               dev_warn(port->dev, "Slave Illegal Burst\n");
+
+       if (status & XILINX_PCIE_INTR_MST_DECERR)
+               dev_warn(port->dev, "Master decode error\n");
+
+       if (status & XILINX_PCIE_INTR_MST_SLVERR)
+               dev_warn(port->dev, "Master slave error\n");
+
+       if (status & XILINX_PCIE_INTR_MST_ERRP)
+               dev_warn(port->dev, "Master error poison\n");
+
+       /* Clear the Interrupt Decode register */
+       pcie_write(port, status, XILINX_PCIE_REG_IDR);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * xilinx_pcie_free_irq_domain - Free IRQ domain
+ * @port: PCIe port information
+ */
+static void xilinx_pcie_free_irq_domain(struct xilinx_pcie_port *port)
+{
+       int i;
+       u32 irq, num_irqs;
+
+       /* Free IRQ Domain */
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+
+               free_pages(port->msi_pages, 0);
+
+               num_irqs = XILINX_NUM_MSI_IRQS;
+       } else {
+               /* INTx */
+               num_irqs = 4;
+       }
+
+       for (i = 0; i < num_irqs; i++) {
+               irq = irq_find_mapping(port->irq_domain, i);
+               if (irq > 0)
+                       irq_dispose_mapping(irq);
+       }
+
+       irq_domain_remove(port->irq_domain);
+}
+
+/**
+ * xilinx_pcie_init_irq_domain - Initialize IRQ domain
+ * @port: PCIe port information
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
+{
+       struct device *dev = port->dev;
+       struct device_node *node = dev->of_node;
+       struct device_node *pcie_intc_node;
+
+       /* Setup INTx */
+       pcie_intc_node = of_get_next_child(node, NULL);
+       if (!pcie_intc_node) {
+               dev_err(dev, "No PCIe Intc node found\n");
+               return PTR_ERR(pcie_intc_node);
+       }
+
+       port->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
+                                                &intx_domain_ops,
+                                                port);
+       if (!port->irq_domain) {
+               dev_err(dev, "Failed to get a INTx IRQ domain\n");
+               return PTR_ERR(port->irq_domain);
+       }
+
+       /* Setup MSI */
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               port->irq_domain = irq_domain_add_linear(node,
+                                                        XILINX_NUM_MSI_IRQS,
+                                                        &msi_domain_ops,
+                                                        &xilinx_pcie_msi_chip);
+               if (!port->irq_domain) {
+                       dev_err(dev, "Failed to get a MSI IRQ domain\n");
+                       return PTR_ERR(port->irq_domain);
+               }
+
+               xilinx_pcie_enable_msi(port);
+       }
+
+       return 0;
+}
+
+/**
+ * xilinx_pcie_init_port - Initialize hardware
+ * @port: PCIe port information
+ */
+static void xilinx_pcie_init_port(struct xilinx_pcie_port *port)
+{
+       if (xilinx_pcie_link_is_up(port))
+               dev_info(port->dev, "PCIe Link is UP\n");
+       else
+               dev_info(port->dev, "PCIe Link is DOWN\n");
+
+       /* Disable all interrupts */
+       pcie_write(port, ~XILINX_PCIE_IDR_ALL_MASK,
+                  XILINX_PCIE_REG_IMR);
+
+       /* Clear pending interrupts */
+       pcie_write(port, pcie_read(port, XILINX_PCIE_REG_IDR) &
+                        XILINX_PCIE_IMR_ALL_MASK,
+                  XILINX_PCIE_REG_IDR);
+
+       /* Enable all interrupts */
+       pcie_write(port, XILINX_PCIE_IMR_ALL_MASK, XILINX_PCIE_REG_IMR);
+
+       /* Enable the Bridge enable bit */
+       pcie_write(port, pcie_read(port, XILINX_PCIE_REG_RPSC) |
+                        XILINX_PCIE_REG_RPSC_BEN,
+                  XILINX_PCIE_REG_RPSC);
+}
+
+/**
+ * xilinx_pcie_setup - Setup memory resources
+ * @nr: Bus number
+ * @sys: Per controller structure
+ *
+ * Return: '1' on success and error value on failure
+ */
+static int xilinx_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+       struct xilinx_pcie_port *port = sys_to_pcie(sys);
+
+       list_splice_init(&port->resources, &sys->resources);
+
+       return 1;
+}
+
+/**
+ * xilinx_pcie_scan_bus - Scan PCIe bus for devices
+ * @nr: Bus number
+ * @sys: Per controller structure
+ *
+ * Return: Valid Bus pointer on success and NULL on failure
+ */
+static struct pci_bus *xilinx_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+       struct xilinx_pcie_port *port = sys_to_pcie(sys);
+       struct pci_bus *bus;
+
+       port->root_busno = sys->busnr;
+       bus = pci_scan_root_bus(port->dev, sys->busnr, &xilinx_pcie_ops,
+                               sys, &sys->resources);
+
+       return bus;
+}
+
+/**
+ * xilinx_pcie_parse_and_add_res - Add resources by parsing ranges
+ * @port: PCIe port information
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port)
+{
+       struct device *dev = port->dev;
+       struct device_node *node = dev->of_node;
+       struct resource *mem;
+       resource_size_t offset;
+       struct of_pci_range_parser parser;
+       struct of_pci_range range;
+       struct pci_host_bridge_window *win;
+       int err = 0, mem_resno = 0;
+
+       /* Get the ranges */
+       if (of_pci_range_parser_init(&parser, node)) {
+               dev_err(dev, "missing \"ranges\" property\n");
+               return -EINVAL;
+       }
+
+       /* Parse the ranges and add the resources found to the list */
+       for_each_of_pci_range(&parser, &range) {
+
+               if (mem_resno >= XILINX_MAX_NUM_RESOURCES) {
+                       dev_err(dev, "Maximum memory resources exceeded\n");
+                       return -EINVAL;
+               }
+
+               mem = devm_kmalloc(dev, sizeof(*mem), GFP_KERNEL);
+               if (!mem) {
+                       err = -ENOMEM;
+                       goto free_resources;
+               }
+
+               of_pci_range_to_resource(&range, node, mem);
+
+               switch (mem->flags & IORESOURCE_TYPE_BITS) {
+               case IORESOURCE_MEM:
+                       offset = range.cpu_addr - range.pci_addr;
+                       mem_resno++;
+                       break;
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (err < 0) {
+                       dev_warn(dev, "Invalid resource found %pR\n", mem);
+                       continue;
+               }
+
+               err = request_resource(&iomem_resource, mem);
+               if (err)
+                       goto free_resources;
+
+               pci_add_resource_offset(&port->resources, mem, offset);
+       }
+
+       /* Get the bus range */
+       if (of_pci_parse_bus_range(node, &port->bus_range)) {
+               u32 val = pcie_read(port, XILINX_PCIE_REG_BIR);
+               u8 last;
+
+               last = (val & XILINX_PCIE_BIR_ECAM_SZ_MASK) >>
+                       XILINX_PCIE_BIR_ECAM_SZ_SHIFT;
+
+               port->bus_range = (struct resource) {
+                       .name   = node->name,
+                       .start  = 0,
+                       .end    = last,
+                       .flags  = IORESOURCE_BUS,
+               };
+       }
+
+       /* Register bus resource */
+       pci_add_resource(&port->resources, &port->bus_range);
+
+       return 0;
+
+free_resources:
+       release_child_resources(&iomem_resource);
+       list_for_each_entry(win, &port->resources, list)
+               devm_kfree(dev, win->res);
+       pci_free_resource_list(&port->resources);
+
+       return err;
+}
+
+/**
+ * xilinx_pcie_parse_dt - Parse Device tree
+ * @port: PCIe port information
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
+{
+       struct device *dev = port->dev;
+       struct device_node *node = dev->of_node;
+       struct resource regs;
+       const char *type;
+       int err;
+
+       type = of_get_property(node, "device_type", NULL);
+       if (!type || strcmp(type, "pci")) {
+               dev_err(dev, "invalid \"device_type\" %s\n", type);
+               return -EINVAL;
+       }
+
+       err = of_address_to_resource(node, 0, &regs);
+       if (err) {
+               dev_err(dev, "missing \"reg\" property\n");
+               return err;
+       }
+
+       port->reg_base = devm_ioremap_resource(dev, &regs);
+       if (IS_ERR(port->reg_base))
+               return PTR_ERR(port->reg_base);
+
+       port->irq = irq_of_parse_and_map(node, 0);
+       err = devm_request_irq(dev, port->irq, xilinx_pcie_intr_handler,
+                              IRQF_SHARED, "xilinx-pcie", port);
+       if (err) {
+               dev_err(dev, "unable to request irq %d\n", port->irq);
+               return err;
+       }
+
+       return 0;
+}
+
+/**
+ * xilinx_pcie_probe - Probe function
+ * @pdev: Platform device pointer
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int xilinx_pcie_probe(struct platform_device *pdev)
+{
+       struct xilinx_pcie_port *port;
+       struct hw_pci hw;
+       struct device *dev = &pdev->dev;
+       int err;
+
+       if (!dev->of_node)
+               return -ENODEV;
+
+       port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+       if (!port)
+               return -ENOMEM;
+
+       port->dev = dev;
+
+       err = xilinx_pcie_parse_dt(port);
+       if (err) {
+               dev_err(dev, "Parsing DT failed\n");
+               return err;
+       }
+
+       xilinx_pcie_init_port(port);
+
+       err = xilinx_pcie_init_irq_domain(port);
+       if (err) {
+               dev_err(dev, "Failed creating IRQ Domain\n");
+               return err;
+       }
+
+       /*
+        * Parse PCI ranges, configuration bus range and
+        * request their resources
+        */
+       INIT_LIST_HEAD(&port->resources);
+       err = xilinx_pcie_parse_and_add_res(port);
+       if (err) {
+               dev_err(dev, "Failed adding resources\n");
+               return err;
+       }
+
+       platform_set_drvdata(pdev, port);
+
+       /* Register the device */
+       memset(&hw, 0, sizeof(hw));
+       hw = (struct hw_pci) {
+               .nr_controllers = 1,
+               .private_data   = (void **)&port,
+               .setup          = xilinx_pcie_setup,
+               .map_irq        = of_irq_parse_and_map_pci,
+               .add_bus        = xilinx_pcie_add_bus,
+               .scan           = xilinx_pcie_scan_bus,
+               .ops            = &xilinx_pcie_ops,
+       };
+       pci_common_init_dev(dev, &hw);
+
+       return 0;
+}
+
+/**
+ * xilinx_pcie_remove - Remove function
+ * @pdev: Platform device pointer
+ *
+ * Return: '0' always
+ */
+static int xilinx_pcie_remove(struct platform_device *pdev)
+{
+       struct xilinx_pcie_port *port = platform_get_drvdata(pdev);
+
+       xilinx_pcie_free_irq_domain(port);
+
+       return 0;
+}
+
+static struct of_device_id xilinx_pcie_of_match[] = {
+       { .compatible = "xlnx,axi-pcie-host-1.00.a", },
+       {}
+};
+
+static struct platform_driver xilinx_pcie_driver = {
+       .driver = {
+               .name = "xilinx-pcie",
+               .owner = THIS_MODULE,
+               .of_match_table = xilinx_pcie_of_match,
+               .suppress_bind_attrs = true,
+       },
+       .probe = xilinx_pcie_probe,
+       .remove = xilinx_pcie_remove,
+};
+module_platform_driver(xilinx_pcie_driver);
+
+MODULE_AUTHOR("Xilinx Inc");
+MODULE_DESCRIPTION("Xilinx AXI PCIe driver");
+MODULE_LICENSE("GPL v2");
index 3e6532b945c1829663ca5d5b6ff9bdb24b944f16..4a9aa08b08f13600ecad592eb7e452c8ed4132b9 100644 (file)
@@ -24,7 +24,7 @@ obj-$(CONFIG_HOTPLUG_PCI_S390)                += s390_pci_hpc.o
 
 obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM)     += acpiphp_ibm.o
 
-pci_hotplug-objs       :=      pci_hotplug_core.o pcihp_slot.o
+pci_hotplug-objs       :=      pci_hotplug_core.o
 
 ifdef CONFIG_HOTPLUG_PCI_CPCI
 pci_hotplug-objs       +=      cpci_hotplug_core.o     \
index a94d850ae228c377387d036fd0f3af7e656130c6..876ccc620440e69b884a96b07f2355f346cac2dd 100644 (file)
 
 static bool debug_acpi;
 
-static acpi_status
-decode_type0_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
-{
-       int i;
-       union acpi_object *fields = record->package.elements;
-       u32 revision = fields[1].integer.value;
-
-       switch (revision) {
-       case 1:
-               if (record->package.count != 6)
-                       return AE_ERROR;
-               for (i = 2; i < 6; i++)
-                       if (fields[i].type != ACPI_TYPE_INTEGER)
-                               return AE_ERROR;
-               hpx->t0 = &hpx->type0_data;
-               hpx->t0->revision        = revision;
-               hpx->t0->cache_line_size = fields[2].integer.value;
-               hpx->t0->latency_timer   = fields[3].integer.value;
-               hpx->t0->enable_serr     = fields[4].integer.value;
-               hpx->t0->enable_perr     = fields[5].integer.value;
-               break;
-       default:
-               printk(KERN_WARNING
-                      "%s: Type 0 Revision %d record not supported\n",
-                      __func__, revision);
-               return AE_ERROR;
-       }
-       return AE_OK;
-}
-
-static acpi_status
-decode_type1_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
-{
-       int i;
-       union acpi_object *fields = record->package.elements;
-       u32 revision = fields[1].integer.value;
-
-       switch (revision) {
-       case 1:
-               if (record->package.count != 5)
-                       return AE_ERROR;
-               for (i = 2; i < 5; i++)
-                       if (fields[i].type != ACPI_TYPE_INTEGER)
-                               return AE_ERROR;
-               hpx->t1 = &hpx->type1_data;
-               hpx->t1->revision      = revision;
-               hpx->t1->max_mem_read  = fields[2].integer.value;
-               hpx->t1->avg_max_split = fields[3].integer.value;
-               hpx->t1->tot_max_split = fields[4].integer.value;
-               break;
-       default:
-               printk(KERN_WARNING
-                      "%s: Type 1 Revision %d record not supported\n",
-                      __func__, revision);
-               return AE_ERROR;
-       }
-       return AE_OK;
-}
-
-static acpi_status
-decode_type2_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
-{
-       int i;
-       union acpi_object *fields = record->package.elements;
-       u32 revision = fields[1].integer.value;
-
-       switch (revision) {
-       case 1:
-               if (record->package.count != 18)
-                       return AE_ERROR;
-               for (i = 2; i < 18; i++)
-                       if (fields[i].type != ACPI_TYPE_INTEGER)
-                               return AE_ERROR;
-               hpx->t2 = &hpx->type2_data;
-               hpx->t2->revision      = revision;
-               hpx->t2->unc_err_mask_and      = fields[2].integer.value;
-               hpx->t2->unc_err_mask_or       = fields[3].integer.value;
-               hpx->t2->unc_err_sever_and     = fields[4].integer.value;
-               hpx->t2->unc_err_sever_or      = fields[5].integer.value;
-               hpx->t2->cor_err_mask_and      = fields[6].integer.value;
-               hpx->t2->cor_err_mask_or       = fields[7].integer.value;
-               hpx->t2->adv_err_cap_and       = fields[8].integer.value;
-               hpx->t2->adv_err_cap_or        = fields[9].integer.value;
-               hpx->t2->pci_exp_devctl_and    = fields[10].integer.value;
-               hpx->t2->pci_exp_devctl_or     = fields[11].integer.value;
-               hpx->t2->pci_exp_lnkctl_and    = fields[12].integer.value;
-               hpx->t2->pci_exp_lnkctl_or     = fields[13].integer.value;
-               hpx->t2->sec_unc_err_sever_and = fields[14].integer.value;
-               hpx->t2->sec_unc_err_sever_or  = fields[15].integer.value;
-               hpx->t2->sec_unc_err_mask_and  = fields[16].integer.value;
-               hpx->t2->sec_unc_err_mask_or   = fields[17].integer.value;
-               break;
-       default:
-               printk(KERN_WARNING
-                      "%s: Type 2 Revision %d record not supported\n",
-                      __func__, revision);
-               return AE_ERROR;
-       }
-       return AE_OK;
-}
-
-static acpi_status
-acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
-{
-       acpi_status status;
-       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
-       union acpi_object *package, *record, *fields;
-       u32 type;
-       int i;
-
-       /* Clear the return buffer with zeros */
-       memset(hpx, 0, sizeof(struct hotplug_params));
-
-       status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       package = (union acpi_object *)buffer.pointer;
-       if (package->type != ACPI_TYPE_PACKAGE) {
-               status = AE_ERROR;
-               goto exit;
-       }
-
-       for (i = 0; i < package->package.count; i++) {
-               record = &package->package.elements[i];
-               if (record->type != ACPI_TYPE_PACKAGE) {
-                       status = AE_ERROR;
-                       goto exit;
-               }
-
-               fields = record->package.elements;
-               if (fields[0].type != ACPI_TYPE_INTEGER ||
-                   fields[1].type != ACPI_TYPE_INTEGER) {
-                       status = AE_ERROR;
-                       goto exit;
-               }
-
-               type = fields[0].integer.value;
-               switch (type) {
-               case 0:
-                       status = decode_type0_hpx_record(record, hpx);
-                       if (ACPI_FAILURE(status))
-                               goto exit;
-                       break;
-               case 1:
-                       status = decode_type1_hpx_record(record, hpx);
-                       if (ACPI_FAILURE(status))
-                               goto exit;
-                       break;
-               case 2:
-                       status = decode_type2_hpx_record(record, hpx);
-                       if (ACPI_FAILURE(status))
-                               goto exit;
-                       break;
-               default:
-                       printk(KERN_ERR "%s: Type %d record not supported\n",
-                              __func__, type);
-                       status = AE_ERROR;
-                       goto exit;
-               }
-       }
- exit:
-       kfree(buffer.pointer);
-       return status;
-}
-
-static acpi_status
-acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
-{
-       acpi_status status;
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *package, *fields;
-       int i;
-
-       memset(hpp, 0, sizeof(struct hotplug_params));
-
-       status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       package = (union acpi_object *) buffer.pointer;
-       if (package->type != ACPI_TYPE_PACKAGE ||
-           package->package.count != 4) {
-               status = AE_ERROR;
-               goto exit;
-       }
-
-       fields = package->package.elements;
-       for (i = 0; i < 4; i++) {
-               if (fields[i].type != ACPI_TYPE_INTEGER) {
-                       status = AE_ERROR;
-                       goto exit;
-               }
-       }
-
-       hpp->t0 = &hpp->type0_data;
-       hpp->t0->revision        = 1;
-       hpp->t0->cache_line_size = fields[0].integer.value;
-       hpp->t0->latency_timer   = fields[1].integer.value;
-       hpp->t0->enable_serr     = fields[2].integer.value;
-       hpp->t0->enable_perr     = fields[3].integer.value;
-
-exit:
-       kfree(buffer.pointer);
-       return status;
-}
-
-
-
 /* acpi_run_oshp - get control of hotplug from the firmware
  *
  * @handle - the handle of the hotplug controller.
@@ -283,48 +74,6 @@ static acpi_status acpi_run_oshp(acpi_handle handle)
        return status;
 }
 
-/* pci_get_hp_params
- *
- * @dev - the pci_dev for which we want parameters
- * @hpp - allocated by the caller
- */
-int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
-{
-       acpi_status status;
-       acpi_handle handle, phandle;
-       struct pci_bus *pbus;
-
-       handle = NULL;
-       for (pbus = dev->bus; pbus; pbus = pbus->parent) {
-               handle = acpi_pci_get_bridge_handle(pbus);
-               if (handle)
-                       break;
-       }
-
-       /*
-        * _HPP settings apply to all child buses, until another _HPP is
-        * encountered. If we don't find an _HPP for the input pci dev,
-        * look for it in the parent device scope since that would apply to
-        * this pci dev.
-        */
-       while (handle) {
-               status = acpi_run_hpx(handle, hpp);
-               if (ACPI_SUCCESS(status))
-                       return 0;
-               status = acpi_run_hpp(handle, hpp);
-               if (ACPI_SUCCESS(status))
-                       return 0;
-               if (acpi_is_root_bridge(handle))
-                       break;
-               status = acpi_get_parent(handle, &phandle);
-               if (ACPI_FAILURE(status))
-                       break;
-               handle = phandle;
-       }
-       return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(pci_get_hp_params);
-
 /**
  * acpi_get_hp_hw_control_from_firmware
  * @dev: the pci_dev of the bridge that has a hotplug controller
@@ -433,7 +182,8 @@ int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle)
 {
        acpi_handle bridge_handle, parent_handle;
 
-       if (!(bridge_handle = acpi_pci_get_bridge_handle(pbus)))
+       bridge_handle = acpi_pci_get_bridge_handle(pbus);
+       if (!bridge_handle)
                return 0;
        if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle))))
                return 0;
index 6cd5160fc057654a20e2630e438513028a5bab39..bcb90e4888dd82d628337018c30d0d1712aad231 100644 (file)
@@ -61,7 +61,6 @@ static DEFINE_MUTEX(bridge_mutex);
 static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type);
 static void acpiphp_post_dock_fixup(struct acpi_device *adev);
 static void acpiphp_sanitize_bus(struct pci_bus *bus);
-static void acpiphp_set_hpp_values(struct pci_bus *bus);
 static void hotplug_event(u32 type, struct acpiphp_context *context);
 static void free_bridge(struct kref *kref);
 
@@ -510,7 +509,7 @@ static void enable_slot(struct acpiphp_slot *slot)
        __pci_bus_assign_resources(bus, &add_list, NULL);
 
        acpiphp_sanitize_bus(bus);
-       acpiphp_set_hpp_values(bus);
+       pcie_bus_configure_settings(bus);
        acpiphp_set_acpi_region(slot);
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -698,14 +697,6 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
        }
 }
 
-static void acpiphp_set_hpp_values(struct pci_bus *bus)
-{
-       struct pci_dev *dev;
-
-       list_for_each_entry(dev, &bus->devices, bus_list)
-               pci_configure_slot(dev);
-}
-
 /*
  * Remove devices for which we could not assign resources, call
  * arch specific code to fix-up the bus
index 8dcccffd6e216b8529da5530019fe557e0f61ff1..6ca23998ee8f36b1d486dcaa4c50c77ae74c8979 100644 (file)
@@ -302,7 +302,7 @@ static int ibm_get_table_from_acpi(char **bufp)
                goto read_table_done;
        }
 
-       for(size = 0, i = 0; i < package->package.count; i++) {
+       for (size = 0, i = 0; i < package->package.count; i++) {
                if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
                        pr_err("%s:  Invalid APCI element %d\n", __func__, i);
                        goto read_table_done;
index e09cf7827d68a3cf9b8f1755cd11a209e0f473f8..a5a7fd8332ac1cb7557285298d2824a05f127f00 100644 (file)
@@ -125,7 +125,8 @@ disable_slot(struct hotplug_slot *hotplug_slot)
 
        /* Unconfigure device */
        dbg("%s - unconfiguring slot %s", __func__, slot_name(slot));
-       if ((retval = cpci_unconfigure_slot(slot))) {
+       retval = cpci_unconfigure_slot(slot);
+       if (retval) {
                err("%s - could not unconfigure slot %s",
                    __func__, slot_name(slot));
                goto disable_error;
@@ -141,9 +142,11 @@ disable_slot(struct hotplug_slot *hotplug_slot)
        }
        cpci_led_on(slot);
 
-       if (controller->ops->set_power)
-               if ((retval = controller->ops->set_power(slot, 0)))
+       if (controller->ops->set_power) {
+               retval = controller->ops->set_power(slot, 0);
+               if (retval)
                        goto disable_error;
+       }
 
        if (update_adapter_status(slot->hotplug_slot, 0))
                warn("failure to update adapter file");
@@ -467,9 +470,9 @@ check_slots(void)
                            __func__, slot_name(slot), hs_csr);
 
                        if (!slot->extracting) {
-                               if (update_latch_status(slot->hotplug_slot, 0)) {
+                               if (update_latch_status(slot->hotplug_slot, 0))
                                        warn("failure to update latch file");
-                               }
+
                                slot->extracting = 1;
                                atomic_inc(&extracting);
                        }
index 04fcd7811400bdfb6e3afe3b592bcfbbc7121f14..66b7bbebe493af569d3eae456cd9ec59e4d37fac 100644 (file)
@@ -56,7 +56,7 @@
                if (debug)                                      \
                        printk (KERN_DEBUG "%s: " format "\n",  \
                                MY_NAME , ## arg);              \
-       } while(0)
+       } while (0)
 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
 #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
 #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
@@ -82,28 +82,28 @@ static int __init validate_parameters(void)
        char *p;
        unsigned long tmp;
 
-       if(!bridge) {
+       if (!bridge) {
                info("not configured, disabling.");
                return -EINVAL;
        }
        str = bridge;
-       if(!*str)
+       if (!*str)
                return -EINVAL;
 
        tmp = simple_strtoul(str, &p, 16);
-       if(p == str || tmp > 0xff) {
+       if (p == str || tmp > 0xff) {
                err("Invalid hotplug bus bridge device bus number");
                return -EINVAL;
        }
        bridge_busnr = (u8) tmp;
        dbg("bridge_busnr = 0x%02x", bridge_busnr);
-       if(*p != ':') {
+       if (*p != ':') {
                err("Invalid hotplug bus bridge device");
                return -EINVAL;
        }
        str = p + 1;
        tmp = simple_strtoul(str, &p, 16);
-       if(p == str || tmp > 0x1f) {
+       if (p == str || tmp > 0x1f) {
                err("Invalid hotplug bus bridge device slot number");
                return -EINVAL;
        }
@@ -112,18 +112,18 @@ static int __init validate_parameters(void)
 
        dbg("first_slot = 0x%02x", first_slot);
        dbg("last_slot = 0x%02x", last_slot);
-       if(!(first_slot && last_slot)) {
+       if (!(first_slot && last_slot)) {
                err("Need to specify first_slot and last_slot");
                return -EINVAL;
        }
-       if(last_slot < first_slot) {
+       if (last_slot < first_slot) {
                err("first_slot must be less than last_slot");
                return -EINVAL;
        }
 
        dbg("port = 0x%04x", port);
        dbg("enum_bit = 0x%02x", enum_bit);
-       if(enum_bit > 7) {
+       if (enum_bit > 7) {
                err("Invalid #ENUM bit");
                return -EINVAL;
        }
@@ -151,12 +151,12 @@ static int __init cpcihp_generic_init(void)
                return status;
 
        r = request_region(port, 1, "#ENUM hotswap signal register");
-       if(!r)
+       if (!r)
                return -EBUSY;
 
        dev = pci_get_domain_bus_and_slot(0, bridge_busnr,
                                          PCI_DEVFN(bridge_slot, 0));
-       if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
+       if (!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
                err("Invalid bridge device %s", bridge);
                pci_dev_put(dev);
                return -EINVAL;
@@ -169,21 +169,21 @@ static int __init cpcihp_generic_init(void)
        generic_hpc.ops = &generic_hpc_ops;
 
        status = cpci_hp_register_controller(&generic_hpc);
-       if(status != 0) {
+       if (status != 0) {
                err("Could not register cPCI hotplug controller");
                return -ENODEV;
        }
        dbg("registered controller");
 
        status = cpci_hp_register_bus(bus, first_slot, last_slot);
-       if(status != 0) {
+       if (status != 0) {
                err("Could not register cPCI hotplug bus");
                goto init_bus_register_error;
        }
        dbg("registered bus");
 
        status = cpci_hp_start();
-       if(status != 0) {
+       if (status != 0) {
                err("Could not started cPCI hotplug system");
                goto init_start_error;
        }
index 6757b3ef7e10217333302f52ba4b21ab9fd6363f..7ecf34e76a61eb73ebe6418eb4b1f904a997a90f 100644 (file)
@@ -51,7 +51,7 @@
                if (debug)                                      \
                        printk (KERN_DEBUG "%s: " format "\n",  \
                                MY_NAME , ## arg);              \
-       } while(0)
+       } while (0)
 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
 #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
 #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
@@ -82,13 +82,13 @@ static int zt5550_hc_config(struct pci_dev *pdev)
        int ret;
 
        /* Since we know that no boards exist with two HC chips, treat it as an error */
-       if(hc_dev) {
+       if (hc_dev) {
                err("too many host controller devices?");
                return -EBUSY;
        }
 
        ret = pci_enable_device(pdev);
-       if(ret) {
+       if (ret) {
                err("cannot enable %s\n", pci_name(pdev));
                return ret;
        }
@@ -98,7 +98,7 @@ static int zt5550_hc_config(struct pci_dev *pdev)
        dbg("pci resource start %llx", (unsigned long long)pci_resource_start(hc_dev, 1));
        dbg("pci resource len %llx", (unsigned long long)pci_resource_len(hc_dev, 1));
 
-       if(!request_mem_region(pci_resource_start(hc_dev, 1),
+       if (!request_mem_region(pci_resource_start(hc_dev, 1),
                                pci_resource_len(hc_dev, 1), MY_NAME)) {
                err("cannot reserve MMIO region");
                ret = -ENOMEM;
@@ -107,7 +107,7 @@ static int zt5550_hc_config(struct pci_dev *pdev)
 
        hc_registers =
            ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1));
-       if(!hc_registers) {
+       if (!hc_registers) {
                err("cannot remap MMIO region %llx @ %llx",
                        (unsigned long long)pci_resource_len(hc_dev, 1),
                        (unsigned long long)pci_resource_start(hc_dev, 1));
@@ -146,7 +146,7 @@ exit_disable_device:
 
 static int zt5550_hc_cleanup(void)
 {
-       if(!hc_dev)
+       if (!hc_dev)
                return -ENODEV;
 
        iounmap(hc_registers);
@@ -170,9 +170,9 @@ static int zt5550_hc_check_irq(void *dev_id)
        u8 reg;
 
        ret = 0;
-       if(dev_id == zt5550_hpc.dev_id) {
+       if (dev_id == zt5550_hpc.dev_id) {
                reg = readb(csr_int_status);
-               if(reg)
+               if (reg)
                        ret = 1;
        }
        return ret;
@@ -182,9 +182,9 @@ static int zt5550_hc_enable_irq(void)
 {
        u8 reg;
 
-       if(hc_dev == NULL) {
+       if (hc_dev == NULL)
                return -ENODEV;
-       }
+
        reg = readb(csr_int_mask);
        reg = reg & ~ENUM_INT_MASK;
        writeb(reg, csr_int_mask);
@@ -195,9 +195,8 @@ static int zt5550_hc_disable_irq(void)
 {
        u8 reg;
 
-       if(hc_dev == NULL) {
+       if (hc_dev == NULL)
                return -ENODEV;
-       }
 
        reg = readb(csr_int_mask);
        reg = reg | ENUM_INT_MASK;
@@ -210,15 +209,15 @@ static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id
        int status;
 
        status = zt5550_hc_config(pdev);
-       if(status != 0) {
+       if (status != 0)
                return status;
-       }
+
        dbg("returned from zt5550_hc_config");
 
        memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller));
        zt5550_hpc_ops.query_enum = zt5550_hc_query_enum;
        zt5550_hpc.ops = &zt5550_hpc_ops;
-       if(!poll) {
+       if (!poll) {
                zt5550_hpc.irq = hc_dev->irq;
                zt5550_hpc.irq_flags = IRQF_SHARED;
                zt5550_hpc.dev_id = hc_dev;
@@ -231,15 +230,16 @@ static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id
        }
 
        status = cpci_hp_register_controller(&zt5550_hpc);
-       if(status != 0) {
+       if (status != 0) {
                err("could not register cPCI hotplug controller");
                goto init_hc_error;
        }
        dbg("registered controller");
 
        /* Look for first device matching cPCI bus's bridge vendor and device IDs */
-       if(!(bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC,
-                                        PCI_DEVICE_ID_DEC_21154, NULL))) {
+       bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC,
+                                 PCI_DEVICE_ID_DEC_21154, NULL);
+       if (!bus0_dev) {
                status = -ENODEV;
                goto init_register_error;
        }
@@ -247,14 +247,14 @@ static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id
        pci_dev_put(bus0_dev);
 
        status = cpci_hp_register_bus(bus0, 0x0a, 0x0f);
-       if(status != 0) {
+       if (status != 0) {
                err("could not register cPCI hotplug bus");
                goto init_register_error;
        }
        dbg("registered bus");
 
        status = cpci_hp_start();
-       if(status != 0) {
+       if (status != 0) {
                err("could not started cPCI hotplug system");
                cpci_hp_unregister_bus(bus0);
                goto init_register_error;
@@ -300,11 +300,11 @@ static int __init zt5550_init(void)
 
        info(DRIVER_DESC " version: " DRIVER_VERSION);
        r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register");
-       if(!r)
+       if (!r)
                return -EBUSY;
 
        rc = pci_register_driver(&zt5550_hc_driver);
-       if(rc < 0)
+       if (rc < 0)
                release_region(ENUM_PORT, 1);
        return rc;
 }
index 0450f405807de036060d043e688a0840f809dcc4..b28b2d2184cdcf493d3640f23b8fcfad75006d3b 100644 (file)
@@ -690,7 +690,7 @@ static inline int cpq_get_latch_status(struct controller *ctrl,
 
        status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot));
 
-       return(status == 0) ? 1 : 0;
+       return (status == 0) ? 1 : 0;
 }
 
 
index 4aaee746df88424dbf730f5431ddee927947c0fb..a53084ddc1180df3c371c1483bec74e6f92c68fc 100644 (file)
@@ -1096,9 +1096,8 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        /* initialize our threads if they haven't already been started up */
        rc = one_time_init();
-       if (rc) {
+       if (rc)
                goto err_free_bus;
-       }
 
        dbg("pdev = %p\n", pdev);
        dbg("pci resource start %llx\n", (unsigned long long)pci_resource_start(pdev, 0));
index bde47fce324822c5efd5b59b20d0d15cbfb0544c..c5cbefee5236586ddae5ca0a4e7683f3336feecf 100644 (file)
@@ -705,9 +705,8 @@ static struct pci_resource *get_max_resource(struct pci_resource **head, u32 siz
                if (temp == max) {
                        *head = max->next;
                } else {
-                       while (temp && temp->next != max) {
+                       while (temp && temp->next != max)
                                temp = temp->next;
-                       }
 
                        if (temp)
                                temp->next = max->next;
@@ -903,9 +902,8 @@ irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
        /*
         * Check to see if it was our interrupt
         */
-       if (!(misc & 0x000C)) {
+       if (!(misc & 0x000C))
                return IRQ_NONE;
-       }
 
        if (misc & 0x0004) {
                /*
@@ -1143,7 +1141,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
        /* We don't allow freq/mode changes if we find another adapter running
         * in another slot on this controller
         */
-       for(slot = ctrl->slot; slot; slot = slot->next) {
+       for (slot = ctrl->slot; slot; slot = slot->next) {
                if (slot->device == (hp_slot + ctrl->slot_device_offset))
                        continue;
                if (!slot->hotplug_slot || !slot->hotplug_slot->info)
@@ -1193,7 +1191,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
 
        reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ);
        reg16 &= ~0x000F;
-       switch(adapter_speed) {
+       switch (adapter_speed) {
                case(PCI_SPEED_133MHz_PCIX):
                        reg = 0x75;
                        reg16 |= 0xB;
@@ -2006,9 +2004,8 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
        /* Check to see if the interlock is closed */
        tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
 
-       if (tempdword & (0x01 << hp_slot)) {
+       if (tempdword & (0x01 << hp_slot))
                return 1;
-       }
 
        if (func->is_a_board) {
                rc = board_replaced(func, ctrl);
@@ -2070,9 +2067,8 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
                }
        }
 
-       if (rc) {
+       if (rc)
                dbg("%s: rc = %d\n", __func__, rc);
-       }
 
        if (p_slot)
                update_slot_info(ctrl, p_slot);
@@ -2095,9 +2091,8 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
        device = func->device;
        func = cpqhp_slot_find(ctrl->bus, device, index++);
        p_slot = cpqhp_find_slot(ctrl, device);
-       if (p_slot) {
+       if (p_slot)
                physical_slot = p_slot->number;
-       }
 
        /* Make sure there are no video controllers here */
        while (func && !rc) {
index 0968a9bcb34568d9d83ecd5c9d7834e29dcf1616..1e08ff8c229c06680dc88b34bc3eb2ca547d3884 100644 (file)
@@ -204,9 +204,8 @@ static int load_HRT (void __iomem *rom_start)
        u8 temp_byte = 0xFF;
        u32 rc;
 
-       if (!check_for_compaq_ROM(rom_start)) {
+       if (!check_for_compaq_ROM(rom_start))
                return -ENODEV;
-       }
 
        available = 1024;
 
@@ -250,9 +249,8 @@ static u32 store_HRT (void __iomem *rom_start)
 
        available = 1024;
 
-       if (!check_for_compaq_ROM(rom_start)) {
+       if (!check_for_compaq_ROM(rom_start))
                return(1);
-       }
 
        buffer = (u32*) evbuffer;
 
@@ -427,9 +425,9 @@ static u32 store_HRT (void __iomem *rom_start)
 
 void compaq_nvram_init (void __iomem *rom_start)
 {
-       if (rom_start) {
+       if (rom_start)
                compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR);
-       }
+
        dbg("int15 entry  = %p\n", compaq_int15_entry_point);
 
        /* initialize our int15 lock */
@@ -661,9 +659,8 @@ int compaq_nvram_store (void __iomem *rom_start)
 
        if (evbuffer_init) {
                rc = store_HRT(rom_start);
-               if (rc) {
+               if (rc)
                        err(msg_unable_to_save);
-               }
        }
        return rc;
 }
index f7b8684a773969df4c88b9d0e358d19e5c7f069d..3efaf4c38528adc35fc693240d5b4ddb52c6d5cf 100644 (file)
@@ -1023,7 +1023,8 @@ static int enable_slot(struct hotplug_slot *hs)
        debug("ENABLING SLOT........\n");
        slot_cur = hs->private;
 
-       if ((rc = validate(slot_cur, ENABLE))) {
+       rc = validate(slot_cur, ENABLE);
+       if (rc) {
                err("validate function failed\n");
                goto error_nopower;
        }
@@ -1199,9 +1200,8 @@ int ibmphp_do_disable_slot(struct slot *slot_cur)
 
        debug("DISABLING SLOT...\n");
 
-       if ((slot_cur == NULL) || (slot_cur->ctrl == NULL)) {
+       if ((slot_cur == NULL) || (slot_cur->ctrl == NULL))
                return -ENODEV;
-       }
 
        flag = slot_cur->flag;
        slot_cur->flag = 1;
@@ -1336,17 +1336,20 @@ static int __init ibmphp_init(void)
        for (i = 0; i < 16; i++)
                irqs[i] = 0;
 
-       if ((rc = ibmphp_access_ebda()))
+       rc = ibmphp_access_ebda();
+       if (rc)
                goto error;
        debug("after ibmphp_access_ebda()\n");
 
-       if ((rc = ibmphp_rsrc_init()))
+       rc = ibmphp_rsrc_init();
+       if (rc)
                goto error;
        debug("AFTER Resource & EBDA INITIALIZATIONS\n");
 
        max_slots = get_max_slots();
 
-       if ((rc = ibmphp_register_pci()))
+       rc = ibmphp_register_pci();
+       if (rc)
                goto error;
 
        if (init_ops()) {
@@ -1355,9 +1358,9 @@ static int __init ibmphp_init(void)
        }
 
        ibmphp_print_test();
-       if ((rc = ibmphp_hpc_start_poll_thread())) {
+       rc = ibmphp_hpc_start_poll_thread();
+       if (rc)
                goto error;
-       }
 
 exit:
        return rc;
index 0f65ac5554344ce4a3c1bc474af73d12e0833dda..d9b197d5c6b450e2ba44439aaf0ec9a41f3e08f6 100644 (file)
@@ -215,9 +215,8 @@ static void __init print_ebda_hpc (void)
                        debug ("%s - cap of the slot: %x\n", __func__, hpc_ptr->slots[index].slot_cap);
                }
 
-               for (index = 0; index < hpc_ptr->bus_count; index++) {
+               for (index = 0; index < hpc_ptr->bus_count; index++)
                        debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __func__, hpc_ptr->buses[index].bus_num);
-               }
 
                debug ("%s - type of hpc: %x\n", __func__, hpc_ptr->ctlr_type);
                switch (hpc_ptr->ctlr_type) {
index a936022956e6d4fc8416c8992b827744952f7109..220876715a08493a315a8ef9d04e2a697972710f 100644 (file)
@@ -997,9 +997,8 @@ static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
                rc = ibmphp_do_disable_slot (pslot);
        }
 
-       if (update || disable) {
+       if (update || disable)
                ibmphp_update_slot_info (pslot);
-       }
 
        debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __func__, rc, disable, update);
 
index 2fd296706ce7f7a056ba5a2813c97f11d68ecdd1..814cea22a9fa5f599fdd44e0af479a0552d8f274 100644 (file)
@@ -145,7 +145,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
                                case PCI_HEADER_TYPE_NORMAL:
                                        debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class);
                                        assign_alt_irq (cur_func, class_code);
-                                       if ((rc = configure_device (cur_func)) < 0) {
+                                       rc = configure_device(cur_func);
+                                       if (rc < 0) {
                                                /* We need to do this in case some other BARs were properly inserted */
                                                err ("was not able to configure devfunc %x on bus %x.\n",
                                                     cur_func->device, cur_func->busno);
@@ -157,7 +158,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
                                        break;
                                case PCI_HEADER_TYPE_MULTIDEVICE:
                                        assign_alt_irq (cur_func, class_code);
-                                       if ((rc = configure_device (cur_func)) < 0) {
+                                       rc = configure_device(cur_func);
+                                       if (rc < 0) {
                                                /* We need to do this in case some other BARs were properly inserted */
                                                err ("was not able to configure devfunc %x on bus %x...bailing out\n",
                                                     cur_func->device, cur_func->busno);
index f34745abd5b625e3d2588cbee2ca0568d8f98128..219ba8090a375d64d1bee01321372e4817d6596d 100644 (file)
@@ -224,7 +224,8 @@ int __init ibmphp_rsrc_init (void)
                        if ((curr->rsrc_type & RESTYPE) == MMASK) {
                                /* no bus structure exists in place yet */
                                if (list_empty (&gbuses)) {
-                                       if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1)))
+                                       rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
+                                       if (rc)
                                                return rc;
                                        list_add_tail (&newbus->bus_list, &gbuses);
                                        debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
@@ -237,7 +238,8 @@ int __init ibmphp_rsrc_init (void)
                                                        return rc;
                                        } else {
                                                /* went through all the buses and didn't find ours, need to create a new bus node */
-                                               if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1)))
+                                               rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
+                                               if (rc)
                                                        return rc;
 
                                                list_add_tail (&newbus->bus_list, &gbuses);
@@ -248,7 +250,8 @@ int __init ibmphp_rsrc_init (void)
                                /* prefetchable memory */
                                if (list_empty (&gbuses)) {
                                        /* no bus structure exists in place yet */
-                                       if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1)))
+                                       rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
+                                       if (rc)
                                                return rc;
                                        list_add_tail (&newbus->bus_list, &gbuses);
                                        debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
@@ -261,7 +264,8 @@ int __init ibmphp_rsrc_init (void)
                                                        return rc;
                                        } else {
                                                /* went through all the buses and didn't find ours, need to create a new bus node */
-                                               if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1)))
+                                               rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
+                                               if (rc)
                                                        return rc;
                                                list_add_tail (&newbus->bus_list, &gbuses);
                                                debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
@@ -271,7 +275,8 @@ int __init ibmphp_rsrc_init (void)
                                /* IO */
                                if (list_empty (&gbuses)) {
                                        /* no bus structure exists in place yet */
-                                       if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1)))
+                                       rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
+                                       if (rc)
                                                return rc;
                                        list_add_tail (&newbus->bus_list, &gbuses);
                                        debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
@@ -283,7 +288,8 @@ int __init ibmphp_rsrc_init (void)
                                                        return rc;
                                        } else {
                                                /* went through all the buses and didn't find ours, need to create a new bus node */
-                                               if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1)))
+                                               rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
+                                               if (rc)
                                                        return rc;
                                                list_add_tail (&newbus->bus_list, &gbuses);
                                                debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
@@ -1038,7 +1044,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
                /* found our range */
                if (!res_prev) {
                        /* first time in the loop */
-                       if ((res_cur->start != range->start) && ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
+                       len_tmp = res_cur->start - 1 - range->start;
+
+                       if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
                                debug ("len_tmp = %x\n", len_tmp);
 
                                if ((len_tmp < len_cur) || (len_cur == 0)) {
@@ -1078,7 +1086,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
                }
                if (!res_cur->next) {
                        /* last device on the range */
-                       if ((range->end != res_cur->end) && ((len_tmp = range->end - (res_cur->end + 1)) >= res->len)) {
+                       len_tmp = range->end - (res_cur->end + 1);
+
+                       if ((range->end != res_cur->end) && (len_tmp >= res->len)) {
                                debug ("len_tmp = %x\n", len_tmp);
                                if ((len_tmp < len_cur) || (len_cur == 0)) {
 
@@ -1117,8 +1127,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
                if (res_prev) {
                        if (res_prev->rangeno != res_cur->rangeno) {
                                /* 1st device on this range */
-                               if ((res_cur->start != range->start) &&
-                                       ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
+                               len_tmp = res_cur->start - 1 - range->start;
+
+                               if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
                                        if ((len_tmp < len_cur) || (len_cur == 0)) {
                                                if ((range->start % tmp_divide) == 0) {
                                                        /* just perfect, starting address is divisible by length */
@@ -1153,7 +1164,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
                                }
                        } else {
                                /* in the same range */
-                               if ((len_tmp = res_cur->start - 1 - res_prev->end - 1) >= res->len) {
+                               len_tmp = res_cur->start - 1 - res_prev->end - 1;
+
+                               if (len_tmp >= res->len) {
                                        if ((len_tmp < len_cur) || (len_cur == 0)) {
                                                if (((res_prev->end + 1) % tmp_divide) == 0) {
                                                        /* just perfect, starting address's divisible by length */
@@ -1212,7 +1225,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
                                break;
                }
                while (range) {
-                       if ((len_tmp = range->end - range->start) >= res->len) {
+                       len_tmp = range->end - range->start;
+
+                       if (len_tmp >= res->len) {
                                if ((len_tmp < len_cur) || (len_cur == 0)) {
                                        if ((range->start % tmp_divide) == 0) {
                                                /* just perfect, starting address's divisible by length */
@@ -1276,7 +1291,9 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
                                        break;
                        }
                        while (range) {
-                               if ((len_tmp = range->end - range->start) >= res->len) {
+                               len_tmp = range->end - range->start;
+
+                               if (len_tmp >= res->len) {
                                        if ((len_tmp < len_cur) || (len_cur == 0)) {
                                                if ((range->start % tmp_divide) == 0) {
                                                        /* just perfect, starting address's divisible by length */
@@ -1335,7 +1352,7 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
                                return -EINVAL;
                        }
                }
-       }       /* end if(!res_cur) */
+       }       /* end if (!res_cur) */
        return -EINVAL;
 }
 
index 9e5a9fbb93d778cd8e123e6bdc669c1587d1764f..b11521953485bb99892b85dd4c2c3e93378c2d56 100644 (file)
@@ -92,7 +92,7 @@ struct controller {
        struct slot *slot;
        wait_queue_head_t queue;        /* sleep & wake process */
        u32 slot_cap;
-       u32 slot_ctrl;
+       u16 slot_ctrl;
        struct timer_list poll_timer;
        unsigned long cmd_started;      /* jiffies */
        unsigned int cmd_busy:1;
index 07aa722bb12cd61a6a3a8767b2efe1dd826e6952..3a5e7e28b8740307efd8eb2aa5e69c9717cd0e81 100644 (file)
@@ -262,6 +262,13 @@ static int pciehp_probe(struct pcie_device *dev)
                goto err_out_none;
        }
 
+       if (!dev->port->subordinate) {
+               /* Can happen if we run out of bus numbers during probe */
+               dev_err(&dev->device,
+                       "Hotplug bridge without secondary bus, ignoring\n");
+               goto err_out_none;
+       }
+
        ctrl = pcie_init(dev);
        if (!ctrl) {
                dev_err(&dev->device, "Controller initialization failed\n");
index 2a412fa3b338f4b8399685c5b46e43a4e925871a..0ebf754fc1775d7afffdc6fb9bea37828408d24a 100644 (file)
@@ -171,9 +171,9 @@ static void pcie_wait_cmd(struct controller *ctrl)
         * interrupts.
         */
        if (!rc)
-               ctrl_info(ctrl, "Timeout on hotplug command %#010x (issued %u msec ago)\n",
+               ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n",
                          ctrl->slot_ctrl,
-                         jiffies_to_msecs(now - ctrl->cmd_started));
+                         jiffies_to_msecs(jiffies - ctrl->cmd_started));
 }
 
 /**
@@ -422,9 +422,9 @@ void pciehp_set_attention_status(struct slot *slot, u8 value)
        default:
                return;
        }
+       pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
-       pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
 }
 
 void pciehp_green_led_on(struct slot *slot)
@@ -614,6 +614,8 @@ void pcie_enable_notification(struct controller *ctrl)
                PCI_EXP_SLTCTL_DLLSCE);
 
        pcie_write_cmd(ctrl, cmd, mask);
+       ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+                pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
 }
 
 static void pcie_disable_notification(struct controller *ctrl)
@@ -625,6 +627,8 @@ static void pcie_disable_notification(struct controller *ctrl)
                PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
                PCI_EXP_SLTCTL_DLLSCE);
        pcie_write_cmd(ctrl, 0, mask);
+       ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+                pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
 }
 
 /*
@@ -652,6 +656,8 @@ int pciehp_reset_slot(struct slot *slot, int probe)
        stat_mask |= PCI_EXP_SLTSTA_DLLSC;
 
        pcie_write_cmd(ctrl, 0, ctrl_mask);
+       ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+                pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
        if (pciehp_poll_mode)
                del_timer_sync(&ctrl->poll_timer);
 
@@ -659,6 +665,8 @@ int pciehp_reset_slot(struct slot *slot, int probe)
 
        pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
        pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask);
+       ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+                pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask);
        if (pciehp_poll_mode)
                int_poll_timeout(ctrl->poll_timer.data);
 
@@ -797,9 +805,6 @@ struct controller *pcie_init(struct pcie_device *dev)
                PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
                PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
 
-       /* Disable software notification */
-       pcie_disable_notification(ctrl);
-
        ctrl_info(ctrl, "Slot #%d AttnBtn%c AttnInd%c PwrInd%c PwrCtrl%c MRL%c Interlock%c NoCompl%c LLActRep%c\n",
                (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
                FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
index 5f871f4c4af10944e3417712f99194c2c1936349..9e69403be6327156c0a7a95fa8ce5b422aa60cb9 100644 (file)
@@ -65,14 +65,7 @@ int pciehp_configure_device(struct slot *p_slot)
                        pci_hp_add_bridge(dev);
 
        pci_assign_unassigned_bridge_resources(bridge);
-
-       list_for_each_entry(dev, &parent->devices, bus_list) {
-               if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
-                       continue;
-
-               pci_configure_slot(dev);
-       }
-
+       pcie_bus_configure_settings(parent);
        pci_bus_add_devices(parent);
 
  out:
diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c
deleted file mode 100644 (file)
index 3e36ec8..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 1995,2001 Compaq Computer Corporation
- * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
- * Copyright (C) 2001 IBM Corp.
- * Copyright (C) 2003-2004 Intel Corporation
- * (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
- *
- * 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 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, GOOD TITLE or
- * NON INFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/pci.h>
-#include <linux/export.h>
-#include <linux/pci_hotplug.h>
-
-static struct hpp_type0 pci_default_type0 = {
-       .revision = 1,
-       .cache_line_size = 8,
-       .latency_timer = 0x40,
-       .enable_serr = 0,
-       .enable_perr = 0,
-};
-
-static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
-{
-       u16 pci_cmd, pci_bctl;
-
-       if (!hpp) {
-               /*
-                * Perhaps we *should* use default settings for PCIe, but
-                * pciehp didn't, so we won't either.
-                */
-               if (pci_is_pcie(dev))
-                       return;
-               hpp = &pci_default_type0;
-       }
-
-       if (hpp->revision > 1) {
-               dev_warn(&dev->dev,
-                        "PCI settings rev %d not supported; using defaults\n",
-                        hpp->revision);
-               hpp = &pci_default_type0;
-       }
-
-       pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
-       pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
-       pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
-       if (hpp->enable_serr)
-               pci_cmd |= PCI_COMMAND_SERR;
-       else
-               pci_cmd &= ~PCI_COMMAND_SERR;
-       if (hpp->enable_perr)
-               pci_cmd |= PCI_COMMAND_PARITY;
-       else
-               pci_cmd &= ~PCI_COMMAND_PARITY;
-       pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
-
-       /* Program bridge control value */
-       if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
-               pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
-                                     hpp->latency_timer);
-               pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
-               if (hpp->enable_serr)
-                       pci_bctl |= PCI_BRIDGE_CTL_SERR;
-               else
-                       pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
-               if (hpp->enable_perr)
-                       pci_bctl |= PCI_BRIDGE_CTL_PARITY;
-               else
-                       pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
-               pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
-       }
-}
-
-static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
-{
-       if (hpp)
-               dev_warn(&dev->dev, "PCI-X settings not supported\n");
-}
-
-static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
-{
-       int pos;
-       u32 reg32;
-
-       if (!hpp)
-               return;
-
-       if (hpp->revision > 1) {
-               dev_warn(&dev->dev, "PCIe settings rev %d not supported\n",
-                        hpp->revision);
-               return;
-       }
-
-       /* Initialize Device Control Register */
-       pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
-                       ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
-
-       /* Initialize Link Control Register */
-       if (dev->subordinate)
-               pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
-                       ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
-
-       /* Find Advanced Error Reporting Enhanced Capability */
-       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
-       if (!pos)
-               return;
-
-       /* Initialize Uncorrectable Error Mask Register */
-       pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &reg32);
-       reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
-       pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
-
-       /* Initialize Uncorrectable Error Severity Register */
-       pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &reg32);
-       reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
-       pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
-
-       /* Initialize Correctable Error Mask Register */
-       pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg32);
-       reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
-       pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
-
-       /* Initialize Advanced Error Capabilities and Control Register */
-       pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
-       reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
-       pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
-
-       /*
-        * FIXME: The following two registers are not supported yet.
-        *
-        *   o Secondary Uncorrectable Error Severity Register
-        *   o Secondary Uncorrectable Error Mask Register
-        */
-}
-
-void pci_configure_slot(struct pci_dev *dev)
-{
-       struct pci_dev *cdev;
-       struct hotplug_params hpp;
-
-       if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL ||
-                       (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
-                       (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
-               return;
-
-       pcie_bus_configure_settings(dev->bus);
-
-       memset(&hpp, 0, sizeof(hpp));
-       pci_get_hp_params(dev, &hpp);
-
-       program_hpp_type2(dev, hpp.t2);
-       program_hpp_type1(dev, hpp.t1);
-       program_hpp_type0(dev, hpp.t0);
-
-       if (dev->subordinate) {
-               list_for_each_entry(cdev, &dev->subordinate->devices,
-                                   bus_list)
-                       pci_configure_slot(cdev);
-       }
-}
-EXPORT_SYMBOL_GPL(pci_configure_slot);
index a81fb67ea9a18a405dce84c2bd4b88aee4b9e6fc..10c7927599b32aa1d67d67d3f299f079333b5c8c 100644 (file)
@@ -195,7 +195,8 @@ static int change_bus_speed(struct controller *ctrl, struct slot *p_slot,
        int rc = 0;
 
        ctrl_dbg(ctrl, "Change speed to %d\n", speed);
-       if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) {
+       rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed);
+       if (rc) {
                ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
                         __func__);
                return WRONG_BUS_FREQUENCY;
@@ -261,14 +262,16 @@ static int board_added(struct slot *p_slot)
        }
 
        if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
-               if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) {
+               rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz);
+               if (rc) {
                        ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
                                 __func__);
                        return WRONG_BUS_FREQUENCY;
                }
 
                /* turn on board, blink green LED, turn off Amber LED */
-               if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
+               rc = p_slot->hpc_ops->slot_enable(p_slot);
+               if (rc) {
                        ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
                        return rc;
                }
@@ -296,7 +299,8 @@ static int board_added(struct slot *p_slot)
                return rc;
 
        /* turn on board, blink green LED, turn off Amber LED */
-       if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
+       rc = p_slot->hpc_ops->slot_enable(p_slot);
+       if (rc) {
                ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
                return rc;
        }
@@ -595,7 +599,7 @@ static int shpchp_enable_slot (struct slot *p_slot)
        ctrl_dbg(ctrl, "%s: p_slot->pwr_save %x\n", __func__, p_slot->pwr_save);
        p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
 
-       if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) ||
+       if (((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) ||
            (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458))
             && p_slot->ctrl->num_slots == 1) {
                /* handle amd pogo errata; this must be done before enable  */
index 29e22352822cb64ee7c3ae8bf3341e1d19abac1d..7d223e9080efb34aef5b1d02f77732c77b812402 100644 (file)
@@ -466,7 +466,8 @@ static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value)
        u8 m66_cap  = !!(slot_reg & MHZ66_CAP);
        u8 pi, pcix_cap;
 
-       if ((retval = hpc_get_prog_int(slot, &pi)))
+       retval = hpc_get_prog_int(slot, &pi);
+       if (retval)
                return retval;
 
        switch (pi) {
@@ -798,7 +799,7 @@ static irqreturn_t shpc_isr(int irq, void *dev_id)
 
        ctrl_dbg(ctrl, "%s: intr_loc = %x\n", __func__, intr_loc);
 
-       if(!shpchp_poll_mode) {
+       if (!shpchp_poll_mode) {
                /*
                 * Mask Global Interrupt Mask - see implementation
                 * note on p. 139 of SHPC spec rev 1.0
index 469454e0cc48d8eca080dc2f5035f87d83769f53..f8cd3a27e3515e630639e86a8e58478517f5ec2a 100644 (file)
@@ -69,13 +69,7 @@ int shpchp_configure_device(struct slot *p_slot)
        }
 
        pci_assign_unassigned_bridge_resources(bridge);
-
-       list_for_each_entry(dev, &parent->devices, bus_list) {
-               if (PCI_SLOT(dev->devfn) != p_slot->device)
-                       continue;
-               pci_configure_slot(dev);
-       }
-
+       pcie_bus_configure_settings(parent);
        pci_bus_add_devices(parent);
 
  out:
index cb6f24740ee3e5b3f6763f7b25c2bf8700e6a1b2..4d109c07294a8d7326583e43ffb594569cf1c1e7 100644 (file)
@@ -633,7 +633,7 @@ int pci_vfs_assigned(struct pci_dev *dev)
                 * our dev as the physical function and the assigned bit is set
                 */
                if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
-                   (vfdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED))
+                       pci_is_dev_assigned(vfdev))
                        vfs_assigned++;
 
                vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
index 5a40516444f33d4c46f6067e089434b9e7840cda..9fab30af0e75abdcec135707363951d7e9e26f8c 100644 (file)
@@ -56,16 +56,6 @@ void __weak arch_teardown_msi_irq(unsigned int irq)
        chip->teardown_irq(chip, irq);
 }
 
-int __weak arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
-{
-       struct msi_chip *chip = dev->bus->msi;
-
-       if (!chip || !chip->check_device)
-               return 0;
-
-       return chip->check_device(chip, dev, nvec, type);
-}
-
 int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 {
        struct msi_desc *entry;
@@ -130,7 +120,7 @@ static void default_restore_msi_irq(struct pci_dev *dev, int irq)
        }
 
        if (entry)
-               write_msi_msg(irq, &entry->msg);
+               __write_msi_msg(entry, &entry->msg);
 }
 
 void __weak arch_restore_msi_irqs(struct pci_dev *dev)
@@ -312,6 +302,7 @@ void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg)
 
        __get_cached_msi_msg(entry, msg);
 }
+EXPORT_SYMBOL_GPL(get_cached_msi_msg);
 
 void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
 {
@@ -356,6 +347,7 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg)
 
        __write_msi_msg(entry, msg);
 }
+EXPORT_SYMBOL_GPL(write_msi_msg);
 
 static void free_msi_irqs(struct pci_dev *dev)
 {
@@ -384,17 +376,6 @@ static void free_msi_irqs(struct pci_dev *dev)
                                iounmap(entry->mask_base);
                }
 
-               /*
-                * Its possible that we get into this path
-                * When populate_msi_sysfs fails, which means the entries
-                * were not registered with sysfs.  In that case don't
-                * unregister them.
-                */
-               if (entry->kobj.parent) {
-                       kobject_del(&entry->kobj);
-                       kobject_put(&entry->kobj);
-               }
-
                list_del(&entry->list);
                kfree(entry);
        }
@@ -595,7 +576,6 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev)
        entry->msi_attrib.entry_nr      = 0;
        entry->msi_attrib.maskbit       = !!(control & PCI_MSI_FLAGS_MASKBIT);
        entry->msi_attrib.default_irq   = dev->irq;     /* Save IOAPIC IRQ */
-       entry->msi_attrib.pos           = dev->msi_cap;
        entry->msi_attrib.multi_cap     = (control & PCI_MSI_FLAGS_QMASK) >> 1;
 
        if (control & PCI_MSI_FLAGS_64BIT)
@@ -699,7 +679,6 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base,
                entry->msi_attrib.is_64         = 1;
                entry->msi_attrib.entry_nr      = entries[i].entry;
                entry->msi_attrib.default_irq   = dev->irq;
-               entry->msi_attrib.pos           = dev->msix_cap;
                entry->mask_base                = base;
 
                list_add_tail(&entry->list, &dev->msi_list);
@@ -806,23 +785,24 @@ out_free:
 }
 
 /**
- * pci_msi_check_device - check whether MSI may be enabled on a device
+ * pci_msi_supported - check whether MSI may be enabled on a device
  * @dev: pointer to the pci_dev data structure of MSI device function
  * @nvec: how many MSIs have been requested ?
- * @type: are we checking for MSI or MSI-X ?
  *
  * Look at global flags, the device itself, and its parent buses
  * to determine if MSI/-X are supported for the device. If MSI/-X is
- * supported return 0, else return an error code.
+ * supported return 1, else return 0.
  **/
-static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
+static int pci_msi_supported(struct pci_dev *dev, int nvec)
 {
        struct pci_bus *bus;
-       int ret;
 
        /* MSI must be globally enabled and supported by the device */
-       if (!pci_msi_enable || !dev || dev->no_msi)
-               return -EINVAL;
+       if (!pci_msi_enable)
+               return 0;
+
+       if (!dev || dev->no_msi || dev->current_state != PCI_D0)
+               return 0;
 
        /*
         * You can't ask to have 0 or less MSIs configured.
@@ -830,7 +810,7 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
         *  b) the list manipulation code assumes nvec >= 1.
         */
        if (nvec < 1)
-               return -ERANGE;
+               return 0;
 
        /*
         * Any bridge which does NOT route MSI transactions from its
@@ -841,13 +821,9 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
         */
        for (bus = dev->bus; bus; bus = bus->parent)
                if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
-                       return -EINVAL;
-
-       ret = arch_msi_check_device(dev, nvec, type);
-       if (ret)
-               return ret;
+                       return 0;
 
-       return 0;
+       return 1;
 }
 
 /**
@@ -946,15 +922,14 @@ EXPORT_SYMBOL(pci_msix_vec_count);
  **/
 int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
 {
-       int status, nr_entries;
+       int nr_entries;
        int i, j;
 
-       if (!entries || !dev->msix_cap || dev->current_state != PCI_D0)
+       if (!pci_msi_supported(dev, nvec))
                return -EINVAL;
 
-       status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSIX);
-       if (status)
-               return status;
+       if (!entries)
+               return -EINVAL;
 
        nr_entries = pci_msix_vec_count(dev);
        if (nr_entries < 0)
@@ -978,8 +953,7 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
                dev_info(&dev->dev, "can't enable MSI-X (MSI IRQ already assigned)\n");
                return -EINVAL;
        }
-       status = msix_capability_init(dev, entries, nvec);
-       return status;
+       return msix_capability_init(dev, entries, nvec);
 }
 EXPORT_SYMBOL(pci_enable_msix);
 
@@ -1062,7 +1036,7 @@ int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
        int nvec;
        int rc;
 
-       if (dev->current_state != PCI_D0)
+       if (!pci_msi_supported(dev, minvec))
                return -EINVAL;
 
        WARN_ON(!!dev->msi_enabled);
@@ -1085,17 +1059,6 @@ int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
        else if (nvec > maxvec)
                nvec = maxvec;
 
-       do {
-               rc = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI);
-               if (rc < 0) {
-                       return rc;
-               } else if (rc > 0) {
-                       if (rc < minvec)
-                               return -ENOSPC;
-                       nvec = rc;
-               }
-       } while (rc);
-
        do {
                rc = msi_capability_init(dev, nvec);
                if (rc < 0) {
index 37263b0ebfe32570f49a3df7bfc027ab6cd562e0..6ebf8edc5f3c5cbff635ec30cab5d342616b8ab8 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/pci_hotplug.h>
 #include <linux/module.h>
 #include <linux/pci-aspm.h>
 #include <linux/pci-acpi.h>
 #include <linux/pm_qos.h>
 #include "pci.h"
 
+phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
+{
+       acpi_status status = AE_NOT_EXIST;
+       unsigned long long mcfg_addr;
+
+       if (handle)
+               status = acpi_evaluate_integer(handle, METHOD_NAME__CBA,
+                                              NULL, &mcfg_addr);
+       if (ACPI_FAILURE(status))
+               return 0;
+
+       return (phys_addr_t)mcfg_addr;
+}
+
+static acpi_status decode_type0_hpx_record(union acpi_object *record,
+                                          struct hotplug_params *hpx)
+{
+       int i;
+       union acpi_object *fields = record->package.elements;
+       u32 revision = fields[1].integer.value;
+
+       switch (revision) {
+       case 1:
+               if (record->package.count != 6)
+                       return AE_ERROR;
+               for (i = 2; i < 6; i++)
+                       if (fields[i].type != ACPI_TYPE_INTEGER)
+                               return AE_ERROR;
+               hpx->t0 = &hpx->type0_data;
+               hpx->t0->revision        = revision;
+               hpx->t0->cache_line_size = fields[2].integer.value;
+               hpx->t0->latency_timer   = fields[3].integer.value;
+               hpx->t0->enable_serr     = fields[4].integer.value;
+               hpx->t0->enable_perr     = fields[5].integer.value;
+               break;
+       default:
+               printk(KERN_WARNING
+                      "%s: Type 0 Revision %d record not supported\n",
+                      __func__, revision);
+               return AE_ERROR;
+       }
+       return AE_OK;
+}
+
+static acpi_status decode_type1_hpx_record(union acpi_object *record,
+                                          struct hotplug_params *hpx)
+{
+       int i;
+       union acpi_object *fields = record->package.elements;
+       u32 revision = fields[1].integer.value;
+
+       switch (revision) {
+       case 1:
+               if (record->package.count != 5)
+                       return AE_ERROR;
+               for (i = 2; i < 5; i++)
+                       if (fields[i].type != ACPI_TYPE_INTEGER)
+                               return AE_ERROR;
+               hpx->t1 = &hpx->type1_data;
+               hpx->t1->revision      = revision;
+               hpx->t1->max_mem_read  = fields[2].integer.value;
+               hpx->t1->avg_max_split = fields[3].integer.value;
+               hpx->t1->tot_max_split = fields[4].integer.value;
+               break;
+       default:
+               printk(KERN_WARNING
+                      "%s: Type 1 Revision %d record not supported\n",
+                      __func__, revision);
+               return AE_ERROR;
+       }
+       return AE_OK;
+}
+
+static acpi_status decode_type2_hpx_record(union acpi_object *record,
+                                          struct hotplug_params *hpx)
+{
+       int i;
+       union acpi_object *fields = record->package.elements;
+       u32 revision = fields[1].integer.value;
+
+       switch (revision) {
+       case 1:
+               if (record->package.count != 18)
+                       return AE_ERROR;
+               for (i = 2; i < 18; i++)
+                       if (fields[i].type != ACPI_TYPE_INTEGER)
+                               return AE_ERROR;
+               hpx->t2 = &hpx->type2_data;
+               hpx->t2->revision      = revision;
+               hpx->t2->unc_err_mask_and      = fields[2].integer.value;
+               hpx->t2->unc_err_mask_or       = fields[3].integer.value;
+               hpx->t2->unc_err_sever_and     = fields[4].integer.value;
+               hpx->t2->unc_err_sever_or      = fields[5].integer.value;
+               hpx->t2->cor_err_mask_and      = fields[6].integer.value;
+               hpx->t2->cor_err_mask_or       = fields[7].integer.value;
+               hpx->t2->adv_err_cap_and       = fields[8].integer.value;
+               hpx->t2->adv_err_cap_or        = fields[9].integer.value;
+               hpx->t2->pci_exp_devctl_and    = fields[10].integer.value;
+               hpx->t2->pci_exp_devctl_or     = fields[11].integer.value;
+               hpx->t2->pci_exp_lnkctl_and    = fields[12].integer.value;
+               hpx->t2->pci_exp_lnkctl_or     = fields[13].integer.value;
+               hpx->t2->sec_unc_err_sever_and = fields[14].integer.value;
+               hpx->t2->sec_unc_err_sever_or  = fields[15].integer.value;
+               hpx->t2->sec_unc_err_mask_and  = fields[16].integer.value;
+               hpx->t2->sec_unc_err_mask_or   = fields[17].integer.value;
+               break;
+       default:
+               printk(KERN_WARNING
+                      "%s: Type 2 Revision %d record not supported\n",
+                      __func__, revision);
+               return AE_ERROR;
+       }
+       return AE_OK;
+}
+
+static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
+{
+       acpi_status status;
+       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object *package, *record, *fields;
+       u32 type;
+       int i;
+
+       /* Clear the return buffer with zeros */
+       memset(hpx, 0, sizeof(struct hotplug_params));
+
+       status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       package = (union acpi_object *)buffer.pointer;
+       if (package->type != ACPI_TYPE_PACKAGE) {
+               status = AE_ERROR;
+               goto exit;
+       }
+
+       for (i = 0; i < package->package.count; i++) {
+               record = &package->package.elements[i];
+               if (record->type != ACPI_TYPE_PACKAGE) {
+                       status = AE_ERROR;
+                       goto exit;
+               }
+
+               fields = record->package.elements;
+               if (fields[0].type != ACPI_TYPE_INTEGER ||
+                   fields[1].type != ACPI_TYPE_INTEGER) {
+                       status = AE_ERROR;
+                       goto exit;
+               }
+
+               type = fields[0].integer.value;
+               switch (type) {
+               case 0:
+                       status = decode_type0_hpx_record(record, hpx);
+                       if (ACPI_FAILURE(status))
+                               goto exit;
+                       break;
+               case 1:
+                       status = decode_type1_hpx_record(record, hpx);
+                       if (ACPI_FAILURE(status))
+                               goto exit;
+                       break;
+               case 2:
+                       status = decode_type2_hpx_record(record, hpx);
+                       if (ACPI_FAILURE(status))
+                               goto exit;
+                       break;
+               default:
+                       printk(KERN_ERR "%s: Type %d record not supported\n",
+                              __func__, type);
+                       status = AE_ERROR;
+                       goto exit;
+               }
+       }
+ exit:
+       kfree(buffer.pointer);
+       return status;
+}
+
+static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
+{
+       acpi_status status;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *package, *fields;
+       int i;
+
+       memset(hpp, 0, sizeof(struct hotplug_params));
+
+       status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       package = (union acpi_object *) buffer.pointer;
+       if (package->type != ACPI_TYPE_PACKAGE ||
+           package->package.count != 4) {
+               status = AE_ERROR;
+               goto exit;
+       }
+
+       fields = package->package.elements;
+       for (i = 0; i < 4; i++) {
+               if (fields[i].type != ACPI_TYPE_INTEGER) {
+                       status = AE_ERROR;
+                       goto exit;
+               }
+       }
+
+       hpp->t0 = &hpp->type0_data;
+       hpp->t0->revision        = 1;
+       hpp->t0->cache_line_size = fields[0].integer.value;
+       hpp->t0->latency_timer   = fields[1].integer.value;
+       hpp->t0->enable_serr     = fields[2].integer.value;
+       hpp->t0->enable_perr     = fields[3].integer.value;
+
+exit:
+       kfree(buffer.pointer);
+       return status;
+}
+
+/* pci_get_hp_params
+ *
+ * @dev - the pci_dev for which we want parameters
+ * @hpp - allocated by the caller
+ */
+int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
+{
+       acpi_status status;
+       acpi_handle handle, phandle;
+       struct pci_bus *pbus;
+
+       handle = NULL;
+       for (pbus = dev->bus; pbus; pbus = pbus->parent) {
+               handle = acpi_pci_get_bridge_handle(pbus);
+               if (handle)
+                       break;
+       }
+
+       /*
+        * _HPP settings apply to all child buses, until another _HPP is
+        * encountered. If we don't find an _HPP for the input pci dev,
+        * look for it in the parent device scope since that would apply to
+        * this pci dev.
+        */
+       while (handle) {
+               status = acpi_run_hpx(handle, hpp);
+               if (ACPI_SUCCESS(status))
+                       return 0;
+               status = acpi_run_hpp(handle, hpp);
+               if (ACPI_SUCCESS(status))
+                       return 0;
+               if (acpi_is_root_bridge(handle))
+                       break;
+               status = acpi_get_parent(handle, &phandle);
+               if (ACPI_FAILURE(status))
+                       break;
+               handle = phandle;
+       }
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(pci_get_hp_params);
+
 /**
  * pci_acpi_wake_bus - Root bus wakeup notification fork function.
  * @work: Work item to handle.
@@ -84,20 +346,6 @@ acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
        return acpi_add_pm_notifier(dev, &pci_dev->dev, pci_acpi_wake_dev);
 }
 
-phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
-{
-       acpi_status status = AE_NOT_EXIST;
-       unsigned long long mcfg_addr;
-
-       if (handle)
-               status = acpi_evaluate_integer(handle, METHOD_NAME__CBA,
-                                              NULL, &mcfg_addr);
-       if (ACPI_FAILURE(status))
-               return 0;
-
-       return (phys_addr_t)mcfg_addr;
-}
-
 /*
  * _SxD returns the D-state with the highest power
  * (lowest D-state number) supported in the S-state "x".
index d04c5adafc1655624a528fb1a77b5f49fbbc3472..2b3c89425bb5d03296ceb7d5fa71213fdf5f8025 100644 (file)
@@ -55,7 +55,6 @@ int pci_add_dynid(struct pci_driver *drv,
                  unsigned long driver_data)
 {
        struct pci_dynid *dynid;
-       int retval;
 
        dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
        if (!dynid)
@@ -73,9 +72,7 @@ int pci_add_dynid(struct pci_driver *drv,
        list_add_tail(&dynid->node, &drv->dynids.list);
        spin_unlock(&drv->dynids.lock);
 
-       retval = driver_attach(&drv->driver);
-
-       return retval;
+       return driver_attach(&drv->driver);
 }
 EXPORT_SYMBOL_GPL(pci_add_dynid);
 
index 9ff0a901ecf7ed2691418845bf46d82bb2d68754..92b6d9ab00e498ac02fdd08ead2d4f3bfc76d207 100644 (file)
@@ -177,7 +177,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
 
-       return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x\n",
+       return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X\n",
                       pci_dev->vendor, pci_dev->device,
                       pci_dev->subsystem_vendor, pci_dev->subsystem_device,
                       (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8),
@@ -250,46 +250,45 @@ static ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr,
                            char *buf)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
+       struct pci_bus *subordinate = pdev->subordinate;
 
-       if (!pdev->subordinate)
-               return 0;
-
-       return sprintf(buf, "%u\n",
-                      !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
+       return sprintf(buf, "%u\n", subordinate ?
+                      !(subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI)
+                          : !pdev->no_msi);
 }
 
 static ssize_t msi_bus_store(struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t count)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
+       struct pci_bus *subordinate = pdev->subordinate;
        unsigned long val;
 
        if (kstrtoul(buf, 0, &val) < 0)
                return -EINVAL;
 
-       /*
-        * Bad things may happen if the no_msi flag is changed
-        * while drivers are loaded.
-        */
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
        /*
-        * Maybe devices without subordinate buses shouldn't have this
-        * attribute in the first place?
+        * "no_msi" and "bus_flags" only affect what happens when a driver
+        * requests MSI or MSI-X.  They don't affect any drivers that have
+        * already requested MSI or MSI-X.
         */
-       if (!pdev->subordinate)
+       if (!subordinate) {
+               pdev->no_msi = !val;
+               dev_info(&pdev->dev, "MSI/MSI-X %s for future drivers\n",
+                        val ? "allowed" : "disallowed");
                return count;
-
-       /* Is the flag going to change, or keep the value it already had? */
-       if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^
-           !!val) {
-               pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI;
-
-               dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI, bad things could happen\n",
-                        val ? "" : " not");
        }
 
+       if (val)
+               subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI;
+       else
+               subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+
+       dev_info(&subordinate->dev, "MSI/MSI-X %s for future drivers of devices on this bus\n",
+                val ? "allowed" : "disallowed");
        return count;
 }
 static DEVICE_ATTR_RW(msi_bus);
index 2c9ac70254e2dc480a868f4717319b4d01294a02..625a4ace10b4411aed1cbda278a3851d9299965d 100644 (file)
@@ -1003,12 +1003,19 @@ int pci_save_state(struct pci_dev *dev)
        for (i = 0; i < 16; i++)
                pci_read_config_dword(dev, i * 4, &dev->saved_config_space[i]);
        dev->state_saved = true;
-       if ((i = pci_save_pcie_state(dev)) != 0)
+
+       i = pci_save_pcie_state(dev);
+       if (i != 0)
                return i;
-       if ((i = pci_save_pcix_state(dev)) != 0)
+
+       i = pci_save_pcix_state(dev);
+       if (i != 0)
                return i;
-       if ((i = pci_save_vc_state(dev)) != 0)
+
+       i = pci_save_vc_state(dev);
+       if (i != 0)
                return i;
+
        return 0;
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1907,10 +1914,6 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
        if (target_state == PCI_POWER_ERROR)
                return -EIO;
 
-       /* D3cold during system suspend/hibernate is not supported */
-       if (target_state > PCI_D3hot)
-               target_state = PCI_D3hot;
-
        pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev));
 
        error = pci_set_power_state(dev, target_state);
@@ -2704,6 +2707,37 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
 }
 EXPORT_SYMBOL(pci_request_regions_exclusive);
 
+/**
+ *     pci_remap_iospace - Remap the memory mapped I/O space
+ *     @res: Resource describing the I/O space
+ *     @phys_addr: physical address of range to be mapped
+ *
+ *     Remap the memory mapped I/O space described by the @res
+ *     and the CPU physical address @phys_addr into virtual address space.
+ *     Only architectures that have memory mapped IO functions defined
+ *     (and the PCI_IOBASE value defined) should call this function.
+ */
+int __weak pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)
+{
+#if defined(PCI_IOBASE) && defined(CONFIG_MMU)
+       unsigned long vaddr = (unsigned long)PCI_IOBASE + res->start;
+
+       if (!(res->flags & IORESOURCE_IO))
+               return -EINVAL;
+
+       if (res->end > IO_SPACE_LIMIT)
+               return -EINVAL;
+
+       return ioremap_page_range(vaddr, vaddr + resource_size(res), phys_addr,
+                                 pgprot_device(PAGE_KERNEL));
+#else
+       /* this architecture does not have memory mapped I/O space,
+          so this function should never be called */
+       WARN_ONCE(1, "This architecture does not support memory mapped I/O\n");
+       return -ENODEV;
+#endif
+}
+
 static void __pci_set_master(struct pci_dev *dev, bool enable)
 {
        u16 old_cmd, cmd;
@@ -4406,6 +4440,15 @@ static void pci_no_domains(void)
 #endif
 }
 
+#ifdef CONFIG_PCI_DOMAINS
+static atomic_t __domain_nr = ATOMIC_INIT(-1);
+
+int pci_get_new_domain_nr(void)
+{
+       return atomic_inc_return(&__domain_nr);
+}
+#endif
+
 /**
  * pci_ext_cfg_avail - can we access extended PCI config space?
  *
index 35d06e177917b3726d0ec512e5ba72f01dabcb21..c6849d9e86ce6c33bc11ec8ea621e3e042878817 100644 (file)
@@ -89,15 +89,17 @@ static const char *aer_correctable_error_string[] = {
        NULL,
        "Replay Timer Timeout",         /* Bit Position 12      */
        "Advisory Non-Fatal",           /* Bit Position 13      */
+       "Corrected Internal Error",     /* Bit Position 14      */
+       "Header Log Overflow",          /* Bit Position 15      */
 };
 
 static const char *aer_uncorrectable_error_string[] = {
-       NULL,
+       "Undefined",                    /* Bit Position 0       */
        NULL,
        NULL,
        NULL,
        "Data Link Protocol",           /* Bit Position 4       */
-       NULL,
+       "Surprise Down Error",          /* Bit Position 5       */
        NULL,
        NULL,
        NULL,
@@ -113,6 +115,11 @@ static const char *aer_uncorrectable_error_string[] = {
        "Malformed TLP",                /* Bit Position 18      */
        "ECRC",                         /* Bit Position 19      */
        "Unsupported Request",          /* Bit Position 20      */
+       "ACS Violation",                /* Bit Position 21      */
+       "Uncorrectable Internal Error", /* Bit Position 22      */
+       "MC Blocked TLP",               /* Bit Position 23      */
+       "AtomicOp Egress Blocked",      /* Bit Position 24      */
+       "TLP Prefix Blocked Error",     /* Bit Position 25      */
 };
 
 static const char *aer_agent_string[] = {
index 82e06a86cd77b38ae99316e624b1cf72f26c7dd2..a9f9c46e50221d75eefc73457425876199f79a0e 100644 (file)
@@ -41,11 +41,17 @@ static int __init pcie_pme_setup(char *str)
 }
 __setup("pcie_pme=", pcie_pme_setup);
 
+enum pme_suspend_level {
+       PME_SUSPEND_NONE = 0,
+       PME_SUSPEND_WAKEUP,
+       PME_SUSPEND_NOIRQ,
+};
+
 struct pcie_pme_service_data {
        spinlock_t lock;
        struct pcie_device *srv;
        struct work_struct work;
-       bool noirq; /* Don't enable the PME interrupt used by this service. */
+       enum pme_suspend_level suspend_level;
 };
 
 /**
@@ -223,7 +229,7 @@ static void pcie_pme_work_fn(struct work_struct *work)
        spin_lock_irq(&data->lock);
 
        for (;;) {
-               if (data->noirq)
+               if (data->suspend_level != PME_SUSPEND_NONE)
                        break;
 
                pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
@@ -250,7 +256,7 @@ static void pcie_pme_work_fn(struct work_struct *work)
                spin_lock_irq(&data->lock);
        }
 
-       if (!data->noirq)
+       if (data->suspend_level == PME_SUSPEND_NONE)
                pcie_pme_interrupt_enable(port, true);
 
        spin_unlock_irq(&data->lock);
@@ -367,6 +373,21 @@ static int pcie_pme_probe(struct pcie_device *srv)
        return ret;
 }
 
+static bool pcie_pme_check_wakeup(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       if (!bus)
+               return false;
+
+       list_for_each_entry(dev, &bus->devices, bus_list)
+               if (device_may_wakeup(&dev->dev)
+                   || pcie_pme_check_wakeup(dev->subordinate))
+                       return true;
+
+       return false;
+}
+
 /**
  * pcie_pme_suspend - Suspend PCIe PME service device.
  * @srv: PCIe service device to suspend.
@@ -375,11 +396,26 @@ static int pcie_pme_suspend(struct pcie_device *srv)
 {
        struct pcie_pme_service_data *data = get_service_data(srv);
        struct pci_dev *port = srv->port;
+       bool wakeup;
 
+       if (device_may_wakeup(&port->dev)) {
+               wakeup = true;
+       } else {
+               down_read(&pci_bus_sem);
+               wakeup = pcie_pme_check_wakeup(port->subordinate);
+               up_read(&pci_bus_sem);
+       }
        spin_lock_irq(&data->lock);
-       pcie_pme_interrupt_enable(port, false);
-       pcie_clear_root_pme_status(port);
-       data->noirq = true;
+       if (wakeup) {
+               enable_irq_wake(srv->irq);
+               data->suspend_level = PME_SUSPEND_WAKEUP;
+       } else {
+               struct pci_dev *port = srv->port;
+
+               pcie_pme_interrupt_enable(port, false);
+               pcie_clear_root_pme_status(port);
+               data->suspend_level = PME_SUSPEND_NOIRQ;
+       }
        spin_unlock_irq(&data->lock);
 
        synchronize_irq(srv->irq);
@@ -394,12 +430,17 @@ static int pcie_pme_suspend(struct pcie_device *srv)
 static int pcie_pme_resume(struct pcie_device *srv)
 {
        struct pcie_pme_service_data *data = get_service_data(srv);
-       struct pci_dev *port = srv->port;
 
        spin_lock_irq(&data->lock);
-       data->noirq = false;
-       pcie_clear_root_pme_status(port);
-       pcie_pme_interrupt_enable(port, true);
+       if (data->suspend_level == PME_SUSPEND_NOIRQ) {
+               struct pci_dev *port = srv->port;
+
+               pcie_clear_root_pme_status(port);
+               pcie_pme_interrupt_enable(port, true);
+       } else {
+               disable_irq_wake(srv->irq);
+       }
+       data->suspend_level = PME_SUSPEND_NONE;
        spin_unlock_irq(&data->lock);
 
        return 0;
index 2ccc9b926ea77d4c38d2b3ffb6214e2bf6e682f9..be35da2e105e0b39449244416f8ba8934446f58a 100644 (file)
@@ -93,77 +93,6 @@ static int pcie_port_resume_noirq(struct device *dev)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
-struct d3cold_info {
-       bool no_d3cold;
-       unsigned int d3cold_delay;
-};
-
-static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data)
-{
-       struct d3cold_info *info = data;
-
-       info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay,
-                                  info->d3cold_delay);
-       if (pdev->no_d3cold)
-               info->no_d3cold = true;
-       return 0;
-}
-
-static int pcie_port_runtime_suspend(struct device *dev)
-{
-       struct pci_dev *pdev = to_pci_dev(dev);
-       struct d3cold_info d3cold_info = {
-               .no_d3cold      = false,
-               .d3cold_delay   = PCI_PM_D3_WAIT,
-       };
-
-       /*
-        * If any subordinate device disable D3cold, we should not put
-        * the port into D3cold.  The D3cold delay of port should be
-        * the max of that of all subordinate devices.
-        */
-       pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info);
-       pdev->no_d3cold = d3cold_info.no_d3cold;
-       pdev->d3cold_delay = d3cold_info.d3cold_delay;
-       return 0;
-}
-
-static int pcie_port_runtime_resume(struct device *dev)
-{
-       return 0;
-}
-
-static int pci_dev_pme_poll(struct pci_dev *pdev, void *data)
-{
-       bool *pme_poll = data;
-
-       if (pdev->pme_poll)
-               *pme_poll = true;
-       return 0;
-}
-
-static int pcie_port_runtime_idle(struct device *dev)
-{
-       struct pci_dev *pdev = to_pci_dev(dev);
-       bool pme_poll = false;
-
-       /*
-        * If any subordinate device needs pme poll, we should keep
-        * the port in D0, because we need port in D0 to poll it.
-        */
-       pci_walk_bus(pdev->subordinate, pci_dev_pme_poll, &pme_poll);
-       /* Delay for a short while to prevent too frequent suspend/resume */
-       if (!pme_poll)
-               pm_schedule_suspend(dev, 10);
-       return -EBUSY;
-}
-#else
-#define pcie_port_runtime_suspend      NULL
-#define pcie_port_runtime_resume       NULL
-#define pcie_port_runtime_idle         NULL
-#endif
-
 static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .suspend        = pcie_port_device_suspend,
        .resume         = pcie_port_device_resume,
@@ -172,9 +101,6 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .poweroff       = pcie_port_device_suspend,
        .restore        = pcie_port_device_resume,
        .resume_noirq   = pcie_port_resume_noirq,
-       .runtime_suspend = pcie_port_runtime_suspend,
-       .runtime_resume = pcie_port_runtime_resume,
-       .runtime_idle   = pcie_port_runtime_idle,
 };
 
 #define PCIE_PORTDRV_PM_OPS    (&pcie_portdrv_pm_ops)
index 4170113cde6141a9962b23a9d6342647a0465210..5ed99309c75800938b666c5c523d7ea3ca07d51f 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/pci_hotplug.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/cpumask.h>
@@ -485,7 +486,7 @@ void pci_read_bridge_bases(struct pci_bus *child)
        }
 }
 
-static struct pci_bus *pci_alloc_bus(void)
+static struct pci_bus *pci_alloc_bus(struct pci_bus *parent)
 {
        struct pci_bus *b;
 
@@ -500,6 +501,10 @@ static struct pci_bus *pci_alloc_bus(void)
        INIT_LIST_HEAD(&b->resources);
        b->max_bus_speed = PCI_SPEED_UNKNOWN;
        b->cur_bus_speed = PCI_SPEED_UNKNOWN;
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+       if (parent)
+               b->domain_nr = parent->domain_nr;
+#endif
        return b;
 }
 
@@ -671,7 +676,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
        /*
         * Allocate a new bus, and inherit stuff from the parent..
         */
-       child = pci_alloc_bus();
+       child = pci_alloc_bus(parent);
        if (!child)
                return NULL;
 
@@ -740,6 +745,17 @@ struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev,
 }
 EXPORT_SYMBOL(pci_add_new_bus);
 
+static void pci_enable_crs(struct pci_dev *pdev)
+{
+       u16 root_cap = 0;
+
+       /* Enable CRS Software Visibility if supported */
+       pcie_capability_read_word(pdev, PCI_EXP_RTCAP, &root_cap);
+       if (root_cap & PCI_EXP_RTCAP_CRSVIS)
+               pcie_capability_set_word(pdev, PCI_EXP_RTCTL,
+                                        PCI_EXP_RTCTL_CRSSVE);
+}
+
 /*
  * If it's a bridge, configure it and scan the bus behind it.
  * For CardBus bridges, we don't scan behind as the devices will
@@ -787,6 +803,8 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
        pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
                              bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
 
+       pci_enable_crs(dev);
+
        if ((secondary || subordinate) && !pcibios_assign_all_busses() &&
            !is_cardbus && !broken) {
                unsigned int cmax;
@@ -1226,6 +1244,137 @@ int pci_setup_device(struct pci_dev *dev)
        return 0;
 }
 
+static struct hpp_type0 pci_default_type0 = {
+       .revision = 1,
+       .cache_line_size = 8,
+       .latency_timer = 0x40,
+       .enable_serr = 0,
+       .enable_perr = 0,
+};
+
+static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
+{
+       u16 pci_cmd, pci_bctl;
+
+       if (!hpp)
+               hpp = &pci_default_type0;
+
+       if (hpp->revision > 1) {
+               dev_warn(&dev->dev,
+                        "PCI settings rev %d not supported; using defaults\n",
+                        hpp->revision);
+               hpp = &pci_default_type0;
+       }
+
+       pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
+       pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
+       pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
+       if (hpp->enable_serr)
+               pci_cmd |= PCI_COMMAND_SERR;
+       if (hpp->enable_perr)
+               pci_cmd |= PCI_COMMAND_PARITY;
+       pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
+
+       /* Program bridge control value */
+       if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+               pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
+                                     hpp->latency_timer);
+               pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
+               if (hpp->enable_serr)
+                       pci_bctl |= PCI_BRIDGE_CTL_SERR;
+               if (hpp->enable_perr)
+                       pci_bctl |= PCI_BRIDGE_CTL_PARITY;
+               pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
+       }
+}
+
+static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
+{
+       if (hpp)
+               dev_warn(&dev->dev, "PCI-X settings not supported\n");
+}
+
+static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
+{
+       int pos;
+       u32 reg32;
+
+       if (!hpp)
+               return;
+
+       if (hpp->revision > 1) {
+               dev_warn(&dev->dev, "PCIe settings rev %d not supported\n",
+                        hpp->revision);
+               return;
+       }
+
+       /*
+        * Don't allow _HPX to change MPS or MRRS settings.  We manage
+        * those to make sure they're consistent with the rest of the
+        * platform.
+        */
+       hpp->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD |
+                                   PCI_EXP_DEVCTL_READRQ;
+       hpp->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD |
+                                   PCI_EXP_DEVCTL_READRQ);
+
+       /* Initialize Device Control Register */
+       pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+                       ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
+
+       /* Initialize Link Control Register */
+       if (dev->subordinate)
+               pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
+                       ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
+
+       /* Find Advanced Error Reporting Enhanced Capability */
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+       if (!pos)
+               return;
+
+       /* Initialize Uncorrectable Error Mask Register */
+       pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &reg32);
+       reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
+       pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
+
+       /* Initialize Uncorrectable Error Severity Register */
+       pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &reg32);
+       reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
+       pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
+
+       /* Initialize Correctable Error Mask Register */
+       pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg32);
+       reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
+       pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
+
+       /* Initialize Advanced Error Capabilities and Control Register */
+       pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
+       reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
+       pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
+
+       /*
+        * FIXME: The following two registers are not supported yet.
+        *
+        *   o Secondary Uncorrectable Error Severity Register
+        *   o Secondary Uncorrectable Error Mask Register
+        */
+}
+
+static void pci_configure_device(struct pci_dev *dev)
+{
+       struct hotplug_params hpp;
+       int ret;
+
+       memset(&hpp, 0, sizeof(hpp));
+       ret = pci_get_hp_params(dev, &hpp);
+       if (ret)
+               return;
+
+       program_hpp_type2(dev, hpp.t2);
+       program_hpp_type1(dev, hpp.t1);
+       program_hpp_type0(dev, hpp.t0);
+}
+
 static void pci_release_capabilities(struct pci_dev *dev)
 {
        pci_vpd_release(dev);
@@ -1282,8 +1431,13 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
            *l == 0x0000ffff || *l == 0xffff0000)
                return false;
 
-       /* Configuration request Retry Status */
-       while (*l == 0xffff0001) {
+       /*
+        * Configuration Request Retry Status.  Some root ports return the
+        * actual device ID instead of the synthetic ID (0xFFFF) required
+        * by the PCIe spec.  Ignore the device ID and only check for
+        * (vendor id == 1).
+        */
+       while ((*l & 0xffff) == 0x0001) {
                if (!crs_timeout)
                        return false;
 
@@ -1363,6 +1517,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
 {
        int ret;
 
+       pci_configure_device(dev);
+
        device_initialize(&dev->dev);
        dev->dev.release = pci_release_dev;
 
@@ -1751,13 +1907,14 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
        char bus_addr[64];
        char *fmt;
 
-       b = pci_alloc_bus();
+       b = pci_alloc_bus(NULL);
        if (!b)
                return NULL;
 
        b->sysdata = sysdata;
        b->ops = ops;
        b->number = b->busn_res.start = bus;
+       pci_bus_assign_domain_nr(b, parent);
        b2 = pci_find_bus(pci_domain_nr(b), bus);
        if (b2) {
                /* If we already got to this bus through a different bridge, ignore it */
index 80c2d014283d12c86d83ad22e98531bc8121cdc3..90acb32c85b1c637aa918a13b236c17c015b73c8 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/ioport.h>
 #include <linux/sched.h>
 #include <linux/ktime.h>
+#include <linux/mm.h>
 #include <asm/dma.h>   /* isa_dma_bridge_buggy */
 #include "pci.h"
 
@@ -287,6 +288,25 @@ static void quirk_citrine(struct pci_dev *dev)
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM,    PCI_DEVICE_ID_IBM_CITRINE,      quirk_citrine);
 
+/*  On IBM Crocodile ipr SAS adapters, expand BAR to system page size */
+static void quirk_extend_bar_to_page(struct pci_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < PCI_STD_RESOURCE_END; i++) {
+               struct resource *r = &dev->resource[i];
+
+               if (r->flags & IORESOURCE_MEM && resource_size(r) < PAGE_SIZE) {
+                       r->end = PAGE_SIZE - 1;
+                       r->start = 0;
+                       r->flags |= IORESOURCE_UNSET;
+                       dev_info(&dev->dev, "expanded BAR %d to page size: %pR\n",
+                                i, r);
+               }
+       }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, 0x034a, quirk_extend_bar_to_page);
+
 /*
  *  S3 868 and 968 chips report region size equal to 32M, but they decode 64M.
  *  If it's needed, re-allocate the region.
@@ -2985,6 +3005,8 @@ DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */
  */
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169,
                         quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MELLANOX, PCI_ANY_ID,
+                        quirk_broken_intx_masking);
 
 #ifdef CONFIG_ACPI
 /*
@@ -3512,57 +3534,6 @@ DECLARE_PCI_FIXUP_HEADER(0x1283, 0x8892, quirk_use_pcie_bridge_dma_alias);
 /* Intel 82801, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c49 */
 DECLARE_PCI_FIXUP_HEADER(0x8086, 0x244e, quirk_use_pcie_bridge_dma_alias);
 
-static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev)
-{
-       if (!PCI_FUNC(dev->devfn))
-               return pci_dev_get(dev);
-
-       return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
-}
-
-static const struct pci_dev_dma_source {
-       u16 vendor;
-       u16 device;
-       struct pci_dev *(*dma_source)(struct pci_dev *dev);
-} pci_dev_dma_source[] = {
-       /*
-        * https://bugzilla.redhat.com/show_bug.cgi?id=605888
-        *
-        * Some Ricoh devices use the function 0 source ID for DMA on
-        * other functions of a multifunction device.  The DMA devices
-        * is therefore function 0, which will have implications of the
-        * iommu grouping of these devices.
-        */
-       { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source },
-       { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source },
-       { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source },
-       { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source },
-       { 0 }
-};
-
-/*
- * IOMMUs with isolation capabilities need to be programmed with the
- * correct source ID of a device.  In most cases, the source ID matches
- * the device doing the DMA, but sometimes hardware is broken and will
- * tag the DMA as being sourced from a different device.  This function
- * allows that translation.  Note that the reference count of the
- * returned device is incremented on all paths.
- */
-struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
-{
-       const struct pci_dev_dma_source *i;
-
-       for (i = pci_dev_dma_source; i->dma_source; i++) {
-               if ((i->vendor == dev->vendor ||
-                    i->vendor == (u16)PCI_ANY_ID) &&
-                   (i->device == dev->device ||
-                    i->device == (u16)PCI_ANY_ID))
-                       return i->dma_source(dev);
-       }
-
-       return pci_dev_get(dev);
-}
-
 /*
  * AMD has indicated that the devices below do not support peer-to-peer
  * in any system where they are found in the southbridge with an AMD
@@ -3582,6 +3553,11 @@ struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
  * 1002:439d SB7x0/SB8x0/SB9x0 LPC host controller
  * 1002:4384 SBx00 PCI to PCI Bridge
  * 1002:4399 SB7x0/SB8x0/SB9x0 USB OHCI2 Controller
+ *
+ * https://bugzilla.kernel.org/show_bug.cgi?id=81841#c15
+ *
+ * 1022:780f [AMD] FCH PCI Bridge
+ * 1022:7809 [AMD] FCH USB OHCI Controller
  */
 static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags)
 {
@@ -3664,6 +3640,23 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
        return acs_flags & ~flags ? 0 : 1;
 }
 
+static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags)
+{
+       /*
+        * SV, TB, and UF are not relevant to multifunction endpoints.
+        *
+        * Multifunction devices are only required to implement RR, CR, and DT
+        * in their ACS capability if they support peer-to-peer transactions.
+        * Devices matching this quirk have been verified by the vendor to not
+        * perform peer-to-peer with other functions, allowing us to mask out
+        * these bits as if they were unimplemented in the ACS capability.
+        */
+       acs_flags &= ~(PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR |
+                      PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_DT);
+
+       return acs_flags ? 0 : 1;
+}
+
 static const struct pci_dev_acs_enabled {
        u16 vendor;
        u16 device;
@@ -3675,6 +3668,30 @@ static const struct pci_dev_acs_enabled {
        { PCI_VENDOR_ID_ATI, 0x439d, pci_quirk_amd_sb_acs },
        { PCI_VENDOR_ID_ATI, 0x4384, pci_quirk_amd_sb_acs },
        { PCI_VENDOR_ID_ATI, 0x4399, pci_quirk_amd_sb_acs },
+       { PCI_VENDOR_ID_AMD, 0x780f, pci_quirk_amd_sb_acs },
+       { PCI_VENDOR_ID_AMD, 0x7809, pci_quirk_amd_sb_acs },
+       { PCI_VENDOR_ID_SOLARFLARE, 0x0903, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_SOLARFLARE, 0x0923, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10C6, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10DB, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10DD, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10E1, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10F1, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10F7, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10F8, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10F9, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10FA, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10FB, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x10FC, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x1507, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x1514, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x151C, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x1529, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x152A, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x154D, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x154F, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x1551, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x1558, pci_quirk_mf_endpoint_acs },
        { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
        { 0 }
 };
index 827ad831f1dd67335cf3918ed960f81579fc8405..a81f413083e49e5751013bf4f0602a2605286dc2 100644 (file)
@@ -103,40 +103,6 @@ int pci_for_each_dma_alias(struct pci_dev *pdev,
        return ret;
 }
 
-/*
- * find the upstream PCIe-to-PCI bridge of a PCI device
- * if the device is PCIE, return NULL
- * if the device isn't connected to a PCIe bridge (that is its parent is a
- * legacy PCI bridge and the bridge is directly connected to bus 0), return its
- * parent
- */
-struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
-{
-       struct pci_dev *tmp = NULL;
-
-       if (pci_is_pcie(pdev))
-               return NULL;
-       while (1) {
-               if (pci_is_root_bus(pdev->bus))
-                       break;
-               pdev = pdev->bus->self;
-               /* a p2p bridge */
-               if (!pci_is_pcie(pdev)) {
-                       tmp = pdev;
-                       continue;
-               }
-               /* PCI device should connect to a PCIe bridge */
-               if (pci_pcie_type(pdev) != PCI_EXP_TYPE_PCI_BRIDGE) {
-                       /* Busted hardware? */
-                       WARN_ON_ONCE(1);
-                       return NULL;
-               }
-               return pdev;
-       }
-
-       return tmp;
-}
-
 static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
 {
        struct pci_bus *child;
index 6373985ad3f7ba2326c955d660029878b51ddf53..0482235eee9262f79ecd69f907c8c41c2364d914 100644 (file)
@@ -1652,7 +1652,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
        struct pci_dev_resource *fail_res;
        int retval;
        unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
-                                 IORESOURCE_PREFETCH;
+                                 IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
 
 again:
        __pci_bus_size_bridges(parent, &add_list);
index 53df39a22c8acb20f6dda5dff8aa4050516dbfb2..116ca3746adb47cd5887bad8b8014ccdb5641b9c 100644 (file)
@@ -1136,11 +1136,13 @@ static const struct xenbus_device_id xenpci_ids[] = {
        {""},
 };
 
-static DEFINE_XENBUS_DRIVER(xenpci, "pcifront",
+static struct xenbus_driver xenpci_driver = {
+       .name                   = "pcifront",
+       .ids                    = xenpci_ids,
        .probe                  = pcifront_xenbus_probe,
        .remove                 = pcifront_xenbus_remove,
        .otherend_changed       = pcifront_backend_changed,
-);
+};
 
 static int __init pcifront_init(void)
 {
index 64d06b52f98a8650c10510391121085a5f6c398c..c6a66de6ed72d0d4e96d061737d4ba3b9a4f3cd3 100644 (file)
@@ -86,7 +86,7 @@ config PINCTRL_BCM2835
 
 config PINCTRL_BCM281XX
        bool "Broadcom BCM281xx pinctrl driver"
-       depends on OF
+       depends on OF && (ARCH_BCM_MOBILE || COMPILE_TEST)
        select PINMUX
        select PINCONF
        select GENERIC_PINCONF
index 47f49314986356eb86497d17e251dc7cbecc2957..22897282713251291c47f926b6042c3689993d6c 100644 (file)
@@ -1174,7 +1174,7 @@ static int abx500_gpio_probe(struct platform_device *pdev)
        const struct of_device_id *match;
        struct abx500_pinctrl *pct;
        unsigned int id = -1;
-       int ret, err;
+       int ret;
        int i;
 
        if (!np) {
@@ -1266,10 +1266,7 @@ static int abx500_gpio_probe(struct platform_device *pdev)
        return 0;
 
 out_rem_chip:
-       err = gpiochip_remove(&pct->chip);
-       if (err)
-               dev_info(&pdev->dev, "failed to remove gpiochip\n");
-
+       gpiochip_remove(&pct->chip);
        return ret;
 }
 
@@ -1280,15 +1277,8 @@ out_rem_chip:
 static int abx500_gpio_remove(struct platform_device *pdev)
 {
        struct abx500_pinctrl *pct = platform_get_drvdata(pdev);
-       int ret;
-
-       ret = gpiochip_remove(&pct->chip);
-       if (ret < 0) {
-               dev_err(pct->dev, "unable to remove gpiochip: %d\n",
-                       ret);
-               return ret;
-       }
 
+       gpiochip_remove(&pct->chip);
        return 0;
 }
 
index 3c29d9187146d88d8326d4a54ea224fbaa7b13a0..746db6acf64865d9bf7713f79d6744508bbc9f25 100644 (file)
@@ -1276,7 +1276,7 @@ static int nmk_gpio_probe(struct platform_device *dev)
                                   IRQ_TYPE_EDGE_FALLING);
        if (ret) {
                dev_err(&dev->dev, "could not add irqchip\n");
-               ret = gpiochip_remove(&nmk_chip->chip);
+               gpiochip_remove(&nmk_chip->chip);
                return -ENODEV;
        }
        /* Then register the chain on the parent IRQ */
index d30dddd213231a7c54bfc2c6765d7733d4a5f5a6..e730935fa4577deb2582ea493ab60aa2b0f176fe 100644 (file)
@@ -936,14 +936,8 @@ EXPORT_SYMBOL(msm_pinctrl_probe);
 int msm_pinctrl_remove(struct platform_device *pdev)
 {
        struct msm_pinctrl *pctrl = platform_get_drvdata(pdev);
-       int ret;
-
-       ret = gpiochip_remove(&pctrl->chip);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to remove gpiochip\n");
-               return ret;
-       }
 
+       gpiochip_remove(&pctrl->chip);
        pinctrl_unregister(pctrl->pctrl);
 
        unregister_restart_handler(&pctrl->restart_nb);
index b995ec2c5d16134e1fa274f1d212171fae0b1bc1..88acfc0efd543085f033e7218dd0a5f77290d044 100644 (file)
@@ -874,11 +874,7 @@ static int exynos5440_gpiolib_register(struct platform_device *pdev,
 static int exynos5440_gpiolib_unregister(struct platform_device *pdev,
                                struct exynos5440_pinctrl_priv_data *priv)
 {
-       int ret = gpiochip_remove(priv->gc);
-       if (ret) {
-               dev_err(&pdev->dev, "gpio chip remove failed\n");
-               return ret;
-       }
+       gpiochip_remove(priv->gc);
        return 0;
 }
 
index 4a47691c32b1c81fe28118cbc04adf2d809776a5..2d37c8f49f3c95ed11a42abce1f52ea5124b9c11 100644 (file)
@@ -946,9 +946,7 @@ static int samsung_gpiolib_register(struct platform_device *pdev,
 
 fail:
        for (--i, --bank; i >= 0; --i, --bank)
-               if (gpiochip_remove(&bank->gpio_chip))
-                       dev_err(&pdev->dev, "gpio chip %s remove failed\n",
-                                                       bank->gpio_chip.label);
+               gpiochip_remove(&bank->gpio_chip);
        return ret;
 }
 
@@ -958,16 +956,11 @@ static int samsung_gpiolib_unregister(struct platform_device *pdev,
 {
        struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
        struct samsung_pin_bank *bank = ctrl->pin_banks;
-       int ret = 0;
        int i;
 
-       for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank)
-               ret = gpiochip_remove(&bank->gpio_chip);
-
-       if (ret)
-               dev_err(&pdev->dev, "gpio chip remove failed\n");
-
-       return ret;
+       for (i = 0; i < ctrl->nr_banks; ++i, ++bank)
+               gpiochip_remove(&bank->gpio_chip);
+       return 0;
 }
 
 static const struct of_device_id samsung_pinctrl_dt_match[];
index b713bd59ffbb8f11b5283bff796c8e239e1a8f2d..4c831fdfcc2fb151db757198e5497a3a5d9497fc 100644 (file)
@@ -891,8 +891,7 @@ static int sirfsoc_gpio_probe(struct device_node *np)
 
 out_no_range:
 out_banks:
-       if (gpiochip_remove(&sgpio->chip.gc))
-               dev_err(&pdev->dev, "could not remove gpio chip\n");
+       gpiochip_remove(&sgpio->chip.gc);
 out:
        iounmap(regs);
        return err;
index 87aa28c4280fe815fa488006c814e026c1c99350..2655d4a988f36ae1ac18510b850d7b7b70a741ba 100644 (file)
@@ -1050,6 +1050,13 @@ static struct acpi_driver acpi_fujitsu_hotkey_driver = {
                },
 };
 
+static const struct acpi_device_id fujitsu_ids[] __used = {
+       {ACPI_FUJITSU_HID, 0},
+       {ACPI_FUJITSU_HOTKEY_HID, 0},
+       {"", 0}
+};
+MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
+
 static int __init fujitsu_init(void)
 {
        int ret, result, max_brightness;
@@ -1208,12 +1215,3 @@ MODULE_LICENSE("GPL");
 MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
 MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*");
 MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
-
-static struct pnp_device_id pnp_ids[] __used = {
-       {.id = "FUJ02bf"},
-       {.id = "FUJ02B1"},
-       {.id = "FUJ02E3"},
-       {.id = ""}
-};
-
-MODULE_DEVICE_TABLE(pnp, pnp_ids);
index 40929e4f7ad73f407bada0062d3fd8e8bb5debe5..04fed00b88e9546a159a6585b6a0326da55e16a2 100644 (file)
@@ -301,8 +301,7 @@ static int platform_pmic_gpio_probe(struct platform_device *pdev)
        return 0;
 
 fail_request_irq:
-       if (gpiochip_remove(&pg->chip))
-               pr_err("gpiochip_remove failed\n");
+       gpiochip_remove(&pg->chip);
 err:
        iounmap(pg->gpiointr);
 err2:
index 2a1008b61121ae6cfa49f5a757037ade4d14ed25..7f3d389bd601e7d616c8d528b64a248706c3131a 100644 (file)
@@ -10,3 +10,11 @@ menuconfig POWER_AVS
          AVS is also called SmartReflex on OMAP devices.
 
          Say Y here to enable Adaptive Voltage Scaling class support.
+
+config ROCKCHIP_IODOMAIN
+        tristate "Rockchip IO domain support"
+        depends on ARCH_ROCKCHIP && OF
+        help
+          Say y here to enable support io domains on Rockchip SoCs. It is
+          necessary for the io domain setting of the SoC to match the
+          voltage supplied by the regulators.
index 0843386a6c1951e6fe5b44f59d9a43992409a922..ba4c7bc6922533dcc15627ecaff584a0e3d122e9 100644 (file)
@@ -1 +1,2 @@
 obj-$(CONFIG_POWER_AVS_OMAP)           += smartreflex.o
+obj-$(CONFIG_ROCKCHIP_IODOMAIN)                += rockchip-io-domain.o
diff --git a/drivers/power/avs/rockchip-io-domain.c b/drivers/power/avs/rockchip-io-domain.c
new file mode 100644 (file)
index 0000000..3ae35d0
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Rockchip IO Voltage Domain driver
+ *
+ * Copyright 2014 MundoReader S.L.
+ * Copyright 2014 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define MAX_SUPPLIES           16
+
+/*
+ * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under
+ * "Recommended Operating Conditions" for "Digital GPIO".   When the typical
+ * is 3.3V the max is 3.6V.  When the typical is 1.8V the max is 1.98V.
+ *
+ * They are used like this:
+ * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the
+ *   SoC we're at 3.3.
+ * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider
+ *   that to be an error.
+ */
+#define MAX_VOLTAGE_1_8                1980000
+#define MAX_VOLTAGE_3_3                3600000
+
+#define RK3288_SOC_CON2                        0x24c
+#define RK3288_SOC_CON2_FLASH0         BIT(7)
+#define RK3288_SOC_FLASH_SUPPLY_NUM    2
+
+struct rockchip_iodomain;
+
+/**
+ * @supplies: voltage settings matching the register bits.
+ */
+struct rockchip_iodomain_soc_data {
+       int grf_offset;
+       const char *supply_names[MAX_SUPPLIES];
+       void (*init)(struct rockchip_iodomain *iod);
+};
+
+struct rockchip_iodomain_supply {
+       struct rockchip_iodomain *iod;
+       struct regulator *reg;
+       struct notifier_block nb;
+       int idx;
+};
+
+struct rockchip_iodomain {
+       struct device *dev;
+       struct regmap *grf;
+       struct rockchip_iodomain_soc_data *soc_data;
+       struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
+};
+
+static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,
+                                  int uV)
+{
+       struct rockchip_iodomain *iod = supply->iod;
+       u32 val;
+       int ret;
+
+       /* set value bit */
+       val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1;
+       val <<= supply->idx;
+
+       /* apply hiword-mask */
+       val |= (BIT(supply->idx) << 16);
+
+       ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val);
+       if (ret)
+               dev_err(iod->dev, "Couldn't write to GRF\n");
+
+       return ret;
+}
+
+static int rockchip_iodomain_notify(struct notifier_block *nb,
+                                   unsigned long event,
+                                   void *data)
+{
+       struct rockchip_iodomain_supply *supply =
+                       container_of(nb, struct rockchip_iodomain_supply, nb);
+       int uV;
+       int ret;
+
+       /*
+        * According to Rockchip it's important to keep the SoC IO domain
+        * higher than (or equal to) the external voltage.  That means we need
+        * to change it before external voltage changes happen in the case
+        * of an increase.
+        *
+        * Note that in the "pre" change we pick the max possible voltage that
+        * the regulator might end up at (the client requests a range and we
+        * don't know for certain the exact voltage).  Right now we rely on the
+        * slop in MAX_VOLTAGE_1_8 and MAX_VOLTAGE_3_3 to save us if clients
+        * request something like a max of 3.6V when they really want 3.3V.
+        * We could attempt to come up with better rules if this fails.
+        */
+       if (event & REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) {
+               struct pre_voltage_change_data *pvc_data = data;
+
+               uV = max_t(unsigned long, pvc_data->old_uV, pvc_data->max_uV);
+       } else if (event & (REGULATOR_EVENT_VOLTAGE_CHANGE |
+                           REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE)) {
+               uV = (unsigned long)data;
+       } else {
+               return NOTIFY_OK;
+       }
+
+       dev_dbg(supply->iod->dev, "Setting to %d\n", uV);
+
+       if (uV > MAX_VOLTAGE_3_3) {
+               dev_err(supply->iod->dev, "Voltage too high: %d\n", uV);
+
+               if (event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
+                       return NOTIFY_BAD;
+       }
+
+       ret = rockchip_iodomain_write(supply, uV);
+       if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
+               return NOTIFY_BAD;
+
+       dev_info(supply->iod->dev, "Setting to %d done\n", uV);
+       return NOTIFY_OK;
+}
+
+static void rk3288_iodomain_init(struct rockchip_iodomain *iod)
+{
+       int ret;
+       u32 val;
+
+       /* if no flash supply we should leave things alone */
+       if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg)
+               return;
+
+       /*
+        * set flash0 iodomain to also use this framework
+        * instead of a special gpio.
+        */
+       val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16);
+       ret = regmap_write(iod->grf, RK3288_SOC_CON2, val);
+       if (ret < 0)
+               dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
+}
+
+/*
+ * On the rk3188 the io-domains are handled by a shared register with the
+ * lower 8 bits being still being continuing drive-strength settings.
+ */
+static const struct rockchip_iodomain_soc_data soc_data_rk3188 = {
+       .grf_offset = 0x104,
+       .supply_names = {
+               NULL,
+               NULL,
+               NULL,
+               NULL,
+               NULL,
+               NULL,
+               NULL,
+               NULL,
+               "ap0",
+               "ap1",
+               "cif",
+               "flash",
+               "vccio0",
+               "vccio1",
+               "lcdc0",
+               "lcdc1",
+       },
+};
+
+static const struct rockchip_iodomain_soc_data soc_data_rk3288 = {
+       .grf_offset = 0x380,
+       .supply_names = {
+               "lcdc",         /* LCDC_VDD */
+               "dvp",          /* DVPIO_VDD */
+               "flash0",       /* FLASH0_VDD (emmc) */
+               "flash1",       /* FLASH1_VDD (sdio1) */
+               "wifi",         /* APIO3_VDD  (sdio0) */
+               "bb",           /* APIO5_VDD */
+               "audio",        /* APIO4_VDD */
+               "sdcard",       /* SDMMC0_VDD (sdmmc) */
+               "gpio30",       /* APIO1_VDD */
+               "gpio1830",     /* APIO2_VDD */
+       },
+       .init = rk3288_iodomain_init,
+};
+
+static const struct of_device_id rockchip_iodomain_match[] = {
+       {
+               .compatible = "rockchip,rk3188-io-voltage-domain",
+               .data = (void *)&soc_data_rk3188
+       },
+       {
+               .compatible = "rockchip,rk3288-io-voltage-domain",
+               .data = (void *)&soc_data_rk3288
+       },
+       { /* sentinel */ },
+};
+
+static int rockchip_iodomain_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       const struct of_device_id *match;
+       struct rockchip_iodomain *iod;
+       int i, ret = 0;
+
+       if (!np)
+               return -ENODEV;
+
+       iod = devm_kzalloc(&pdev->dev, sizeof(*iod), GFP_KERNEL);
+       if (!iod)
+               return -ENOMEM;
+
+       iod->dev = &pdev->dev;
+       platform_set_drvdata(pdev, iod);
+
+       match = of_match_node(rockchip_iodomain_match, np);
+       iod->soc_data = (struct rockchip_iodomain_soc_data *)match->data;
+
+       iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+       if (IS_ERR(iod->grf)) {
+               dev_err(&pdev->dev, "couldn't find grf regmap\n");
+               return PTR_ERR(iod->grf);
+       }
+
+       for (i = 0; i < MAX_SUPPLIES; i++) {
+               const char *supply_name = iod->soc_data->supply_names[i];
+               struct rockchip_iodomain_supply *supply = &iod->supplies[i];
+               struct regulator *reg;
+               int uV;
+
+               if (!supply_name)
+                       continue;
+
+               reg = devm_regulator_get_optional(iod->dev, supply_name);
+               if (IS_ERR(reg)) {
+                       ret = PTR_ERR(reg);
+
+                       /* If a supply wasn't specified, that's OK */
+                       if (ret == -ENODEV)
+                               continue;
+                       else if (ret != -EPROBE_DEFER)
+                               dev_err(iod->dev, "couldn't get regulator %s\n",
+                                       supply_name);
+                       goto unreg_notify;
+               }
+
+               /* set initial correct value */
+               uV = regulator_get_voltage(reg);
+
+               /* must be a regulator we can get the voltage of */
+               if (uV < 0) {
+                       dev_err(iod->dev, "Can't determine voltage: %s\n",
+                               supply_name);
+                       goto unreg_notify;
+               }
+
+               if (uV > MAX_VOLTAGE_3_3) {
+                       dev_crit(iod->dev,
+                                "%d uV is too high. May damage SoC!\n",
+                                uV);
+                       ret = -EINVAL;
+                       goto unreg_notify;
+               }
+
+               /* setup our supply */
+               supply->idx = i;
+               supply->iod = iod;
+               supply->reg = reg;
+               supply->nb.notifier_call = rockchip_iodomain_notify;
+
+               ret = rockchip_iodomain_write(supply, uV);
+               if (ret) {
+                       supply->reg = NULL;
+                       goto unreg_notify;
+               }
+
+               /* register regulator notifier */
+               ret = regulator_register_notifier(reg, &supply->nb);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "regulator notifier request failed\n");
+                       supply->reg = NULL;
+                       goto unreg_notify;
+               }
+       }
+
+       if (iod->soc_data->init)
+               iod->soc_data->init(iod);
+
+       return 0;
+
+unreg_notify:
+       for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
+               struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
+
+               if (io_supply->reg)
+                       regulator_unregister_notifier(io_supply->reg,
+                                                     &io_supply->nb);
+       }
+
+       return ret;
+}
+
+static int rockchip_iodomain_remove(struct platform_device *pdev)
+{
+       struct rockchip_iodomain *iod = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
+               struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
+
+               if (io_supply->reg)
+                       regulator_unregister_notifier(io_supply->reg,
+                                                     &io_supply->nb);
+       }
+
+       return 0;
+}
+
+static struct platform_driver rockchip_iodomain_driver = {
+       .probe   = rockchip_iodomain_probe,
+       .remove  = rockchip_iodomain_remove,
+       .driver  = {
+               .name  = "rockchip-iodomain",
+               .of_match_table = rockchip_iodomain_match,
+       },
+};
+
+module_platform_driver(rockchip_iodomain_driver);
+
+MODULE_DESCRIPTION("Rockchip IO-domain driver");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
+MODULE_LICENSE("GPL v2");
index 3e51f8d29bfefe34a47224fa967b0dde1e20b2c1..edd707ee7281154ae11995fcfc196c63120a927f 100644 (file)
@@ -20,7 +20,8 @@
 
 static void restart_poweroff_do_poweroff(void)
 {
-       arm_pm_restart(REBOOT_HARD, NULL);
+       reboot_mode = REBOOT_HARD;
+       machine_restart(NULL);
 }
 
 static int restart_poweroff_probe(struct platform_device *pdev)
index e85e64a07d02f1852df460c1d8dc613821dfef91..296619b7426c5cb1de04a4556e9531a919106fff 100644 (file)
@@ -587,6 +587,16 @@ config VMWARE_PVSCSI
          To compile this driver as a module, choose M here: the
          module will be called vmw_pvscsi.
 
+config XEN_SCSI_FRONTEND
+       tristate "XEN SCSI frontend driver"
+       depends on SCSI && XEN
+       select XEN_XENBUS_FRONTEND
+       help
+         The XEN SCSI frontend driver allows the kernel to access SCSI Devices
+         within another guest OS (usually Dom0).
+         Only needed if the kernel is running in a XEN guest and generic
+         SCSI access to a device is needed.
+
 config HYPERV_STORAGE
        tristate "Microsoft Hyper-V virtual storage driver"
        depends on SCSI && HYPERV
index 5f0d299b009384446a07ff99587e346fbdfdb4fb..59f1ce6df2d6461af2136a784252f44d0dc351c1 100644 (file)
@@ -141,6 +141,7 @@ obj-$(CONFIG_SCSI_ESAS2R)   += esas2r/
 obj-$(CONFIG_SCSI_PMCRAID)     += pmcraid.o
 obj-$(CONFIG_SCSI_VIRTIO)      += virtio_scsi.o
 obj-$(CONFIG_VMWARE_PVSCSI)    += vmw_pvscsi.o
+obj-$(CONFIG_XEN_SCSI_FRONTEND)        += xen-scsifront.o
 obj-$(CONFIG_HYPERV_STORAGE)   += hv_storvsc.o
 
 obj-$(CONFIG_ARM)              += arm/
index ce458885127457ac88653ae47ba9553bfd9ea6c8..ee16f0c5c47d06acb040561859e4fe62e1ed0111 100644 (file)
@@ -32,7 +32,6 @@
 
 #define MASK(n)        ((1 << (n)) - 1)        /* make an n-bit mask */
 
-#define PCI_VENDOR_ID_VMWARE           0x15AD
 #define PCI_DEVICE_ID_VMWARE_PVSCSI    0x07C0
 
 /*
diff --git a/drivers/scsi/xen-scsifront.c b/drivers/scsi/xen-scsifront.c
new file mode 100644 (file)
index 0000000..34199d2
--- /dev/null
@@ -0,0 +1,1026 @@
+/*
+ * Xen SCSI frontend driver
+ *
+ * Copyright (c) 2008, FUJITSU Limited
+ *
+ * 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; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/blkdev.h>
+#include <linux/pfn.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+#include <xen/grant_table.h>
+#include <xen/events.h>
+#include <xen/page.h>
+
+#include <xen/interface/grant_table.h>
+#include <xen/interface/io/vscsiif.h>
+#include <xen/interface/io/protocols.h>
+
+#include <asm/xen/hypervisor.h>
+
+
+#define GRANT_INVALID_REF      0
+
+#define VSCSIFRONT_OP_ADD_LUN  1
+#define VSCSIFRONT_OP_DEL_LUN  2
+
+/* Tuning point. */
+#define VSCSIIF_DEFAULT_CMD_PER_LUN 10
+#define VSCSIIF_MAX_TARGET          64
+#define VSCSIIF_MAX_LUN             255
+
+#define VSCSIIF_RING_SIZE      __CONST_RING_SIZE(vscsiif, PAGE_SIZE)
+#define VSCSIIF_MAX_REQS       VSCSIIF_RING_SIZE
+
+#define vscsiif_grants_sg(_sg) (PFN_UP((_sg) *         \
+                               sizeof(struct scsiif_request_segment)))
+
+struct vscsifrnt_shadow {
+       /* command between backend and frontend */
+       unsigned char act;
+       uint16_t rqid;
+
+       unsigned int nr_grants;         /* number of grants in gref[] */
+       struct scsiif_request_segment *sg;      /* scatter/gather elements */
+
+       /* Do reset or abort function. */
+       wait_queue_head_t wq_reset;     /* reset work queue           */
+       int wait_reset;                 /* reset work queue condition */
+       int32_t rslt_reset;             /* reset response status:     */
+                                       /* SUCCESS or FAILED or:      */
+#define RSLT_RESET_WAITING     0
+#define RSLT_RESET_ERR         -1
+
+       /* Requested struct scsi_cmnd is stored from kernel. */
+       struct scsi_cmnd *sc;
+       int gref[vscsiif_grants_sg(SG_ALL) + SG_ALL];
+};
+
+struct vscsifrnt_info {
+       struct xenbus_device *dev;
+
+       struct Scsi_Host *host;
+       int host_active;
+
+       unsigned int evtchn;
+       unsigned int irq;
+
+       grant_ref_t ring_ref;
+       struct vscsiif_front_ring ring;
+       struct vscsiif_response ring_rsp;
+
+       spinlock_t shadow_lock;
+       DECLARE_BITMAP(shadow_free_bitmap, VSCSIIF_MAX_REQS);
+       struct vscsifrnt_shadow *shadow[VSCSIIF_MAX_REQS];
+
+       wait_queue_head_t wq_sync;
+       unsigned int wait_ring_available:1;
+
+       char dev_state_path[64];
+       struct task_struct *curr;
+};
+
+static DEFINE_MUTEX(scsifront_mutex);
+
+static void scsifront_wake_up(struct vscsifrnt_info *info)
+{
+       info->wait_ring_available = 0;
+       wake_up(&info->wq_sync);
+}
+
+static int scsifront_get_rqid(struct vscsifrnt_info *info)
+{
+       unsigned long flags;
+       int free;
+
+       spin_lock_irqsave(&info->shadow_lock, flags);
+
+       free = find_first_bit(info->shadow_free_bitmap, VSCSIIF_MAX_REQS);
+       __clear_bit(free, info->shadow_free_bitmap);
+
+       spin_unlock_irqrestore(&info->shadow_lock, flags);
+
+       return free;
+}
+
+static int _scsifront_put_rqid(struct vscsifrnt_info *info, uint32_t id)
+{
+       int empty = bitmap_empty(info->shadow_free_bitmap, VSCSIIF_MAX_REQS);
+
+       __set_bit(id, info->shadow_free_bitmap);
+       info->shadow[id] = NULL;
+
+       return empty || info->wait_ring_available;
+}
+
+static void scsifront_put_rqid(struct vscsifrnt_info *info, uint32_t id)
+{
+       unsigned long flags;
+       int kick;
+
+       spin_lock_irqsave(&info->shadow_lock, flags);
+       kick = _scsifront_put_rqid(info, id);
+       spin_unlock_irqrestore(&info->shadow_lock, flags);
+
+       if (kick)
+               scsifront_wake_up(info);
+}
+
+static struct vscsiif_request *scsifront_pre_req(struct vscsifrnt_info *info)
+{
+       struct vscsiif_front_ring *ring = &(info->ring);
+       struct vscsiif_request *ring_req;
+       uint32_t id;
+
+       id = scsifront_get_rqid(info);  /* use id in response */
+       if (id >= VSCSIIF_MAX_REQS)
+               return NULL;
+
+       ring_req = RING_GET_REQUEST(&(info->ring), ring->req_prod_pvt);
+
+       ring->req_prod_pvt++;
+
+       ring_req->rqid = (uint16_t)id;
+
+       return ring_req;
+}
+
+static void scsifront_do_request(struct vscsifrnt_info *info)
+{
+       struct vscsiif_front_ring *ring = &(info->ring);
+       int notify;
+
+       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(ring, notify);
+       if (notify)
+               notify_remote_via_irq(info->irq);
+}
+
+static void scsifront_gnttab_done(struct vscsifrnt_info *info, uint32_t id)
+{
+       struct vscsifrnt_shadow *s = info->shadow[id];
+       int i;
+
+       if (s->sc->sc_data_direction == DMA_NONE)
+               return;
+
+       for (i = 0; i < s->nr_grants; i++) {
+               if (unlikely(gnttab_query_foreign_access(s->gref[i]) != 0)) {
+                       shost_printk(KERN_ALERT, info->host, KBUILD_MODNAME
+                                    "grant still in use by backend\n");
+                       BUG();
+               }
+               gnttab_end_foreign_access(s->gref[i], 0, 0UL);
+       }
+
+       kfree(s->sg);
+}
+
+static void scsifront_cdb_cmd_done(struct vscsifrnt_info *info,
+                                  struct vscsiif_response *ring_rsp)
+{
+       struct scsi_cmnd *sc;
+       uint32_t id;
+       uint8_t sense_len;
+
+       id = ring_rsp->rqid;
+       sc = info->shadow[id]->sc;
+
+       BUG_ON(sc == NULL);
+
+       scsifront_gnttab_done(info, id);
+       scsifront_put_rqid(info, id);
+
+       sc->result = ring_rsp->rslt;
+       scsi_set_resid(sc, ring_rsp->residual_len);
+
+       sense_len = min_t(uint8_t, VSCSIIF_SENSE_BUFFERSIZE,
+                         ring_rsp->sense_len);
+
+       if (sense_len)
+               memcpy(sc->sense_buffer, ring_rsp->sense_buffer, sense_len);
+
+       sc->scsi_done(sc);
+}
+
+static void scsifront_sync_cmd_done(struct vscsifrnt_info *info,
+                                   struct vscsiif_response *ring_rsp)
+{
+       uint16_t id = ring_rsp->rqid;
+       unsigned long flags;
+       struct vscsifrnt_shadow *shadow = info->shadow[id];
+       int kick;
+
+       spin_lock_irqsave(&info->shadow_lock, flags);
+       shadow->wait_reset = 1;
+       switch (shadow->rslt_reset) {
+       case RSLT_RESET_WAITING:
+               shadow->rslt_reset = ring_rsp->rslt;
+               break;
+       case RSLT_RESET_ERR:
+               kick = _scsifront_put_rqid(info, id);
+               spin_unlock_irqrestore(&info->shadow_lock, flags);
+               kfree(shadow);
+               if (kick)
+                       scsifront_wake_up(info);
+               return;
+       default:
+               shost_printk(KERN_ERR, info->host, KBUILD_MODNAME
+                            "bad reset state %d, possibly leaking %u\n",
+                            shadow->rslt_reset, id);
+               break;
+       }
+       spin_unlock_irqrestore(&info->shadow_lock, flags);
+
+       wake_up(&shadow->wq_reset);
+}
+
+static int scsifront_cmd_done(struct vscsifrnt_info *info)
+{
+       struct vscsiif_response *ring_rsp;
+       RING_IDX i, rp;
+       int more_to_do = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(info->host->host_lock, flags);
+
+       rp = info->ring.sring->rsp_prod;
+       rmb();  /* ordering required respective to dom0 */
+       for (i = info->ring.rsp_cons; i != rp; i++) {
+
+               ring_rsp = RING_GET_RESPONSE(&info->ring, i);
+
+               if (WARN(ring_rsp->rqid >= VSCSIIF_MAX_REQS ||
+                        test_bit(ring_rsp->rqid, info->shadow_free_bitmap),
+                        "illegal rqid %u returned by backend!\n",
+                        ring_rsp->rqid))
+                       continue;
+
+               if (info->shadow[ring_rsp->rqid]->act == VSCSIIF_ACT_SCSI_CDB)
+                       scsifront_cdb_cmd_done(info, ring_rsp);
+               else
+                       scsifront_sync_cmd_done(info, ring_rsp);
+       }
+
+       info->ring.rsp_cons = i;
+
+       if (i != info->ring.req_prod_pvt)
+               RING_FINAL_CHECK_FOR_RESPONSES(&info->ring, more_to_do);
+       else
+               info->ring.sring->rsp_event = i + 1;
+
+       info->wait_ring_available = 0;
+
+       spin_unlock_irqrestore(info->host->host_lock, flags);
+
+       wake_up(&info->wq_sync);
+
+       return more_to_do;
+}
+
+static irqreturn_t scsifront_irq_fn(int irq, void *dev_id)
+{
+       struct vscsifrnt_info *info = dev_id;
+
+       while (scsifront_cmd_done(info))
+               /* Yield point for this unbounded loop. */
+               cond_resched();
+
+       return IRQ_HANDLED;
+}
+
+static int map_data_for_request(struct vscsifrnt_info *info,
+                               struct scsi_cmnd *sc,
+                               struct vscsiif_request *ring_req,
+                               struct vscsifrnt_shadow *shadow)
+{
+       grant_ref_t gref_head;
+       struct page *page;
+       int err, ref, ref_cnt = 0;
+       int grant_ro = (sc->sc_data_direction == DMA_TO_DEVICE);
+       unsigned int i, off, len, bytes;
+       unsigned int data_len = scsi_bufflen(sc);
+       unsigned int data_grants = 0, seg_grants = 0;
+       struct scatterlist *sg;
+       unsigned long mfn;
+       struct scsiif_request_segment *seg;
+
+       ring_req->nr_segments = 0;
+       if (sc->sc_data_direction == DMA_NONE || !data_len)
+               return 0;
+
+       scsi_for_each_sg(sc, sg, scsi_sg_count(sc), i)
+               data_grants += PFN_UP(sg->offset + sg->length);
+
+       if (data_grants > VSCSIIF_SG_TABLESIZE) {
+               if (data_grants > info->host->sg_tablesize) {
+                       shost_printk(KERN_ERR, info->host, KBUILD_MODNAME
+                            "Unable to map request_buffer for command!\n");
+                       return -E2BIG;
+               }
+               seg_grants = vscsiif_grants_sg(data_grants);
+               shadow->sg = kcalloc(data_grants,
+                       sizeof(struct scsiif_request_segment), GFP_ATOMIC);
+               if (!shadow->sg)
+                       return -ENOMEM;
+       }
+       seg = shadow->sg ? : ring_req->seg;
+
+       err = gnttab_alloc_grant_references(seg_grants + data_grants,
+                                           &gref_head);
+       if (err) {
+               kfree(shadow->sg);
+               shost_printk(KERN_ERR, info->host, KBUILD_MODNAME
+                            "gnttab_alloc_grant_references() error\n");
+               return -ENOMEM;
+       }
+
+       if (seg_grants) {
+               page = virt_to_page(seg);
+               off = (unsigned long)seg & ~PAGE_MASK;
+               len = sizeof(struct scsiif_request_segment) * data_grants;
+               while (len > 0) {
+                       bytes = min_t(unsigned int, len, PAGE_SIZE - off);
+
+                       ref = gnttab_claim_grant_reference(&gref_head);
+                       BUG_ON(ref == -ENOSPC);
+
+                       mfn = pfn_to_mfn(page_to_pfn(page));
+                       gnttab_grant_foreign_access_ref(ref,
+                               info->dev->otherend_id, mfn, 1);
+                       shadow->gref[ref_cnt] = ref;
+                       ring_req->seg[ref_cnt].gref   = ref;
+                       ring_req->seg[ref_cnt].offset = (uint16_t)off;
+                       ring_req->seg[ref_cnt].length = (uint16_t)bytes;
+
+                       page++;
+                       len -= bytes;
+                       off = 0;
+                       ref_cnt++;
+               }
+               BUG_ON(seg_grants < ref_cnt);
+               seg_grants = ref_cnt;
+       }
+
+       scsi_for_each_sg(sc, sg, scsi_sg_count(sc), i) {
+               page = sg_page(sg);
+               off = sg->offset;
+               len = sg->length;
+
+               while (len > 0 && data_len > 0) {
+                       /*
+                        * sg sends a scatterlist that is larger than
+                        * the data_len it wants transferred for certain
+                        * IO sizes.
+                        */
+                       bytes = min_t(unsigned int, len, PAGE_SIZE - off);
+                       bytes = min(bytes, data_len);
+
+                       ref = gnttab_claim_grant_reference(&gref_head);
+                       BUG_ON(ref == -ENOSPC);
+
+                       mfn = pfn_to_mfn(page_to_pfn(page));
+                       gnttab_grant_foreign_access_ref(ref,
+                               info->dev->otherend_id, mfn, grant_ro);
+
+                       shadow->gref[ref_cnt] = ref;
+                       seg->gref   = ref;
+                       seg->offset = (uint16_t)off;
+                       seg->length = (uint16_t)bytes;
+
+                       page++;
+                       seg++;
+                       len -= bytes;
+                       data_len -= bytes;
+                       off = 0;
+                       ref_cnt++;
+               }
+       }
+
+       if (seg_grants)
+               ring_req->nr_segments = VSCSIIF_SG_GRANT | seg_grants;
+       else
+               ring_req->nr_segments = (uint8_t)ref_cnt;
+       shadow->nr_grants = ref_cnt;
+
+       return 0;
+}
+
+static struct vscsiif_request *scsifront_command2ring(
+               struct vscsifrnt_info *info, struct scsi_cmnd *sc,
+               struct vscsifrnt_shadow *shadow)
+{
+       struct vscsiif_request *ring_req;
+
+       memset(shadow, 0, sizeof(*shadow));
+
+       ring_req = scsifront_pre_req(info);
+       if (!ring_req)
+               return NULL;
+
+       info->shadow[ring_req->rqid] = shadow;
+       shadow->rqid = ring_req->rqid;
+
+       ring_req->id      = sc->device->id;
+       ring_req->lun     = sc->device->lun;
+       ring_req->channel = sc->device->channel;
+       ring_req->cmd_len = sc->cmd_len;
+
+       BUG_ON(sc->cmd_len > VSCSIIF_MAX_COMMAND_SIZE);
+
+       memcpy(ring_req->cmnd, sc->cmnd, sc->cmd_len);
+
+       ring_req->sc_data_direction   = (uint8_t)sc->sc_data_direction;
+       ring_req->timeout_per_command = sc->request->timeout / HZ;
+
+       return ring_req;
+}
+
+static int scsifront_queuecommand(struct Scsi_Host *shost,
+                                 struct scsi_cmnd *sc)
+{
+       struct vscsifrnt_info *info = shost_priv(shost);
+       struct vscsiif_request *ring_req;
+       struct vscsifrnt_shadow *shadow = scsi_cmd_priv(sc);
+       unsigned long flags;
+       int err;
+       uint16_t rqid;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (RING_FULL(&info->ring))
+               goto busy;
+
+       ring_req = scsifront_command2ring(info, sc, shadow);
+       if (!ring_req)
+               goto busy;
+
+       sc->result = 0;
+
+       rqid = ring_req->rqid;
+       ring_req->act = VSCSIIF_ACT_SCSI_CDB;
+
+       shadow->sc  = sc;
+       shadow->act = VSCSIIF_ACT_SCSI_CDB;
+
+       err = map_data_for_request(info, sc, ring_req, shadow);
+       if (err < 0) {
+               pr_debug("%s: err %d\n", __func__, err);
+               scsifront_put_rqid(info, rqid);
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               if (err == -ENOMEM)
+                       return SCSI_MLQUEUE_HOST_BUSY;
+               sc->result = DID_ERROR << 16;
+               sc->scsi_done(sc);
+               return 0;
+       }
+
+       scsifront_do_request(info);
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       return 0;
+
+busy:
+       spin_unlock_irqrestore(shost->host_lock, flags);
+       pr_debug("%s: busy\n", __func__);
+       return SCSI_MLQUEUE_HOST_BUSY;
+}
+
+/*
+ * Any exception handling (reset or abort) must be forwarded to the backend.
+ * We have to wait until an answer is returned. This answer contains the
+ * result to be returned to the requestor.
+ */
+static int scsifront_action_handler(struct scsi_cmnd *sc, uint8_t act)
+{
+       struct Scsi_Host *host = sc->device->host;
+       struct vscsifrnt_info *info = shost_priv(host);
+       struct vscsifrnt_shadow *shadow, *s = scsi_cmd_priv(sc);
+       struct vscsiif_request *ring_req;
+       int err = 0;
+
+       shadow = kmalloc(sizeof(*shadow), GFP_NOIO);
+       if (!shadow)
+               return FAILED;
+
+       spin_lock_irq(host->host_lock);
+
+       for (;;) {
+               if (!RING_FULL(&info->ring)) {
+                       ring_req = scsifront_command2ring(info, sc, shadow);
+                       if (ring_req)
+                               break;
+               }
+               if (err) {
+                       spin_unlock_irq(host->host_lock);
+                       kfree(shadow);
+                       return FAILED;
+               }
+               info->wait_ring_available = 1;
+               spin_unlock_irq(host->host_lock);
+               err = wait_event_interruptible(info->wq_sync,
+                                              !info->wait_ring_available);
+               spin_lock_irq(host->host_lock);
+       }
+
+       ring_req->act = act;
+       ring_req->ref_rqid = s->rqid;
+
+       shadow->act = act;
+       shadow->rslt_reset = RSLT_RESET_WAITING;
+       init_waitqueue_head(&shadow->wq_reset);
+
+       ring_req->nr_segments = 0;
+
+       scsifront_do_request(info);
+
+       spin_unlock_irq(host->host_lock);
+       err = wait_event_interruptible(shadow->wq_reset, shadow->wait_reset);
+       spin_lock_irq(host->host_lock);
+
+       if (!err) {
+               err = shadow->rslt_reset;
+               scsifront_put_rqid(info, shadow->rqid);
+               kfree(shadow);
+       } else {
+               spin_lock(&info->shadow_lock);
+               shadow->rslt_reset = RSLT_RESET_ERR;
+               spin_unlock(&info->shadow_lock);
+               err = FAILED;
+       }
+
+       spin_unlock_irq(host->host_lock);
+       return err;
+}
+
+static int scsifront_eh_abort_handler(struct scsi_cmnd *sc)
+{
+       pr_debug("%s\n", __func__);
+       return scsifront_action_handler(sc, VSCSIIF_ACT_SCSI_ABORT);
+}
+
+static int scsifront_dev_reset_handler(struct scsi_cmnd *sc)
+{
+       pr_debug("%s\n", __func__);
+       return scsifront_action_handler(sc, VSCSIIF_ACT_SCSI_RESET);
+}
+
+static int scsifront_sdev_configure(struct scsi_device *sdev)
+{
+       struct vscsifrnt_info *info = shost_priv(sdev->host);
+
+       if (info && current == info->curr)
+               xenbus_printf(XBT_NIL, info->dev->nodename,
+                             info->dev_state_path, "%d", XenbusStateConnected);
+
+       return 0;
+}
+
+static void scsifront_sdev_destroy(struct scsi_device *sdev)
+{
+       struct vscsifrnt_info *info = shost_priv(sdev->host);
+
+       if (info && current == info->curr)
+               xenbus_printf(XBT_NIL, info->dev->nodename,
+                             info->dev_state_path, "%d", XenbusStateClosed);
+}
+
+static struct scsi_host_template scsifront_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = "Xen SCSI frontend driver",
+       .queuecommand           = scsifront_queuecommand,
+       .eh_abort_handler       = scsifront_eh_abort_handler,
+       .eh_device_reset_handler = scsifront_dev_reset_handler,
+       .slave_configure        = scsifront_sdev_configure,
+       .slave_destroy          = scsifront_sdev_destroy,
+       .cmd_per_lun            = VSCSIIF_DEFAULT_CMD_PER_LUN,
+       .can_queue              = VSCSIIF_MAX_REQS,
+       .this_id                = -1,
+       .cmd_size               = sizeof(struct vscsifrnt_shadow),
+       .sg_tablesize           = VSCSIIF_SG_TABLESIZE,
+       .use_clustering         = DISABLE_CLUSTERING,
+       .proc_name              = "scsifront",
+};
+
+static int scsifront_alloc_ring(struct vscsifrnt_info *info)
+{
+       struct xenbus_device *dev = info->dev;
+       struct vscsiif_sring *sring;
+       int err = -ENOMEM;
+
+       /***** Frontend to Backend ring start *****/
+       sring = (struct vscsiif_sring *)__get_free_page(GFP_KERNEL);
+       if (!sring) {
+               xenbus_dev_fatal(dev, err,
+                       "fail to allocate shared ring (Front to Back)");
+               return err;
+       }
+       SHARED_RING_INIT(sring);
+       FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
+
+       err = xenbus_grant_ring(dev, virt_to_mfn(sring));
+       if (err < 0) {
+               free_page((unsigned long)sring);
+               xenbus_dev_fatal(dev, err,
+                       "fail to grant shared ring (Front to Back)");
+               return err;
+       }
+       info->ring_ref = err;
+
+       err = xenbus_alloc_evtchn(dev, &info->evtchn);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "xenbus_alloc_evtchn");
+               goto free_gnttab;
+       }
+
+       err = bind_evtchn_to_irq(info->evtchn);
+       if (err <= 0) {
+               xenbus_dev_fatal(dev, err, "bind_evtchn_to_irq");
+               goto free_gnttab;
+       }
+
+       info->irq = err;
+
+       err = request_threaded_irq(info->irq, NULL, scsifront_irq_fn,
+                                  IRQF_ONESHOT, "scsifront", info);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "request_threaded_irq");
+               goto free_irq;
+       }
+
+       return 0;
+
+/* free resource */
+free_irq:
+       unbind_from_irqhandler(info->irq, info);
+free_gnttab:
+       gnttab_end_foreign_access(info->ring_ref, 0,
+                                 (unsigned long)info->ring.sring);
+
+       return err;
+}
+
+static int scsifront_init_ring(struct vscsifrnt_info *info)
+{
+       struct xenbus_device *dev = info->dev;
+       struct xenbus_transaction xbt;
+       int err;
+
+       pr_debug("%s\n", __func__);
+
+       err = scsifront_alloc_ring(info);
+       if (err)
+               return err;
+       pr_debug("%s: %u %u\n", __func__, info->ring_ref, info->evtchn);
+
+again:
+       err = xenbus_transaction_start(&xbt);
+       if (err)
+               xenbus_dev_fatal(dev, err, "starting transaction");
+
+       err = xenbus_printf(xbt, dev->nodename, "ring-ref", "%u",
+                           info->ring_ref);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "%s", "writing ring-ref");
+               goto fail;
+       }
+
+       err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+                           info->evtchn);
+
+       if (err) {
+               xenbus_dev_fatal(dev, err, "%s", "writing event-channel");
+               goto fail;
+       }
+
+       err = xenbus_transaction_end(xbt, 0);
+       if (err) {
+               if (err == -EAGAIN)
+                       goto again;
+               xenbus_dev_fatal(dev, err, "completing transaction");
+               goto free_sring;
+       }
+
+       return 0;
+
+fail:
+       xenbus_transaction_end(xbt, 1);
+free_sring:
+       unbind_from_irqhandler(info->irq, info);
+       gnttab_end_foreign_access(info->ring_ref, 0,
+                                 (unsigned long)info->ring.sring);
+
+       return err;
+}
+
+
+static int scsifront_probe(struct xenbus_device *dev,
+                          const struct xenbus_device_id *id)
+{
+       struct vscsifrnt_info *info;
+       struct Scsi_Host *host;
+       int err = -ENOMEM;
+       char name[TASK_COMM_LEN];
+
+       host = scsi_host_alloc(&scsifront_sht, sizeof(*info));
+       if (!host) {
+               xenbus_dev_fatal(dev, err, "fail to allocate scsi host");
+               return err;
+       }
+       info = (struct vscsifrnt_info *)host->hostdata;
+
+       dev_set_drvdata(&dev->dev, info);
+       info->dev = dev;
+
+       bitmap_fill(info->shadow_free_bitmap, VSCSIIF_MAX_REQS);
+
+       err = scsifront_init_ring(info);
+       if (err) {
+               scsi_host_put(host);
+               return err;
+       }
+
+       init_waitqueue_head(&info->wq_sync);
+       spin_lock_init(&info->shadow_lock);
+
+       snprintf(name, TASK_COMM_LEN, "vscsiif.%d", host->host_no);
+
+       host->max_id      = VSCSIIF_MAX_TARGET;
+       host->max_channel = 0;
+       host->max_lun     = VSCSIIF_MAX_LUN;
+       host->max_sectors = (host->sg_tablesize - 1) * PAGE_SIZE / 512;
+       host->max_cmd_len = VSCSIIF_MAX_COMMAND_SIZE;
+
+       err = scsi_add_host(host, &dev->dev);
+       if (err) {
+               dev_err(&dev->dev, "fail to add scsi host %d\n", err);
+               goto free_sring;
+       }
+       info->host = host;
+       info->host_active = 1;
+
+       xenbus_switch_state(dev, XenbusStateInitialised);
+
+       return 0;
+
+free_sring:
+       unbind_from_irqhandler(info->irq, info);
+       gnttab_end_foreign_access(info->ring_ref, 0,
+                                 (unsigned long)info->ring.sring);
+       scsi_host_put(host);
+       return err;
+}
+
+static int scsifront_remove(struct xenbus_device *dev)
+{
+       struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
+
+       pr_debug("%s: %s removed\n", __func__, dev->nodename);
+
+       mutex_lock(&scsifront_mutex);
+       if (info->host_active) {
+               /* Scsi_host not yet removed */
+               scsi_remove_host(info->host);
+               info->host_active = 0;
+       }
+       mutex_unlock(&scsifront_mutex);
+
+       gnttab_end_foreign_access(info->ring_ref, 0,
+                                 (unsigned long)info->ring.sring);
+       unbind_from_irqhandler(info->irq, info);
+
+       scsi_host_put(info->host);
+
+       return 0;
+}
+
+static void scsifront_disconnect(struct vscsifrnt_info *info)
+{
+       struct xenbus_device *dev = info->dev;
+       struct Scsi_Host *host = info->host;
+
+       pr_debug("%s: %s disconnect\n", __func__, dev->nodename);
+
+       /*
+        * When this function is executed, all devices of
+        * Frontend have been deleted.
+        * Therefore, it need not block I/O before remove_host.
+        */
+
+       mutex_lock(&scsifront_mutex);
+       if (info->host_active) {
+               scsi_remove_host(host);
+               info->host_active = 0;
+       }
+       mutex_unlock(&scsifront_mutex);
+
+       xenbus_frontend_closed(dev);
+}
+
+static void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op)
+{
+       struct xenbus_device *dev = info->dev;
+       int i, err = 0;
+       char str[64];
+       char **dir;
+       unsigned int dir_n = 0;
+       unsigned int device_state;
+       unsigned int hst, chn, tgt, lun;
+       struct scsi_device *sdev;
+
+       dir = xenbus_directory(XBT_NIL, dev->otherend, "vscsi-devs", &dir_n);
+       if (IS_ERR(dir))
+               return;
+
+       /* mark current task as the one allowed to modify device states */
+       BUG_ON(info->curr);
+       info->curr = current;
+
+       for (i = 0; i < dir_n; i++) {
+               /* read status */
+               snprintf(str, sizeof(str), "vscsi-devs/%s/state", dir[i]);
+               err = xenbus_scanf(XBT_NIL, dev->otherend, str, "%u",
+                                  &device_state);
+               if (XENBUS_EXIST_ERR(err))
+                       continue;
+
+               /* virtual SCSI device */
+               snprintf(str, sizeof(str), "vscsi-devs/%s/v-dev", dir[i]);
+               err = xenbus_scanf(XBT_NIL, dev->otherend, str,
+                                  "%u:%u:%u:%u", &hst, &chn, &tgt, &lun);
+               if (XENBUS_EXIST_ERR(err))
+                       continue;
+
+               /*
+                * Front device state path, used in slave_configure called
+                * on successfull scsi_add_device, and in slave_destroy called
+                * on remove of a device.
+                */
+               snprintf(info->dev_state_path, sizeof(info->dev_state_path),
+                        "vscsi-devs/%s/state", dir[i]);
+
+               switch (op) {
+               case VSCSIFRONT_OP_ADD_LUN:
+                       if (device_state != XenbusStateInitialised)
+                               break;
+
+                       if (scsi_add_device(info->host, chn, tgt, lun)) {
+                               dev_err(&dev->dev, "scsi_add_device\n");
+                               xenbus_printf(XBT_NIL, dev->nodename,
+                                             info->dev_state_path,
+                                             "%d", XenbusStateClosed);
+                       }
+                       break;
+               case VSCSIFRONT_OP_DEL_LUN:
+                       if (device_state != XenbusStateClosing)
+                               break;
+
+                       sdev = scsi_device_lookup(info->host, chn, tgt, lun);
+                       if (sdev) {
+                               scsi_remove_device(sdev);
+                               scsi_device_put(sdev);
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       info->curr = NULL;
+
+       kfree(dir);
+}
+
+static void scsifront_read_backend_params(struct xenbus_device *dev,
+                                         struct vscsifrnt_info *info)
+{
+       unsigned int sg_grant;
+       int ret;
+       struct Scsi_Host *host = info->host;
+
+       ret = xenbus_scanf(XBT_NIL, dev->otherend, "feature-sg-grant", "%u",
+                          &sg_grant);
+       if (ret == 1 && sg_grant) {
+               sg_grant = min_t(unsigned int, sg_grant, SG_ALL);
+               sg_grant = max_t(unsigned int, sg_grant, VSCSIIF_SG_TABLESIZE);
+               host->sg_tablesize = min_t(unsigned int, sg_grant,
+                       VSCSIIF_SG_TABLESIZE * PAGE_SIZE /
+                       sizeof(struct scsiif_request_segment));
+               host->max_sectors = (host->sg_tablesize - 1) * PAGE_SIZE / 512;
+       }
+       dev_info(&dev->dev, "using up to %d SG entries\n", host->sg_tablesize);
+}
+
+static void scsifront_backend_changed(struct xenbus_device *dev,
+                                     enum xenbus_state backend_state)
+{
+       struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
+
+       pr_debug("%s: %p %u %u\n", __func__, dev, dev->state, backend_state);
+
+       switch (backend_state) {
+       case XenbusStateUnknown:
+       case XenbusStateInitialising:
+       case XenbusStateInitWait:
+       case XenbusStateInitialised:
+               break;
+
+       case XenbusStateConnected:
+               scsifront_read_backend_params(dev, info);
+               if (xenbus_read_driver_state(dev->nodename) ==
+                   XenbusStateInitialised)
+                       scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN);
+
+               if (dev->state != XenbusStateConnected)
+                       xenbus_switch_state(dev, XenbusStateConnected);
+               break;
+
+       case XenbusStateClosed:
+               if (dev->state == XenbusStateClosed)
+                       break;
+               /* Missed the backend's Closing state -- fallthrough */
+       case XenbusStateClosing:
+               scsifront_disconnect(info);
+               break;
+
+       case XenbusStateReconfiguring:
+               scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_DEL_LUN);
+               xenbus_switch_state(dev, XenbusStateReconfiguring);
+               break;
+
+       case XenbusStateReconfigured:
+               scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN);
+               xenbus_switch_state(dev, XenbusStateConnected);
+               break;
+       }
+}
+
+static const struct xenbus_device_id scsifront_ids[] = {
+       { "vscsi" },
+       { "" }
+};
+
+static struct xenbus_driver scsifront_driver = {
+       .ids                    = scsifront_ids,
+       .probe                  = scsifront_probe,
+       .remove                 = scsifront_remove,
+       .otherend_changed       = scsifront_backend_changed,
+};
+
+static int __init scsifront_init(void)
+{
+       if (!xen_domain())
+               return -ENODEV;
+
+       return xenbus_register_frontend(&scsifront_driver);
+}
+module_init(scsifront_init);
+
+static void __exit scsifront_exit(void)
+{
+       xenbus_unregister_driver(&scsifront_driver);
+}
+module_exit(scsifront_exit);
+
+MODULE_DESCRIPTION("Xen SCSI frontend driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:vscsi");
+MODULE_AUTHOR("Juergen Gross <jgross@suse.com>");
index 72f63817a1a0e474e13b56b783c6586e6ffc4712..fe2c2d595f599b2f21ea446a824f9f0a7d238282 100644 (file)
@@ -75,8 +75,6 @@ static struct pm_clk_notifier_block platform_bus_notifier = {
        .con_ids = { NULL, },
 };
 
-static bool default_pm_on;
-
 static int __init sh_pm_runtime_init(void)
 {
        if (IS_ENABLED(CONFIG_ARCH_SHMOBILE_MULTI)) {
@@ -96,16 +94,7 @@ static int __init sh_pm_runtime_init(void)
                        return 0;
        }
 
-       default_pm_on = true;
        pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
        return 0;
 }
 core_initcall(sh_pm_runtime_init);
-
-static int __init sh_pm_runtime_late_init(void)
-{
-       if (default_pm_on)
-               pm_genpd_poweroff_unused();
-       return 0;
-}
-late_initcall(sh_pm_runtime_late_init);
index e19512ffc40e5ef34fefeffd70568f2fbd959f59..ebcb33df2eb22facb58cebc10277c3ab12925a42 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/spi/spi.h>
 #include <linux/of_gpio.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
 #include <linux/export.h>
 #include <linux/sched/rt.h>
 #include <linux/delay.h>
@@ -264,10 +265,12 @@ static int spi_drv_probe(struct device *dev)
        if (ret)
                return ret;
 
-       acpi_dev_pm_attach(dev, true);
-       ret = sdrv->probe(to_spi_device(dev));
-       if (ret)
-               acpi_dev_pm_detach(dev, true);
+       ret = dev_pm_domain_attach(dev, true);
+       if (ret != -EPROBE_DEFER) {
+               ret = sdrv->probe(to_spi_device(dev));
+               if (ret)
+                       dev_pm_domain_detach(dev, true);
+       }
 
        return ret;
 }
@@ -278,7 +281,7 @@ static int spi_drv_remove(struct device *dev)
        int ret;
 
        ret = sdrv->remove(to_spi_device(dev));
-       acpi_dev_pm_detach(dev, true);
+       dev_pm_domain_detach(dev, true);
 
        return ret;
 }
index ba350d2035c0d868cc6a9436eb65c593805c5ae5..f92e266d48f88980e546e0add7ab26205d81c127 100644 (file)
@@ -475,7 +475,8 @@ int ssb_gpio_unregister(struct ssb_bus *bus)
 {
        if (ssb_chipco_available(&bus->chipco) ||
            ssb_extif_available(&bus->extif)) {
-               return gpiochip_remove(&bus->gpio);
+               gpiochip_remove(&bus->gpio);
+               return 0;
        } else {
                SSB_WARN_ON(1);
        }
index 3323eb5e77b07ea1e56367c21ff9497e2143f661..655cf5037b0b9f2d5e2e88f4dc37ee399bf21d95 100644 (file)
@@ -19,8 +19,6 @@ menuconfig STAGING_MEDIA
 if STAGING_MEDIA
 
 # Please keep them in alphabetic order
-source "drivers/staging/media/as102/Kconfig"
-
 source "drivers/staging/media/bcm2048/Kconfig"
 
 source "drivers/staging/media/cxd2099/Kconfig"
index 7db83f373f63010c8618255415146c9a1d9dd03e..6dbe578178cdcdbb4d988809c40c3241a36816a2 100644 (file)
@@ -1,4 +1,3 @@
-obj-$(CONFIG_DVB_AS102)                += as102/
 obj-$(CONFIG_I2C_BCM2048)      += bcm2048/
 obj-$(CONFIG_DVB_CXD2099)      += cxd2099/
 obj-$(CONFIG_LIRC_STAGING)     += lirc/
diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c
deleted file mode 100644 (file)
index b686b76..0000000
+++ /dev/null
@@ -1,571 +0,0 @@
-/*
- * Abilis Systems Single DVB-T Receiver
- * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
- * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.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, 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 "as102_drv.h"
-#include "as10x_types.h"
-#include "as10x_cmd.h"
-
-static void as10x_fe_copy_tps_parameters(struct dtv_frontend_properties *dst,
-                                        struct as10x_tps *src);
-
-static void as102_fe_copy_tune_parameters(struct as10x_tune_args *dst,
-                                         struct dtv_frontend_properties *src);
-
-static int as102_fe_set_frontend(struct dvb_frontend *fe)
-{
-       struct dtv_frontend_properties *p = &fe->dtv_property_cache;
-       int ret = 0;
-       struct as102_dev_t *dev;
-       struct as10x_tune_args tune_args = { 0 };
-
-       dev = (struct as102_dev_t *) fe->tuner_priv;
-       if (dev == NULL)
-               return -ENODEV;
-
-       if (mutex_lock_interruptible(&dev->bus_adap.lock))
-               return -EBUSY;
-
-       as102_fe_copy_tune_parameters(&tune_args, p);
-
-       /* send abilis command: SET_TUNE */
-       ret =  as10x_cmd_set_tune(&dev->bus_adap, &tune_args);
-       if (ret != 0)
-               dprintk(debug, "as10x_cmd_set_tune failed. (err = %d)\n", ret);
-
-       mutex_unlock(&dev->bus_adap.lock);
-
-       return (ret < 0) ? -EINVAL : 0;
-}
-
-static int as102_fe_get_frontend(struct dvb_frontend *fe)
-{
-       struct dtv_frontend_properties *p = &fe->dtv_property_cache;
-       int ret = 0;
-       struct as102_dev_t *dev;
-       struct as10x_tps tps = { 0 };
-
-       dev = (struct as102_dev_t *) fe->tuner_priv;
-       if (dev == NULL)
-               return -EINVAL;
-
-       if (mutex_lock_interruptible(&dev->bus_adap.lock))
-               return -EBUSY;
-
-       /* send abilis command: GET_TPS */
-       ret = as10x_cmd_get_tps(&dev->bus_adap, &tps);
-
-       if (ret == 0)
-               as10x_fe_copy_tps_parameters(p, &tps);
-
-       mutex_unlock(&dev->bus_adap.lock);
-
-       return (ret < 0) ? -EINVAL : 0;
-}
-
-static int as102_fe_get_tune_settings(struct dvb_frontend *fe,
-                       struct dvb_frontend_tune_settings *settings) {
-
-#if 0
-       dprintk(debug, "step_size    = %d\n", settings->step_size);
-       dprintk(debug, "max_drift    = %d\n", settings->max_drift);
-       dprintk(debug, "min_delay_ms = %d -> %d\n", settings->min_delay_ms,
-               1000);
-#endif
-
-       settings->min_delay_ms = 1000;
-
-       return 0;
-}
-
-
-static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
-       int ret = 0;
-       struct as102_dev_t *dev;
-       struct as10x_tune_status tstate = { 0 };
-
-       dev = (struct as102_dev_t *) fe->tuner_priv;
-       if (dev == NULL)
-               return -ENODEV;
-
-       if (mutex_lock_interruptible(&dev->bus_adap.lock))
-               return -EBUSY;
-
-       /* send abilis command: GET_TUNE_STATUS */
-       ret = as10x_cmd_get_tune_status(&dev->bus_adap, &tstate);
-       if (ret < 0) {
-               dprintk(debug, "as10x_cmd_get_tune_status failed (err = %d)\n",
-                       ret);
-               goto out;
-       }
-
-       dev->signal_strength  = tstate.signal_strength;
-       dev->ber  = tstate.BER;
-
-       switch (tstate.tune_state) {
-       case TUNE_STATUS_SIGNAL_DVB_OK:
-               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
-               break;
-       case TUNE_STATUS_STREAM_DETECTED:
-               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC;
-               break;
-       case TUNE_STATUS_STREAM_TUNED:
-               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
-                       FE_HAS_LOCK;
-               break;
-       default:
-               *status = TUNE_STATUS_NOT_TUNED;
-       }
-
-       dprintk(debug, "tuner status: 0x%02x, strength %d, per: %d, ber: %d\n",
-                       tstate.tune_state, tstate.signal_strength,
-                       tstate.PER, tstate.BER);
-
-       if (*status & FE_HAS_LOCK) {
-               if (as10x_cmd_get_demod_stats(&dev->bus_adap,
-                       (struct as10x_demod_stats *) &dev->demod_stats) < 0) {
-                       memset(&dev->demod_stats, 0, sizeof(dev->demod_stats));
-                       dprintk(debug,
-                               "as10x_cmd_get_demod_stats failed (probably not tuned)\n");
-               } else {
-                       dprintk(debug,
-                               "demod status: fc: 0x%08x, bad fc: 0x%08x, "
-                               "bytes corrected: 0x%08x , MER: 0x%04x\n",
-                               dev->demod_stats.frame_count,
-                               dev->demod_stats.bad_frame_count,
-                               dev->demod_stats.bytes_fixed_by_rs,
-                               dev->demod_stats.mer);
-               }
-       } else {
-               memset(&dev->demod_stats, 0, sizeof(dev->demod_stats));
-       }
-
-out:
-       mutex_unlock(&dev->bus_adap.lock);
-       return ret;
-}
-
-/*
- * Note:
- * - in AS102 SNR=MER
- *   - the SNR will be returned in linear terms, i.e. not in dB
- *   - the accuracy equals Â±2dB for a SNR range from 4dB to 30dB
- *   - the accuracy is >2dB for SNR values outside this range
- */
-static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
-       struct as102_dev_t *dev;
-
-       dev = (struct as102_dev_t *) fe->tuner_priv;
-       if (dev == NULL)
-               return -ENODEV;
-
-       *snr = dev->demod_stats.mer;
-
-       return 0;
-}
-
-static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
-       struct as102_dev_t *dev;
-
-       dev = (struct as102_dev_t *) fe->tuner_priv;
-       if (dev == NULL)
-               return -ENODEV;
-
-       *ber = dev->ber;
-
-       return 0;
-}
-
-static int as102_fe_read_signal_strength(struct dvb_frontend *fe,
-                                        u16 *strength)
-{
-       struct as102_dev_t *dev;
-
-       dev = (struct as102_dev_t *) fe->tuner_priv;
-       if (dev == NULL)
-               return -ENODEV;
-
-       *strength = (((0xffff * 400) * dev->signal_strength + 41000) * 2);
-
-       return 0;
-}
-
-static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
-       struct as102_dev_t *dev;
-
-       dev = (struct as102_dev_t *) fe->tuner_priv;
-       if (dev == NULL)
-               return -ENODEV;
-
-       if (dev->demod_stats.has_started)
-               *ucblocks = dev->demod_stats.bad_frame_count;
-       else
-               *ucblocks = 0;
-
-       return 0;
-}
-
-static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
-{
-       struct as102_dev_t *dev;
-       int ret;
-
-       dev = (struct as102_dev_t *) fe->tuner_priv;
-       if (dev == NULL)
-               return -ENODEV;
-
-       if (mutex_lock_interruptible(&dev->bus_adap.lock))
-               return -EBUSY;
-
-       if (acquire) {
-               if (elna_enable)
-                       as10x_cmd_set_context(&dev->bus_adap,
-                                             CONTEXT_LNA, dev->elna_cfg);
-
-               ret = as10x_cmd_turn_on(&dev->bus_adap);
-       } else {
-               ret = as10x_cmd_turn_off(&dev->bus_adap);
-       }
-
-       mutex_unlock(&dev->bus_adap.lock);
-
-       return ret;
-}
-
-static struct dvb_frontend_ops as102_fe_ops = {
-       .delsys = { SYS_DVBT },
-       .info = {
-               .name                   = "Unknown AS102 device",
-               .frequency_min          = 174000000,
-               .frequency_max          = 862000000,
-               .frequency_stepsize     = 166667,
-               .caps = FE_CAN_INVERSION_AUTO
-                       | 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_FEC_AUTO
-                       | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK
-                       | FE_CAN_QAM_AUTO
-                       | FE_CAN_TRANSMISSION_MODE_AUTO
-                       | FE_CAN_GUARD_INTERVAL_AUTO
-                       | FE_CAN_HIERARCHY_AUTO
-                       | FE_CAN_RECOVER
-                       | FE_CAN_MUTE_TS
-       },
-
-       .set_frontend           = as102_fe_set_frontend,
-       .get_frontend           = as102_fe_get_frontend,
-       .get_tune_settings      = as102_fe_get_tune_settings,
-
-       .read_status            = as102_fe_read_status,
-       .read_snr               = as102_fe_read_snr,
-       .read_ber               = as102_fe_read_ber,
-       .read_signal_strength   = as102_fe_read_signal_strength,
-       .read_ucblocks          = as102_fe_read_ucblocks,
-       .ts_bus_ctrl            = as102_fe_ts_bus_ctrl,
-};
-
-int as102_dvb_unregister_fe(struct dvb_frontend *fe)
-{
-       /* unregister frontend */
-       dvb_unregister_frontend(fe);
-
-       /* detach frontend */
-       dvb_frontend_detach(fe);
-
-       return 0;
-}
-
-int as102_dvb_register_fe(struct as102_dev_t *as102_dev,
-                         struct dvb_frontend *dvb_fe)
-{
-       int errno;
-       struct dvb_adapter *dvb_adap;
-
-       if (as102_dev == NULL)
-               return -EINVAL;
-
-       /* extract dvb_adapter */
-       dvb_adap = &as102_dev->dvb_adap;
-
-       /* init frontend callback ops */
-       memcpy(&dvb_fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops));
-       strncpy(dvb_fe->ops.info.name, as102_dev->name,
-               sizeof(dvb_fe->ops.info.name));
-
-       /* register dvb frontend */
-       errno = dvb_register_frontend(dvb_adap, dvb_fe);
-       if (errno == 0)
-               dvb_fe->tuner_priv = as102_dev;
-
-       return errno;
-}
-
-static void as10x_fe_copy_tps_parameters(struct dtv_frontend_properties *fe_tps,
-                                        struct as10x_tps *as10x_tps)
-{
-
-       /* extract constellation */
-       switch (as10x_tps->modulation) {
-       case CONST_QPSK:
-               fe_tps->modulation = QPSK;
-               break;
-       case CONST_QAM16:
-               fe_tps->modulation = QAM_16;
-               break;
-       case CONST_QAM64:
-               fe_tps->modulation = QAM_64;
-               break;
-       }
-
-       /* extract hierarchy */
-       switch (as10x_tps->hierarchy) {
-       case HIER_NONE:
-               fe_tps->hierarchy = HIERARCHY_NONE;
-               break;
-       case HIER_ALPHA_1:
-               fe_tps->hierarchy = HIERARCHY_1;
-               break;
-       case HIER_ALPHA_2:
-               fe_tps->hierarchy = HIERARCHY_2;
-               break;
-       case HIER_ALPHA_4:
-               fe_tps->hierarchy = HIERARCHY_4;
-               break;
-       }
-
-       /* extract code rate HP */
-       switch (as10x_tps->code_rate_HP) {
-       case CODE_RATE_1_2:
-               fe_tps->code_rate_HP = FEC_1_2;
-               break;
-       case CODE_RATE_2_3:
-               fe_tps->code_rate_HP = FEC_2_3;
-               break;
-       case CODE_RATE_3_4:
-               fe_tps->code_rate_HP = FEC_3_4;
-               break;
-       case CODE_RATE_5_6:
-               fe_tps->code_rate_HP = FEC_5_6;
-               break;
-       case CODE_RATE_7_8:
-               fe_tps->code_rate_HP = FEC_7_8;
-               break;
-       }
-
-       /* extract code rate LP */
-       switch (as10x_tps->code_rate_LP) {
-       case CODE_RATE_1_2:
-               fe_tps->code_rate_LP = FEC_1_2;
-               break;
-       case CODE_RATE_2_3:
-               fe_tps->code_rate_LP = FEC_2_3;
-               break;
-       case CODE_RATE_3_4:
-               fe_tps->code_rate_LP = FEC_3_4;
-               break;
-       case CODE_RATE_5_6:
-               fe_tps->code_rate_LP = FEC_5_6;
-               break;
-       case CODE_RATE_7_8:
-               fe_tps->code_rate_LP = FEC_7_8;
-               break;
-       }
-
-       /* extract guard interval */
-       switch (as10x_tps->guard_interval) {
-       case GUARD_INT_1_32:
-               fe_tps->guard_interval = GUARD_INTERVAL_1_32;
-               break;
-       case GUARD_INT_1_16:
-               fe_tps->guard_interval = GUARD_INTERVAL_1_16;
-               break;
-       case GUARD_INT_1_8:
-               fe_tps->guard_interval = GUARD_INTERVAL_1_8;
-               break;
-       case GUARD_INT_1_4:
-               fe_tps->guard_interval = GUARD_INTERVAL_1_4;
-               break;
-       }
-
-       /* extract transmission mode */
-       switch (as10x_tps->transmission_mode) {
-       case TRANS_MODE_2K:
-               fe_tps->transmission_mode = TRANSMISSION_MODE_2K;
-               break;
-       case TRANS_MODE_8K:
-               fe_tps->transmission_mode = TRANSMISSION_MODE_8K;
-               break;
-       }
-}
-
-static uint8_t as102_fe_get_code_rate(fe_code_rate_t arg)
-{
-       uint8_t c;
-
-       switch (arg) {
-       case FEC_1_2:
-               c = CODE_RATE_1_2;
-               break;
-       case FEC_2_3:
-               c = CODE_RATE_2_3;
-               break;
-       case FEC_3_4:
-               c = CODE_RATE_3_4;
-               break;
-       case FEC_5_6:
-               c = CODE_RATE_5_6;
-               break;
-       case FEC_7_8:
-               c = CODE_RATE_7_8;
-               break;
-       default:
-               c = CODE_RATE_UNKNOWN;
-               break;
-       }
-
-       return c;
-}
-
-static void as102_fe_copy_tune_parameters(struct as10x_tune_args *tune_args,
-                         struct dtv_frontend_properties *params)
-{
-
-       /* set frequency */
-       tune_args->freq = params->frequency / 1000;
-
-       /* fix interleaving_mode */
-       tune_args->interleaving_mode = INTLV_NATIVE;
-
-       switch (params->bandwidth_hz) {
-       case 8000000:
-               tune_args->bandwidth = BW_8_MHZ;
-               break;
-       case 7000000:
-               tune_args->bandwidth = BW_7_MHZ;
-               break;
-       case 6000000:
-               tune_args->bandwidth = BW_6_MHZ;
-               break;
-       default:
-               tune_args->bandwidth = BW_8_MHZ;
-       }
-
-       switch (params->guard_interval) {
-       case GUARD_INTERVAL_1_32:
-               tune_args->guard_interval = GUARD_INT_1_32;
-               break;
-       case GUARD_INTERVAL_1_16:
-               tune_args->guard_interval = GUARD_INT_1_16;
-               break;
-       case GUARD_INTERVAL_1_8:
-               tune_args->guard_interval = GUARD_INT_1_8;
-               break;
-       case GUARD_INTERVAL_1_4:
-               tune_args->guard_interval = GUARD_INT_1_4;
-               break;
-       case GUARD_INTERVAL_AUTO:
-       default:
-               tune_args->guard_interval = GUARD_UNKNOWN;
-               break;
-       }
-
-       switch (params->modulation) {
-       case QPSK:
-               tune_args->modulation = CONST_QPSK;
-               break;
-       case QAM_16:
-               tune_args->modulation = CONST_QAM16;
-               break;
-       case QAM_64:
-               tune_args->modulation = CONST_QAM64;
-               break;
-       default:
-               tune_args->modulation = CONST_UNKNOWN;
-               break;
-       }
-
-       switch (params->transmission_mode) {
-       case TRANSMISSION_MODE_2K:
-               tune_args->transmission_mode = TRANS_MODE_2K;
-               break;
-       case TRANSMISSION_MODE_8K:
-               tune_args->transmission_mode = TRANS_MODE_8K;
-               break;
-       default:
-               tune_args->transmission_mode = TRANS_MODE_UNKNOWN;
-       }
-
-       switch (params->hierarchy) {
-       case HIERARCHY_NONE:
-               tune_args->hierarchy = HIER_NONE;
-               break;
-       case HIERARCHY_1:
-               tune_args->hierarchy = HIER_ALPHA_1;
-               break;
-       case HIERARCHY_2:
-               tune_args->hierarchy = HIER_ALPHA_2;
-               break;
-       case HIERARCHY_4:
-               tune_args->hierarchy = HIER_ALPHA_4;
-               break;
-       case HIERARCHY_AUTO:
-               tune_args->hierarchy = HIER_UNKNOWN;
-               break;
-       }
-
-       dprintk(debug, "tuner parameters: freq: %d  bw: 0x%02x  gi: 0x%02x\n",
-                       params->frequency,
-                       tune_args->bandwidth,
-                       tune_args->guard_interval);
-
-       /*
-        * Detect a hierarchy selection
-        * if HP/LP are both set to FEC_NONE, HP will be selected.
-        */
-       if ((tune_args->hierarchy != HIER_NONE) &&
-                      ((params->code_rate_LP == FEC_NONE) ||
-                       (params->code_rate_HP == FEC_NONE))) {
-
-               if (params->code_rate_LP == FEC_NONE) {
-                       tune_args->hier_select = HIER_HIGH_PRIORITY;
-                       tune_args->code_rate =
-                          as102_fe_get_code_rate(params->code_rate_HP);
-               }
-
-               if (params->code_rate_HP == FEC_NONE) {
-                       tune_args->hier_select = HIER_LOW_PRIORITY;
-                       tune_args->code_rate =
-                          as102_fe_get_code_rate(params->code_rate_LP);
-               }
-
-               dprintk(debug,
-                       "\thierarchy: 0x%02x  selected: %s  code_rate_%s: 0x%02x\n",
-                       tune_args->hierarchy,
-                       tune_args->hier_select == HIER_HIGH_PRIORITY ?
-                       "HP" : "LP",
-                       tune_args->hier_select == HIER_HIGH_PRIORITY ?
-                       "HP" : "LP",
-                       tune_args->code_rate);
-       } else {
-               tune_args->code_rate =
-                       as102_fe_get_code_rate(params->code_rate_HP);
-       }
-}
index 12f321dd23993ea8a1d35c54e6d14b733470484c..4de2f082491d8a007cac94b17f2fb55096b9c371 100644 (file)
@@ -1,6 +1,7 @@
 config VIDEO_DM365_VPFE
        tristate "DM365 VPFE Media Controller Capture Driver"
        depends on VIDEO_V4L2 && ARCH_DAVINCI_DM365 && !VIDEO_DM365_ISIF
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        help
          Support for DM365 VPFE based Media Controller Capture driver.
index 226a1ca90b3c6a11098dd9846559db986c5506e0..2d496001b6e84d5d04e44b64274d4431f2c56ff2 100644 (file)
@@ -1,6 +1,7 @@
 config VIDEO_DT3155
        tristate "DT3155 frame grabber, Video4Linux interface"
        depends on PCI && VIDEO_DEV && VIDEO_V4L2
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        default n
        ---help---
index 726cc3a31856673952525e0343fa0be95d7f41ec..7aca44f28c5ad54087b9a6fdd2dbe0fc6ff1bb20 100644 (file)
@@ -414,6 +414,7 @@ static ssize_t vfd_write(struct file *file, const char __user *buf,
        data_buf = memdup_user(buf, n_bytes);
        if (IS_ERR(data_buf)) {
                retval = PTR_ERR(data_buf);
+               data_buf = NULL;
                goto exit;
        }
 
index 86ad811fda24c1723e91ad816e97d4ca761c3f32..c20ef56202bf05ab9c8c72860c01f08ef813beeb 100644 (file)
@@ -392,6 +392,7 @@ static ssize_t vfd_write(struct file *file, const char __user *buf,
        data_buf = memdup_user((void const __user *)buf, n_bytes);
        if (IS_ERR(data_buf)) {
                retval = PTR_ERR(data_buf);
+               data_buf = NULL;
                goto exit;
        }
 
index 8afc6fee40c547ccca5f5c106e651c3fbfce9942..b78643f907e7a4e7cd4974a877c3ddd98a19a77e 100644 (file)
@@ -1,6 +1,7 @@
 config VIDEO_OMAP4
        bool "OMAP 4 Camera support"
        depends on VIDEO_V4L2=y && VIDEO_V4L2_SUBDEV_API && I2C=y && ARCH_OMAP4
+       depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
        ---help---
          Driver for an OMAP 4 ISS controller.
index d8a118d3956683165f7d2b9f2663cc9e07403727..c64776f71809f0fc9ee04232a9cef72647897989 100644 (file)
@@ -221,9 +221,7 @@ void pio2_gpio_exit(struct pio2_card *card)
 {
        const char *label = card->gc.label;
 
-       if (gpiochip_remove(&(card->gc)))
-               dev_err(&card->vdev->dev, "Failed to remove GPIO\n");
-
+       gpiochip_remove(&(card->gc));
        kfree(label);
 }
 
index fddfae61222f2e3bf3a063d6ab11d6438e4ff897..be783f717f1923013f44caa03482e064d51510c6 100644 (file)
@@ -819,7 +819,8 @@ int core_tpg_add_lun(
 {
        int ret;
 
-       ret = percpu_ref_init(&lun->lun_ref, core_tpg_lun_ref_release);
+       ret = percpu_ref_init(&lun->lun_ref, core_tpg_lun_ref_release, 0,
+                             GFP_KERNEL);
        if (ret < 0)
                return ret;
 
index 5618b5fc7500e149dfd79ac2515309749f905834..f575a9b5ede7b05bd24c87ecf58425e89ec20fdc 100644 (file)
@@ -452,7 +452,7 @@ void __init hvc_vio_init_early(void)
                return;
 #endif
        /* Check whether the user has requested a different console. */
-       if (!strstr(cmd_line, "console="))
+       if (!strstr(boot_command_line, "console="))
                add_preferred_console("hvc", 0, NULL);
        hvc_instantiate(0, 0, ops);
 }
index 2967f0388d2c0153a09471a9b5e059ed033d66f9..f1e57425e39ff00b1a929500f4ea297aba5ae095 100644 (file)
@@ -347,8 +347,6 @@ static int xen_console_remove(struct xencons_info *info)
 }
 
 #ifdef CONFIG_HVC_XEN_FRONTEND
-static struct xenbus_driver xencons_driver;
-
 static int xencons_remove(struct xenbus_device *dev)
 {
        return xen_console_remove(dev_get_drvdata(&dev->dev));
@@ -499,13 +497,14 @@ static const struct xenbus_device_id xencons_ids[] = {
        { "" }
 };
 
-
-static DEFINE_XENBUS_DRIVER(xencons, "xenconsole",
+static struct xenbus_driver xencons_driver = {
+       .name = "xenconsole",
+       .ids = xencons_ids,
        .probe = xencons_probe,
        .remove = xencons_remove,
        .resume = xencons_resume,
        .otherend_changed = xencons_backend_changed,
-);
+};
 #endif /* CONFIG_HVC_XEN_FRONTEND */
 
 static int __init xen_hvc_init(void)
index 82573dc4d8cf8ebf99d84d40056d5fb5dac85303..0041a64cc86e576b180dfaf0a78630657f9493ca 100644 (file)
@@ -1248,7 +1248,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype,
        mutex_destroy(&s->mutex);
 
 #ifdef CONFIG_GPIOLIB
-       WARN_ON(gpiochip_remove(&s->gpio));
+       gpiochip_remove(&s->gpio);
 
 out_uart:
 #endif
@@ -1263,12 +1263,10 @@ out_clk:
 static int max310x_remove(struct device *dev)
 {
        struct max310x_port *s = dev_get_drvdata(dev);
-       int i, ret = 0;
+       int i;
 
 #ifdef CONFIG_GPIOLIB
-       ret = gpiochip_remove(&s->gpio);
-       if (ret)
-               return ret;
+       gpiochip_remove(&s->gpio);
 #endif
 
        for (i = 0; i < s->uart.nr; i++) {
@@ -1282,7 +1280,7 @@ static int max310x_remove(struct device *dev)
        uart_unregister_driver(&s->uart);
        clk_disable_unprepare(s->clk);
 
-       return ret;
+       return 0;
 }
 
 static const struct of_device_id __maybe_unused max310x_dt_ids[] = {
index 3284c31085a77815117ed0a0bfd280accaae701f..6246820d7f059cc52f977997dc01b8a45c28a16b 100644 (file)
@@ -1157,7 +1157,7 @@ static int sc16is7xx_probe(struct device *dev,
 
 #ifdef CONFIG_GPIOLIB
        if (devtype->nr_gpio)
-               WARN_ON(gpiochip_remove(&s->gpio));
+               gpiochip_remove(&s->gpio);
 
 out_uart:
 #endif
@@ -1173,14 +1173,11 @@ out_clk:
 static int sc16is7xx_remove(struct device *dev)
 {
        struct sc16is7xx_port *s = dev_get_drvdata(dev);
-       int i, ret = 0;
+       int i;
 
 #ifdef CONFIG_GPIOLIB
-       if (s->devtype->nr_gpio) {
-               ret = gpiochip_remove(&s->gpio);
-               if (ret)
-                       return ret;
-       }
+       if (s->devtype->nr_gpio)
+               gpiochip_remove(&s->gpio);
 #endif
 
        for (i = 0; i < s->uart.nr; i++) {
@@ -1195,7 +1192,7 @@ static int sc16is7xx_remove(struct device *dev)
        if (!IS_ERR(s->clk))
                clk_disable_unprepare(s->clk);
 
-       return ret;
+       return 0;
 }
 
 static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = {
index 2f6f9b5e4891d1fb92cec7687d9066c22993566c..16a2c0237dd60a06513e2ad1f616e13a7bd71c0b 100644 (file)
@@ -2186,8 +2186,9 @@ static int __tty_fasync(int fd, struct file *filp, int on)
                }
                get_pid(pid);
                spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-               retval = __f_setown(filp, pid, type, 0);
+               __f_setown(filp, pid, type, 0);
                put_pid(pid);
+               retval = 0;
        }
 out:
        return retval;
index f7825332a3251b2001645b581eec09524562fbec..9558da3f06a05ac11706a3294c98efc0b2823502 100644 (file)
@@ -876,15 +876,11 @@ static void vfio_pci_remove(struct pci_dev *pdev)
 {
        struct vfio_pci_device *vdev;
 
-       mutex_lock(&driver_lock);
-
        vdev = vfio_del_group_dev(&pdev->dev);
        if (vdev) {
                iommu_group_put(pdev->dev.iommu_group);
                kfree(vdev);
        }
-
-       mutex_unlock(&driver_lock);
 }
 
 static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev,
@@ -927,108 +923,90 @@ static struct pci_driver vfio_pci_driver = {
        .err_handler    = &vfio_err_handlers,
 };
 
-/*
- * Test whether a reset is necessary and possible.  We mark devices as
- * needs_reset when they are released, but don't have a function-local reset
- * available.  If any of these exist in the affected devices, we want to do
- * a bus/slot reset.  We also need all of the affected devices to be unused,
- * so we abort if any device has a non-zero refcnt.  driver_lock prevents a
- * device from being opened during the scan or unbound from vfio-pci.
- */
-static int vfio_pci_test_bus_reset(struct pci_dev *pdev, void *data)
-{
-       bool *needs_reset = data;
-       struct pci_driver *pci_drv = ACCESS_ONCE(pdev->driver);
-       int ret = -EBUSY;
-
-       if (pci_drv == &vfio_pci_driver) {
-               struct vfio_device *device;
-               struct vfio_pci_device *vdev;
-
-               device = vfio_device_get_from_dev(&pdev->dev);
-               if (!device)
-                       return ret;
-
-               vdev = vfio_device_data(device);
-               if (vdev) {
-                       if (vdev->needs_reset)
-                               *needs_reset = true;
-
-                       if (!vdev->refcnt)
-                               ret = 0;
-               }
-
-               vfio_device_put(device);
-       }
-
-       /*
-        * TODO: vfio-core considers groups to be viable even if some devices
-        * are attached to known drivers, like pci-stub or pcieport.  We can't
-        * freeze devices from being unbound to those drivers like we can
-        * here though, so it would be racy to test for them.  We also can't
-        * use device_lock() to prevent changes as that would interfere with
-        * PCI-core taking device_lock during bus reset.  For now, we require
-        * devices to be bound to vfio-pci to get a bus/slot reset on release.
-        */
-
-       return ret;
-}
+struct vfio_devices {
+       struct vfio_device **devices;
+       int cur_index;
+       int max_index;
+};
 
-/* Clear needs_reset on all affected devices after successful bus/slot reset */
-static int vfio_pci_clear_needs_reset(struct pci_dev *pdev, void *data)
+static int vfio_pci_get_devs(struct pci_dev *pdev, void *data)
 {
+       struct vfio_devices *devs = data;
        struct pci_driver *pci_drv = ACCESS_ONCE(pdev->driver);
 
-       if (pci_drv == &vfio_pci_driver) {
-               struct vfio_device *device;
-               struct vfio_pci_device *vdev;
+       if (pci_drv != &vfio_pci_driver)
+               return -EBUSY;
 
-               device = vfio_device_get_from_dev(&pdev->dev);
-               if (!device)
-                       return 0;
+       if (devs->cur_index == devs->max_index)
+               return -ENOSPC;
 
-               vdev = vfio_device_data(device);
-               if (vdev)
-                       vdev->needs_reset = false;
-
-               vfio_device_put(device);
-       }
+       devs->devices[devs->cur_index] = vfio_device_get_from_dev(&pdev->dev);
+       if (!devs->devices[devs->cur_index])
+               return -EINVAL;
 
+       devs->cur_index++;
        return 0;
 }
 
 /*
  * Attempt to do a bus/slot reset if there are devices affected by a reset for
  * this device that are needs_reset and all of the affected devices are unused
- * (!refcnt).  Callers of this function are required to hold driver_lock such
- * that devices can not be unbound from vfio-pci or opened by a user while we
- * test for and perform a bus/slot reset.
+ * (!refcnt).  Callers are required to hold driver_lock when calling this to
+ * prevent device opens and concurrent bus reset attempts.  We prevent device
+ * unbinds by acquiring and holding a reference to the vfio_device.
+ *
+ * NB: vfio-core considers a group to be viable even if some devices are
+ * bound to drivers like pci-stub or pcieport.  Here we require all devices
+ * to be bound to vfio_pci since that's the only way we can be sure they
+ * stay put.
  */
 static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev)
 {
+       struct vfio_devices devs = { .cur_index = 0 };
+       int i = 0, ret = -EINVAL;
        bool needs_reset = false, slot = false;
-       int ret;
+       struct vfio_pci_device *tmp;
 
        if (!pci_probe_reset_slot(vdev->pdev->slot))
                slot = true;
        else if (pci_probe_reset_bus(vdev->pdev->bus))
                return;
 
-       if (vfio_pci_for_each_slot_or_bus(vdev->pdev,
-                                         vfio_pci_test_bus_reset,
-                                         &needs_reset, slot) || !needs_reset)
+       if (vfio_pci_for_each_slot_or_bus(vdev->pdev, vfio_pci_count_devs,
+                                         &i, slot) || !i)
                return;
 
-       if (slot)
-               ret = pci_try_reset_slot(vdev->pdev->slot);
-       else
-               ret = pci_try_reset_bus(vdev->pdev->bus);
-
-       if (ret)
+       devs.max_index = i;
+       devs.devices = kcalloc(i, sizeof(struct vfio_device *), GFP_KERNEL);
+       if (!devs.devices)
                return;
 
-       vfio_pci_for_each_slot_or_bus(vdev->pdev,
-                                     vfio_pci_clear_needs_reset, NULL, slot);
+       if (vfio_pci_for_each_slot_or_bus(vdev->pdev,
+                                         vfio_pci_get_devs, &devs, slot))
+               goto put_devs;
+
+       for (i = 0; i < devs.cur_index; i++) {
+               tmp = vfio_device_data(devs.devices[i]);
+               if (tmp->needs_reset)
+                       needs_reset = true;
+               if (tmp->refcnt)
+                       goto put_devs;
+       }
+
+       if (needs_reset)
+               ret = slot ? pci_try_reset_slot(vdev->pdev->slot) :
+                            pci_try_reset_bus(vdev->pdev->bus);
+
+put_devs:
+       for (i = 0; i < devs.cur_index; i++) {
+               if (!ret) {
+                       tmp = vfio_device_data(devs.devices[i]);
+                       tmp->needs_reset = false;
+               }
+               vfio_device_put(devs.devices[i]);
+       }
+
+       kfree(devs.devices);
 }
 
 static void __exit vfio_pci_cleanup(void)
index e50790e91f769825fad7284512eb5050b7d3f92a..1de3f94aa7de486d2d0e7bff5b3d603f1aa3bd13 100644 (file)
@@ -727,7 +727,7 @@ static int __init init_pci_ext_cap_err_perm(struct perm_bits *perm)
        p_setd(perm, 0, ALL_VIRT, NO_WRITE);
 
        /* Writable bits mask */
-       mask =  PCI_ERR_UNC_TRAIN |             /* Training */
+       mask =  PCI_ERR_UNC_UND |               /* Undefined */
                PCI_ERR_UNC_DLP |               /* Data Link Protocol */
                PCI_ERR_UNC_SURPDN |            /* Surprise Down */
                PCI_ERR_UNC_POISON_TLP |        /* Poisoned TLP */
index 9dd49c9839ac6de24496d37e0f4020e424415a1d..553212f037c37304b0a07d6741b9b68d97ed2c54 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/eventfd.h>
+#include <linux/msi.h>
 #include <linux/pci.h>
 #include <linux/file.h>
 #include <linux/poll.h>
@@ -548,6 +549,20 @@ static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
                return PTR_ERR(trigger);
        }
 
+       /*
+        * The MSIx vector table resides in device memory which may be cleared
+        * via backdoor resets. We don't allow direct access to the vector
+        * table so even if a userspace driver attempts to save/restore around
+        * such a reset it would be unsuccessful. To avoid this, restore the
+        * cached value of the message prior to enabling.
+        */
+       if (msix) {
+               struct msi_msg msg;
+
+               get_cached_msi_msg(irq, &msg);
+               write_msi_msg(irq, &msg);
+       }
+
        ret = request_irq(irq, vfio_msihandler, 0,
                          vdev->ctx[vector].name, trigger);
        if (ret) {
index 0734fbe5b651ef68ca464e9a667ded19a8df5f0b..583ccdb2c58f110fc45e7a7abdc56f81af8d3b4a 100644 (file)
@@ -57,7 +57,8 @@ struct vfio_iommu {
        struct list_head        domain_list;
        struct mutex            lock;
        struct rb_root          dma_list;
-       bool v2;
+       bool                    v2;
+       bool                    nesting;
 };
 
 struct vfio_domain {
@@ -705,6 +706,15 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
                goto out_free;
        }
 
+       if (iommu->nesting) {
+               int attr = 1;
+
+               ret = iommu_domain_set_attr(domain->domain, DOMAIN_ATTR_NESTING,
+                                           &attr);
+               if (ret)
+                       goto out_domain;
+       }
+
        ret = iommu_attach_group(domain->domain, iommu_group);
        if (ret)
                goto out_domain;
@@ -819,17 +829,26 @@ static void *vfio_iommu_type1_open(unsigned long arg)
 {
        struct vfio_iommu *iommu;
 
-       if (arg != VFIO_TYPE1_IOMMU && arg != VFIO_TYPE1v2_IOMMU)
-               return ERR_PTR(-EINVAL);
-
        iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
        if (!iommu)
                return ERR_PTR(-ENOMEM);
 
+       switch (arg) {
+       case VFIO_TYPE1_IOMMU:
+               break;
+       case VFIO_TYPE1_NESTING_IOMMU:
+               iommu->nesting = true;
+       case VFIO_TYPE1v2_IOMMU:
+               iommu->v2 = true;
+               break;
+       default:
+               kfree(iommu);
+               return ERR_PTR(-EINVAL);
+       }
+
        INIT_LIST_HEAD(&iommu->domain_list);
        iommu->dma_list = RB_ROOT;
        mutex_init(&iommu->lock);
-       iommu->v2 = (arg == VFIO_TYPE1v2_IOMMU);
 
        return iommu;
 }
@@ -885,6 +904,7 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
                switch (arg) {
                case VFIO_TYPE1_IOMMU:
                case VFIO_TYPE1v2_IOMMU:
+               case VFIO_TYPE1_NESTING_IOMMU:
                        return 1;
                case VFIO_DMA_CC_IOMMU:
                        if (!iommu)
index 86dfceb9201f2503e2050dc71c8906fcd8abf281..5fa42db769ee8e5d9d88edd8dbccc71102290ad1 100644 (file)
@@ -92,7 +92,7 @@ long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group,
 
        return ret;
 }
-EXPORT_SYMBOL(vfio_spapr_iommu_eeh_ioctl);
+EXPORT_SYMBOL_GPL(vfio_spapr_iommu_eeh_ioctl);
 
 MODULE_VERSION(DRIVER_VERSION);
 MODULE_LICENSE("GPL v2");
index a6f7cc0a088352c9695196b442a2153e57abb662..9a23698b6fe8398c1025ea3a234db11a4779ba09 100644 (file)
@@ -265,7 +265,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
 static struct platform_driver pm860x_backlight_driver = {
        .driver         = {
                .name   = "88pm860x-backlight",
-               .owner  = THIS_MODULE,
        },
        .probe          = pm860x_backlight_probe,
 };
index 86234c31d79c0a1afb3ae05660f3472342ed1d50..50774e6577003d6fa8f7bb6f03d079fae1191530 100644 (file)
@@ -211,7 +211,6 @@ static int aat2870_bl_remove(struct platform_device *pdev)
 static struct platform_driver aat2870_bl_driver = {
        .driver = {
                .name   = "aat2870-backlight",
-               .owner  = THIS_MODULE,
        },
        .probe          = aat2870_bl_probe,
        .remove         = aat2870_bl_remove,
index f37097a261a210f1c168def54c2ef22e6fa6e965..dd88ba1d71ceb64849535542a8cb3289d89df4c1 100644 (file)
@@ -67,6 +67,7 @@ static int adp5520_bl_set(struct backlight_device *bl, int brightness)
 static int adp5520_bl_update_status(struct backlight_device *bl)
 {
        int brightness = bl->props.brightness;
+
        if (bl->props.power != FB_BLANK_UNBLANK)
                brightness = 0;
 
@@ -374,7 +375,6 @@ static SIMPLE_DEV_PM_OPS(adp5520_bl_pm_ops, adp5520_bl_suspend,
 static struct platform_driver adp5520_bl_driver = {
        .driver         = {
                .name   = "adp5520-backlight",
-               .owner  = THIS_MODULE,
                .pm     = &adp5520_bl_pm_ops,
        },
        .probe          = adp5520_bl_probe,
index be8d83deca7d5a2a7ca563873a7008d33599541b..71147f4461b8cb57fc3a0b62b502bc97bdac843b 100644 (file)
@@ -181,6 +181,7 @@ static int adp8860_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask
 static void adp8860_led_work(struct work_struct *work)
 {
        struct adp8860_led *led = container_of(work, struct adp8860_led, work);
+
        adp8860_write(led->client, ADP8860_ISC1 - led->id + 1,
                         led->new_brightness >> 1);
 }
@@ -362,6 +363,7 @@ static int adp8860_bl_set(struct backlight_device *bl, int brightness)
 static int adp8860_bl_update_status(struct backlight_device *bl)
 {
        int brightness = bl->props.brightness;
+
        if (bl->props.power != FB_BLANK_UNBLANK)
                brightness = 0;
 
@@ -499,6 +501,7 @@ static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev,
 {
        struct adp8860_bl *data = dev_get_drvdata(dev);
        int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
+
        if (ret)
                return ret;
 
index 251af4d38d8648add6c2323c34fe24a7e048a772..037e430833435457381f2c282b3839e2a8964dc5 100644 (file)
@@ -144,6 +144,7 @@ static int adp8870_read(struct i2c_client *client, int reg, uint8_t *val)
 static int adp8870_write(struct i2c_client *client, u8 reg, u8 val)
 {
        int ret = i2c_smbus_write_byte_data(client, reg, val);
+
        if (ret)
                dev_err(&client->dev, "failed to write\n");
 
@@ -195,6 +196,7 @@ static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask
 static void adp8870_led_work(struct work_struct *work)
 {
        struct adp8870_led *led = container_of(work, struct adp8870_led, work);
+
        adp8870_write(led->client, ADP8870_ISC1 + led->id - 1,
                         led->new_brightness >> 1);
 }
@@ -399,6 +401,7 @@ static int adp8870_bl_set(struct backlight_device *bl, int brightness)
 static int adp8870_bl_update_status(struct backlight_device *bl)
 {
        int brightness = bl->props.brightness;
+
        if (bl->props.power != FB_BLANK_UNBLANK)
                brightness = 0;
 
@@ -649,6 +652,7 @@ static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev,
 {
        struct adp8870_bl *data = dev_get_drvdata(dev);
        int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
+
        if (ret)
                return ret;
 
index 4726c8be626f3dc794dfb137da1353a0d69ed887..5f897f99cc9b68c154207bfaad4461137977ea97 100644 (file)
@@ -325,11 +325,11 @@ static int ams369fg06_power_on(struct ams369fg06 *lcd)
        if (!pd->reset) {
                dev_err(lcd->dev, "reset is NULL.\n");
                return -EINVAL;
-       } else {
-               pd->reset(lcd->ld);
-               msleep(pd->reset_delay);
        }
 
+       pd->reset(lcd->ld);
+       msleep(pd->reset_delay);
+
        ret = ams369fg06_ldi_init(lcd);
        if (ret) {
                dev_err(lcd->dev, "failed to initialize ldi.\n");
index bb1fc45b7549e7b0ffd4b49fc1cbce8512e4b0a5..734a9158946b1f805a3738d9e9c31f25cf8b3314 100644 (file)
@@ -467,7 +467,6 @@ static int as3711_backlight_probe(struct platform_device *pdev)
 static struct platform_driver as3711_backlight_driver = {
        .driver         = {
                .name   = "as3711-backlight",
-               .owner  = THIS_MODULE,
        },
        .probe          = as3711_backlight_probe,
 };
index 51d18d637e2b88f02a9ff6ff1ff9702645d07d73..d7c37a8ccd1fbabe00063ded2bb0337e184e73db 100644 (file)
@@ -143,6 +143,7 @@ static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd,
                                uint8_t base, uint8_t data)
 {
        int i;
+
        for (i = 0; i < 8; i++) {
                if (data & 0x80)
                        lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT);
index f3fed9ef745f5aca7290677a2f6ae8d866be151e..3e3880fc8c8e3926cde346259d463aa0703dc469 100644 (file)
@@ -235,6 +235,7 @@ static int cr_backlight_probe(struct platform_device *pdev)
 static int cr_backlight_remove(struct platform_device *pdev)
 {
        struct cr_panel *crp = platform_get_drvdata(pdev);
+
        crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN;
        crp->cr_backlight_device->props.brightness = 0;
        crp->cr_backlight_device->props.max_brightness = 0;
index 12c5d840c5909e173fcf34492487b6409f479f67..f793738f06fbe142b454c7cf677ead073aef5f09 100644 (file)
@@ -162,7 +162,6 @@ static int da903x_backlight_probe(struct platform_device *pdev)
 static struct platform_driver da903x_backlight_driver = {
        .driver         = {
                .name   = "da903x-backlight",
-               .owner  = THIS_MODULE,
        },
        .probe          = da903x_backlight_probe,
 };
index 20d55becaa7434e76704a7a59771ab413014b09f..d4bd74bd5070709215c6ec4db409af67c841c4e8 100644 (file)
@@ -173,7 +173,6 @@ static struct platform_driver da9052_wled_driver = {
        .id_table       = da9052_wled_ids,
        .driver = {
                .name   = "da9052-wled",
-               .owner  = THIS_MODULE,
        },
 };
 
index 0d1f633c6480190d8dc90d0d122832b4e4c98174..0067931821c677b0bcb542d3aa3004633f2479ac 100644 (file)
@@ -128,7 +128,6 @@ static SIMPLE_DEV_PM_OPS(ep93xxbl_pm_ops, ep93xxbl_suspend, ep93xxbl_resume);
 static struct platform_driver ep93xxbl_driver = {
        .driver         = {
                .name   = "ep93xx-bl",
-               .owner  = THIS_MODULE,
                .pm     = &ep93xxbl_pm_ops,
        },
        .probe          = ep93xxbl_probe,
index 5d8d65200db77b739154c44e394138bc8b332641..67dfb939a51428f1fb1a54381bfde0b56d53fe8e 100644 (file)
@@ -52,24 +52,6 @@ static int genericbl_get_intensity(struct backlight_device *bd)
        return genericbl_intensity;
 }
 
-/*
- * Called when the battery is low to limit the backlight intensity.
- * If limit==0 clear any limit, otherwise limit the intensity
- */
-void genericbl_limit_intensity(int limit)
-{
-       struct backlight_device *bd = generic_backlight_device;
-
-       mutex_lock(&bd->ops_lock);
-       if (limit)
-               bd->props.state |= GENERICBL_BATTLOW;
-       else
-               bd->props.state &= ~GENERICBL_BATTLOW;
-       backlight_update_status(generic_backlight_device);
-       mutex_unlock(&bd->ops_lock);
-}
-EXPORT_SYMBOL(genericbl_limit_intensity);
-
 static const struct backlight_ops genericbl_ops = {
        .options = BL_CORE_SUSPENDRESUME,
        .get_brightness = genericbl_get_intensity,
index aaead04a2d541c838e02d7611f42d0c29a1a1084..439feb2389a8cc3d70a45d7a2a3eac431d4f8db9 100644 (file)
@@ -151,7 +151,6 @@ static struct of_device_id gpio_backlight_of_match[] = {
 static struct platform_driver gpio_backlight_driver = {
        .driver         = {
                .name           = "gpio-backlight",
-               .owner          = THIS_MODULE,
                .of_match_table = of_match_ptr(gpio_backlight_of_match),
        },
        .probe          = gpio_backlight_probe,
index ea67fe199e34ed880c305fa1b0ca26f330eccccd..e7f0890cc21142f947eaf41e6975e5007ecf96ce 100644 (file)
@@ -495,17 +495,18 @@ static int ili922x_probe(struct spi_device *spi)
                        "no LCD found: Chip ID 0x%x, ret %d\n",
                        reg, ret);
                return -ENODEV;
-       } else {
-               dev_info(&spi->dev, "ILI%x found, SPI freq %d, mode %d\n",
-                        reg, spi->max_speed_hz, spi->mode);
        }
 
+       dev_info(&spi->dev, "ILI%x found, SPI freq %d, mode %d\n",
+                reg, spi->max_speed_hz, spi->mode);
+
        ret = ili922x_read_status(spi, &reg);
        if (ret) {
                dev_err(&spi->dev, "reading RS failed...\n");
                return ret;
-       } else
-               dev_dbg(&spi->dev, "status: 0x%x\n", reg);
+       }
+
+       dev_dbg(&spi->dev, "status: 0x%x\n", reg);
 
        ili922x_display_init(spi);
 
index 6ce96b4a879696bc04457b329f2f39e16bb5e7ed..7e6ff53468924bcb246a81ad018dcbb397e54ea4 100644 (file)
@@ -41,11 +41,11 @@ static int jornada_bl_get_brightness(struct backlight_device *bd)
                dev_err(&bd->dev, "get brightness timeout\n");
                jornada_ssp_end();
                return -ETIMEDOUT;
-       } else {
-               /* exchange txdummy for value */
-               ret = jornada_ssp_byte(TXDUMMY);
        }
 
+       /* exchange txdummy for value */
+       ret = jornada_ssp_byte(TXDUMMY);
+
        jornada_ssp_end();
 
        return BL_MAX_BRIGHT - ret;
index 228bc319de1922251d3c4cffb5adf190c375393b..dfa0fa0d5c782c082efcd04fe9a5f1ed964787a4 100644 (file)
 
 static int jornada_lcd_get_power(struct lcd_device *ld)
 {
-       /* LDD2 in PPC = LCD POWER */
-       if (PPSR & PPC_LDD2)
-               return FB_BLANK_UNBLANK;        /* PW ON */
-       else
-               return FB_BLANK_POWERDOWN;      /* PW OFF */
+       return PPSR & PPC_LDD2 ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
 }
 
 static int jornada_lcd_get_contrast(struct lcd_device *ld)
index ccb44e8e492721e981bc09dd98e58338020f6c48..f71eaf10c4ebddac11a32b5ec5baf03519743dc0 100644 (file)
@@ -566,11 +566,11 @@ static int ld9040_power_on(struct ld9040 *lcd)
        if (!pd->reset) {
                dev_err(lcd->dev, "reset is NULL.\n");
                return -EINVAL;
-       } else {
-               pd->reset(lcd->ld);
-               msleep(pd->reset_delay);
        }
 
+       pd->reset(lcd->ld);
+       msleep(pd->reset_delay);
+
        ret = ld9040_ldi_init(lcd);
        if (ret) {
                dev_err(lcd->dev, "failed to initialize ldi.\n");
index cff1fbe89a1bf547ed206e7979352dfaa4b0692b..0e2337f367b61bfa0639ef62a4e3c40f01b510cd 100644 (file)
@@ -397,7 +397,6 @@ static void lm3533_bl_shutdown(struct platform_device *pdev)
 static struct platform_driver lm3533_bl_driver = {
        .driver = {
                .name   = "lm3533-backlight",
-               .owner  = THIS_MODULE,
                .pm     = &lm3533_bl_pm_ops,
        },
        .probe          = lm3533_bl_probe,
index 5f36808d214f0d27665ced0baaf87e4f9a95b6ff..cd50df5807eadb2b4bf18b6fa8efc906beda0dee 100644 (file)
@@ -254,7 +254,6 @@ static void lm3639_torch_brightness_set(struct led_classdev *cdev,
        return;
 out:
        dev_err(pchip->dev, "i2c failed to access register\n");
-       return;
 }
 
 /* flash */
@@ -293,7 +292,6 @@ static void lm3639_flash_brightness_set(struct led_classdev *cdev,
        return;
 out:
        dev_err(pchip->dev, "i2c failed to access register\n");
-       return;
 }
 
 static const struct regmap_config lm3639_regmap = {
index 77258b7b04be288214cba7ee141bf87402c514cf..7e3810308c3e456e56d5f5e6512fbef0bbc2d7dd 100644 (file)
@@ -232,19 +232,19 @@ static int lms501kf03_power_on(struct lms501kf03 *lcd)
        if (!pd->power_on) {
                dev_err(lcd->dev, "power_on is NULL.\n");
                return -EINVAL;
-       } else {
-               pd->power_on(lcd->ld, 1);
-               msleep(pd->power_on_delay);
        }
 
+       pd->power_on(lcd->ld, 1);
+       msleep(pd->power_on_delay);
+
        if (!pd->reset) {
                dev_err(lcd->dev, "reset is NULL.\n");
                return -EINVAL;
-       } else {
-               pd->reset(lcd->ld);
-               msleep(pd->reset_delay);
        }
 
+       pd->reset(lcd->ld);
+       msleep(pd->reset_delay);
+
        ret = lms501kf03_ldi_init(lcd);
        if (ret) {
                dev_err(lcd->dev, "failed to initialize ldi.\n");
index dcdd5443efcf42237b0821963933c9b87a65c900..25fb8e3d75b16765e74198cacdf9e816ca84b3ea 100644 (file)
@@ -268,6 +268,7 @@ static int lp855x_bl_update_status(struct backlight_device *bl)
 
        } else if (lp->mode == REGISTER_BASED) {
                u8 val = bl->props.brightness;
+
                lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
        }
 
@@ -308,6 +309,7 @@ static ssize_t lp855x_get_chip_id(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
        struct lp855x *lp = dev_get_drvdata(dev);
+
        return scnprintf(buf, PAGE_SIZE, "%s\n", lp->chipname);
 }
 
index d6c4f6a2d43e2a1c2ec872b061ddd408217a3537..e418d5b1aa55a6973ae1dfee8968f8d7baa778ca 100644 (file)
@@ -315,7 +315,6 @@ static struct platform_driver lp8788_bl_driver = {
        .remove = lp8788_backlight_remove,
        .driver = {
                .name = LP8788_DEV_BACKLIGHT,
-               .owner = THIS_MODULE,
        },
 };
 module_platform_driver(lp8788_bl_driver);
index 66fa08c920d25c61e492e8e639f5eea722835a36..7b738d60ecc22e27560e42c9cef0669628e4aa28 100644 (file)
@@ -197,7 +197,6 @@ static int max8925_backlight_probe(struct platform_device *pdev)
 static struct platform_driver max8925_backlight_driver = {
        .driver         = {
                .name   = "max8925-backlight",
-               .owner  = THIS_MODULE,
        },
        .probe          = max8925_backlight_probe,
 };
index a0dcd88ac74fce38d77531df242a690c4bc1eb12..546d94df21d53ac436891273828058043acb2c2d 100644 (file)
@@ -120,6 +120,7 @@ static int omapbl_update_status(struct backlight_device *dev)
 static int omapbl_get_intensity(struct backlight_device *dev)
 {
        struct omap_backlight *bl = bl_get_data(dev);
+
        return bl->current_intensity;
 }
 
index f5a5202dd79d4c1cbba5b11631f323fa5bf59ee3..3acdb9f646ed05792fa047eb5272d03a23e8eef6 100644 (file)
@@ -152,7 +152,6 @@ static int ot200_backlight_remove(struct platform_device *pdev)
 static struct platform_driver ot200_backlight_driver = {
        .driver         = {
                .name   = "ot200-backlight",
-               .owner  = THIS_MODULE,
        },
        .probe          = ot200_backlight_probe,
        .remove         = ot200_backlight_remove,
index 2e3f82063c03b7243eed9b077c14e0b081297724..5d8bb8b201835ddf96ed1a5465ed25f682a2a9e8 100644 (file)
@@ -142,7 +142,6 @@ static int pandora_backlight_probe(struct platform_device *pdev)
 static struct platform_driver pandora_backlight_driver = {
        .driver         = {
                .name   = "pandora-backlight",
-               .owner  = THIS_MODULE,
        },
        .probe          = pandora_backlight_probe,
 };
index b95d3b0aaffee03dc5f5f0380d91b456ea5293cb..85bd573b6d15c1b248fefb9addc5fd755772b635 100644 (file)
@@ -90,6 +90,7 @@ static int pcf50633_bl_update_status(struct backlight_device *bl)
 static int pcf50633_bl_get_brightness(struct backlight_device *bl)
 {
        struct pcf50633_bl *pcf_bl = bl_get_data(bl);
+
        return pcf_bl->brightness;
 }
 
index c3d2e209fc8f5b2ed148c30deeb5bffe970756a2..872a3bf21fafc014a5ee0453d9001485543b95ff 100644 (file)
@@ -148,7 +148,6 @@ MODULE_DEVICE_TABLE(of, platform_lcd_of_match);
 static struct platform_driver platform_lcd_driver = {
        .driver         = {
                .name   = "platform-lcd",
-               .owner  = THIS_MODULE,
                .pm     = &platform_lcd_pm_ops,
                .of_match_table = of_match_ptr(platform_lcd_of_match),
        },
index b85983e97f0afbc70665d1aa134d5b182e25976e..cb5ae4c08469c57304f337ae862bf46a11b58ac3 100644 (file)
@@ -390,7 +390,6 @@ static const struct dev_pm_ops pwm_backlight_pm_ops = {
 static struct platform_driver pwm_backlight_driver = {
        .driver         = {
                .name           = "pwm-backlight",
-               .owner          = THIS_MODULE,
                .pm             = &pwm_backlight_pm_ops,
                .of_match_table = of_match_ptr(pwm_backlight_of_match),
        },
index f3a65c8940ed9dae8c03fb115b23f09d3df87e39..28bfa127fee45aca7a0bd4c193efa47cb8d80e58 100644 (file)
@@ -507,19 +507,19 @@ static int s6e63m0_power_on(struct s6e63m0 *lcd)
        if (!pd->power_on) {
                dev_err(lcd->dev, "power_on is NULL.\n");
                return -EINVAL;
-       } else {
-               pd->power_on(lcd->ld, 1);
-               msleep(pd->power_on_delay);
        }
 
+       pd->power_on(lcd->ld, 1);
+       msleep(pd->power_on_delay);
+
        if (!pd->reset) {
                dev_err(lcd->dev, "reset is NULL.\n");
                return -EINVAL;
-       } else {
-               pd->reset(lcd->ld);
-               msleep(pd->reset_delay);
        }
 
+       pd->reset(lcd->ld);
+       msleep(pd->reset_delay);
+
        ret = s6e63m0_ldi_init(lcd);
        if (ret) {
                dev_err(lcd->dev, "failed to initialize ldi.\n");
index 908016fc5829ff3ef0667f5be62c51999d30106e..30afce33ef2afcfa364e3aa622d09a48b152d94e 100644 (file)
@@ -300,12 +300,14 @@ static int tdo24m_power(struct tdo24m *lcd, int power)
 static int tdo24m_set_power(struct lcd_device *ld, int power)
 {
        struct tdo24m *lcd = lcd_get_data(ld);
+
        return tdo24m_power(lcd, power);
 }
 
 static int tdo24m_get_power(struct lcd_device *ld)
 {
        struct tdo24m *lcd = lcd_get_data(ld);
+
        return lcd->power;
 }
 
index 2e04d93aa0efe0a8e687279d81e58c1f2d257d7a..61d72bffd402f25c2abc10266c4241c32ce006e5 100644 (file)
@@ -323,7 +323,6 @@ static int tps65217_bl_probe(struct platform_device *pdev)
 static struct platform_driver tps65217_bl_driver = {
        .probe          = tps65217_bl_probe,
        .driver         = {
-               .owner  = THIS_MODULE,
                .name   = "tps65217-bl",
        },
 };
index 8b9455e9306930027b4fa20454b2ac837d8b4ff9..6eab0d6c262aab5d11fa8d4da17f3c5d0e7d8e6c 100644 (file)
@@ -111,6 +111,7 @@ static int wm831x_backlight_update_status(struct backlight_device *bl)
 static int wm831x_backlight_get_brightness(struct backlight_device *bl)
 {
        struct wm831x_backlight_data *data = bl_get_data(bl);
+
        return data->current_brightness;
 }
 
@@ -217,7 +218,6 @@ static int wm831x_backlight_probe(struct platform_device *pdev)
 static struct platform_driver wm831x_backlight_driver = {
        .driver         = {
                .name   = "wm831x-backlight",
-               .owner  = THIS_MODULE,
        },
        .probe          = wm831x_backlight_probe,
 };
index e408679081ab25a8adb3fe543a8c70c45e4289b5..6f433b8cee1267653e05c4a6e4ba7ba5062e4253 100644 (file)
@@ -270,7 +270,7 @@ static int viafb_gpio_probe(struct platform_device *platdev)
 static int viafb_gpio_remove(struct platform_device *platdev)
 {
        unsigned long flags;
-       int ret = 0, i;
+       int i;
 
 #ifdef CONFIG_PM
        viafb_pm_unregister(&viafb_gpio_pm_hooks);
@@ -280,11 +280,7 @@ static int viafb_gpio_remove(struct platform_device *platdev)
         * Get unregistered.
         */
        if (viafb_gpio_config.gpio_chip.ngpio > 0) {
-               ret = gpiochip_remove(&viafb_gpio_config.gpio_chip);
-               if (ret) { /* Somebody still using it? */
-                       printk(KERN_ERR "Viafb: GPIO remove failed\n");
-                       return ret;
-               }
+               gpiochip_remove(&viafb_gpio_config.gpio_chip);
        }
        /*
         * Disable the ports.
@@ -294,7 +290,7 @@ static int viafb_gpio_remove(struct platform_device *platdev)
                viafb_gpio_disable(viafb_gpio_config.active_gpios[i]);
        viafb_gpio_config.gpio_chip.ngpio = 0;
        spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags);
-       return ret;
+       return 0;
 }
 
 static struct platform_driver via_gpio_driver = {
index 901014bbc8210b4ac876df7db9c8a0a4493256eb..09dc44736c1ac72160f5d9aa4d13ab1912151be9 100644 (file)
@@ -684,12 +684,13 @@ static const struct xenbus_device_id xenfb_ids[] = {
        { "" }
 };
 
-static DEFINE_XENBUS_DRIVER(xenfb, ,
+static struct xenbus_driver xenfb_driver = {
+       .ids = xenfb_ids,
        .probe = xenfb_probe,
        .remove = xenfb_remove,
        .resume = xenfb_resume,
        .otherend_changed = xenfb_backend_changed,
-);
+};
 
 static int __init xenfb_init(void)
 {
index c6683f2e396c3ef93effefe61d272627a4e4a24a..00b2286382743203027b834c300a62745caa4a3f 100644 (file)
@@ -25,6 +25,7 @@ config VIRTIO_PCI
 config VIRTIO_BALLOON
        tristate "Virtio balloon driver"
        depends on VIRTIO
+       select MEMORY_BALLOON
        ---help---
         This driver supports increasing and decreasing the amount
         of memory within a KVM guest.
index 25ebe8eecdb7397be646623775bd42c538ba0100..f893148a107bf44d23c953f5df1c0b905b2e3c08 100644 (file)
@@ -59,7 +59,7 @@ struct virtio_balloon
         * Each page on this list adds VIRTIO_BALLOON_PAGES_PER_PAGE
         * to num_pages above.
         */
-       struct balloon_dev_info *vb_dev_info;
+       struct balloon_dev_info vb_dev_info;
 
        /* Synchronize access/update to this struct virtio_balloon elements */
        struct mutex balloon_lock;
@@ -127,7 +127,7 @@ static void set_page_pfns(u32 pfns[], struct page *page)
 
 static void fill_balloon(struct virtio_balloon *vb, size_t num)
 {
-       struct balloon_dev_info *vb_dev_info = vb->vb_dev_info;
+       struct balloon_dev_info *vb_dev_info = &vb->vb_dev_info;
 
        /* We can only do one array worth at a time. */
        num = min(num, ARRAY_SIZE(vb->pfns));
@@ -163,15 +163,15 @@ static void release_pages_by_pfn(const u32 pfns[], unsigned int num)
        /* Find pfns pointing at start of each page, get pages and free them. */
        for (i = 0; i < num; i += VIRTIO_BALLOON_PAGES_PER_PAGE) {
                struct page *page = balloon_pfn_to_page(pfns[i]);
-               balloon_page_free(page);
                adjust_managed_page_count(page, 1);
+               put_page(page); /* balloon reference */
        }
 }
 
 static void leak_balloon(struct virtio_balloon *vb, size_t num)
 {
        struct page *page;
-       struct balloon_dev_info *vb_dev_info = vb->vb_dev_info;
+       struct balloon_dev_info *vb_dev_info = &vb->vb_dev_info;
 
        /* We can only do one array worth at a time. */
        num = min(num, ARRAY_SIZE(vb->pfns));
@@ -353,12 +353,11 @@ static int init_vqs(struct virtio_balloon *vb)
        return 0;
 }
 
-static const struct address_space_operations virtio_balloon_aops;
 #ifdef CONFIG_BALLOON_COMPACTION
 /*
  * virtballoon_migratepage - perform the balloon page migration on behalf of
  *                          a compation thread.     (called under page lock)
- * @mapping: the page->mapping which will be assigned to the new migrated page.
+ * @vb_dev_info: the balloon device
  * @newpage: page that will replace the isolated page after migration finishes.
  * @page   : the isolated (old) page that is about to be migrated to newpage.
  * @mode   : compaction mode -- not used for balloon page migration.
@@ -373,17 +372,13 @@ static const struct address_space_operations virtio_balloon_aops;
  * This function preforms the balloon page migration task.
  * Called through balloon_mapping->a_ops->migratepage
  */
-static int virtballoon_migratepage(struct address_space *mapping,
+static int virtballoon_migratepage(struct balloon_dev_info *vb_dev_info,
                struct page *newpage, struct page *page, enum migrate_mode mode)
 {
-       struct balloon_dev_info *vb_dev_info = balloon_page_device(page);
-       struct virtio_balloon *vb;
+       struct virtio_balloon *vb = container_of(vb_dev_info,
+                       struct virtio_balloon, vb_dev_info);
        unsigned long flags;
 
-       BUG_ON(!vb_dev_info);
-
-       vb = vb_dev_info->balloon_device;
-
        /*
         * In order to avoid lock contention while migrating pages concurrently
         * to leak_balloon() or fill_balloon() we just give up the balloon_lock
@@ -395,21 +390,19 @@ static int virtballoon_migratepage(struct address_space *mapping,
        if (!mutex_trylock(&vb->balloon_lock))
                return -EAGAIN;
 
+       get_page(newpage); /* balloon reference */
+
        /* balloon's page migration 1st step  -- inflate "newpage" */
        spin_lock_irqsave(&vb_dev_info->pages_lock, flags);
-       balloon_page_insert(newpage, mapping, &vb_dev_info->pages);
+       balloon_page_insert(vb_dev_info, newpage);
        vb_dev_info->isolated_pages--;
+       __count_vm_event(BALLOON_MIGRATE);
        spin_unlock_irqrestore(&vb_dev_info->pages_lock, flags);
        vb->num_pfns = VIRTIO_BALLOON_PAGES_PER_PAGE;
        set_page_pfns(vb->pfns, newpage);
        tell_host(vb, vb->inflate_vq);
 
-       /*
-        * balloon's page migration 2nd step -- deflate "page"
-        *
-        * It's safe to delete page->lru here because this page is at
-        * an isolated migration list, and this step is expected to happen here
-        */
+       /* balloon's page migration 2nd step -- deflate "page" */
        balloon_page_delete(page);
        vb->num_pfns = VIRTIO_BALLOON_PAGES_PER_PAGE;
        set_page_pfns(vb->pfns, page);
@@ -417,20 +410,15 @@ static int virtballoon_migratepage(struct address_space *mapping,
 
        mutex_unlock(&vb->balloon_lock);
 
-       return MIGRATEPAGE_BALLOON_SUCCESS;
-}
+       put_page(page); /* balloon reference */
 
-/* define the balloon_mapping->a_ops callback to allow balloon page migration */
-static const struct address_space_operations virtio_balloon_aops = {
-                       .migratepage = virtballoon_migratepage,
-};
+       return MIGRATEPAGE_SUCCESS;
+}
 #endif /* CONFIG_BALLOON_COMPACTION */
 
 static int virtballoon_probe(struct virtio_device *vdev)
 {
        struct virtio_balloon *vb;
-       struct address_space *vb_mapping;
-       struct balloon_dev_info *vb_devinfo;
        int err;
 
        vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL);
@@ -446,30 +434,14 @@ static int virtballoon_probe(struct virtio_device *vdev)
        vb->vdev = vdev;
        vb->need_stats_update = 0;
 
-       vb_devinfo = balloon_devinfo_alloc(vb);
-       if (IS_ERR(vb_devinfo)) {
-               err = PTR_ERR(vb_devinfo);
-               goto out_free_vb;
-       }
-
-       vb_mapping = balloon_mapping_alloc(vb_devinfo,
-                                          (balloon_compaction_check()) ?
-                                          &virtio_balloon_aops : NULL);
-       if (IS_ERR(vb_mapping)) {
-               /*
-                * IS_ERR(vb_mapping) && PTR_ERR(vb_mapping) == -EOPNOTSUPP
-                * This means !CONFIG_BALLOON_COMPACTION, otherwise we get off.
-                */
-               err = PTR_ERR(vb_mapping);
-               if (err != -EOPNOTSUPP)
-                       goto out_free_vb_devinfo;
-       }
-
-       vb->vb_dev_info = vb_devinfo;
+       balloon_devinfo_init(&vb->vb_dev_info);
+#ifdef CONFIG_BALLOON_COMPACTION
+       vb->vb_dev_info.migratepage = virtballoon_migratepage;
+#endif
 
        err = init_vqs(vb);
        if (err)
-               goto out_free_vb_mapping;
+               goto out_free_vb;
 
        vb->thread = kthread_run(balloon, vb, "vballoon");
        if (IS_ERR(vb->thread)) {
@@ -481,10 +453,6 @@ static int virtballoon_probe(struct virtio_device *vdev)
 
 out_del_vqs:
        vdev->config->del_vqs(vdev);
-out_free_vb_mapping:
-       balloon_mapping_free(vb_mapping);
-out_free_vb_devinfo:
-       balloon_devinfo_free(vb_devinfo);
 out_free_vb:
        kfree(vb);
 out:
@@ -510,8 +478,6 @@ static void virtballoon_remove(struct virtio_device *vdev)
 
        kthread_stop(vb->thread);
        remove_common(vb);
-       balloon_mapping_free(vb->vb_dev_info->mapping);
-       balloon_devinfo_free(vb->vb_dev_info);
        kfree(vb);
 }
 
index 1d1330a78af35686ca179e7f4a9b6b848e4c842a..e3d5bf0a50218e0f3c8db39973a90e2cdeabefeb 100644 (file)
@@ -95,6 +95,16 @@ config GPIO_WATCHDOG
          If you say yes here you get support for watchdog device
          controlled through GPIO-line.
 
+config MENF21BMC_WATCHDOG
+       tristate "MEN 14F021P00 BMC Watchdog"
+       depends on MFD_MENF21BMC
+       select WATCHDOG_CORE
+       help
+         Say Y here to include support for the MEN 14F021P00 BMC Watchdog.
+
+         This driver can also be built as a module. If so the module
+         will be called menf21bmc_wdt.
+
 config WM831X_WATCHDOG
        tristate "WM831x watchdog"
        depends on MFD_WM831X
index 468c3204c3b190b7be20616d02d25ba947408581..de1701470c14fd12f2bfcc08b74fd56cedbe3a6a 100644 (file)
@@ -178,3 +178,4 @@ obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
 obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
 obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
 obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
index 996b2f7d330e94ec3143e6bebe0c86f74e162943..665e0e7dfe1e2e2e03eb371bc377f58b6f9f5236 100644 (file)
@@ -301,6 +301,28 @@ static struct miscdevice wdt_miscdev = {
        .fops   =       &wdt_fops,
 };
 
+static int wdt_restart_handle(struct notifier_block *this, unsigned long mode,
+                             void *cmd)
+{
+       /*
+        * Cobalt devices have no way of rebooting themselves other
+        * than getting the watchdog to pull reset, so we restart the
+        * watchdog on reboot with no heartbeat.
+        */
+       wdt_change(WDT_ENABLE);
+
+       /* loop until the watchdog fires */
+       while (true)
+               ;
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block wdt_restart_handler = {
+       .notifier_call = wdt_restart_handle,
+       .priority = 128,
+};
+
 /*
  *     Notifier for system down
  */
@@ -311,15 +333,6 @@ static int wdt_notify_sys(struct notifier_block *this,
        if (code == SYS_DOWN || code == SYS_HALT)
                wdt_turnoff();
 
-       if (code == SYS_RESTART) {
-               /*
-                * Cobalt devices have no way of rebooting themselves other
-                * than getting the watchdog to pull reset, so we restart the
-                * watchdog on reboot with no heartbeat
-                */
-               wdt_change(WDT_ENABLE);
-               pr_info("Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second\n");
-       }
        return NOTIFY_DONE;
 }
 
@@ -338,6 +351,7 @@ static void __exit alim7101_wdt_unload(void)
        /* Deregister */
        misc_deregister(&wdt_miscdev);
        unregister_reboot_notifier(&wdt_notifier);
+       unregister_restart_handler(&wdt_restart_handler);
        pci_dev_put(alim7101_pmu);
 }
 
@@ -390,11 +404,17 @@ static int __init alim7101_wdt_init(void)
                goto err_out;
        }
 
+       rc = register_restart_handler(&wdt_restart_handler);
+       if (rc) {
+               pr_err("cannot register restart handler (err=%d)\n", rc);
+               goto err_out_reboot;
+       }
+
        rc = misc_register(&wdt_miscdev);
        if (rc) {
                pr_err("cannot register miscdev on minor=%d (err=%d)\n",
                       wdt_miscdev.minor, rc);
-               goto err_out_reboot;
+               goto err_out_restart;
        }
 
        if (nowayout)
@@ -404,6 +424,8 @@ static int __init alim7101_wdt_init(void)
                timeout, nowayout);
        return 0;
 
+err_out_restart:
+       unregister_restart_handler(&wdt_restart_handler);
 err_out_reboot:
        unregister_reboot_notifier(&wdt_notifier);
 err_out:
diff --git a/drivers/watchdog/menf21bmc_wdt.c b/drivers/watchdog/menf21bmc_wdt.c
new file mode 100644 (file)
index 0000000..2042874
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ *  MEN 14F021P00 Board Management Controller (BMC) Watchdog Driver.
+ *
+ *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ *  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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+#define DEVNAME "menf21bmc_wdt"
+
+#define BMC_CMD_WD_ON          0x11
+#define BMC_CMD_WD_OFF         0x12
+#define BMC_CMD_WD_TRIG                0x13
+#define BMC_CMD_WD_TIME                0x14
+#define BMC_CMD_WD_STATE       0x17
+#define BMC_WD_OFF_VAL         0x69
+#define BMC_CMD_RST_RSN                0x92
+
+#define BMC_WD_TIMEOUT_MIN     1       /* in sec */
+#define BMC_WD_TIMEOUT_MAX     6553    /* in sec */
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+                               __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct menf21bmc_wdt {
+       struct watchdog_device wdt;
+       struct i2c_client *i2c_client;
+};
+
+static int menf21bmc_wdt_set_bootstatus(struct menf21bmc_wdt *data)
+{
+       int rst_rsn;
+
+       rst_rsn = i2c_smbus_read_byte_data(data->i2c_client, BMC_CMD_RST_RSN);
+       if (rst_rsn < 0)
+               return rst_rsn;
+
+       if (rst_rsn == 0x02)
+               data->wdt.bootstatus |= WDIOF_CARDRESET;
+       else if (rst_rsn == 0x05)
+               data->wdt.bootstatus |= WDIOF_EXTERN1;
+       else if (rst_rsn == 0x06)
+               data->wdt.bootstatus |= WDIOF_EXTERN2;
+       else if (rst_rsn == 0x0A)
+               data->wdt.bootstatus |= WDIOF_POWERUNDER;
+
+       return 0;
+}
+
+static int menf21bmc_wdt_start(struct watchdog_device *wdt)
+{
+       struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
+
+       return i2c_smbus_write_byte(drv_data->i2c_client, BMC_CMD_WD_ON);
+}
+
+static int menf21bmc_wdt_stop(struct watchdog_device *wdt)
+{
+       struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
+
+       return i2c_smbus_write_byte_data(drv_data->i2c_client,
+                                        BMC_CMD_WD_OFF, BMC_WD_OFF_VAL);
+}
+
+static int
+menf21bmc_wdt_settimeout(struct watchdog_device *wdt, unsigned int timeout)
+{
+       int ret;
+       struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
+
+       /*
+        *  BMC Watchdog does have a resolution of 100ms.
+        *  Watchdog API defines the timeout in seconds, so we have to
+        *  multiply the value.
+        */
+       ret = i2c_smbus_write_word_data(drv_data->i2c_client,
+                                       BMC_CMD_WD_TIME, timeout * 10);
+       if (ret < 0)
+               return ret;
+
+       wdt->timeout = timeout;
+
+       return 0;
+}
+
+static int menf21bmc_wdt_ping(struct watchdog_device *wdt)
+{
+       struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
+
+       return i2c_smbus_write_byte(drv_data->i2c_client, BMC_CMD_WD_TRIG);
+}
+
+static const struct watchdog_info menf21bmc_wdt_info = {
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+       .identity = DEVNAME,
+};
+
+static const struct watchdog_ops menf21bmc_wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = menf21bmc_wdt_start,
+       .stop           = menf21bmc_wdt_stop,
+       .ping           = menf21bmc_wdt_ping,
+       .set_timeout    = menf21bmc_wdt_settimeout,
+};
+
+static int menf21bmc_wdt_probe(struct platform_device *pdev)
+{
+       int ret, bmc_timeout;
+       struct menf21bmc_wdt *drv_data;
+       struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent);
+
+       drv_data = devm_kzalloc(&pdev->dev,
+                               sizeof(struct menf21bmc_wdt), GFP_KERNEL);
+       if (!drv_data)
+               return -ENOMEM;
+
+       drv_data->wdt.ops = &menf21bmc_wdt_ops;
+       drv_data->wdt.info = &menf21bmc_wdt_info;
+       drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN;
+       drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX;
+       drv_data->i2c_client = i2c_client;
+
+       /*
+        * Get the current wdt timeout value from the BMC because
+        * the BMC will save the value set before if the system restarts.
+        */
+       bmc_timeout = i2c_smbus_read_word_data(drv_data->i2c_client,
+                                              BMC_CMD_WD_TIME);
+       if (bmc_timeout < 0) {
+               dev_err(&pdev->dev, "failed to get current WDT timeout\n");
+               return bmc_timeout;
+       }
+
+       watchdog_init_timeout(&drv_data->wdt, bmc_timeout / 10, &pdev->dev);
+       watchdog_set_nowayout(&drv_data->wdt, nowayout);
+       watchdog_set_drvdata(&drv_data->wdt, drv_data);
+       platform_set_drvdata(pdev, drv_data);
+
+       ret = menf21bmc_wdt_set_bootstatus(drv_data);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to set Watchdog bootstatus\n");
+               return ret;
+       }
+
+       ret = watchdog_register_device(&drv_data->wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register Watchdog device\n");
+               return ret;
+       }
+
+       dev_info(&pdev->dev, "MEN 14F021P00 BMC Watchdog device enabled\n");
+
+       return 0;
+}
+
+static int menf21bmc_wdt_remove(struct platform_device *pdev)
+{
+       struct menf21bmc_wdt *drv_data = platform_get_drvdata(pdev);
+
+       dev_warn(&pdev->dev,
+                "Unregister MEN 14F021P00 BMC Watchdog device, board may reset\n");
+
+       watchdog_unregister_device(&drv_data->wdt);
+
+       return 0;
+}
+
+static void menf21bmc_wdt_shutdown(struct platform_device *pdev)
+{
+       struct menf21bmc_wdt *drv_data = platform_get_drvdata(pdev);
+
+       i2c_smbus_write_word_data(drv_data->i2c_client,
+                                 BMC_CMD_WD_OFF, BMC_WD_OFF_VAL);
+}
+
+static struct  platform_driver menf21bmc_wdt = {
+       .driver         = {
+               .owner = THIS_MODULE,
+               .name   = DEVNAME,
+       },
+       .probe          = menf21bmc_wdt_probe,
+       .remove         = menf21bmc_wdt_remove,
+       .shutdown       = menf21bmc_wdt_shutdown,
+};
+
+module_platform_driver(menf21bmc_wdt);
+
+MODULE_DESCRIPTION("MEN 14F021P00 BMC Watchdog driver");
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:menf21bmc_wdt");
index 4aa3a8a876fe84f109a211b560e453ccf879172b..a64405b825969c341f00f02ca752853fa8a0de13 100644 (file)
 #include <linux/module.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
+#include <linux/notifier.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 #include <linux/watchdog.h>
 #include <linux/moduleparam.h>
 
-#include <asm/system_misc.h>
-
 #define REG_COUNT                      0x4
 #define REG_MODE                       0x8
 #define REG_ENABLE                     0xC
@@ -29,17 +29,22 @@ struct moxart_wdt_dev {
        struct watchdog_device dev;
        void __iomem *base;
        unsigned int clock_frequency;
+       struct notifier_block restart_handler;
 };
 
-static struct moxart_wdt_dev *moxart_restart_ctx;
-
 static int heartbeat;
 
-static void moxart_wdt_restart(enum reboot_mode reboot_mode, const char *cmd)
+static int moxart_restart_handle(struct notifier_block *this,
+                                unsigned long mode, void *cmd)
 {
-       writel(1, moxart_restart_ctx->base + REG_COUNT);
-       writel(0x5ab9, moxart_restart_ctx->base + REG_MODE);
-       writel(0x03, moxart_restart_ctx->base + REG_ENABLE);
+       struct moxart_wdt_dev *moxart_wdt = container_of(this,
+                                                        struct moxart_wdt_dev,
+                                                        restart_handler);
+       writel(1, moxart_wdt->base + REG_COUNT);
+       writel(0x5ab9, moxart_wdt->base + REG_MODE);
+       writel(0x03, moxart_wdt->base + REG_ENABLE);
+
+       return NOTIFY_DONE;
 }
 
 static int moxart_wdt_stop(struct watchdog_device *wdt_dev)
@@ -136,8 +141,12 @@ static int moxart_wdt_probe(struct platform_device *pdev)
        if (err)
                return err;
 
-       moxart_restart_ctx = moxart_wdt;
-       arm_pm_restart = moxart_wdt_restart;
+       moxart_wdt->restart_handler.notifier_call = moxart_restart_handle;
+       moxart_wdt->restart_handler.priority = 128;
+       err = register_restart_handler(&moxart_wdt->restart_handler);
+       if (err)
+               dev_err(dev, "cannot register restart notifier (err=%d)\n",
+                       err);
 
        dev_dbg(dev, "Watchdog enabled (heartbeat=%d sec, nowayout=%d)\n",
                moxart_wdt->dev.timeout, nowayout);
@@ -149,9 +158,8 @@ static int moxart_wdt_remove(struct platform_device *pdev)
 {
        struct moxart_wdt_dev *moxart_wdt = platform_get_drvdata(pdev);
 
-       arm_pm_restart = NULL;
+       unregister_restart_handler(&moxart_wdt->restart_handler);
        moxart_wdt_stop(&moxart_wdt->dev);
-       watchdog_unregister_device(&moxart_wdt->dev);
 
        return 0;
 }
index 60deb9d304c0dfcea5bb8b5b8111d164cffcf791..480bb557f353cbdfc1923769b5ee0441750e8ca8 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/notifier.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/reboot.h>
 #include <linux/types.h>
 #include <linux/watchdog.h>
 
-#include <asm/system_misc.h>
-
 #define WDT_MAX_TIMEOUT         16
 #define WDT_MIN_TIMEOUT         1
 #define WDT_MODE_TIMEOUT(n)     ((n) << 3)
@@ -50,6 +49,7 @@ static unsigned int timeout = WDT_MAX_TIMEOUT;
 struct sunxi_wdt_dev {
        struct watchdog_device wdt_dev;
        void __iomem *wdt_base;
+       struct notifier_block restart_handler;
 };
 
 /*
@@ -74,24 +74,29 @@ static const int wdt_timeout_map[] = {
        [16] = 0xB, /* 16s */
 };
 
-static void __iomem *reboot_wdt_base;
 
-static void sun4i_wdt_restart(enum reboot_mode mode, const char *cmd)
+static int sunxi_restart_handle(struct notifier_block *this, unsigned long mode,
+                               void *cmd)
 {
+       struct sunxi_wdt_dev *sunxi_wdt = container_of(this,
+                                                      struct sunxi_wdt_dev,
+                                                      restart_handler);
+       void __iomem *wdt_base = sunxi_wdt->wdt_base;
+
        /* Enable timer and set reset bit in the watchdog */
-       writel(WDT_MODE_EN | WDT_MODE_RST_EN, reboot_wdt_base + WDT_MODE);
+       writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE);
 
        /*
         * Restart the watchdog. The default (and lowest) interval
         * value for the watchdog is 0.5s.
         */
-       writel(WDT_CTRL_RELOAD, reboot_wdt_base + WDT_CTRL);
+       writel(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL);
 
        while (1) {
                mdelay(5);
-               writel(WDT_MODE_EN | WDT_MODE_RST_EN,
-                      reboot_wdt_base + WDT_MODE);
+               writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE);
        }
+       return NOTIFY_DONE;
 }
 
 static int sunxi_wdt_ping(struct watchdog_device *wdt_dev)
@@ -205,8 +210,12 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
        if (unlikely(err))
                return err;
 
-       reboot_wdt_base = sunxi_wdt->wdt_base;
-       arm_pm_restart = sun4i_wdt_restart;
+       sunxi_wdt->restart_handler.notifier_call = sunxi_restart_handle;
+       sunxi_wdt->restart_handler.priority = 128;
+       err = register_restart_handler(&sunxi_wdt->restart_handler);
+       if (err)
+               dev_err(&pdev->dev,
+                       "cannot register restart handler (err=%d)\n", err);
 
        dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
                        sunxi_wdt->wdt_dev.timeout, nowayout);
@@ -218,7 +227,7 @@ static int sunxi_wdt_remove(struct platform_device *pdev)
 {
        struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev);
 
-       arm_pm_restart = NULL;
+       unregister_restart_handler(&sunxi_wdt->restart_handler);
 
        watchdog_unregister_device(&sunxi_wdt->wdt_dev);
        watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL);
index 8bc01838daf9dd8ac7fa53524e13c2a252cec6fe..b812462083fcaf8d32c215327d28b7c4b7e6e4da 100644 (file)
@@ -172,6 +172,15 @@ config XEN_PCIDEV_BACKEND
 
          If in doubt, say m.
 
+config XEN_SCSI_BACKEND
+       tristate "XEN SCSI backend driver"
+       depends on XEN && XEN_BACKEND && TARGET_CORE
+       help
+         The SCSI backend driver allows the kernel to export its SCSI Devices
+         to other guests via a high-performance shared-memory interface.
+         Only needed for systems running as XEN driver domains (e.g. Dom0) and
+         if guests need generic access to SCSI devices.
+
 config XEN_PRIVCMD
        tristate
        depends on XEN
index 84044b554e33bd614c59ca6ce75d92684a9b2313..2140398a2a8c6b5f521c6250bb56a56ad41c9df7 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_XEN_ACPI_HOTPLUG_MEMORY) += xen-acpi-memhotplug.o
 obj-$(CONFIG_XEN_ACPI_HOTPLUG_CPU)     += xen-acpi-cpuhotplug.o
 obj-$(CONFIG_XEN_ACPI_PROCESSOR)       += xen-acpi-processor.o
 obj-$(CONFIG_XEN_EFI)                  += efi.o
+obj-$(CONFIG_XEN_SCSI_BACKEND)         += xen-scsiback.o
 xen-evtchn-y                           := evtchn.o
 xen-gntdev-y                           := gntdev.o
 xen-gntalloc-y                         := gntalloc.o
index 31f618a49661cac0a02e5d384c784c05dbb1bf31..1f850c97482f23e8b51a67e2d36a37f39fd21546 100644 (file)
@@ -27,6 +27,8 @@
 #include <xen/interface/platform.h>
 #include <xen/xen.h>
 
+#include <asm/page.h>
+
 #include <asm/xen/hypercall.h>
 
 #define INIT_EFI_OP(name) \
index 5b5c5ff273fdb7b09def11dcee21e4043640ce77..b4bca2d4a7e53c7675b25d632b560e1369b19020 100644 (file)
@@ -900,8 +900,8 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu)
        return irq;
 }
 
-static int bind_interdomain_evtchn_to_irq(unsigned int remote_domain,
-                                         unsigned int remote_port)
+int bind_interdomain_evtchn_to_irq(unsigned int remote_domain,
+                                  unsigned int remote_port)
 {
        struct evtchn_bind_interdomain bind_interdomain;
        int err;
@@ -914,6 +914,7 @@ static int bind_interdomain_evtchn_to_irq(unsigned int remote_domain,
 
        return err ? : bind_evtchn_to_irq(bind_interdomain.local_port);
 }
+EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irq);
 
 static int find_virq(unsigned int virq, unsigned int cpu)
 {
index c254ae036f18fe5f7ceaee5589de7f3501d80f7e..7786291ba229d93190516013863a933cc9eeeb86 100644 (file)
@@ -592,7 +592,7 @@ static int grow_gnttab_list(unsigned int more_frames)
        return 0;
 
 grow_nomem:
-       for ( ; i >= nr_glist_frames; i--)
+       while (i-- > nr_glist_frames)
                free_page((unsigned long) gnttab_list[i]);
        return -ENOMEM;
 }
index 259ba26615436278dfed308624b664acee83450d..017069a455d432af73af8939aee93195a5821a61 100644 (file)
@@ -133,7 +133,7 @@ static void pcistub_device_release(struct kref *kref)
        xen_pcibk_config_free_dyn_fields(dev);
        xen_pcibk_config_free_dev(dev);
 
-       dev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
+       pci_clear_dev_assigned(dev);
        pci_dev_put(dev);
 
        kfree(psdev);
@@ -413,7 +413,7 @@ static int pcistub_init_device(struct pci_dev *dev)
        dev_dbg(&dev->dev, "reset device\n");
        xen_pcibk_reset_device(dev);
 
-       dev->dev_flags |= PCI_DEV_FLAGS_ASSIGNED;
+       pci_set_dev_assigned(dev);
        return 0;
 
 config_release:
index c214daab482910b203b436520d876a618ffcca9b..ad8d30c088fe4b37d4c62b5f863e2561e4724509 100644 (file)
@@ -719,11 +719,13 @@ static const struct xenbus_device_id xen_pcibk_ids[] = {
        {""},
 };
 
-static DEFINE_XENBUS_DRIVER(xen_pcibk, DRV_NAME,
+static struct xenbus_driver xen_pcibk_driver = {
+       .name                   = DRV_NAME,
+       .ids                    = xen_pcibk_ids,
        .probe                  = xen_pcibk_xenbus_probe,
        .remove                 = xen_pcibk_xenbus_remove,
        .otherend_changed       = xen_pcibk_frontend_changed,
-);
+};
 
 const struct xen_pcibk_backend *__read_mostly xen_pcibk_backend;
 
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c
new file mode 100644 (file)
index 0000000..3e32146
--- /dev/null
@@ -0,0 +1,2126 @@
+/*
+ * Xen SCSI backend driver
+ *
+ * Copyright (c) 2008, FUJITSU Limited
+ *
+ * Based on the blkback driver code.
+ * Adaption to kernel taget core infrastructure taken from vhost/scsi.c
+ *
+ * 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; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (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.
+ */
+
+#include <stdarg.h>
+
+#include <linux/module.h>
+#include <linux/utsname.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/gfp.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/configfs.h>
+
+#include <generated/utsrelease.h>
+
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_fabric_configfs.h>
+
+#include <asm/hypervisor.h>
+
+#include <xen/xen.h>
+#include <xen/balloon.h>
+#include <xen/events.h>
+#include <xen/xenbus.h>
+#include <xen/grant_table.h>
+#include <xen/page.h>
+
+#include <xen/interface/grant_table.h>
+#include <xen/interface/io/vscsiif.h>
+
+#define DPRINTK(_f, _a...)                     \
+       pr_debug("(file=%s, line=%d) " _f, __FILE__ , __LINE__ , ## _a)
+
+#define VSCSI_VERSION  "v0.1"
+#define VSCSI_NAMELEN  32
+
+struct ids_tuple {
+       unsigned int hst;               /* host    */
+       unsigned int chn;               /* channel */
+       unsigned int tgt;               /* target  */
+       unsigned int lun;               /* LUN     */
+};
+
+struct v2p_entry {
+       struct ids_tuple v;             /* translate from */
+       struct scsiback_tpg *tpg;       /* translate to   */
+       unsigned int lun;
+       struct kref kref;
+       struct list_head l;
+};
+
+struct vscsibk_info {
+       struct xenbus_device *dev;
+
+       domid_t domid;
+       unsigned int irq;
+
+       struct vscsiif_back_ring ring;
+       int ring_error;
+
+       spinlock_t ring_lock;
+       atomic_t nr_unreplied_reqs;
+
+       spinlock_t v2p_lock;
+       struct list_head v2p_entry_lists;
+
+       wait_queue_head_t waiting_to_free;
+};
+
+/* theoretical maximum of grants for one request */
+#define VSCSI_MAX_GRANTS       (SG_ALL + VSCSIIF_SG_TABLESIZE)
+
+/*
+ * VSCSI_GRANT_BATCH is the maximum number of grants to be processed in one
+ * call to map/unmap grants. Don't choose it too large, as there are arrays
+ * with VSCSI_GRANT_BATCH elements allocated on the stack.
+ */
+#define VSCSI_GRANT_BATCH      16
+
+struct vscsibk_pend {
+       uint16_t rqid;
+
+       uint8_t cmnd[VSCSIIF_MAX_COMMAND_SIZE];
+       uint8_t cmd_len;
+
+       uint8_t sc_data_direction;
+       uint16_t n_sg;          /* real length of SG list */
+       uint16_t n_grants;      /* SG pages and potentially SG list */
+       uint32_t data_len;
+       uint32_t result;
+
+       struct vscsibk_info *info;
+       struct v2p_entry *v2p;
+       struct scatterlist *sgl;
+
+       uint8_t sense_buffer[VSCSIIF_SENSE_BUFFERSIZE];
+
+       grant_handle_t grant_handles[VSCSI_MAX_GRANTS];
+       struct page *pages[VSCSI_MAX_GRANTS];
+
+       struct se_cmd se_cmd;
+};
+
+struct scsiback_tmr {
+       atomic_t tmr_complete;
+       wait_queue_head_t tmr_wait;
+};
+
+struct scsiback_nexus {
+       /* Pointer to TCM session for I_T Nexus */
+       struct se_session *tvn_se_sess;
+};
+
+struct scsiback_tport {
+       /* SCSI protocol the tport is providing */
+       u8 tport_proto_id;
+       /* Binary World Wide unique Port Name for pvscsi Target port */
+       u64 tport_wwpn;
+       /* ASCII formatted WWPN for pvscsi Target port */
+       char tport_name[VSCSI_NAMELEN];
+       /* Returned by scsiback_make_tport() */
+       struct se_wwn tport_wwn;
+};
+
+struct scsiback_tpg {
+       /* scsiback port target portal group tag for TCM */
+       u16 tport_tpgt;
+       /* track number of TPG Port/Lun Links wrt explicit I_T Nexus shutdown */
+       int tv_tpg_port_count;
+       /* xen-pvscsi references to tpg_nexus, protected by tv_tpg_mutex */
+       int tv_tpg_fe_count;
+       /* list for scsiback_list */
+       struct list_head tv_tpg_list;
+       /* Used to protect access for tpg_nexus */
+       struct mutex tv_tpg_mutex;
+       /* Pointer to the TCM pvscsi I_T Nexus for this TPG endpoint */
+       struct scsiback_nexus *tpg_nexus;
+       /* Pointer back to scsiback_tport */
+       struct scsiback_tport *tport;
+       /* Returned by scsiback_make_tpg() */
+       struct se_portal_group se_tpg;
+       /* alias used in xenstore */
+       char param_alias[VSCSI_NAMELEN];
+       /* list of info structures related to this target portal group */
+       struct list_head info_list;
+};
+
+#define SCSIBACK_INVALID_HANDLE (~0)
+
+static bool log_print_stat;
+module_param(log_print_stat, bool, 0644);
+
+static int scsiback_max_buffer_pages = 1024;
+module_param_named(max_buffer_pages, scsiback_max_buffer_pages, int, 0644);
+MODULE_PARM_DESC(max_buffer_pages,
+"Maximum number of free pages to keep in backend buffer");
+
+static struct kmem_cache *scsiback_cachep;
+static DEFINE_SPINLOCK(free_pages_lock);
+static int free_pages_num;
+static LIST_HEAD(scsiback_free_pages);
+
+/* Global spinlock to protect scsiback TPG list */
+static DEFINE_MUTEX(scsiback_mutex);
+static LIST_HEAD(scsiback_list);
+
+/* Local pointer to allocated TCM configfs fabric module */
+static struct target_fabric_configfs *scsiback_fabric_configfs;
+
+static void scsiback_get(struct vscsibk_info *info)
+{
+       atomic_inc(&info->nr_unreplied_reqs);
+}
+
+static void scsiback_put(struct vscsibk_info *info)
+{
+       if (atomic_dec_and_test(&info->nr_unreplied_reqs))
+               wake_up(&info->waiting_to_free);
+}
+
+static void put_free_pages(struct page **page, int num)
+{
+       unsigned long flags;
+       int i = free_pages_num + num, n = num;
+
+       if (num == 0)
+               return;
+       if (i > scsiback_max_buffer_pages) {
+               n = min(num, i - scsiback_max_buffer_pages);
+               free_xenballooned_pages(n, page + num - n);
+               n = num - n;
+       }
+       spin_lock_irqsave(&free_pages_lock, flags);
+       for (i = 0; i < n; i++)
+               list_add(&page[i]->lru, &scsiback_free_pages);
+       free_pages_num += n;
+       spin_unlock_irqrestore(&free_pages_lock, flags);
+}
+
+static int get_free_page(struct page **page)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&free_pages_lock, flags);
+       if (list_empty(&scsiback_free_pages)) {
+               spin_unlock_irqrestore(&free_pages_lock, flags);
+               return alloc_xenballooned_pages(1, page, false);
+       }
+       page[0] = list_first_entry(&scsiback_free_pages, struct page, lru);
+       list_del(&page[0]->lru);
+       free_pages_num--;
+       spin_unlock_irqrestore(&free_pages_lock, flags);
+       return 0;
+}
+
+static unsigned long vaddr_page(struct page *page)
+{
+       unsigned long pfn = page_to_pfn(page);
+
+       return (unsigned long)pfn_to_kaddr(pfn);
+}
+
+static unsigned long vaddr(struct vscsibk_pend *req, int seg)
+{
+       return vaddr_page(req->pages[seg]);
+}
+
+static void scsiback_print_status(char *sense_buffer, int errors,
+                                       struct vscsibk_pend *pending_req)
+{
+       struct scsiback_tpg *tpg = pending_req->v2p->tpg;
+
+       pr_err("xen-pvscsi[%s:%d] cmnd[0]=%02x -> st=%02x msg=%02x host=%02x drv=%02x\n",
+              tpg->tport->tport_name, pending_req->v2p->lun,
+              pending_req->cmnd[0], status_byte(errors), msg_byte(errors),
+              host_byte(errors), driver_byte(errors));
+
+       if (CHECK_CONDITION & status_byte(errors))
+               __scsi_print_sense("xen-pvscsi", sense_buffer,
+                                  SCSI_SENSE_BUFFERSIZE);
+}
+
+static void scsiback_fast_flush_area(struct vscsibk_pend *req)
+{
+       struct gnttab_unmap_grant_ref unmap[VSCSI_GRANT_BATCH];
+       struct page *pages[VSCSI_GRANT_BATCH];
+       unsigned int i, invcount = 0;
+       grant_handle_t handle;
+       int err;
+
+       kfree(req->sgl);
+       req->sgl = NULL;
+       req->n_sg = 0;
+
+       if (!req->n_grants)
+               return;
+
+       for (i = 0; i < req->n_grants; i++) {
+               handle = req->grant_handles[i];
+               if (handle == SCSIBACK_INVALID_HANDLE)
+                       continue;
+               gnttab_set_unmap_op(&unmap[invcount], vaddr(req, i),
+                                   GNTMAP_host_map, handle);
+               req->grant_handles[i] = SCSIBACK_INVALID_HANDLE;
+               pages[invcount] = req->pages[i];
+               put_page(pages[invcount]);
+               invcount++;
+               if (invcount < VSCSI_GRANT_BATCH)
+                       continue;
+               err = gnttab_unmap_refs(unmap, NULL, pages, invcount);
+               BUG_ON(err);
+               invcount = 0;
+       }
+
+       if (invcount) {
+               err = gnttab_unmap_refs(unmap, NULL, pages, invcount);
+               BUG_ON(err);
+       }
+
+       put_free_pages(req->pages, req->n_grants);
+       req->n_grants = 0;
+}
+
+static void scsiback_free_translation_entry(struct kref *kref)
+{
+       struct v2p_entry *entry = container_of(kref, struct v2p_entry, kref);
+       struct scsiback_tpg *tpg = entry->tpg;
+
+       mutex_lock(&tpg->tv_tpg_mutex);
+       tpg->tv_tpg_fe_count--;
+       mutex_unlock(&tpg->tv_tpg_mutex);
+
+       kfree(entry);
+}
+
+static void scsiback_do_resp_with_sense(char *sense_buffer, int32_t result,
+                       uint32_t resid, struct vscsibk_pend *pending_req)
+{
+       struct vscsiif_response *ring_res;
+       struct vscsibk_info *info = pending_req->info;
+       int notify;
+       struct scsi_sense_hdr sshdr;
+       unsigned long flags;
+       unsigned len;
+
+       spin_lock_irqsave(&info->ring_lock, flags);
+
+       ring_res = RING_GET_RESPONSE(&info->ring, info->ring.rsp_prod_pvt);
+       info->ring.rsp_prod_pvt++;
+
+       ring_res->rslt   = result;
+       ring_res->rqid   = pending_req->rqid;
+
+       if (sense_buffer != NULL &&
+           scsi_normalize_sense(sense_buffer, VSCSIIF_SENSE_BUFFERSIZE,
+                                &sshdr)) {
+               len = min_t(unsigned, 8 + sense_buffer[7],
+                           VSCSIIF_SENSE_BUFFERSIZE);
+               memcpy(ring_res->sense_buffer, sense_buffer, len);
+               ring_res->sense_len = len;
+       } else {
+               ring_res->sense_len = 0;
+       }
+
+       ring_res->residual_len = resid;
+
+       RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&info->ring, notify);
+       spin_unlock_irqrestore(&info->ring_lock, flags);
+
+       if (notify)
+               notify_remote_via_irq(info->irq);
+
+       if (pending_req->v2p)
+               kref_put(&pending_req->v2p->kref,
+                        scsiback_free_translation_entry);
+}
+
+static void scsiback_cmd_done(struct vscsibk_pend *pending_req)
+{
+       struct vscsibk_info *info = pending_req->info;
+       unsigned char *sense_buffer;
+       unsigned int resid;
+       int errors;
+
+       sense_buffer = pending_req->sense_buffer;
+       resid        = pending_req->se_cmd.residual_count;
+       errors       = pending_req->result;
+
+       if (errors && log_print_stat)
+               scsiback_print_status(sense_buffer, errors, pending_req);
+
+       scsiback_fast_flush_area(pending_req);
+       scsiback_do_resp_with_sense(sense_buffer, errors, resid, pending_req);
+       scsiback_put(info);
+}
+
+static void scsiback_cmd_exec(struct vscsibk_pend *pending_req)
+{
+       struct se_cmd *se_cmd = &pending_req->se_cmd;
+       struct se_session *sess = pending_req->v2p->tpg->tpg_nexus->tvn_se_sess;
+       int rc;
+
+       memset(pending_req->sense_buffer, 0, VSCSIIF_SENSE_BUFFERSIZE);
+
+       memset(se_cmd, 0, sizeof(*se_cmd));
+
+       scsiback_get(pending_req->info);
+       rc = target_submit_cmd_map_sgls(se_cmd, sess, pending_req->cmnd,
+                       pending_req->sense_buffer, pending_req->v2p->lun,
+                       pending_req->data_len, 0,
+                       pending_req->sc_data_direction, 0,
+                       pending_req->sgl, pending_req->n_sg,
+                       NULL, 0, NULL, 0);
+       if (rc < 0) {
+               transport_send_check_condition_and_sense(se_cmd,
+                               TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
+               transport_generic_free_cmd(se_cmd, 0);
+       }
+}
+
+static int scsiback_gnttab_data_map_batch(struct gnttab_map_grant_ref *map,
+       struct page **pg, grant_handle_t *grant, int cnt)
+{
+       int err, i;
+
+       if (!cnt)
+               return 0;
+
+       err = gnttab_map_refs(map, NULL, pg, cnt);
+       BUG_ON(err);
+       for (i = 0; i < cnt; i++) {
+               if (unlikely(map[i].status != GNTST_okay)) {
+                       pr_err("xen-pvscsi: invalid buffer -- could not remap it\n");
+                       map[i].handle = SCSIBACK_INVALID_HANDLE;
+                       err = -ENOMEM;
+               } else {
+                       get_page(pg[i]);
+               }
+               grant[i] = map[i].handle;
+       }
+       return err;
+}
+
+static int scsiback_gnttab_data_map_list(struct vscsibk_pend *pending_req,
+                       struct scsiif_request_segment *seg, struct page **pg,
+                       grant_handle_t *grant, int cnt, u32 flags)
+{
+       int mapcount = 0, i, err = 0;
+       struct gnttab_map_grant_ref map[VSCSI_GRANT_BATCH];
+       struct vscsibk_info *info = pending_req->info;
+
+       for (i = 0; i < cnt; i++) {
+               if (get_free_page(pg + mapcount)) {
+                       put_free_pages(pg, mapcount);
+                       pr_err("xen-pvscsi: no grant page\n");
+                       return -ENOMEM;
+               }
+               gnttab_set_map_op(&map[mapcount], vaddr_page(pg[mapcount]),
+                                 flags, seg[i].gref, info->domid);
+               mapcount++;
+               if (mapcount < VSCSI_GRANT_BATCH)
+                       continue;
+               err = scsiback_gnttab_data_map_batch(map, pg, grant, mapcount);
+               pg += mapcount;
+               grant += mapcount;
+               pending_req->n_grants += mapcount;
+               if (err)
+                       return err;
+               mapcount = 0;
+       }
+       err = scsiback_gnttab_data_map_batch(map, pg, grant, mapcount);
+       pending_req->n_grants += mapcount;
+       return err;
+}
+
+static int scsiback_gnttab_data_map(struct vscsiif_request *ring_req,
+                                       struct vscsibk_pend *pending_req)
+{
+       u32 flags;
+       int i, err, n_segs, i_seg = 0;
+       struct page **pg;
+       struct scsiif_request_segment *seg;
+       unsigned long end_seg = 0;
+       unsigned int nr_segments = (unsigned int)ring_req->nr_segments;
+       unsigned int nr_sgl = 0;
+       struct scatterlist *sg;
+       grant_handle_t *grant;
+
+       pending_req->n_sg = 0;
+       pending_req->n_grants = 0;
+       pending_req->data_len = 0;
+
+       nr_segments &= ~VSCSIIF_SG_GRANT;
+       if (!nr_segments)
+               return 0;
+
+       if (nr_segments > VSCSIIF_SG_TABLESIZE) {
+               DPRINTK("xen-pvscsi: invalid parameter nr_seg = %d\n",
+                       ring_req->nr_segments);
+               return -EINVAL;
+       }
+
+       if (ring_req->nr_segments & VSCSIIF_SG_GRANT) {
+               err = scsiback_gnttab_data_map_list(pending_req, ring_req->seg,
+                       pending_req->pages, pending_req->grant_handles,
+                       nr_segments, GNTMAP_host_map | GNTMAP_readonly);
+               if (err)
+                       return err;
+               nr_sgl = nr_segments;
+               nr_segments = 0;
+               for (i = 0; i < nr_sgl; i++) {
+                       n_segs = ring_req->seg[i].length /
+                                sizeof(struct scsiif_request_segment);
+                       if ((unsigned)ring_req->seg[i].offset +
+                           (unsigned)ring_req->seg[i].length > PAGE_SIZE ||
+                           n_segs * sizeof(struct scsiif_request_segment) !=
+                           ring_req->seg[i].length)
+                               return -EINVAL;
+                       nr_segments += n_segs;
+               }
+               if (nr_segments > SG_ALL) {
+                       DPRINTK("xen-pvscsi: invalid nr_seg = %d\n",
+                               nr_segments);
+                       return -EINVAL;
+               }
+       }
+
+       /* free of (sgl) in fast_flush_area()*/
+       pending_req->sgl = kmalloc_array(nr_segments,
+                                       sizeof(struct scatterlist), GFP_KERNEL);
+       if (!pending_req->sgl)
+               return -ENOMEM;
+
+       sg_init_table(pending_req->sgl, nr_segments);
+       pending_req->n_sg = nr_segments;
+
+       flags = GNTMAP_host_map;
+       if (pending_req->sc_data_direction == DMA_TO_DEVICE)
+               flags |= GNTMAP_readonly;
+
+       pg = pending_req->pages + nr_sgl;
+       grant = pending_req->grant_handles + nr_sgl;
+       if (!nr_sgl) {
+               seg = ring_req->seg;
+               err = scsiback_gnttab_data_map_list(pending_req, seg,
+                       pg, grant, nr_segments, flags);
+               if (err)
+                       return err;
+       } else {
+               for (i = 0; i < nr_sgl; i++) {
+                       seg = (struct scsiif_request_segment *)(
+                             vaddr(pending_req, i) + ring_req->seg[i].offset);
+                       n_segs = ring_req->seg[i].length /
+                                sizeof(struct scsiif_request_segment);
+                       err = scsiback_gnttab_data_map_list(pending_req, seg,
+                               pg, grant, n_segs, flags);
+                       if (err)
+                               return err;
+                       pg += n_segs;
+                       grant += n_segs;
+               }
+               end_seg = vaddr(pending_req, 0) + ring_req->seg[0].offset;
+               seg = (struct scsiif_request_segment *)end_seg;
+               end_seg += ring_req->seg[0].length;
+               pg = pending_req->pages + nr_sgl;
+       }
+
+       for_each_sg(pending_req->sgl, sg, nr_segments, i) {
+               sg_set_page(sg, pg[i], seg->length, seg->offset);
+               pending_req->data_len += seg->length;
+               seg++;
+               if (nr_sgl && (unsigned long)seg >= end_seg) {
+                       i_seg++;
+                       end_seg = vaddr(pending_req, i_seg) +
+                                 ring_req->seg[i_seg].offset;
+                       seg = (struct scsiif_request_segment *)end_seg;
+                       end_seg += ring_req->seg[i_seg].length;
+               }
+               if (sg->offset >= PAGE_SIZE ||
+                   sg->length > PAGE_SIZE ||
+                   sg->offset + sg->length > PAGE_SIZE)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void scsiback_disconnect(struct vscsibk_info *info)
+{
+       wait_event(info->waiting_to_free,
+               atomic_read(&info->nr_unreplied_reqs) == 0);
+
+       unbind_from_irqhandler(info->irq, info);
+       info->irq = 0;
+       xenbus_unmap_ring_vfree(info->dev, info->ring.sring);
+}
+
+static void scsiback_device_action(struct vscsibk_pend *pending_req,
+       enum tcm_tmreq_table act, int tag)
+{
+       int rc, err = FAILED;
+       struct scsiback_tpg *tpg = pending_req->v2p->tpg;
+       struct se_cmd *se_cmd = &pending_req->se_cmd;
+       struct scsiback_tmr *tmr;
+
+       tmr = kzalloc(sizeof(struct scsiback_tmr), GFP_KERNEL);
+       if (!tmr)
+               goto out;
+
+       init_waitqueue_head(&tmr->tmr_wait);
+
+       transport_init_se_cmd(se_cmd, tpg->se_tpg.se_tpg_tfo,
+               tpg->tpg_nexus->tvn_se_sess, 0, DMA_NONE, MSG_SIMPLE_TAG,
+               &pending_req->sense_buffer[0]);
+
+       rc = core_tmr_alloc_req(se_cmd, tmr, act, GFP_KERNEL);
+       if (rc < 0)
+               goto out;
+
+       se_cmd->se_tmr_req->ref_task_tag = tag;
+
+       if (transport_lookup_tmr_lun(se_cmd, pending_req->v2p->lun) < 0)
+               goto out;
+
+       transport_generic_handle_tmr(se_cmd);
+       wait_event(tmr->tmr_wait, atomic_read(&tmr->tmr_complete));
+
+       err = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ?
+               SUCCESS : FAILED;
+
+out:
+       if (tmr) {
+               transport_generic_free_cmd(&pending_req->se_cmd, 1);
+               kfree(tmr);
+       }
+
+       scsiback_do_resp_with_sense(NULL, err, 0, pending_req);
+
+       kmem_cache_free(scsiback_cachep, pending_req);
+}
+
+/*
+  Perform virtual to physical translation
+*/
+static struct v2p_entry *scsiback_do_translation(struct vscsibk_info *info,
+                       struct ids_tuple *v)
+{
+       struct v2p_entry *entry;
+       struct list_head *head = &(info->v2p_entry_lists);
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->v2p_lock, flags);
+       list_for_each_entry(entry, head, l) {
+               if ((entry->v.chn == v->chn) &&
+                   (entry->v.tgt == v->tgt) &&
+                   (entry->v.lun == v->lun)) {
+                       kref_get(&entry->kref);
+                       goto out;
+               }
+       }
+       entry = NULL;
+
+out:
+       spin_unlock_irqrestore(&info->v2p_lock, flags);
+       return entry;
+}
+
+static int prepare_pending_reqs(struct vscsibk_info *info,
+                               struct vscsiif_request *ring_req,
+                               struct vscsibk_pend *pending_req)
+{
+       struct v2p_entry *v2p;
+       struct ids_tuple vir;
+
+       pending_req->rqid       = ring_req->rqid;
+       pending_req->info       = info;
+
+       vir.chn = ring_req->channel;
+       vir.tgt = ring_req->id;
+       vir.lun = ring_req->lun;
+
+       v2p = scsiback_do_translation(info, &vir);
+       if (!v2p) {
+               pending_req->v2p = NULL;
+               DPRINTK("xen-pvscsi: doesn't exist.\n");
+               return -ENODEV;
+       }
+       pending_req->v2p = v2p;
+
+       /* request range check from frontend */
+       pending_req->sc_data_direction = ring_req->sc_data_direction;
+       if ((pending_req->sc_data_direction != DMA_BIDIRECTIONAL) &&
+               (pending_req->sc_data_direction != DMA_TO_DEVICE) &&
+               (pending_req->sc_data_direction != DMA_FROM_DEVICE) &&
+               (pending_req->sc_data_direction != DMA_NONE)) {
+               DPRINTK("xen-pvscsi: invalid parameter data_dir = %d\n",
+                       pending_req->sc_data_direction);
+               return -EINVAL;
+       }
+
+       pending_req->cmd_len = ring_req->cmd_len;
+       if (pending_req->cmd_len > VSCSIIF_MAX_COMMAND_SIZE) {
+               DPRINTK("xen-pvscsi: invalid parameter cmd_len = %d\n",
+                       pending_req->cmd_len);
+               return -EINVAL;
+       }
+       memcpy(pending_req->cmnd, ring_req->cmnd, pending_req->cmd_len);
+
+       return 0;
+}
+
+static int scsiback_do_cmd_fn(struct vscsibk_info *info)
+{
+       struct vscsiif_back_ring *ring = &info->ring;
+       struct vscsiif_request *ring_req;
+       struct vscsibk_pend *pending_req;
+       RING_IDX rc, rp;
+       int err, more_to_do;
+       uint32_t result;
+       uint8_t act;
+
+       rc = ring->req_cons;
+       rp = ring->sring->req_prod;
+       rmb();  /* guest system is accessing ring, too */
+
+       if (RING_REQUEST_PROD_OVERFLOW(ring, rp)) {
+               rc = ring->rsp_prod_pvt;
+               pr_warn("xen-pvscsi: Dom%d provided bogus ring requests (%#x - %#x = %u). Halting ring processing\n",
+                          info->domid, rp, rc, rp - rc);
+               info->ring_error = 1;
+               return 0;
+       }
+
+       while ((rc != rp)) {
+               if (RING_REQUEST_CONS_OVERFLOW(ring, rc))
+                       break;
+               pending_req = kmem_cache_alloc(scsiback_cachep, GFP_KERNEL);
+               if (!pending_req)
+                       return 1;
+
+               ring_req = RING_GET_REQUEST(ring, rc);
+               ring->req_cons = ++rc;
+
+               act = ring_req->act;
+               err = prepare_pending_reqs(info, ring_req, pending_req);
+               if (err) {
+                       switch (err) {
+                       case -ENODEV:
+                               result = DID_NO_CONNECT;
+                               break;
+                       default:
+                               result = DRIVER_ERROR;
+                               break;
+                       }
+                       scsiback_do_resp_with_sense(NULL, result << 24, 0,
+                                                   pending_req);
+                       kmem_cache_free(scsiback_cachep, pending_req);
+                       return 1;
+               }
+
+               switch (act) {
+               case VSCSIIF_ACT_SCSI_CDB:
+                       if (scsiback_gnttab_data_map(ring_req, pending_req)) {
+                               scsiback_fast_flush_area(pending_req);
+                               scsiback_do_resp_with_sense(NULL,
+                                       DRIVER_ERROR << 24, 0, pending_req);
+                               kmem_cache_free(scsiback_cachep, pending_req);
+                       } else {
+                               scsiback_cmd_exec(pending_req);
+                       }
+                       break;
+               case VSCSIIF_ACT_SCSI_ABORT:
+                       scsiback_device_action(pending_req, TMR_ABORT_TASK,
+                               ring_req->ref_rqid);
+                       break;
+               case VSCSIIF_ACT_SCSI_RESET:
+                       scsiback_device_action(pending_req, TMR_LUN_RESET, 0);
+                       break;
+               default:
+                       pr_err_ratelimited("xen-pvscsi: invalid request\n");
+                       scsiback_do_resp_with_sense(NULL, DRIVER_ERROR << 24,
+                                                   0, pending_req);
+                       kmem_cache_free(scsiback_cachep, pending_req);
+                       break;
+               }
+
+               /* Yield point for this unbounded loop. */
+               cond_resched();
+       }
+
+       RING_FINAL_CHECK_FOR_REQUESTS(&info->ring, more_to_do);
+       return more_to_do;
+}
+
+static irqreturn_t scsiback_irq_fn(int irq, void *dev_id)
+{
+       struct vscsibk_info *info = dev_id;
+
+       if (info->ring_error)
+               return IRQ_HANDLED;
+
+       while (scsiback_do_cmd_fn(info))
+               cond_resched();
+
+       return IRQ_HANDLED;
+}
+
+static int scsiback_init_sring(struct vscsibk_info *info, grant_ref_t ring_ref,
+                       evtchn_port_t evtchn)
+{
+       void *area;
+       struct vscsiif_sring *sring;
+       int err;
+
+       if (info->irq)
+               return -1;
+
+       err = xenbus_map_ring_valloc(info->dev, ring_ref, &area);
+       if (err)
+               return err;
+
+       sring = (struct vscsiif_sring *)area;
+       BACK_RING_INIT(&info->ring, sring, PAGE_SIZE);
+
+       err = bind_interdomain_evtchn_to_irq(info->domid, evtchn);
+       if (err < 0)
+               goto unmap_page;
+
+       info->irq = err;
+
+       err = request_threaded_irq(info->irq, NULL, scsiback_irq_fn,
+                                  IRQF_ONESHOT, "vscsiif-backend", info);
+       if (err)
+               goto free_irq;
+
+       return 0;
+
+free_irq:
+       unbind_from_irqhandler(info->irq, info);
+       info->irq = 0;
+unmap_page:
+       xenbus_unmap_ring_vfree(info->dev, area);
+
+       return err;
+}
+
+static int scsiback_map(struct vscsibk_info *info)
+{
+       struct xenbus_device *dev = info->dev;
+       unsigned int ring_ref, evtchn;
+       int err;
+
+       err = xenbus_gather(XBT_NIL, dev->otherend,
+                       "ring-ref", "%u", &ring_ref,
+                       "event-channel", "%u", &evtchn, NULL);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "reading %s ring", dev->otherend);
+               return err;
+       }
+
+       return scsiback_init_sring(info, ring_ref, evtchn);
+}
+
+/*
+  Add a new translation entry
+*/
+static int scsiback_add_translation_entry(struct vscsibk_info *info,
+                                         char *phy, struct ids_tuple *v)
+{
+       int err = 0;
+       struct v2p_entry *entry;
+       struct v2p_entry *new;
+       struct list_head *head = &(info->v2p_entry_lists);
+       unsigned long flags;
+       char *lunp;
+       unsigned int lun;
+       struct scsiback_tpg *tpg_entry, *tpg = NULL;
+       char *error = "doesn't exist";
+
+       lunp = strrchr(phy, ':');
+       if (!lunp) {
+               pr_err("xen-pvscsi: illegal format of physical device %s\n",
+                       phy);
+               return -EINVAL;
+       }
+       *lunp = 0;
+       lunp++;
+       if (kstrtouint(lunp, 10, &lun) || lun >= TRANSPORT_MAX_LUNS_PER_TPG) {
+               pr_err("xen-pvscsi: lun number not valid: %s\n", lunp);
+               return -EINVAL;
+       }
+
+       mutex_lock(&scsiback_mutex);
+       list_for_each_entry(tpg_entry, &scsiback_list, tv_tpg_list) {
+               if (!strcmp(phy, tpg_entry->tport->tport_name) ||
+                   !strcmp(phy, tpg_entry->param_alias)) {
+                       spin_lock(&tpg_entry->se_tpg.tpg_lun_lock);
+                       if (tpg_entry->se_tpg.tpg_lun_list[lun]->lun_status ==
+                           TRANSPORT_LUN_STATUS_ACTIVE) {
+                               if (!tpg_entry->tpg_nexus)
+                                       error = "nexus undefined";
+                               else
+                                       tpg = tpg_entry;
+                       }
+                       spin_unlock(&tpg_entry->se_tpg.tpg_lun_lock);
+                       break;
+               }
+       }
+       if (tpg) {
+               mutex_lock(&tpg->tv_tpg_mutex);
+               tpg->tv_tpg_fe_count++;
+               mutex_unlock(&tpg->tv_tpg_mutex);
+       }
+       mutex_unlock(&scsiback_mutex);
+
+       if (!tpg) {
+               pr_err("xen-pvscsi: %s:%d %s\n", phy, lun, error);
+               return -ENODEV;
+       }
+
+       new = kmalloc(sizeof(struct v2p_entry), GFP_KERNEL);
+       if (new == NULL) {
+               err = -ENOMEM;
+               goto out_free;
+       }
+
+       spin_lock_irqsave(&info->v2p_lock, flags);
+
+       /* Check double assignment to identical virtual ID */
+       list_for_each_entry(entry, head, l) {
+               if ((entry->v.chn == v->chn) &&
+                   (entry->v.tgt == v->tgt) &&
+                   (entry->v.lun == v->lun)) {
+                       pr_warn("xen-pvscsi: Virtual ID is already used. Assignment was not performed.\n");
+                       err = -EEXIST;
+                       goto out;
+               }
+
+       }
+
+       /* Create a new translation entry and add to the list */
+       kref_init(&new->kref);
+       new->v = *v;
+       new->tpg = tpg;
+       new->lun = lun;
+       list_add_tail(&new->l, head);
+
+out:
+       spin_unlock_irqrestore(&info->v2p_lock, flags);
+
+out_free:
+       mutex_lock(&tpg->tv_tpg_mutex);
+       tpg->tv_tpg_fe_count--;
+       mutex_unlock(&tpg->tv_tpg_mutex);
+
+       if (err)
+               kfree(new);
+
+       return err;
+}
+
+static void __scsiback_del_translation_entry(struct v2p_entry *entry)
+{
+       list_del(&entry->l);
+       kref_put(&entry->kref, scsiback_free_translation_entry);
+}
+
+/*
+  Delete the translation entry specfied
+*/
+static int scsiback_del_translation_entry(struct vscsibk_info *info,
+                                         struct ids_tuple *v)
+{
+       struct v2p_entry *entry;
+       struct list_head *head = &(info->v2p_entry_lists);
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->v2p_lock, flags);
+       /* Find out the translation entry specified */
+       list_for_each_entry(entry, head, l) {
+               if ((entry->v.chn == v->chn) &&
+                   (entry->v.tgt == v->tgt) &&
+                   (entry->v.lun == v->lun)) {
+                       goto found;
+               }
+       }
+
+       spin_unlock_irqrestore(&info->v2p_lock, flags);
+       return 1;
+
+found:
+       /* Delete the translation entry specfied */
+       __scsiback_del_translation_entry(entry);
+
+       spin_unlock_irqrestore(&info->v2p_lock, flags);
+       return 0;
+}
+
+static void scsiback_do_add_lun(struct vscsibk_info *info, const char *state,
+                               char *phy, struct ids_tuple *vir)
+{
+       if (!scsiback_add_translation_entry(info, phy, vir)) {
+               if (xenbus_printf(XBT_NIL, info->dev->nodename, state,
+                                 "%d", XenbusStateInitialised)) {
+                       pr_err("xen-pvscsi: xenbus_printf error %s\n", state);
+                       scsiback_del_translation_entry(info, vir);
+               }
+       } else {
+               xenbus_printf(XBT_NIL, info->dev->nodename, state,
+                             "%d", XenbusStateClosed);
+       }
+}
+
+static void scsiback_do_del_lun(struct vscsibk_info *info, const char *state,
+                               struct ids_tuple *vir)
+{
+       if (!scsiback_del_translation_entry(info, vir)) {
+               if (xenbus_printf(XBT_NIL, info->dev->nodename, state,
+                                 "%d", XenbusStateClosed))
+                       pr_err("xen-pvscsi: xenbus_printf error %s\n", state);
+       }
+}
+
+#define VSCSIBACK_OP_ADD_OR_DEL_LUN    1
+#define VSCSIBACK_OP_UPDATEDEV_STATE   2
+
+static void scsiback_do_1lun_hotplug(struct vscsibk_info *info, int op,
+                                    char *ent)
+{
+       int err;
+       struct ids_tuple vir;
+       char *val;
+       int device_state;
+       char phy[VSCSI_NAMELEN];
+       char str[64];
+       char state[64];
+       struct xenbus_device *dev = info->dev;
+
+       /* read status */
+       snprintf(state, sizeof(state), "vscsi-devs/%s/state", ent);
+       err = xenbus_scanf(XBT_NIL, dev->nodename, state, "%u", &device_state);
+       if (XENBUS_EXIST_ERR(err))
+               return;
+
+       /* physical SCSI device */
+       snprintf(str, sizeof(str), "vscsi-devs/%s/p-dev", ent);
+       val = xenbus_read(XBT_NIL, dev->nodename, str, NULL);
+       if (IS_ERR(val)) {
+               xenbus_printf(XBT_NIL, dev->nodename, state,
+                             "%d", XenbusStateClosed);
+               return;
+       }
+       strlcpy(phy, val, VSCSI_NAMELEN);
+       kfree(val);
+
+       /* virtual SCSI device */
+       snprintf(str, sizeof(str), "vscsi-devs/%s/v-dev", ent);
+       err = xenbus_scanf(XBT_NIL, dev->nodename, str, "%u:%u:%u:%u",
+                          &vir.hst, &vir.chn, &vir.tgt, &vir.lun);
+       if (XENBUS_EXIST_ERR(err)) {
+               xenbus_printf(XBT_NIL, dev->nodename, state,
+                             "%d", XenbusStateClosed);
+               return;
+       }
+
+       switch (op) {
+       case VSCSIBACK_OP_ADD_OR_DEL_LUN:
+               if (device_state == XenbusStateInitialising)
+                       scsiback_do_add_lun(info, state, phy, &vir);
+               if (device_state == XenbusStateClosing)
+                       scsiback_do_del_lun(info, state, &vir);
+               break;
+
+       case VSCSIBACK_OP_UPDATEDEV_STATE:
+               if (device_state == XenbusStateInitialised) {
+                       /* modify vscsi-devs/dev-x/state */
+                       if (xenbus_printf(XBT_NIL, dev->nodename, state,
+                                         "%d", XenbusStateConnected)) {
+                               pr_err("xen-pvscsi: xenbus_printf error %s\n",
+                                      str);
+                               scsiback_del_translation_entry(info, &vir);
+                               xenbus_printf(XBT_NIL, dev->nodename, state,
+                                             "%d", XenbusStateClosed);
+                       }
+               }
+               break;
+       /*When it is necessary, processing is added here.*/
+       default:
+               break;
+       }
+}
+
+static void scsiback_do_lun_hotplug(struct vscsibk_info *info, int op)
+{
+       int i;
+       char **dir;
+       unsigned int ndir = 0;
+
+       dir = xenbus_directory(XBT_NIL, info->dev->nodename, "vscsi-devs",
+                              &ndir);
+       if (IS_ERR(dir))
+               return;
+
+       for (i = 0; i < ndir; i++)
+               scsiback_do_1lun_hotplug(info, op, dir[i]);
+
+       kfree(dir);
+}
+
+static void scsiback_frontend_changed(struct xenbus_device *dev,
+                                       enum xenbus_state frontend_state)
+{
+       struct vscsibk_info *info = dev_get_drvdata(&dev->dev);
+
+       switch (frontend_state) {
+       case XenbusStateInitialising:
+               break;
+
+       case XenbusStateInitialised:
+               if (scsiback_map(info))
+                       break;
+
+               scsiback_do_lun_hotplug(info, VSCSIBACK_OP_ADD_OR_DEL_LUN);
+               xenbus_switch_state(dev, XenbusStateConnected);
+               break;
+
+       case XenbusStateConnected:
+               scsiback_do_lun_hotplug(info, VSCSIBACK_OP_UPDATEDEV_STATE);
+
+               if (dev->state == XenbusStateConnected)
+                       break;
+
+               xenbus_switch_state(dev, XenbusStateConnected);
+               break;
+
+       case XenbusStateClosing:
+               if (info->irq)
+                       scsiback_disconnect(info);
+
+               xenbus_switch_state(dev, XenbusStateClosing);
+               break;
+
+       case XenbusStateClosed:
+               xenbus_switch_state(dev, XenbusStateClosed);
+               if (xenbus_dev_is_online(dev))
+                       break;
+               /* fall through if not online */
+       case XenbusStateUnknown:
+               device_unregister(&dev->dev);
+               break;
+
+       case XenbusStateReconfiguring:
+               scsiback_do_lun_hotplug(info, VSCSIBACK_OP_ADD_OR_DEL_LUN);
+               xenbus_switch_state(dev, XenbusStateReconfigured);
+
+               break;
+
+       default:
+               xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+                                       frontend_state);
+               break;
+       }
+}
+
+/*
+  Release the translation entry specfied
+*/
+static void scsiback_release_translation_entry(struct vscsibk_info *info)
+{
+       struct v2p_entry *entry, *tmp;
+       struct list_head *head = &(info->v2p_entry_lists);
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->v2p_lock, flags);
+
+       list_for_each_entry_safe(entry, tmp, head, l)
+               __scsiback_del_translation_entry(entry);
+
+       spin_unlock_irqrestore(&info->v2p_lock, flags);
+}
+
+static int scsiback_remove(struct xenbus_device *dev)
+{
+       struct vscsibk_info *info = dev_get_drvdata(&dev->dev);
+
+       if (info->irq)
+               scsiback_disconnect(info);
+
+       scsiback_release_translation_entry(info);
+
+       dev_set_drvdata(&dev->dev, NULL);
+
+       return 0;
+}
+
+static int scsiback_probe(struct xenbus_device *dev,
+                          const struct xenbus_device_id *id)
+{
+       int err;
+
+       struct vscsibk_info *info = kzalloc(sizeof(struct vscsibk_info),
+                                           GFP_KERNEL);
+
+       DPRINTK("%p %d\n", dev, dev->otherend_id);
+
+       if (!info) {
+               xenbus_dev_fatal(dev, -ENOMEM, "allocating backend structure");
+               return -ENOMEM;
+       }
+       info->dev = dev;
+       dev_set_drvdata(&dev->dev, info);
+
+       info->domid = dev->otherend_id;
+       spin_lock_init(&info->ring_lock);
+       info->ring_error = 0;
+       atomic_set(&info->nr_unreplied_reqs, 0);
+       init_waitqueue_head(&info->waiting_to_free);
+       info->dev = dev;
+       info->irq = 0;
+       INIT_LIST_HEAD(&info->v2p_entry_lists);
+       spin_lock_init(&info->v2p_lock);
+
+       err = xenbus_printf(XBT_NIL, dev->nodename, "feature-sg-grant", "%u",
+                           SG_ALL);
+       if (err)
+               xenbus_dev_error(dev, err, "writing feature-sg-grant");
+
+       err = xenbus_switch_state(dev, XenbusStateInitWait);
+       if (err)
+               goto fail;
+
+       return 0;
+
+fail:
+       pr_warn("xen-pvscsi: %s failed\n", __func__);
+       scsiback_remove(dev);
+
+       return err;
+}
+
+static char *scsiback_dump_proto_id(struct scsiback_tport *tport)
+{
+       switch (tport->tport_proto_id) {
+       case SCSI_PROTOCOL_SAS:
+               return "SAS";
+       case SCSI_PROTOCOL_FCP:
+               return "FCP";
+       case SCSI_PROTOCOL_ISCSI:
+               return "iSCSI";
+       default:
+               break;
+       }
+
+       return "Unknown";
+}
+
+static u8 scsiback_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+       struct scsiback_tport *tport = tpg->tport;
+
+       switch (tport->tport_proto_id) {
+       case SCSI_PROTOCOL_SAS:
+               return sas_get_fabric_proto_ident(se_tpg);
+       case SCSI_PROTOCOL_FCP:
+               return fc_get_fabric_proto_ident(se_tpg);
+       case SCSI_PROTOCOL_ISCSI:
+               return iscsi_get_fabric_proto_ident(se_tpg);
+       default:
+               pr_err("Unknown tport_proto_id: 0x%02x, using SAS emulation\n",
+                       tport->tport_proto_id);
+               break;
+       }
+
+       return sas_get_fabric_proto_ident(se_tpg);
+}
+
+static char *scsiback_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+       struct scsiback_tport *tport = tpg->tport;
+
+       return &tport->tport_name[0];
+}
+
+static u16 scsiback_get_tag(struct se_portal_group *se_tpg)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+       return tpg->tport_tpgt;
+}
+
+static u32 scsiback_get_default_depth(struct se_portal_group *se_tpg)
+{
+       return 1;
+}
+
+static u32
+scsiback_get_pr_transport_id(struct se_portal_group *se_tpg,
+                             struct se_node_acl *se_nacl,
+                             struct t10_pr_registration *pr_reg,
+                             int *format_code,
+                             unsigned char *buf)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+       struct scsiback_tport *tport = tpg->tport;
+
+       switch (tport->tport_proto_id) {
+       case SCSI_PROTOCOL_SAS:
+               return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+                                       format_code, buf);
+       case SCSI_PROTOCOL_FCP:
+               return fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+                                       format_code, buf);
+       case SCSI_PROTOCOL_ISCSI:
+               return iscsi_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+                                       format_code, buf);
+       default:
+               pr_err("Unknown tport_proto_id: 0x%02x, using SAS emulation\n",
+                       tport->tport_proto_id);
+               break;
+       }
+
+       return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+                       format_code, buf);
+}
+
+static u32
+scsiback_get_pr_transport_id_len(struct se_portal_group *se_tpg,
+                                 struct se_node_acl *se_nacl,
+                                 struct t10_pr_registration *pr_reg,
+                                 int *format_code)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+       struct scsiback_tport *tport = tpg->tport;
+
+       switch (tport->tport_proto_id) {
+       case SCSI_PROTOCOL_SAS:
+               return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+                                       format_code);
+       case SCSI_PROTOCOL_FCP:
+               return fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+                                       format_code);
+       case SCSI_PROTOCOL_ISCSI:
+               return iscsi_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+                                       format_code);
+       default:
+               pr_err("Unknown tport_proto_id: 0x%02x, using SAS emulation\n",
+                       tport->tport_proto_id);
+               break;
+       }
+
+       return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+                       format_code);
+}
+
+static char *
+scsiback_parse_pr_out_transport_id(struct se_portal_group *se_tpg,
+                                   const char *buf,
+                                   u32 *out_tid_len,
+                                   char **port_nexus_ptr)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+       struct scsiback_tport *tport = tpg->tport;
+
+       switch (tport->tport_proto_id) {
+       case SCSI_PROTOCOL_SAS:
+               return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+                                       port_nexus_ptr);
+       case SCSI_PROTOCOL_FCP:
+               return fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+                                       port_nexus_ptr);
+       case SCSI_PROTOCOL_ISCSI:
+               return iscsi_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+                                       port_nexus_ptr);
+       default:
+               pr_err("Unknown tport_proto_id: 0x%02x, using SAS emulation\n",
+                       tport->tport_proto_id);
+               break;
+       }
+
+       return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+                       port_nexus_ptr);
+}
+
+static struct se_wwn *
+scsiback_make_tport(struct target_fabric_configfs *tf,
+                    struct config_group *group,
+                    const char *name)
+{
+       struct scsiback_tport *tport;
+       char *ptr;
+       u64 wwpn = 0;
+       int off = 0;
+
+       tport = kzalloc(sizeof(struct scsiback_tport), GFP_KERNEL);
+       if (!tport)
+               return ERR_PTR(-ENOMEM);
+
+       tport->tport_wwpn = wwpn;
+       /*
+        * Determine the emulated Protocol Identifier and Target Port Name
+        * based on the incoming configfs directory name.
+        */
+       ptr = strstr(name, "naa.");
+       if (ptr) {
+               tport->tport_proto_id = SCSI_PROTOCOL_SAS;
+               goto check_len;
+       }
+       ptr = strstr(name, "fc.");
+       if (ptr) {
+               tport->tport_proto_id = SCSI_PROTOCOL_FCP;
+               off = 3; /* Skip over "fc." */
+               goto check_len;
+       }
+       ptr = strstr(name, "iqn.");
+       if (ptr) {
+               tport->tport_proto_id = SCSI_PROTOCOL_ISCSI;
+               goto check_len;
+       }
+
+       pr_err("Unable to locate prefix for emulated Target Port: %s\n", name);
+       kfree(tport);
+       return ERR_PTR(-EINVAL);
+
+check_len:
+       if (strlen(name) >= VSCSI_NAMELEN) {
+               pr_err("Emulated %s Address: %s, exceeds max: %d\n", name,
+                       scsiback_dump_proto_id(tport), VSCSI_NAMELEN);
+               kfree(tport);
+               return ERR_PTR(-EINVAL);
+       }
+       snprintf(&tport->tport_name[0], VSCSI_NAMELEN, "%s", &name[off]);
+
+       pr_debug("xen-pvscsi: Allocated emulated Target %s Address: %s\n",
+                scsiback_dump_proto_id(tport), name);
+
+       return &tport->tport_wwn;
+}
+
+static void scsiback_drop_tport(struct se_wwn *wwn)
+{
+       struct scsiback_tport *tport = container_of(wwn,
+                               struct scsiback_tport, tport_wwn);
+
+       pr_debug("xen-pvscsi: Deallocating emulated Target %s Address: %s\n",
+                scsiback_dump_proto_id(tport), tport->tport_name);
+
+       kfree(tport);
+}
+
+static struct se_node_acl *
+scsiback_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+       return kzalloc(sizeof(struct se_node_acl), GFP_KERNEL);
+}
+
+static void
+scsiback_release_fabric_acl(struct se_portal_group *se_tpg,
+                            struct se_node_acl *se_nacl)
+{
+       kfree(se_nacl);
+}
+
+static u32 scsiback_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+       return 1;
+}
+
+static int scsiback_check_stop_free(struct se_cmd *se_cmd)
+{
+       /*
+        * Do not release struct se_cmd's containing a valid TMR
+        * pointer.  These will be released directly in scsiback_device_action()
+        * with transport_generic_free_cmd().
+        */
+       if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
+               return 0;
+
+       transport_generic_free_cmd(se_cmd, 0);
+       return 1;
+}
+
+static void scsiback_release_cmd(struct se_cmd *se_cmd)
+{
+       struct vscsibk_pend *pending_req = container_of(se_cmd,
+                               struct vscsibk_pend, se_cmd);
+
+       kmem_cache_free(scsiback_cachep, pending_req);
+}
+
+static int scsiback_shutdown_session(struct se_session *se_sess)
+{
+       return 0;
+}
+
+static void scsiback_close_session(struct se_session *se_sess)
+{
+}
+
+static u32 scsiback_sess_get_index(struct se_session *se_sess)
+{
+       return 0;
+}
+
+static int scsiback_write_pending(struct se_cmd *se_cmd)
+{
+       /* Go ahead and process the write immediately */
+       target_execute_cmd(se_cmd);
+
+       return 0;
+}
+
+static int scsiback_write_pending_status(struct se_cmd *se_cmd)
+{
+       return 0;
+}
+
+static void scsiback_set_default_node_attrs(struct se_node_acl *nacl)
+{
+}
+
+static u32 scsiback_get_task_tag(struct se_cmd *se_cmd)
+{
+       struct vscsibk_pend *pending_req = container_of(se_cmd,
+                               struct vscsibk_pend, se_cmd);
+
+       return pending_req->rqid;
+}
+
+static int scsiback_get_cmd_state(struct se_cmd *se_cmd)
+{
+       return 0;
+}
+
+static int scsiback_queue_data_in(struct se_cmd *se_cmd)
+{
+       struct vscsibk_pend *pending_req = container_of(se_cmd,
+                               struct vscsibk_pend, se_cmd);
+
+       pending_req->result = SAM_STAT_GOOD;
+       scsiback_cmd_done(pending_req);
+       return 0;
+}
+
+static int scsiback_queue_status(struct se_cmd *se_cmd)
+{
+       struct vscsibk_pend *pending_req = container_of(se_cmd,
+                               struct vscsibk_pend, se_cmd);
+
+       if (se_cmd->sense_buffer &&
+           ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ||
+            (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE)))
+               pending_req->result = (DRIVER_SENSE << 24) |
+                                     SAM_STAT_CHECK_CONDITION;
+       else
+               pending_req->result = se_cmd->scsi_status;
+
+       scsiback_cmd_done(pending_req);
+       return 0;
+}
+
+static void scsiback_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+       struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;
+       struct scsiback_tmr *tmr = se_tmr->fabric_tmr_ptr;
+
+       atomic_set(&tmr->tmr_complete, 1);
+       wake_up(&tmr->tmr_wait);
+}
+
+static void scsiback_aborted_task(struct se_cmd *se_cmd)
+{
+}
+
+static ssize_t scsiback_tpg_param_show_alias(struct se_portal_group *se_tpg,
+                                            char *page)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg, struct scsiback_tpg,
+                                               se_tpg);
+       ssize_t rb;
+
+       mutex_lock(&tpg->tv_tpg_mutex);
+       rb = snprintf(page, PAGE_SIZE, "%s\n", tpg->param_alias);
+       mutex_unlock(&tpg->tv_tpg_mutex);
+
+       return rb;
+}
+
+static ssize_t scsiback_tpg_param_store_alias(struct se_portal_group *se_tpg,
+                                             const char *page, size_t count)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg, struct scsiback_tpg,
+                                               se_tpg);
+       int len;
+
+       if (strlen(page) >= VSCSI_NAMELEN) {
+               pr_err("param alias: %s, exceeds max: %d\n", page,
+                       VSCSI_NAMELEN);
+               return -EINVAL;
+       }
+
+       mutex_lock(&tpg->tv_tpg_mutex);
+       len = snprintf(tpg->param_alias, VSCSI_NAMELEN, "%s", page);
+       if (tpg->param_alias[len - 1] == '\n')
+               tpg->param_alias[len - 1] = '\0';
+       mutex_unlock(&tpg->tv_tpg_mutex);
+
+       return count;
+}
+
+TF_TPG_PARAM_ATTR(scsiback, alias, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *scsiback_param_attrs[] = {
+       &scsiback_tpg_param_alias.attr,
+       NULL,
+};
+
+static int scsiback_make_nexus(struct scsiback_tpg *tpg,
+                               const char *name)
+{
+       struct se_portal_group *se_tpg;
+       struct se_session *se_sess;
+       struct scsiback_nexus *tv_nexus;
+
+       mutex_lock(&tpg->tv_tpg_mutex);
+       if (tpg->tpg_nexus) {
+               mutex_unlock(&tpg->tv_tpg_mutex);
+               pr_debug("tpg->tpg_nexus already exists\n");
+               return -EEXIST;
+       }
+       se_tpg = &tpg->se_tpg;
+
+       tv_nexus = kzalloc(sizeof(struct scsiback_nexus), GFP_KERNEL);
+       if (!tv_nexus) {
+               mutex_unlock(&tpg->tv_tpg_mutex);
+               return -ENOMEM;
+       }
+       /*
+        *  Initialize the struct se_session pointer
+        */
+       tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL);
+       if (IS_ERR(tv_nexus->tvn_se_sess)) {
+               mutex_unlock(&tpg->tv_tpg_mutex);
+               kfree(tv_nexus);
+               return -ENOMEM;
+       }
+       se_sess = tv_nexus->tvn_se_sess;
+       /*
+        * Since we are running in 'demo mode' this call with generate a
+        * struct se_node_acl for the scsiback struct se_portal_group with
+        * the SCSI Initiator port name of the passed configfs group 'name'.
+        */
+       tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+                               se_tpg, (unsigned char *)name);
+       if (!tv_nexus->tvn_se_sess->se_node_acl) {
+               mutex_unlock(&tpg->tv_tpg_mutex);
+               pr_debug("core_tpg_check_initiator_node_acl() failed for %s\n",
+                        name);
+               goto out;
+       }
+       /*
+        * Now register the TCM pvscsi virtual I_T Nexus as active with the
+        * call to __transport_register_session()
+        */
+       __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+                       tv_nexus->tvn_se_sess, tv_nexus);
+       tpg->tpg_nexus = tv_nexus;
+
+       mutex_unlock(&tpg->tv_tpg_mutex);
+       return 0;
+
+out:
+       transport_free_session(se_sess);
+       kfree(tv_nexus);
+       return -ENOMEM;
+}
+
+static int scsiback_drop_nexus(struct scsiback_tpg *tpg)
+{
+       struct se_session *se_sess;
+       struct scsiback_nexus *tv_nexus;
+
+       mutex_lock(&tpg->tv_tpg_mutex);
+       tv_nexus = tpg->tpg_nexus;
+       if (!tv_nexus) {
+               mutex_unlock(&tpg->tv_tpg_mutex);
+               return -ENODEV;
+       }
+
+       se_sess = tv_nexus->tvn_se_sess;
+       if (!se_sess) {
+               mutex_unlock(&tpg->tv_tpg_mutex);
+               return -ENODEV;
+       }
+
+       if (tpg->tv_tpg_port_count != 0) {
+               mutex_unlock(&tpg->tv_tpg_mutex);
+               pr_err("Unable to remove xen-pvscsi I_T Nexus with active TPG port count: %d\n",
+                       tpg->tv_tpg_port_count);
+               return -EBUSY;
+       }
+
+       if (tpg->tv_tpg_fe_count != 0) {
+               mutex_unlock(&tpg->tv_tpg_mutex);
+               pr_err("Unable to remove xen-pvscsi I_T Nexus with active TPG frontend count: %d\n",
+                       tpg->tv_tpg_fe_count);
+               return -EBUSY;
+       }
+
+       pr_debug("xen-pvscsi: Removing I_T Nexus to emulated %s Initiator Port: %s\n",
+               scsiback_dump_proto_id(tpg->tport),
+               tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+
+       /*
+        * Release the SCSI I_T Nexus to the emulated xen-pvscsi Target Port
+        */
+       transport_deregister_session(tv_nexus->tvn_se_sess);
+       tpg->tpg_nexus = NULL;
+       mutex_unlock(&tpg->tv_tpg_mutex);
+
+       kfree(tv_nexus);
+       return 0;
+}
+
+static ssize_t scsiback_tpg_show_nexus(struct se_portal_group *se_tpg,
+                                       char *page)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+       struct scsiback_nexus *tv_nexus;
+       ssize_t ret;
+
+       mutex_lock(&tpg->tv_tpg_mutex);
+       tv_nexus = tpg->tpg_nexus;
+       if (!tv_nexus) {
+               mutex_unlock(&tpg->tv_tpg_mutex);
+               return -ENODEV;
+       }
+       ret = snprintf(page, PAGE_SIZE, "%s\n",
+                       tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+       mutex_unlock(&tpg->tv_tpg_mutex);
+
+       return ret;
+}
+
+static ssize_t scsiback_tpg_store_nexus(struct se_portal_group *se_tpg,
+                                        const char *page,
+                                        size_t count)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+       struct scsiback_tport *tport_wwn = tpg->tport;
+       unsigned char i_port[VSCSI_NAMELEN], *ptr, *port_ptr;
+       int ret;
+       /*
+        * Shutdown the active I_T nexus if 'NULL' is passed..
+        */
+       if (!strncmp(page, "NULL", 4)) {
+               ret = scsiback_drop_nexus(tpg);
+               return (!ret) ? count : ret;
+       }
+       /*
+        * Otherwise make sure the passed virtual Initiator port WWN matches
+        * the fabric protocol_id set in scsiback_make_tport(), and call
+        * scsiback_make_nexus().
+        */
+       if (strlen(page) >= VSCSI_NAMELEN) {
+               pr_err("Emulated NAA Sas Address: %s, exceeds max: %d\n",
+                       page, VSCSI_NAMELEN);
+               return -EINVAL;
+       }
+       snprintf(&i_port[0], VSCSI_NAMELEN, "%s", page);
+
+       ptr = strstr(i_port, "naa.");
+       if (ptr) {
+               if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_SAS) {
+                       pr_err("Passed SAS Initiator Port %s does not match target port protoid: %s\n",
+                               i_port, scsiback_dump_proto_id(tport_wwn));
+                       return -EINVAL;
+               }
+               port_ptr = &i_port[0];
+               goto check_newline;
+       }
+       ptr = strstr(i_port, "fc.");
+       if (ptr) {
+               if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_FCP) {
+                       pr_err("Passed FCP Initiator Port %s does not match target port protoid: %s\n",
+                               i_port, scsiback_dump_proto_id(tport_wwn));
+                       return -EINVAL;
+               }
+               port_ptr = &i_port[3]; /* Skip over "fc." */
+               goto check_newline;
+       }
+       ptr = strstr(i_port, "iqn.");
+       if (ptr) {
+               if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_ISCSI) {
+                       pr_err("Passed iSCSI Initiator Port %s does not match target port protoid: %s\n",
+                               i_port, scsiback_dump_proto_id(tport_wwn));
+                       return -EINVAL;
+               }
+               port_ptr = &i_port[0];
+               goto check_newline;
+       }
+       pr_err("Unable to locate prefix for emulated Initiator Port: %s\n",
+               i_port);
+       return -EINVAL;
+       /*
+        * Clear any trailing newline for the NAA WWN
+        */
+check_newline:
+       if (i_port[strlen(i_port) - 1] == '\n')
+               i_port[strlen(i_port) - 1] = '\0';
+
+       ret = scsiback_make_nexus(tpg, port_ptr);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+TF_TPG_BASE_ATTR(scsiback, nexus, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *scsiback_tpg_attrs[] = {
+       &scsiback_tpg_nexus.attr,
+       NULL,
+};
+
+static ssize_t
+scsiback_wwn_show_attr_version(struct target_fabric_configfs *tf,
+                               char *page)
+{
+       return sprintf(page, "xen-pvscsi fabric module %s on %s/%s on "
+               UTS_RELEASE"\n",
+               VSCSI_VERSION, utsname()->sysname, utsname()->machine);
+}
+
+TF_WWN_ATTR_RO(scsiback, version);
+
+static struct configfs_attribute *scsiback_wwn_attrs[] = {
+       &scsiback_wwn_version.attr,
+       NULL,
+};
+
+static char *scsiback_get_fabric_name(void)
+{
+       return "xen-pvscsi";
+}
+
+static int scsiback_port_link(struct se_portal_group *se_tpg,
+                              struct se_lun *lun)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+
+       mutex_lock(&tpg->tv_tpg_mutex);
+       tpg->tv_tpg_port_count++;
+       mutex_unlock(&tpg->tv_tpg_mutex);
+
+       return 0;
+}
+
+static void scsiback_port_unlink(struct se_portal_group *se_tpg,
+                                 struct se_lun *lun)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+
+       mutex_lock(&tpg->tv_tpg_mutex);
+       tpg->tv_tpg_port_count--;
+       mutex_unlock(&tpg->tv_tpg_mutex);
+}
+
+static struct se_portal_group *
+scsiback_make_tpg(struct se_wwn *wwn,
+                  struct config_group *group,
+                  const char *name)
+{
+       struct scsiback_tport *tport = container_of(wwn,
+                       struct scsiback_tport, tport_wwn);
+
+       struct scsiback_tpg *tpg;
+       u16 tpgt;
+       int ret;
+
+       if (strstr(name, "tpgt_") != name)
+               return ERR_PTR(-EINVAL);
+       ret = kstrtou16(name + 5, 10, &tpgt);
+       if (ret)
+               return ERR_PTR(ret);
+
+       tpg = kzalloc(sizeof(struct scsiback_tpg), GFP_KERNEL);
+       if (!tpg)
+               return ERR_PTR(-ENOMEM);
+
+       mutex_init(&tpg->tv_tpg_mutex);
+       INIT_LIST_HEAD(&tpg->tv_tpg_list);
+       INIT_LIST_HEAD(&tpg->info_list);
+       tpg->tport = tport;
+       tpg->tport_tpgt = tpgt;
+
+       ret = core_tpg_register(&scsiback_fabric_configfs->tf_ops, wwn,
+                               &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL);
+       if (ret < 0) {
+               kfree(tpg);
+               return NULL;
+       }
+       mutex_lock(&scsiback_mutex);
+       list_add_tail(&tpg->tv_tpg_list, &scsiback_list);
+       mutex_unlock(&scsiback_mutex);
+
+       return &tpg->se_tpg;
+}
+
+static void scsiback_drop_tpg(struct se_portal_group *se_tpg)
+{
+       struct scsiback_tpg *tpg = container_of(se_tpg,
+                               struct scsiback_tpg, se_tpg);
+
+       mutex_lock(&scsiback_mutex);
+       list_del(&tpg->tv_tpg_list);
+       mutex_unlock(&scsiback_mutex);
+       /*
+        * Release the virtual I_T Nexus for this xen-pvscsi TPG
+        */
+       scsiback_drop_nexus(tpg);
+       /*
+        * Deregister the se_tpg from TCM..
+        */
+       core_tpg_deregister(se_tpg);
+       kfree(tpg);
+}
+
+static int scsiback_check_true(struct se_portal_group *se_tpg)
+{
+       return 1;
+}
+
+static int scsiback_check_false(struct se_portal_group *se_tpg)
+{
+       return 0;
+}
+
+static struct target_core_fabric_ops scsiback_ops = {
+       .get_fabric_name                = scsiback_get_fabric_name,
+       .get_fabric_proto_ident         = scsiback_get_fabric_proto_ident,
+       .tpg_get_wwn                    = scsiback_get_fabric_wwn,
+       .tpg_get_tag                    = scsiback_get_tag,
+       .tpg_get_default_depth          = scsiback_get_default_depth,
+       .tpg_get_pr_transport_id        = scsiback_get_pr_transport_id,
+       .tpg_get_pr_transport_id_len    = scsiback_get_pr_transport_id_len,
+       .tpg_parse_pr_out_transport_id  = scsiback_parse_pr_out_transport_id,
+       .tpg_check_demo_mode            = scsiback_check_true,
+       .tpg_check_demo_mode_cache      = scsiback_check_true,
+       .tpg_check_demo_mode_write_protect = scsiback_check_false,
+       .tpg_check_prod_mode_write_protect = scsiback_check_false,
+       .tpg_alloc_fabric_acl           = scsiback_alloc_fabric_acl,
+       .tpg_release_fabric_acl         = scsiback_release_fabric_acl,
+       .tpg_get_inst_index             = scsiback_tpg_get_inst_index,
+       .check_stop_free                = scsiback_check_stop_free,
+       .release_cmd                    = scsiback_release_cmd,
+       .put_session                    = NULL,
+       .shutdown_session               = scsiback_shutdown_session,
+       .close_session                  = scsiback_close_session,
+       .sess_get_index                 = scsiback_sess_get_index,
+       .sess_get_initiator_sid         = NULL,
+       .write_pending                  = scsiback_write_pending,
+       .write_pending_status           = scsiback_write_pending_status,
+       .set_default_node_attributes    = scsiback_set_default_node_attrs,
+       .get_task_tag                   = scsiback_get_task_tag,
+       .get_cmd_state                  = scsiback_get_cmd_state,
+       .queue_data_in                  = scsiback_queue_data_in,
+       .queue_status                   = scsiback_queue_status,
+       .queue_tm_rsp                   = scsiback_queue_tm_rsp,
+       .aborted_task                   = scsiback_aborted_task,
+       /*
+        * Setup callers for generic logic in target_core_fabric_configfs.c
+        */
+       .fabric_make_wwn                = scsiback_make_tport,
+       .fabric_drop_wwn                = scsiback_drop_tport,
+       .fabric_make_tpg                = scsiback_make_tpg,
+       .fabric_drop_tpg                = scsiback_drop_tpg,
+       .fabric_post_link               = scsiback_port_link,
+       .fabric_pre_unlink              = scsiback_port_unlink,
+       .fabric_make_np                 = NULL,
+       .fabric_drop_np                 = NULL,
+#if 0
+       .fabric_make_nodeacl            = scsiback_make_nodeacl,
+       .fabric_drop_nodeacl            = scsiback_drop_nodeacl,
+#endif
+};
+
+static int scsiback_register_configfs(void)
+{
+       struct target_fabric_configfs *fabric;
+       int ret;
+
+       pr_debug("xen-pvscsi: fabric module %s on %s/%s on "UTS_RELEASE"\n",
+                VSCSI_VERSION, utsname()->sysname, utsname()->machine);
+       /*
+        * Register the top level struct config_item_type with TCM core
+        */
+       fabric = target_fabric_configfs_init(THIS_MODULE, "xen-pvscsi");
+       if (IS_ERR(fabric))
+               return PTR_ERR(fabric);
+
+       /*
+        * Setup fabric->tf_ops from our local scsiback_ops
+        */
+       fabric->tf_ops = scsiback_ops;
+       /*
+        * Setup default attribute lists for various fabric->tf_cit_tmpl
+        */
+       fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = scsiback_wwn_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = scsiback_tpg_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = scsiback_param_attrs;
+       fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       /*
+        * Register the fabric for use within TCM
+        */
+       ret = target_fabric_configfs_register(fabric);
+       if (ret < 0) {
+               target_fabric_configfs_free(fabric);
+               return ret;
+       }
+       /*
+        * Setup our local pointer to *fabric
+        */
+       scsiback_fabric_configfs = fabric;
+       pr_debug("xen-pvscsi: Set fabric -> scsiback_fabric_configfs\n");
+       return 0;
+};
+
+static void scsiback_deregister_configfs(void)
+{
+       if (!scsiback_fabric_configfs)
+               return;
+
+       target_fabric_configfs_deregister(scsiback_fabric_configfs);
+       scsiback_fabric_configfs = NULL;
+       pr_debug("xen-pvscsi: Cleared scsiback_fabric_configfs\n");
+};
+
+static const struct xenbus_device_id scsiback_ids[] = {
+       { "vscsi" },
+       { "" }
+};
+
+static struct xenbus_driver scsiback_driver = {
+       .ids                    = scsiback_ids,
+       .probe                  = scsiback_probe,
+       .remove                 = scsiback_remove,
+       .otherend_changed       = scsiback_frontend_changed
+};
+
+static void scsiback_init_pend(void *p)
+{
+       struct vscsibk_pend *pend = p;
+       int i;
+
+       memset(pend, 0, sizeof(*pend));
+       for (i = 0; i < VSCSI_MAX_GRANTS; i++)
+               pend->grant_handles[i] = SCSIBACK_INVALID_HANDLE;
+}
+
+static int __init scsiback_init(void)
+{
+       int ret;
+
+       if (!xen_domain())
+               return -ENODEV;
+
+       scsiback_cachep = kmem_cache_create("vscsiif_cache",
+               sizeof(struct vscsibk_pend), 0, 0, scsiback_init_pend);
+       if (!scsiback_cachep)
+               return -ENOMEM;
+
+       ret = xenbus_register_backend(&scsiback_driver);
+       if (ret)
+               goto out_cache_destroy;
+
+       ret = scsiback_register_configfs();
+       if (ret)
+               goto out_unregister_xenbus;
+
+       return 0;
+
+out_unregister_xenbus:
+       xenbus_unregister_driver(&scsiback_driver);
+out_cache_destroy:
+       kmem_cache_destroy(scsiback_cachep);
+       pr_err("xen-pvscsi: %s: error %d\n", __func__, ret);
+       return ret;
+}
+
+static void __exit scsiback_exit(void)
+{
+       struct page *page;
+
+       while (free_pages_num) {
+               if (get_free_page(&page))
+                       BUG();
+               free_xenballooned_pages(1, &page);
+       }
+       scsiback_deregister_configfs();
+       xenbus_unregister_driver(&scsiback_driver);
+       kmem_cache_destroy(scsiback_cachep);
+}
+
+module_init(scsiback_init);
+module_exit(scsiback_exit);
+
+MODULE_DESCRIPTION("Xen SCSI backend driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("xen-backend:vscsi");
+MODULE_AUTHOR("Juergen Gross <jgross@suse.com>");
index 439c9dca9eeedea8c490a9e57174c6745cae8637..ca744102b6663fb92f37da6f9bc4974fac393d53 100644 (file)
@@ -259,7 +259,6 @@ static char *error_path(struct xenbus_device *dev)
 static void xenbus_va_dev_error(struct xenbus_device *dev, int err,
                                const char *fmt, va_list ap)
 {
-       int ret;
        unsigned int len;
        char *printf_buffer = NULL;
        char *path_buffer = NULL;
@@ -270,9 +269,7 @@ static void xenbus_va_dev_error(struct xenbus_device *dev, int err,
                goto fail;
 
        len = sprintf(printf_buffer, "%i ", -err);
-       ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
-
-       BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1);
+       vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
 
        dev_err(&dev->dev, "%s\n", printf_buffer);
 
@@ -361,8 +358,8 @@ static void xenbus_switch_fatal(struct xenbus_device *dev, int depth, int err,
  * @ring_mfn: mfn of ring to grant
 
  * Grant access to the given @ring_mfn to the peer of the given device.  Return
- * 0 on success, or -errno on error.  On error, the device will switch to
- * XenbusStateClosing, and the error will be saved in the store.
+ * a grant reference on success, or -errno on error. On error, the device will
+ * switch to XenbusStateClosing, and the error will be saved in the store.
  */
 int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn)
 {
index 3c0a74b3e9b15af5355c8619294805f59030f75d..564b31584860432634a898a8fce9c7dc155a6662 100644 (file)
@@ -297,9 +297,13 @@ void xenbus_dev_shutdown(struct device *_dev)
 EXPORT_SYMBOL_GPL(xenbus_dev_shutdown);
 
 int xenbus_register_driver_common(struct xenbus_driver *drv,
-                                 struct xen_bus_type *bus)
+                                 struct xen_bus_type *bus,
+                                 struct module *owner, const char *mod_name)
 {
+       drv->driver.name = drv->name ? drv->name : drv->ids[0].devicetype;
        drv->driver.bus = &bus->bus;
+       drv->driver.owner = owner;
+       drv->driver.mod_name = mod_name;
 
        return driver_register(&drv->driver);
 }
index 1085ec294a1987a5620b24c4a2e120528d13cac0..c9ec7ca1f7ab6f4e3f78099c434d2533f0881b2f 100644 (file)
@@ -60,7 +60,9 @@ extern int xenbus_match(struct device *_dev, struct device_driver *_drv);
 extern int xenbus_dev_probe(struct device *_dev);
 extern int xenbus_dev_remove(struct device *_dev);
 extern int xenbus_register_driver_common(struct xenbus_driver *drv,
-                                        struct xen_bus_type *bus);
+                                        struct xen_bus_type *bus,
+                                        struct module *owner,
+                                        const char *mod_name);
 extern int xenbus_probe_node(struct xen_bus_type *bus,
                             const char *type,
                             const char *nodename);
index 5125dce11a6083da92fc5942a09c2aa786f181d8..04f7f85a5edf88ae21a01c6acae092b3a17c5a83 100644 (file)
@@ -234,13 +234,15 @@ int xenbus_dev_is_online(struct xenbus_device *dev)
 }
 EXPORT_SYMBOL_GPL(xenbus_dev_is_online);
 
-int xenbus_register_backend(struct xenbus_driver *drv)
+int __xenbus_register_backend(struct xenbus_driver *drv, struct module *owner,
+                             const char *mod_name)
 {
        drv->read_otherend_details = read_frontend_details;
 
-       return xenbus_register_driver_common(drv, &xenbus_backend);
+       return xenbus_register_driver_common(drv, &xenbus_backend,
+                                            owner, mod_name);
 }
-EXPORT_SYMBOL_GPL(xenbus_register_backend);
+EXPORT_SYMBOL_GPL(__xenbus_register_backend);
 
 static int backend_probe_and_watch(struct notifier_block *notifier,
                                   unsigned long event,
index cb385c10d2b15679729810dd24e5c9bb3cd9117c..bcb53bdc469c43a66914eec1f63acc05936d482f 100644 (file)
@@ -317,13 +317,15 @@ static void wait_for_devices(struct xenbus_driver *xendrv)
                         print_device_status);
 }
 
-int xenbus_register_frontend(struct xenbus_driver *drv)
+int __xenbus_register_frontend(struct xenbus_driver *drv, struct module *owner,
+                              const char *mod_name)
 {
        int ret;
 
        drv->read_otherend_details = read_backend_details;
 
-       ret = xenbus_register_driver_common(drv, &xenbus_frontend);
+       ret = xenbus_register_driver_common(drv, &xenbus_frontend,
+                                           owner, mod_name);
        if (ret)
                return ret;
 
@@ -332,7 +334,7 @@ int xenbus_register_frontend(struct xenbus_driver *drv)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(xenbus_register_frontend);
+EXPORT_SYMBOL_GPL(__xenbus_register_frontend);
 
 static DECLARE_WAIT_QUEUE_HEAD(backend_state_wq);
 static int backend_state;
index 733750096b71b38d23783926b4f85da9ac2d35ea..84a751005f5b8ad0f0f5cd6e3c5ec67df5f7440c 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -661,10 +661,10 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
 
        INIT_LIST_HEAD(&ctx->active_reqs);
 
-       if (percpu_ref_init(&ctx->users, free_ioctx_users))
+       if (percpu_ref_init(&ctx->users, free_ioctx_users, 0, GFP_KERNEL))
                goto err;
 
-       if (percpu_ref_init(&ctx->reqs, free_ioctx_reqs))
+       if (percpu_ref_init(&ctx->reqs, free_ioctx_reqs, 0, GFP_KERNEL))
                goto err;
 
        ctx->cpu = alloc_percpu(struct kioctx_cpu);
index 6d7274619bf916c2dcf0d7744ba8d888d948d711..e2f3ad0879ce9165f60d6e5c8cd4a6b4e523d87e 100644 (file)
@@ -304,6 +304,12 @@ static int blkdev_readpage(struct file * file, struct page * page)
        return block_read_full_page(page, blkdev_get_block);
 }
 
+static int blkdev_readpages(struct file *file, struct address_space *mapping,
+                       struct list_head *pages, unsigned nr_pages)
+{
+       return mpage_readpages(mapping, pages, nr_pages, blkdev_get_block);
+}
+
 static int blkdev_write_begin(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned flags,
                        struct page **pagep, void **fsdata)
@@ -1622,6 +1628,7 @@ static int blkdev_releasepage(struct page *page, gfp_t wait)
 
 static const struct address_space_operations def_blk_aops = {
        .readpage       = blkdev_readpage,
+       .readpages      = blkdev_readpages,
        .writepage      = blkdev_writepage,
        .write_begin    = blkdev_write_begin,
        .write_end      = blkdev_write_end,
index fbd76ded9a34b3260a5e794115fff0312b3c6e08..4dabeb893b7c18cced67230fd8fbcc36c677983b 100644 (file)
@@ -74,6 +74,7 @@ BTRFS_WORK_HELPER(endio_helper);
 BTRFS_WORK_HELPER(endio_meta_helper);
 BTRFS_WORK_HELPER(endio_meta_write_helper);
 BTRFS_WORK_HELPER(endio_raid56_helper);
+BTRFS_WORK_HELPER(endio_repair_helper);
 BTRFS_WORK_HELPER(rmw_helper);
 BTRFS_WORK_HELPER(endio_write_helper);
 BTRFS_WORK_HELPER(freespace_write_helper);
@@ -91,7 +92,7 @@ __btrfs_alloc_workqueue(const char *name, int flags, int max_active,
 {
        struct __btrfs_workqueue *ret = kzalloc(sizeof(*ret), GFP_NOFS);
 
-       if (unlikely(!ret))
+       if (!ret)
                return NULL;
 
        ret->max_active = max_active;
@@ -115,7 +116,7 @@ __btrfs_alloc_workqueue(const char *name, int flags, int max_active,
                ret->normal_wq = alloc_workqueue("%s-%s", flags,
                                                 ret->max_active, "btrfs",
                                                 name);
-       if (unlikely(!ret->normal_wq)) {
+       if (!ret->normal_wq) {
                kfree(ret);
                return NULL;
        }
@@ -137,12 +138,12 @@ struct btrfs_workqueue *btrfs_alloc_workqueue(const char *name,
 {
        struct btrfs_workqueue *ret = kzalloc(sizeof(*ret), GFP_NOFS);
 
-       if (unlikely(!ret))
+       if (!ret)
                return NULL;
 
        ret->normal = __btrfs_alloc_workqueue(name, flags & ~WQ_HIGHPRI,
                                              max_active, thresh);
-       if (unlikely(!ret->normal)) {
+       if (!ret->normal) {
                kfree(ret);
                return NULL;
        }
@@ -150,7 +151,7 @@ struct btrfs_workqueue *btrfs_alloc_workqueue(const char *name,
        if (flags & WQ_HIGHPRI) {
                ret->high = __btrfs_alloc_workqueue(name, flags, max_active,
                                                    thresh);
-               if (unlikely(!ret->high)) {
+               if (!ret->high) {
                        __btrfs_destroy_workqueue(ret->normal);
                        kfree(ret);
                        return NULL;
index e9e31c94758fd6ddea5cbd458d5aca04a27e9a73..e386c29ef1f62c559a184e4ae31884c2bcc425ba 100644 (file)
@@ -53,6 +53,7 @@ BTRFS_WORK_HELPER_PROTO(endio_helper);
 BTRFS_WORK_HELPER_PROTO(endio_meta_helper);
 BTRFS_WORK_HELPER_PROTO(endio_meta_write_helper);
 BTRFS_WORK_HELPER_PROTO(endio_raid56_helper);
+BTRFS_WORK_HELPER_PROTO(endio_repair_helper);
 BTRFS_WORK_HELPER_PROTO(rmw_helper);
 BTRFS_WORK_HELPER_PROTO(endio_write_helper);
 BTRFS_WORK_HELPER_PROTO(freespace_write_helper);
index 54a201dac7f9455bd1e294c2731d5b47226b7bef..2d3e32ebfd15510b8e97519a006486c83755121b 100644 (file)
@@ -25,6 +25,9 @@
 #include "delayed-ref.h"
 #include "locking.h"
 
+/* Just an arbitrary number so we can be sure this happened */
+#define BACKREF_FOUND_SHARED 6
+
 struct extent_inode_elem {
        u64 inum;
        u64 offset;
@@ -377,7 +380,8 @@ out:
 static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
                                   struct btrfs_path *path, u64 time_seq,
                                   struct list_head *head,
-                                  const u64 *extent_item_pos, u64 total_refs)
+                                  const u64 *extent_item_pos, u64 total_refs,
+                                  u64 root_objectid)
 {
        int err;
        int ret = 0;
@@ -402,6 +406,10 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
                        continue;
                if (ref->count == 0)
                        continue;
+               if (root_objectid && ref->root_id != root_objectid) {
+                       ret = BACKREF_FOUND_SHARED;
+                       goto out;
+               }
                err = __resolve_indirect_ref(fs_info, path, time_seq, ref,
                                             parents, extent_item_pos,
                                             total_refs);
@@ -482,7 +490,7 @@ static int __add_missing_keys(struct btrfs_fs_info *fs_info,
                        continue;
                BUG_ON(!ref->wanted_disk_byte);
                eb = read_tree_block(fs_info->tree_root, ref->wanted_disk_byte,
-                                    fs_info->tree_root->leafsize, 0);
+                                    0);
                if (!eb || !extent_buffer_uptodate(eb)) {
                        free_extent_buffer(eb);
                        return -EIO;
@@ -561,7 +569,8 @@ static void __merge_refs(struct list_head *head, int mode)
  * smaller or equal that seq to the list
  */
 static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
-                             struct list_head *prefs, u64 *total_refs)
+                             struct list_head *prefs, u64 *total_refs,
+                             u64 inum)
 {
        struct btrfs_delayed_extent_op *extent_op = head->extent_op;
        struct rb_node *n = &head->node.rb_node;
@@ -625,6 +634,16 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
                        key.objectid = ref->objectid;
                        key.type = BTRFS_EXTENT_DATA_KEY;
                        key.offset = ref->offset;
+
+                       /*
+                        * Found a inum that doesn't match our known inum, we
+                        * know it's shared.
+                        */
+                       if (inum && ref->objectid != inum) {
+                               ret = BACKREF_FOUND_SHARED;
+                               break;
+                       }
+
                        ret = __add_prelim_ref(prefs, ref->root, &key, 0, 0,
                                               node->bytenr,
                                               node->ref_mod * sgn, GFP_ATOMIC);
@@ -659,7 +678,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
 static int __add_inline_refs(struct btrfs_fs_info *fs_info,
                             struct btrfs_path *path, u64 bytenr,
                             int *info_level, struct list_head *prefs,
-                            u64 *total_refs)
+                            u64 *total_refs, u64 inum)
 {
        int ret = 0;
        int slot;
@@ -744,6 +763,12 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
                                                                      dref);
                        key.type = BTRFS_EXTENT_DATA_KEY;
                        key.offset = btrfs_extent_data_ref_offset(leaf, dref);
+
+                       if (inum && key.objectid != inum) {
+                               ret = BACKREF_FOUND_SHARED;
+                               break;
+                       }
+
                        root = btrfs_extent_data_ref_root(leaf, dref);
                        ret = __add_prelim_ref(prefs, root, &key, 0, 0,
                                               bytenr, count, GFP_NOFS);
@@ -765,7 +790,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
  */
 static int __add_keyed_refs(struct btrfs_fs_info *fs_info,
                            struct btrfs_path *path, u64 bytenr,
-                           int info_level, struct list_head *prefs)
+                           int info_level, struct list_head *prefs, u64 inum)
 {
        struct btrfs_root *extent_root = fs_info->extent_root;
        int ret;
@@ -827,6 +852,12 @@ static int __add_keyed_refs(struct btrfs_fs_info *fs_info,
                                                                      dref);
                        key.type = BTRFS_EXTENT_DATA_KEY;
                        key.offset = btrfs_extent_data_ref_offset(leaf, dref);
+
+                       if (inum && key.objectid != inum) {
+                               ret = BACKREF_FOUND_SHARED;
+                               break;
+                       }
+
                        root = btrfs_extent_data_ref_root(leaf, dref);
                        ret = __add_prelim_ref(prefs, root, &key, 0, 0,
                                               bytenr, count, GFP_NOFS);
@@ -854,7 +885,8 @@ static int __add_keyed_refs(struct btrfs_fs_info *fs_info,
 static int find_parent_nodes(struct btrfs_trans_handle *trans,
                             struct btrfs_fs_info *fs_info, u64 bytenr,
                             u64 time_seq, struct ulist *refs,
-                            struct ulist *roots, const u64 *extent_item_pos)
+                            struct ulist *roots, const u64 *extent_item_pos,
+                            u64 root_objectid, u64 inum)
 {
        struct btrfs_key key;
        struct btrfs_path *path;
@@ -929,7 +961,8 @@ again:
                        }
                        spin_unlock(&delayed_refs->lock);
                        ret = __add_delayed_refs(head, time_seq,
-                                                &prefs_delayed, &total_refs);
+                                                &prefs_delayed, &total_refs,
+                                                inum);
                        mutex_unlock(&head->mutex);
                        if (ret)
                                goto out;
@@ -951,11 +984,11 @@ again:
                     key.type == BTRFS_METADATA_ITEM_KEY)) {
                        ret = __add_inline_refs(fs_info, path, bytenr,
                                                &info_level, &prefs,
-                                               &total_refs);
+                                               &total_refs, inum);
                        if (ret)
                                goto out;
                        ret = __add_keyed_refs(fs_info, path, bytenr,
-                                              info_level, &prefs);
+                                              info_level, &prefs, inum);
                        if (ret)
                                goto out;
                }
@@ -971,7 +1004,8 @@ again:
        __merge_refs(&prefs, 1);
 
        ret = __resolve_indirect_refs(fs_info, path, time_seq, &prefs,
-                                     extent_item_pos, total_refs);
+                                     extent_item_pos, total_refs,
+                                     root_objectid);
        if (ret)
                goto out;
 
@@ -981,6 +1015,11 @@ again:
                ref = list_first_entry(&prefs, struct __prelim_ref, list);
                WARN_ON(ref->count < 0);
                if (roots && ref->count && ref->root_id && ref->parent == 0) {
+                       if (root_objectid && ref->root_id != root_objectid) {
+                               ret = BACKREF_FOUND_SHARED;
+                               goto out;
+                       }
+
                        /* no parent == root of tree */
                        ret = ulist_add(roots, ref->root_id, 0, GFP_NOFS);
                        if (ret < 0)
@@ -989,12 +1028,10 @@ again:
                if (ref->count && ref->parent) {
                        if (extent_item_pos && !ref->inode_list &&
                            ref->level == 0) {
-                               u32 bsz;
                                struct extent_buffer *eb;
-                               bsz = btrfs_level_size(fs_info->extent_root,
-                                                       ref->level);
+
                                eb = read_tree_block(fs_info->extent_root,
-                                                          ref->parent, bsz, 0);
+                                                          ref->parent, 0);
                                if (!eb || !extent_buffer_uptodate(eb)) {
                                        free_extent_buffer(eb);
                                        ret = -EIO;
@@ -1087,7 +1124,7 @@ static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans,
                return -ENOMEM;
 
        ret = find_parent_nodes(trans, fs_info, bytenr,
-                               time_seq, *leafs, NULL, extent_item_pos);
+                               time_seq, *leafs, NULL, extent_item_pos, 0, 0);
        if (ret < 0 && ret != -ENOENT) {
                free_leaf_list(*leafs);
                return ret;
@@ -1130,7 +1167,7 @@ static int __btrfs_find_all_roots(struct btrfs_trans_handle *trans,
        ULIST_ITER_INIT(&uiter);
        while (1) {
                ret = find_parent_nodes(trans, fs_info, bytenr,
-                                       time_seq, tmp, *roots, NULL);
+                                       time_seq, tmp, *roots, NULL, 0, 0);
                if (ret < 0 && ret != -ENOENT) {
                        ulist_free(tmp);
                        ulist_free(*roots);
@@ -1161,6 +1198,54 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
        return ret;
 }
 
+int btrfs_check_shared(struct btrfs_trans_handle *trans,
+                      struct btrfs_fs_info *fs_info, u64 root_objectid,
+                      u64 inum, u64 bytenr)
+{
+       struct ulist *tmp = NULL;
+       struct ulist *roots = NULL;
+       struct ulist_iterator uiter;
+       struct ulist_node *node;
+       struct seq_list elem = {};
+       int ret = 0;
+
+       tmp = ulist_alloc(GFP_NOFS);
+       roots = ulist_alloc(GFP_NOFS);
+       if (!tmp || !roots) {
+               ulist_free(tmp);
+               ulist_free(roots);
+               return -ENOMEM;
+       }
+
+       if (trans)
+               btrfs_get_tree_mod_seq(fs_info, &elem);
+       else
+               down_read(&fs_info->commit_root_sem);
+       ULIST_ITER_INIT(&uiter);
+       while (1) {
+               ret = find_parent_nodes(trans, fs_info, bytenr, elem.seq, tmp,
+                                       roots, NULL, root_objectid, inum);
+               if (ret == BACKREF_FOUND_SHARED) {
+                       ret = 1;
+                       break;
+               }
+               if (ret < 0 && ret != -ENOENT)
+                       break;
+               node = ulist_next(tmp, &uiter);
+               if (!node)
+                       break;
+               bytenr = node->val;
+               cond_resched();
+       }
+       if (trans)
+               btrfs_put_tree_mod_seq(fs_info, &elem);
+       else
+               up_read(&fs_info->commit_root_sem);
+       ulist_free(tmp);
+       ulist_free(roots);
+       return ret;
+}
+
 /*
  * this makes the path point to (inum INODE_ITEM ioff)
  */
@@ -1193,7 +1278,7 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
        unsigned long ptr;
 
        key.objectid = inode_objectid;
-       btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY);
+       key.type = BTRFS_INODE_EXTREF_KEY;
        key.offset = start_off;
 
        ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
@@ -1233,7 +1318,7 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
                ret = -ENOENT;
                if (found_key.objectid != inode_objectid)
                        break;
-               if (btrfs_key_type(&found_key) != BTRFS_INODE_EXTREF_KEY)
+               if (found_key.type != BTRFS_INODE_EXTREF_KEY)
                        break;
 
                ret = 0;
@@ -1366,7 +1451,7 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
        }
        btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]);
        if (found_key->type == BTRFS_METADATA_ITEM_KEY)
-               size = fs_info->extent_root->leafsize;
+               size = fs_info->extent_root->nodesize;
        else if (found_key->type == BTRFS_EXTENT_ITEM_KEY)
                size = found_key->offset;
 
index 86fc20fec28243f0f594a40a17da64a5d79da30f..2a1ac6bfc724637f3a80ac15c6ce6237dc7998f0 100644 (file)
@@ -71,6 +71,9 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
                          u64 start_off, struct btrfs_path *path,
                          struct btrfs_inode_extref **ret_extref,
                          u64 *found_off);
+int btrfs_check_shared(struct btrfs_trans_handle *trans,
+                      struct btrfs_fs_info *fs_info, u64 root_objectid,
+                      u64 inum, u64 bytenr);
 
 int __init btrfs_prelim_ref_init(void);
 void btrfs_prelim_ref_exit(void);
index 56b8522d5767b5858928d4152c0ab64ca43886ca..4aadadcfab20178d734ad395c1448676603a01b8 100644 (file)
 #define BTRFS_INODE_IN_DELALLOC_LIST           9
 #define BTRFS_INODE_READDIO_NEED_LOCK          10
 #define BTRFS_INODE_HAS_PROPS                  11
+/*
+ * The following 3 bits are meant only for the btree inode.
+ * When any of them is set, it means an error happened while writing an
+ * extent buffer belonging to:
+ * 1) a non-log btree
+ * 2) a log btree and first log sub-transaction
+ * 3) a log btree and second log sub-transaction
+ */
+#define BTRFS_INODE_BTREE_ERR                  12
+#define BTRFS_INODE_BTREE_LOG1_ERR             13
+#define BTRFS_INODE_BTREE_LOG2_ERR             14
 
 /* in memory btrfs inode */
 struct btrfs_inode {
@@ -120,6 +131,12 @@ struct btrfs_inode {
         */
        u64 delalloc_bytes;
 
+       /*
+        * total number of bytes pending defrag, used by stat to check whether
+        * it needs COW.
+        */
+       u64 defrag_bytes;
+
        /*
         * the size of the file stored in the metadata on disk.  data=ordered
         * means the in-memory i_size might be larger than the size on disk
@@ -248,8 +265,11 @@ static inline int btrfs_inode_in_log(struct inode *inode, u64 generation)
        return 0;
 }
 
+#define BTRFS_DIO_ORIG_BIO_SUBMITTED   0x1
+
 struct btrfs_dio_private {
        struct inode *inode;
+       unsigned long flags;
        u64 logical_offset;
        u64 disk_bytenr;
        u64 bytes;
@@ -266,7 +286,12 @@ struct btrfs_dio_private {
 
        /* dio_bio came from fs/direct-io.c */
        struct bio *dio_bio;
-       u8 csum[0];
+
+       /*
+        * The original bio may be splited to several sub-bios, this is
+        * done during endio of sub-bios
+        */
+       int (*subio_endio)(struct inode *, struct btrfs_io_bio *, int);
 };
 
 /*
index ce92ae30250fb256fbbbdb9158315c757a24adc1..cb7f3fe9c9f6ff21bacffd3ab5a4a2bc7eccf1f0 100644 (file)
@@ -807,7 +807,7 @@ static int btrfsic_process_superblock_dev_mirror(
 
        /* super block bytenr is always the unmapped device bytenr */
        dev_bytenr = btrfs_sb_offset(superblock_mirror_num);
-       if (dev_bytenr + BTRFS_SUPER_INFO_SIZE > device->total_bytes)
+       if (dev_bytenr + BTRFS_SUPER_INFO_SIZE > device->commit_total_bytes)
                return -1;
        bh = __bread(superblock_bdev, dev_bytenr / 4096,
                     BTRFS_SUPER_INFO_SIZE);
@@ -820,7 +820,6 @@ static int btrfsic_process_superblock_dev_mirror(
            btrfs_super_magic(super_tmp) != BTRFS_MAGIC ||
            memcmp(device->uuid, super_tmp->dev_item.uuid, BTRFS_UUID_SIZE) ||
            btrfs_super_nodesize(super_tmp) != state->metablock_size ||
-           btrfs_super_leafsize(super_tmp) != state->metablock_size ||
            btrfs_super_sectorsize(super_tmp) != state->datablock_size) {
                brelse(bh);
                return 0;
@@ -1252,8 +1251,7 @@ static void btrfsic_read_from_block_data(
 
        while (len > 0) {
                cur = min(len, ((size_t)PAGE_CACHE_SIZE - offset_in_page));
-               BUG_ON(i >= (block_ctx->len + PAGE_CACHE_SIZE - 1) >>
-                           PAGE_CACHE_SHIFT);
+               BUG_ON(i >= DIV_ROUND_UP(block_ctx->len, PAGE_CACHE_SIZE));
                kaddr = block_ctx->datav[i];
                memcpy(dst, kaddr + offset_in_page, cur);
 
@@ -3120,24 +3118,12 @@ int btrfsic_mount(struct btrfs_root *root,
        struct list_head *dev_head = &fs_devices->devices;
        struct btrfs_device *device;
 
-       if (root->nodesize != root->leafsize) {
-               printk(KERN_INFO
-                      "btrfsic: cannot handle nodesize %d != leafsize %d!\n",
-                      root->nodesize, root->leafsize);
-               return -1;
-       }
        if (root->nodesize & ((u64)PAGE_CACHE_SIZE - 1)) {
                printk(KERN_INFO
                       "btrfsic: cannot handle nodesize %d not being a multiple of PAGE_CACHE_SIZE %ld!\n",
                       root->nodesize, PAGE_CACHE_SIZE);
                return -1;
        }
-       if (root->leafsize & ((u64)PAGE_CACHE_SIZE - 1)) {
-               printk(KERN_INFO
-                      "btrfsic: cannot handle leafsize %d not being a multiple of PAGE_CACHE_SIZE %ld!\n",
-                      root->leafsize, PAGE_CACHE_SIZE);
-               return -1;
-       }
        if (root->sectorsize & ((u64)PAGE_CACHE_SIZE - 1)) {
                printk(KERN_INFO
                       "btrfsic: cannot handle sectorsize %d not being a multiple of PAGE_CACHE_SIZE %ld!\n",
index 1daea0b47187b58db65dde86411e88578473fcf4..d3220d31d3cbf0e653898d15816f5895045c06d5 100644 (file)
@@ -91,8 +91,7 @@ static inline int compressed_bio_size(struct btrfs_root *root,
        u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
 
        return sizeof(struct compressed_bio) +
-               ((disk_size + root->sectorsize - 1) / root->sectorsize) *
-               csum_size;
+               (DIV_ROUND_UP(disk_size, root->sectorsize)) * csum_size;
 }
 
 static struct bio *compressed_bio_alloc(struct block_device *bdev,
@@ -389,7 +388,8 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
                         * freed before we're done setting it up
                         */
                        atomic_inc(&cb->pending_bios);
-                       ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
+                       ret = btrfs_bio_wq_end_io(root->fs_info, bio,
+                                       BTRFS_WQ_ENDIO_DATA);
                        BUG_ON(ret); /* -ENOMEM */
 
                        if (!skip_sum) {
@@ -420,7 +420,7 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
        }
        bio_get(bio);
 
-       ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
+       ret = btrfs_bio_wq_end_io(root->fs_info, bio, BTRFS_WQ_ENDIO_DATA);
        BUG_ON(ret); /* -ENOMEM */
 
        if (!skip_sum) {
@@ -615,8 +615,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
        cb->compress_type = extent_compress_type(bio_flags);
        cb->orig_bio = bio;
 
-       nr_pages = (compressed_len + PAGE_CACHE_SIZE - 1) /
-                                PAGE_CACHE_SIZE;
+       nr_pages = DIV_ROUND_UP(compressed_len, PAGE_CACHE_SIZE);
        cb->compressed_pages = kzalloc(sizeof(struct page *) * nr_pages,
                                       GFP_NOFS);
        if (!cb->compressed_pages)
@@ -670,7 +669,8 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
                    PAGE_CACHE_SIZE) {
                        bio_get(comp_bio);
 
-                       ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0);
+                       ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio,
+                                       BTRFS_WQ_ENDIO_DATA);
                        BUG_ON(ret); /* -ENOMEM */
 
                        /*
@@ -686,8 +686,8 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
                                                        comp_bio, sums);
                                BUG_ON(ret); /* -ENOMEM */
                        }
-                       sums += (comp_bio->bi_iter.bi_size +
-                                root->sectorsize - 1) / root->sectorsize;
+                       sums += DIV_ROUND_UP(comp_bio->bi_iter.bi_size,
+                                            root->sectorsize);
 
                        ret = btrfs_map_bio(root, READ, comp_bio,
                                            mirror_num, 0);
@@ -708,7 +708,8 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
        }
        bio_get(comp_bio);
 
-       ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0);
+       ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio,
+                       BTRFS_WQ_ENDIO_DATA);
        BUG_ON(ret); /* -ENOMEM */
 
        if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
index 44ee5d2e52a41935828ac954ebeefe8ce17568e6..19bc6162fb8e899cc51bd3d777fcbddf91589aa6 100644 (file)
@@ -258,9 +258,8 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
        else
                btrfs_node_key(buf, &disk_key, 0);
 
-       cow = btrfs_alloc_free_block(trans, root, buf->len, 0,
-                                    new_root_objectid, &disk_key, level,
-                                    buf->start, 0);
+       cow = btrfs_alloc_tree_block(trans, root, 0, new_root_objectid,
+                       &disk_key, level, buf->start, 0);
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
@@ -1133,9 +1132,9 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
        } else
                parent_start = 0;
 
-       cow = btrfs_alloc_free_block(trans, root, buf->len, parent_start,
-                                    root->root_key.objectid, &disk_key,
-                                    level, search_start, empty_size);
+       cow = btrfs_alloc_tree_block(trans, root, parent_start,
+                       root->root_key.objectid, &disk_key, level,
+                       search_start, empty_size);
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
@@ -1425,7 +1424,6 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
        struct tree_mod_root *old_root = NULL;
        u64 old_generation = 0;
        u64 logical;
-       u32 blocksize;
 
        eb_root = btrfs_read_lock_root_node(root);
        tm = __tree_mod_log_oldest_root(root->fs_info, eb_root, time_seq);
@@ -1444,8 +1442,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
        if (old_root && tm && tm->op != MOD_LOG_KEY_REMOVE_WHILE_FREEING) {
                btrfs_tree_read_unlock(eb_root);
                free_extent_buffer(eb_root);
-               blocksize = btrfs_level_size(root, old_root->level);
-               old = read_tree_block(root, logical, blocksize, 0);
+               old = read_tree_block(root, logical, 0);
                if (WARN_ON(!old || !extent_buffer_uptodate(old))) {
                        free_extent_buffer(old);
                        btrfs_warn(root->fs_info,
@@ -1506,10 +1503,9 @@ static inline int should_cow_block(struct btrfs_trans_handle *trans,
                                   struct btrfs_root *root,
                                   struct extent_buffer *buf)
 {
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
-       if (unlikely(test_bit(BTRFS_ROOT_DUMMY_ROOT, &root->state)))
+       if (btrfs_test_is_dummy_root(root))
                return 0;
-#endif
+
        /* ensure we can see the force_cow */
        smp_rmb();
 
@@ -1651,7 +1647,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
        WARN_ON(trans->transid != root->fs_info->generation);
 
        parent_nritems = btrfs_header_nritems(parent);
-       blocksize = btrfs_level_size(root, parent_level - 1);
+       blocksize = root->nodesize;
        end_slot = parent_nritems;
 
        if (parent_nritems == 1)
@@ -1685,15 +1681,14 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                        continue;
                }
 
-               cur = btrfs_find_tree_block(root, blocknr, blocksize);
+               cur = btrfs_find_tree_block(root, blocknr);
                if (cur)
                        uptodate = btrfs_buffer_uptodate(cur, gen, 0);
                else
                        uptodate = 0;
                if (!cur || !uptodate) {
                        if (!cur) {
-                               cur = read_tree_block(root, blocknr,
-                                                        blocksize, gen);
+                               cur = read_tree_block(root, blocknr, gen);
                                if (!cur || !extent_buffer_uptodate(cur)) {
                                        free_extent_buffer(cur);
                                        return -EIO;
@@ -1872,7 +1867,6 @@ static noinline struct extent_buffer *read_node_slot(struct btrfs_root *root,
        BUG_ON(level == 0);
 
        eb = read_tree_block(root, btrfs_node_blockptr(parent, slot),
-                            btrfs_level_size(root, level - 1),
                             btrfs_node_ptr_generation(parent, slot));
        if (eb && !extent_buffer_uptodate(eb)) {
                free_extent_buffer(eb);
@@ -2267,8 +2261,8 @@ static void reada_for_search(struct btrfs_root *root,
        node = path->nodes[level];
 
        search = btrfs_node_blockptr(node, slot);
-       blocksize = btrfs_level_size(root, level - 1);
-       eb = btrfs_find_tree_block(root, search, blocksize);
+       blocksize = root->nodesize;
+       eb = btrfs_find_tree_block(root, search);
        if (eb) {
                free_extent_buffer(eb);
                return;
@@ -2298,7 +2292,7 @@ static void reada_for_search(struct btrfs_root *root,
                if ((search <= target && target - search <= 65536) ||
                    (search > target && search - target <= 65536)) {
                        gen = btrfs_node_ptr_generation(node, nr);
-                       readahead_tree_block(root, search, blocksize, gen);
+                       readahead_tree_block(root, search, blocksize);
                        nread += blocksize;
                }
                nscan++;
@@ -2325,12 +2319,12 @@ static noinline void reada_for_balance(struct btrfs_root *root,
 
        nritems = btrfs_header_nritems(parent);
        slot = path->slots[level + 1];
-       blocksize = btrfs_level_size(root, level);
+       blocksize = root->nodesize;
 
        if (slot > 0) {
                block1 = btrfs_node_blockptr(parent, slot - 1);
                gen = btrfs_node_ptr_generation(parent, slot - 1);
-               eb = btrfs_find_tree_block(root, block1, blocksize);
+               eb = btrfs_find_tree_block(root, block1);
                /*
                 * if we get -eagain from btrfs_buffer_uptodate, we
                 * don't want to return eagain here.  That will loop
@@ -2343,16 +2337,16 @@ static noinline void reada_for_balance(struct btrfs_root *root,
        if (slot + 1 < nritems) {
                block2 = btrfs_node_blockptr(parent, slot + 1);
                gen = btrfs_node_ptr_generation(parent, slot + 1);
-               eb = btrfs_find_tree_block(root, block2, blocksize);
+               eb = btrfs_find_tree_block(root, block2);
                if (eb && btrfs_buffer_uptodate(eb, gen, 1) != 0)
                        block2 = 0;
                free_extent_buffer(eb);
        }
 
        if (block1)
-               readahead_tree_block(root, block1, blocksize, 0);
+               readahead_tree_block(root, block1, blocksize);
        if (block2)
-               readahead_tree_block(root, block2, blocksize, 0);
+               readahead_tree_block(root, block2, blocksize);
 }
 
 
@@ -2454,16 +2448,14 @@ read_block_for_search(struct btrfs_trans_handle *trans,
 {
        u64 blocknr;
        u64 gen;
-       u32 blocksize;
        struct extent_buffer *b = *eb_ret;
        struct extent_buffer *tmp;
        int ret;
 
        blocknr = btrfs_node_blockptr(b, slot);
        gen = btrfs_node_ptr_generation(b, slot);
-       blocksize = btrfs_level_size(root, level - 1);
 
-       tmp = btrfs_find_tree_block(root, blocknr, blocksize);
+       tmp = btrfs_find_tree_block(root, blocknr);
        if (tmp) {
                /* first we do an atomic uptodate check */
                if (btrfs_buffer_uptodate(tmp, gen, 1) > 0) {
@@ -2507,7 +2499,7 @@ read_block_for_search(struct btrfs_trans_handle *trans,
        btrfs_release_path(p);
 
        ret = -EAGAIN;
-       tmp = read_tree_block(root, blocknr, blocksize, 0);
+       tmp = read_tree_block(root, blocknr, 0);
        if (tmp) {
                /*
                 * If the read above didn't mark this buffer up to date,
@@ -2792,8 +2784,6 @@ again:
                        if (!should_cow_block(trans, root, b))
                                goto cow_done;
 
-                       btrfs_set_path_blocking(p);
-
                        /*
                         * must have write locks on this node and the
                         * parent
@@ -2807,6 +2797,7 @@ again:
                                goto again;
                        }
 
+                       btrfs_set_path_blocking(p);
                        err = btrfs_cow_block(trans, root, b,
                                              p->nodes[level + 1],
                                              p->slots[level + 1], &b);
@@ -3362,9 +3353,8 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
        else
                btrfs_node_key(lower, &lower_key, 0);
 
-       c = btrfs_alloc_free_block(trans, root, root->nodesize, 0,
-                                  root->root_key.objectid, &lower_key,
-                                  level, root->node->start, 0);
+       c = btrfs_alloc_tree_block(trans, root, 0, root->root_key.objectid,
+                                  &lower_key, level, root->node->start, 0);
        if (IS_ERR(c))
                return PTR_ERR(c);
 
@@ -3502,9 +3492,8 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
        mid = (c_nritems + 1) / 2;
        btrfs_node_key(c, &disk_key, mid);
 
-       split = btrfs_alloc_free_block(trans, root, root->nodesize, 0,
-                                       root->root_key.objectid,
-                                       &disk_key, level, c->start, 0);
+       split = btrfs_alloc_tree_block(trans, root, 0, root->root_key.objectid,
+                       &disk_key, level, c->start, 0);
        if (IS_ERR(split))
                return PTR_ERR(split);
 
@@ -4282,13 +4271,12 @@ again:
        else
                btrfs_item_key(l, &disk_key, mid);
 
-       right = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
-                                       root->root_key.objectid,
-                                       &disk_key, 0, l->start, 0);
+       right = btrfs_alloc_tree_block(trans, root, 0, root->root_key.objectid,
+                       &disk_key, 0, l->start, 0);
        if (IS_ERR(right))
                return PTR_ERR(right);
 
-       root_add_used(root, root->leafsize);
+       root_add_used(root, root->nodesize);
 
        memset_extent_buffer(right, 0, 0, sizeof(struct btrfs_header));
        btrfs_set_header_bytenr(right, right->start);
@@ -4626,8 +4614,7 @@ void btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path,
                                ptr = btrfs_item_ptr_offset(leaf, slot);
                                memmove_extent_buffer(leaf, ptr,
                                      (unsigned long)fi,
-                                     offsetof(struct btrfs_file_extent_item,
-                                                disk_bytenr));
+                                     BTRFS_FILE_EXTENT_INLINE_DATA_START);
                        }
                }
 
@@ -4738,6 +4725,12 @@ void setup_items_for_insert(struct btrfs_root *root, struct btrfs_path *path,
        int slot;
        struct btrfs_map_token token;
 
+       if (path->slots[0] == 0) {
+               btrfs_cpu_key_to_disk(&disk_key, cpu_key);
+               fixup_low_keys(root, path, &disk_key, 1);
+       }
+       btrfs_unlock_up_safe(path, 1);
+
        btrfs_init_map_token(&token);
 
        leaf = path->nodes[0];
@@ -4798,12 +4791,6 @@ void setup_items_for_insert(struct btrfs_root *root, struct btrfs_path *path,
        }
 
        btrfs_set_header_nritems(leaf, nritems + nr);
-
-       if (slot == 0) {
-               btrfs_cpu_key_to_disk(&disk_key, cpu_key);
-               fixup_low_keys(root, path, &disk_key, 1);
-       }
-       btrfs_unlock_up_safe(path, 1);
        btrfs_mark_buffer_dirty(leaf);
 
        if (btrfs_leaf_free_space(root, leaf) < 0) {
@@ -5145,8 +5132,9 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
        u32 nritems;
        int level;
        int ret = 1;
+       int keep_locks = path->keep_locks;
 
-       WARN_ON(!path->keep_locks);
+       path->keep_locks = 1;
 again:
        cur = btrfs_read_lock_root_node(root);
        level = btrfs_header_level(cur);
@@ -5210,7 +5198,6 @@ find_next_key:
                path->slots[level] = slot;
                if (level == path->lowest_level) {
                        ret = 0;
-                       unlock_up(path, level, 1, 0, NULL);
                        goto out;
                }
                btrfs_set_path_blocking(path);
@@ -5225,9 +5212,12 @@ find_next_key:
                btrfs_clear_path_blocking(path, NULL, 0);
        }
 out:
-       if (ret == 0)
+       path->keep_locks = keep_locks;
+       if (ret == 0) {
+               btrfs_unlock_up_safe(path, path->lowest_level + 1);
+               btrfs_set_path_blocking(path);
                memcpy(min_key, &found_key, sizeof(found_key));
-       btrfs_set_path_blocking(path);
+       }
        return ret;
 }
 
@@ -5375,7 +5365,7 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
                goto out;
        }
 
-       tmp_buf = kmalloc(left_root->leafsize, GFP_NOFS);
+       tmp_buf = kmalloc(left_root->nodesize, GFP_NOFS);
        if (!tmp_buf) {
                ret = -ENOMEM;
                goto out;
@@ -5520,18 +5510,18 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
                                        goto out;
                                advance_right = ADVANCE;
                        } else {
-                               enum btrfs_compare_tree_result cmp;
+                               enum btrfs_compare_tree_result result;
 
                                WARN_ON(!extent_buffer_uptodate(left_path->nodes[0]));
                                ret = tree_compare_item(left_root, left_path,
                                                right_path, tmp_buf);
                                if (ret)
-                                       cmp = BTRFS_COMPARE_TREE_CHANGED;
+                                       result = BTRFS_COMPARE_TREE_CHANGED;
                                else
-                                       cmp = BTRFS_COMPARE_TREE_SAME;
+                                       result = BTRFS_COMPARE_TREE_SAME;
                                ret = changed_cb(left_root, right_root,
                                                 left_path, right_path,
-                                                &left_key, cmp, ctx);
+                                                &left_key, result, ctx);
                                if (ret < 0)
                                        goto out;
                                advance_left = ADVANCE;
index 8e29b614fe93d9b8cc22c9eb5d54377d617bb9e7..d557264ee974deab61c33736d93af24ffb2a93d2 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/pagemap.h>
 #include <linux/btrfs.h>
 #include <linux/workqueue.h>
+#include <linux/security.h>
 #include "extent_io.h"
 #include "extent_map.h"
 #include "async-thread.h"
@@ -62,13 +63,6 @@ struct btrfs_ordered_sum;
 
 #define BTRFS_COMPAT_EXTENT_TREE_V0
 
-/*
- * files bigger than this get some pre-flushing when they are added
- * to the ordered operations list.  That way we limit the total
- * work done by the commit
- */
-#define BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT (8 * 1024 * 1024)
-
 /* holds pointers to all of the tree roots */
 #define BTRFS_ROOT_TREE_OBJECTID 1ULL
 
@@ -391,10 +385,12 @@ struct btrfs_header {
                                      sizeof(struct btrfs_header)) / \
                                     sizeof(struct btrfs_key_ptr))
 #define __BTRFS_LEAF_DATA_SIZE(bs) ((bs) - sizeof(struct btrfs_header))
-#define BTRFS_LEAF_DATA_SIZE(r) (__BTRFS_LEAF_DATA_SIZE(r->leafsize))
+#define BTRFS_LEAF_DATA_SIZE(r) (__BTRFS_LEAF_DATA_SIZE(r->nodesize))
+#define BTRFS_FILE_EXTENT_INLINE_DATA_START            \
+               (offsetof(struct btrfs_file_extent_item, disk_bytenr))
 #define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \
                                        sizeof(struct btrfs_item) - \
-                                       sizeof(struct btrfs_file_extent_item))
+                                       BTRFS_FILE_EXTENT_INLINE_DATA_START)
 #define BTRFS_MAX_XATTR_SIZE(r)        (BTRFS_LEAF_DATA_SIZE(r) - \
                                 sizeof(struct btrfs_item) -\
                                 sizeof(struct btrfs_dir_item))
@@ -474,7 +470,7 @@ struct btrfs_super_block {
        __le64 num_devices;
        __le32 sectorsize;
        __le32 nodesize;
-       __le32 leafsize;
+       __le32 __unused_leafsize;
        __le32 stripesize;
        __le32 sys_chunk_array_size;
        __le64 chunk_root_generation;
@@ -903,6 +899,8 @@ struct btrfs_file_extent_item {
        /*
         * disk space consumed by the extent, checksum blocks are included
         * in these numbers
+        *
+        * At this offset in the structure, the inline extent data start.
         */
        __le64 disk_bytenr;
        __le64 disk_num_bytes;
@@ -1305,8 +1303,8 @@ struct btrfs_block_group_cache {
         */
        struct list_head cluster_list;
 
-       /* For delayed block group creation */
-       struct list_head new_bg_list;
+       /* For delayed block group creation or deletion of empty block groups */
+       struct list_head bg_list;
 };
 
 /* delayed seq elem */
@@ -1545,6 +1543,7 @@ struct btrfs_fs_info {
        struct btrfs_workqueue *endio_workers;
        struct btrfs_workqueue *endio_meta_workers;
        struct btrfs_workqueue *endio_raid56_workers;
+       struct btrfs_workqueue *endio_repair_workers;
        struct btrfs_workqueue *rmw_workers;
        struct btrfs_workqueue *endio_meta_write_workers;
        struct btrfs_workqueue *endio_write_workers;
@@ -1574,6 +1573,7 @@ struct btrfs_fs_info {
        int do_barriers;
        int closing;
        int log_root_recovering;
+       int open;
 
        u64 total_pinned;
 
@@ -1723,6 +1723,12 @@ struct btrfs_fs_info {
 
        /* Used to reclaim the metadata space in the background. */
        struct work_struct async_reclaim_work;
+
+       spinlock_t unused_bgs_lock;
+       struct list_head unused_bgs;
+
+       /* For btrfs to record security options */
+       struct security_mnt_opts security_opts;
 };
 
 struct btrfs_subvolume_writers {
@@ -1776,12 +1782,12 @@ struct btrfs_root {
 
        /* free ino cache stuff */
        struct btrfs_free_space_ctl *free_ino_ctl;
-       enum btrfs_caching_type cached;
-       spinlock_t cache_lock;
-       wait_queue_head_t cache_wait;
+       enum btrfs_caching_type ino_cache_state;
+       spinlock_t ino_cache_lock;
+       wait_queue_head_t ino_cache_wait;
        struct btrfs_free_space_ctl *free_ino_pinned;
-       u64 cache_progress;
-       struct inode *cache_inode;
+       u64 ino_cache_progress;
+       struct inode *ino_cache_inode;
 
        struct mutex log_mutex;
        wait_queue_head_t log_writer_wait;
@@ -1806,18 +1812,14 @@ struct btrfs_root {
        /* node allocations are done in nodesize units */
        u32 nodesize;
 
-       /* leaf allocations are done in leafsize units */
-       u32 leafsize;
-
        u32 stripesize;
 
        u32 type;
 
        u64 highest_objectid;
 
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+       /* only used with CONFIG_BTRFS_FS_RUN_SANITY_TESTS is enabled */
        u64 alloc_bytenr;
-#endif
 
        u64 defrag_trans_start;
        struct btrfs_key defrag_progress;
@@ -2094,6 +2096,7 @@ struct btrfs_ioctl_defrag_range_args {
 #define        BTRFS_MOUNT_CHANGE_INODE_CACHE  (1 << 24)
 
 #define BTRFS_DEFAULT_COMMIT_INTERVAL  (30)
+#define BTRFS_DEFAULT_MAX_INLINE       (8192)
 
 #define btrfs_clear_opt(o, opt)                ((o) &= ~BTRFS_MOUNT_##opt)
 #define btrfs_set_opt(o, opt)          ((o) |= BTRFS_MOUNT_##opt)
@@ -2995,8 +2998,6 @@ BTRFS_SETGET_STACK_FUNCS(super_sectorsize, struct btrfs_super_block,
                         sectorsize, 32);
 BTRFS_SETGET_STACK_FUNCS(super_nodesize, struct btrfs_super_block,
                         nodesize, 32);
-BTRFS_SETGET_STACK_FUNCS(super_leafsize, struct btrfs_super_block,
-                        leafsize, 32);
 BTRFS_SETGET_STACK_FUNCS(super_stripesize, struct btrfs_super_block,
                         stripesize, 32);
 BTRFS_SETGET_STACK_FUNCS(super_root_dir, struct btrfs_super_block,
@@ -3049,14 +3050,12 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression,
 static inline unsigned long
 btrfs_file_extent_inline_start(struct btrfs_file_extent_item *e)
 {
-       unsigned long offset = (unsigned long)e;
-       offset += offsetof(struct btrfs_file_extent_item, disk_bytenr);
-       return offset;
+       return (unsigned long)e + BTRFS_FILE_EXTENT_INLINE_DATA_START;
 }
 
 static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize)
 {
-       return offsetof(struct btrfs_file_extent_item, disk_bytenr) + datasize;
+       return BTRFS_FILE_EXTENT_INLINE_DATA_START + datasize;
 }
 
 BTRFS_SETGET_FUNCS(file_extent_disk_bytenr, struct btrfs_file_extent_item,
@@ -3086,9 +3085,7 @@ BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
 static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb,
                                                    struct btrfs_item *e)
 {
-       unsigned long offset;
-       offset = offsetof(struct btrfs_file_extent_item, disk_bytenr);
-       return btrfs_item_size(eb, e) - offset;
+       return btrfs_item_size(eb, e) - BTRFS_FILE_EXTENT_INLINE_DATA_START;
 }
 
 /* this returns the number of file bytes represented by the inline item.
@@ -3232,13 +3229,6 @@ static inline struct btrfs_fs_info *btrfs_sb(struct super_block *sb)
        return sb->s_fs_info;
 }
 
-static inline u32 btrfs_level_size(struct btrfs_root *root, int level)
-{
-       if (level == 0)
-               return root->leafsize;
-       return root->nodesize;
-}
-
 /* helper function to cast into the data area of the leaf. */
 #define btrfs_item_ptr(leaf, slot, type) \
        ((type *)(btrfs_leaf_data(leaf) + \
@@ -3263,7 +3253,7 @@ static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping)
 static inline u64 btrfs_calc_trans_metadata_size(struct btrfs_root *root,
                                                 unsigned num_items)
 {
-       return (root->leafsize + root->nodesize * (BTRFS_MAX_LEVEL - 1)) *
+       return (root->nodesize + root->nodesize * (BTRFS_MAX_LEVEL - 1)) *
                2 * num_items;
 }
 
@@ -3274,8 +3264,7 @@ static inline u64 btrfs_calc_trans_metadata_size(struct btrfs_root *root,
 static inline u64 btrfs_calc_trunc_metadata_size(struct btrfs_root *root,
                                                 unsigned num_items)
 {
-       return (root->leafsize + root->nodesize * (BTRFS_MAX_LEVEL - 1)) *
-               num_items;
+       return root->nodesize * BTRFS_MAX_LEVEL * num_items;
 }
 
 int btrfs_should_throttle_delayed_refs(struct btrfs_trans_handle *trans,
@@ -3305,9 +3294,9 @@ struct btrfs_block_group_cache *btrfs_lookup_block_group(
                                                 u64 bytenr);
 void btrfs_put_block_group(struct btrfs_block_group_cache *cache);
 int get_block_group_index(struct btrfs_block_group_cache *cache);
-struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
-                                       struct btrfs_root *root, u32 blocksize,
-                                       u64 parent, u64 root_objectid,
+struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
+                                       struct btrfs_root *root, u64 parent,
+                                       u64 root_objectid,
                                        struct btrfs_disk_key *key, int level,
                                        u64 hint, u64 empty_size);
 void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
@@ -3363,6 +3352,7 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
                           u64 size);
 int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root, u64 group_start);
+void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info);
 void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans,
                                       struct btrfs_root *root);
 u64 btrfs_get_alloc_profile(struct btrfs_root *root, int data);
@@ -3604,6 +3594,7 @@ static inline void free_fs_info(struct btrfs_fs_info *fs_info)
        kfree(fs_info->uuid_root);
        kfree(fs_info->super_copy);
        kfree(fs_info->super_for_commit);
+       security_free_mnt_opts(&fs_info->security_opts);
        kfree(fs_info);
 }
 
@@ -3739,8 +3730,7 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
 int btrfs_lookup_bio_sums(struct btrfs_root *root, struct inode *inode,
                          struct bio *bio, u32 *dst);
 int btrfs_lookup_bio_sums_dio(struct btrfs_root *root, struct inode *inode,
-                             struct btrfs_dio_private *dip, struct bio *bio,
-                             u64 logical_offset);
+                             struct bio *bio, u64 logical_offset);
 int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root,
                             u64 objectid, u64 pos,
@@ -4141,8 +4131,15 @@ static inline int btrfs_defrag_cancelled(struct btrfs_fs_info *fs_info)
 /* Sanity test specific functions */
 #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
 void btrfs_test_destroy_inode(struct inode *inode);
-int btrfs_verify_qgroup_counts(struct btrfs_fs_info *fs_info, u64 qgroupid,
-                              u64 rfer, u64 excl);
 #endif
 
+static inline int btrfs_test_is_dummy_root(struct btrfs_root *root)
+{
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+       if (unlikely(test_bit(BTRFS_ROOT_DUMMY_ROOT, &root->state)))
+               return 1;
+#endif
+       return 0;
+}
+
 #endif
index a2e90f855d7d1e3cad650fc1d9691fd2de01cf65..054577bddaf27869d9a524a73d4df5a76072e4e1 100644 (file)
@@ -1042,7 +1042,7 @@ static int __btrfs_update_delayed_inode(struct btrfs_trans_handle *trans,
        int ret;
 
        key.objectid = node->inode_id;
-       btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
+       key.type = BTRFS_INODE_ITEM_KEY;
        key.offset = 0;
 
        if (test_bit(BTRFS_DELAYED_NODE_DEL_IREF, &node->flags))
@@ -1099,7 +1099,7 @@ err_out:
 search:
        btrfs_release_path(path);
 
-       btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY);
+       key.type = BTRFS_INODE_EXTREF_KEY;
        key.offset = -1;
        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
        if (ret < 0)
@@ -1473,7 +1473,7 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
        }
 
        delayed_item->key.objectid = btrfs_ino(dir);
-       btrfs_set_key_type(&delayed_item->key, BTRFS_DIR_INDEX_KEY);
+       delayed_item->key.type = BTRFS_DIR_INDEX_KEY;
        delayed_item->key.offset = index;
 
        dir_item = (struct btrfs_dir_item *)delayed_item->data;
@@ -1542,7 +1542,7 @@ int btrfs_delete_delayed_dir_index(struct btrfs_trans_handle *trans,
                return PTR_ERR(node);
 
        item_key.objectid = btrfs_ino(dir);
-       btrfs_set_key_type(&item_key, BTRFS_DIR_INDEX_KEY);
+       item_key.type = BTRFS_DIR_INDEX_KEY;
        item_key.offset = index;
 
        ret = btrfs_delete_delayed_insertion_item(root, node, &item_key);
index eea26e1b2fda1d21230dd5f1e01e25f8fb23a3e8..6f662b34ba0e8392c58d7d481092f9e461631af4 100644 (file)
@@ -168,8 +168,12 @@ no_valid_dev_replace_entry_found:
                                        dev_replace->srcdev->total_bytes;
                                dev_replace->tgtdev->disk_total_bytes =
                                        dev_replace->srcdev->disk_total_bytes;
+                               dev_replace->tgtdev->commit_total_bytes =
+                                       dev_replace->srcdev->commit_total_bytes;
                                dev_replace->tgtdev->bytes_used =
                                        dev_replace->srcdev->bytes_used;
+                               dev_replace->tgtdev->commit_bytes_used =
+                                       dev_replace->srcdev->commit_bytes_used;
                        }
                        dev_replace->tgtdev->is_tgtdev_for_dev_replace = 1;
                        btrfs_init_dev_replace_tgtdev_for_resume(fs_info,
@@ -329,30 +333,34 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
            args->start.tgtdev_name[0] == '\0')
                return -EINVAL;
 
-       mutex_lock(&fs_info->volume_mutex);
-       ret = btrfs_init_dev_replace_tgtdev(root, args->start.tgtdev_name,
-                                           &tgt_device);
-       if (ret) {
-               btrfs_err(fs_info, "target device %s is invalid!",
-                      args->start.tgtdev_name);
-               mutex_unlock(&fs_info->volume_mutex);
-               return -EINVAL;
+       /*
+        * Here we commit the transaction to make sure commit_total_bytes
+        * of all the devices are updated.
+        */
+       trans = btrfs_attach_transaction(root);
+       if (!IS_ERR(trans)) {
+               ret = btrfs_commit_transaction(trans, root);
+               if (ret)
+                       return ret;
+       } else if (PTR_ERR(trans) != -ENOENT) {
+               return PTR_ERR(trans);
        }
 
+       /* the disk copy procedure reuses the scrub code */
+       mutex_lock(&fs_info->volume_mutex);
        ret = btrfs_dev_replace_find_srcdev(root, args->start.srcdevid,
                                            args->start.srcdev_name,
                                            &src_device);
-       mutex_unlock(&fs_info->volume_mutex);
        if (ret) {
-               ret = -EINVAL;
-               goto leave_no_lock;
+               mutex_unlock(&fs_info->volume_mutex);
+               return ret;
        }
 
-       if (tgt_device->total_bytes < src_device->total_bytes) {
-               btrfs_err(fs_info, "target device is smaller than source device!");
-               ret = -EINVAL;
-               goto leave_no_lock;
-       }
+       ret = btrfs_init_dev_replace_tgtdev(root, args->start.tgtdev_name,
+                                           src_device, &tgt_device);
+       mutex_unlock(&fs_info->volume_mutex);
+       if (ret)
+               return ret;
 
        btrfs_dev_replace_lock(dev_replace);
        switch (dev_replace->replace_state) {
@@ -380,10 +388,6 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
                      src_device->devid,
                      rcu_str_deref(tgt_device->name));
 
-       tgt_device->total_bytes = src_device->total_bytes;
-       tgt_device->disk_total_bytes = src_device->disk_total_bytes;
-       tgt_device->bytes_used = src_device->bytes_used;
-
        /*
         * from now on, the writes to the srcdev are all duplicated to
         * go to the tgtdev as well (refer to btrfs_map_block()).
@@ -414,7 +418,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
 
        /* the disk copy procedure reuses the scrub code */
        ret = btrfs_scrub_dev(fs_info, src_device->devid, 0,
-                             src_device->total_bytes,
+                             btrfs_device_get_total_bytes(src_device),
                              &dev_replace->scrub_progress, 0, 1);
 
        ret = btrfs_dev_replace_finishing(root->fs_info, ret);
@@ -426,9 +430,7 @@ leave:
        dev_replace->srcdev = NULL;
        dev_replace->tgtdev = NULL;
        btrfs_dev_replace_unlock(dev_replace);
-leave_no_lock:
-       if (tgt_device)
-               btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
+       btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
        return ret;
 }
 
@@ -507,9 +509,10 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        ret = btrfs_commit_transaction(trans, root);
        WARN_ON(ret);
 
+       mutex_lock(&uuid_mutex);
        /* keep away write_all_supers() during the finishing procedure */
-       mutex_lock(&root->fs_info->chunk_mutex);
        mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
+       mutex_lock(&root->fs_info->chunk_mutex);
        btrfs_dev_replace_lock(dev_replace);
        dev_replace->replace_state =
                scrub_ret ? BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED
@@ -532,8 +535,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                              src_device->devid,
                              rcu_str_deref(tgt_device->name), scrub_ret);
                btrfs_dev_replace_unlock(dev_replace);
-               mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
                mutex_unlock(&root->fs_info->chunk_mutex);
+               mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
+               mutex_unlock(&uuid_mutex);
                if (tgt_device)
                        btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
                mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
@@ -542,7 +546,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        }
 
        printk_in_rcu(KERN_INFO
-                     "BTRFS: dev_replace from %s (devid %llu) to %s) finished\n",
+                     "BTRFS: dev_replace from %s (devid %llu) to %s finished\n",
                      src_device->missing ? "<missing disk>" :
                        rcu_str_deref(src_device->name),
                      src_device->devid,
@@ -550,23 +554,29 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        tgt_device->is_tgtdev_for_dev_replace = 0;
        tgt_device->devid = src_device->devid;
        src_device->devid = BTRFS_DEV_REPLACE_DEVID;
-       tgt_device->bytes_used = src_device->bytes_used;
        memcpy(uuid_tmp, tgt_device->uuid, sizeof(uuid_tmp));
        memcpy(tgt_device->uuid, src_device->uuid, sizeof(tgt_device->uuid));
        memcpy(src_device->uuid, uuid_tmp, sizeof(src_device->uuid));
-       tgt_device->total_bytes = src_device->total_bytes;
-       tgt_device->disk_total_bytes = src_device->disk_total_bytes;
-       tgt_device->bytes_used = src_device->bytes_used;
+       btrfs_device_set_total_bytes(tgt_device, src_device->total_bytes);
+       btrfs_device_set_disk_total_bytes(tgt_device,
+                                         src_device->disk_total_bytes);
+       btrfs_device_set_bytes_used(tgt_device, src_device->bytes_used);
+       ASSERT(list_empty(&src_device->resized_list));
+       tgt_device->commit_total_bytes = src_device->commit_total_bytes;
+       tgt_device->commit_bytes_used = src_device->bytes_used;
        if (fs_info->sb->s_bdev == src_device->bdev)
                fs_info->sb->s_bdev = tgt_device->bdev;
        if (fs_info->fs_devices->latest_bdev == src_device->bdev)
                fs_info->fs_devices->latest_bdev = tgt_device->bdev;
        list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list);
+       fs_info->fs_devices->rw_devices++;
 
        /* replace the sysfs entry */
        btrfs_kobj_rm_device(fs_info, src_device);
        btrfs_kobj_add_device(fs_info, tgt_device);
 
+       btrfs_dev_replace_unlock(dev_replace);
+
        btrfs_rm_dev_replace_blocked(fs_info);
 
        btrfs_rm_dev_replace_srcdev(fs_info, src_device);
@@ -580,9 +590,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
         * superblock is scratched out so that it is no longer marked to
         * belong to this filesystem.
         */
-       btrfs_dev_replace_unlock(dev_replace);
-       mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
        mutex_unlock(&root->fs_info->chunk_mutex);
+       mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
+       mutex_unlock(&uuid_mutex);
 
        /* write back the superblocks */
        trans = btrfs_start_transaction(root, 0);
@@ -643,6 +653,7 @@ void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info,
                              struct btrfs_ioctl_dev_replace_args *args)
 {
        struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
+       struct btrfs_device *srcdev;
 
        btrfs_dev_replace_lock(dev_replace);
        /* even if !dev_replace_is_valid, the values are good enough for
@@ -665,8 +676,9 @@ void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info,
                break;
        case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
        case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
+               srcdev = dev_replace->srcdev;
                args->status.progress_1000 = div64_u64(dev_replace->cursor_left,
-                       div64_u64(dev_replace->srcdev->total_bytes, 1000));
+                       div64_u64(btrfs_device_get_total_bytes(srcdev), 1000));
                break;
        }
        btrfs_dev_replace_unlock(dev_replace);
@@ -825,7 +837,7 @@ static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info)
 
        ret = btrfs_scrub_dev(fs_info, dev_replace->srcdev->devid,
                              dev_replace->committed_cursor_left,
-                             dev_replace->srcdev->total_bytes,
+                             btrfs_device_get_total_bytes(dev_replace->srcdev),
                              &dev_replace->scrub_progress, 0, 1);
        ret = btrfs_dev_replace_finishing(fs_info, ret);
        WARN_ON(ret);
index a0691df5dceaa9dfdb3100732bf82ee81793f32c..fc8df866e91988bdbcbee0d9e50f770ebd0de905 100644 (file)
@@ -86,7 +86,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
        BUG_ON(name_len + data_len > BTRFS_MAX_XATTR_SIZE(root));
 
        key.objectid = objectid;
-       btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
+       key.type = BTRFS_XATTR_ITEM_KEY;
        key.offset = btrfs_name_hash(name, name_len);
 
        data_size = sizeof(*dir_item) + name_len + data_len;
@@ -137,7 +137,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
        u32 data_size;
 
        key.objectid = btrfs_ino(dir);
-       btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
+       key.type = BTRFS_DIR_ITEM_KEY;
        key.offset = btrfs_name_hash(name, name_len);
 
        path = btrfs_alloc_path();
@@ -204,7 +204,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
        int cow = mod != 0;
 
        key.objectid = dir;
-       btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
+       key.type = BTRFS_DIR_ITEM_KEY;
 
        key.offset = btrfs_name_hash(name, name_len);
 
@@ -234,7 +234,7 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
                return -ENOMEM;
 
        key.objectid = dir;
-       btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
+       key.type = BTRFS_DIR_ITEM_KEY;
        key.offset = btrfs_name_hash(name, name_len);
 
        ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
@@ -297,7 +297,7 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
        int cow = mod != 0;
 
        key.objectid = dir;
-       btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
+       key.type = BTRFS_DIR_INDEX_KEY;
        key.offset = objectid;
 
        ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
@@ -367,7 +367,7 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
        int cow = mod != 0;
 
        key.objectid = dir;
-       btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
+       key.type = BTRFS_XATTR_ITEM_KEY;
        key.offset = btrfs_name_hash(name, name_len);
        ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
        if (ret < 0)
index a1d36e62179c528041f292675e7452102863de45..fa45e3cae40db660c6170811cfc54c66d81556c1 100644 (file)
@@ -72,21 +72,41 @@ static int btrfs_cleanup_transaction(struct btrfs_root *root);
 static void btrfs_error_commit_super(struct btrfs_root *root);
 
 /*
- * end_io_wq structs are used to do processing in task context when an IO is
- * complete.  This is used during reads to verify checksums, and it is used
+ * btrfs_end_io_wq structs are used to do processing in task context when an IO
+ * is complete.  This is used during reads to verify checksums, and it is used
  * by writes to insert metadata for new file extents after IO is complete.
  */
-struct end_io_wq {
+struct btrfs_end_io_wq {
        struct bio *bio;
        bio_end_io_t *end_io;
        void *private;
        struct btrfs_fs_info *info;
        int error;
-       int metadata;
+       enum btrfs_wq_endio_type metadata;
        struct list_head list;
        struct btrfs_work work;
 };
 
+static struct kmem_cache *btrfs_end_io_wq_cache;
+
+int __init btrfs_end_io_wq_init(void)
+{
+       btrfs_end_io_wq_cache = kmem_cache_create("btrfs_end_io_wq",
+                                       sizeof(struct btrfs_end_io_wq),
+                                       0,
+                                       SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD,
+                                       NULL);
+       if (!btrfs_end_io_wq_cache)
+               return -ENOMEM;
+       return 0;
+}
+
+void btrfs_end_io_wq_exit(void)
+{
+       if (btrfs_end_io_wq_cache)
+               kmem_cache_destroy(btrfs_end_io_wq_cache);
+}
+
 /*
  * async submit bios are used to offload expensive checksumming
  * onto the worker threads.  They checksum file and metadata bios
@@ -327,8 +347,7 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
 {
        struct extent_state *cached_state = NULL;
        int ret;
-       bool need_lock = (current->journal_info ==
-                         (void *)BTRFS_SEND_TRANS_STUB);
+       bool need_lock = (current->journal_info == BTRFS_SEND_TRANS_STUB);
 
        if (!parent_transid || btrfs_header_generation(eb) == parent_transid)
                return 0;
@@ -348,9 +367,9 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
                ret = 0;
                goto out;
        }
-       printk_ratelimited("parent transid verify failed on %llu wanted %llu "
-                      "found %llu\n",
-                      eb->start, parent_transid, btrfs_header_generation(eb));
+       printk_ratelimited(KERN_INFO "BTRFS (device %s): parent transid verify failed on %llu wanted %llu found %llu\n",
+                       eb->fs_info->sb->s_id, eb->start,
+                       parent_transid, btrfs_header_generation(eb));
        ret = 1;
 
        /*
@@ -607,22 +626,22 @@ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
                goto err;
 
        eb->read_mirror = mirror;
-       if (test_bit(EXTENT_BUFFER_IOERR, &eb->bflags)) {
+       if (test_bit(EXTENT_BUFFER_READ_ERR, &eb->bflags)) {
                ret = -EIO;
                goto err;
        }
 
        found_start = btrfs_header_bytenr(eb);
        if (found_start != eb->start) {
-               printk_ratelimited(KERN_INFO "BTRFS: bad tree block start "
+               printk_ratelimited(KERN_INFO "BTRFS (device %s): bad tree block start "
                               "%llu %llu\n",
-                              found_start, eb->start);
+                              eb->fs_info->sb->s_id, found_start, eb->start);
                ret = -EIO;
                goto err;
        }
        if (check_tree_block_fsid(root, eb)) {
-               printk_ratelimited(KERN_INFO "BTRFS: bad fsid on block %llu\n",
-                              eb->start);
+               printk_ratelimited(KERN_INFO "BTRFS (device %s): bad fsid on block %llu\n",
+                              eb->fs_info->sb->s_id, eb->start);
                ret = -EIO;
                goto err;
        }
@@ -680,7 +699,7 @@ static int btree_io_failed_hook(struct page *page, int failed_mirror)
        struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
 
        eb = (struct extent_buffer *)page->private;
-       set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
+       set_bit(EXTENT_BUFFER_READ_ERR, &eb->bflags);
        eb->read_mirror = failed_mirror;
        atomic_dec(&eb->io_pages);
        if (test_and_clear_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags))
@@ -690,7 +709,7 @@ static int btree_io_failed_hook(struct page *page, int failed_mirror)
 
 static void end_workqueue_bio(struct bio *bio, int err)
 {
-       struct end_io_wq *end_io_wq = bio->bi_private;
+       struct btrfs_end_io_wq *end_io_wq = bio->bi_private;
        struct btrfs_fs_info *fs_info;
        struct btrfs_workqueue *wq;
        btrfs_work_func_t func;
@@ -713,7 +732,11 @@ static void end_workqueue_bio(struct bio *bio, int err)
                        func = btrfs_endio_write_helper;
                }
        } else {
-               if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56) {
+               if (unlikely(end_io_wq->metadata ==
+                            BTRFS_WQ_ENDIO_DIO_REPAIR)) {
+                       wq = fs_info->endio_repair_workers;
+                       func = btrfs_endio_repair_helper;
+               } else if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56) {
                        wq = fs_info->endio_raid56_workers;
                        func = btrfs_endio_raid56_helper;
                } else if (end_io_wq->metadata) {
@@ -729,19 +752,12 @@ static void end_workqueue_bio(struct bio *bio, int err)
        btrfs_queue_work(wq, &end_io_wq->work);
 }
 
-/*
- * For the metadata arg you want
- *
- * 0 - if data
- * 1 - if normal metadta
- * 2 - if writing to the free space cache area
- * 3 - raid parity work
- */
 int btrfs_bio_wq_end_io(struct btrfs_fs_info *info, struct bio *bio,
-                       int metadata)
+                       enum btrfs_wq_endio_type metadata)
 {
-       struct end_io_wq *end_io_wq;
-       end_io_wq = kmalloc(sizeof(*end_io_wq), GFP_NOFS);
+       struct btrfs_end_io_wq *end_io_wq;
+
+       end_io_wq = kmem_cache_alloc(btrfs_end_io_wq_cache, GFP_NOFS);
        if (!end_io_wq)
                return -ENOMEM;
 
@@ -925,7 +941,7 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
                 * can happen in the async kernel threads
                 */
                ret = btrfs_bio_wq_end_io(BTRFS_I(inode)->root->fs_info,
-                                         bio, 1);
+                                         bio, BTRFS_WQ_ENDIO_METADATA);
                if (ret)
                        goto out_w_error;
                ret = btrfs_map_bio(BTRFS_I(inode)->root, rw, bio,
@@ -1057,20 +1073,17 @@ static const struct address_space_operations btree_aops = {
        .set_page_dirty = btree_set_page_dirty,
 };
 
-int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
-                        u64 parent_transid)
+void readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize)
 {
        struct extent_buffer *buf = NULL;
        struct inode *btree_inode = root->fs_info->btree_inode;
-       int ret = 0;
 
        buf = btrfs_find_create_tree_block(root, bytenr, blocksize);
        if (!buf)
-               return 0;
+               return;
        read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree,
                                 buf, 0, WAIT_NONE, btree_get_extent, 0);
        free_extent_buffer(buf);
-       return ret;
 }
 
 int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, u32 blocksize,
@@ -1106,7 +1119,7 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, u32 blocksize,
 }
 
 struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
-                                           u64 bytenr, u32 blocksize)
+                                           u64 bytenr)
 {
        return find_extent_buffer(root->fs_info, bytenr);
 }
@@ -1114,11 +1127,9 @@ struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
 struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
                                                 u64 bytenr, u32 blocksize)
 {
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
-       if (unlikely(test_bit(BTRFS_ROOT_DUMMY_ROOT, &root->state)))
+       if (btrfs_test_is_dummy_root(root))
                return alloc_test_extent_buffer(root->fs_info, bytenr,
                                                blocksize);
-#endif
        return alloc_extent_buffer(root->fs_info, bytenr, blocksize);
 }
 
@@ -1136,12 +1147,12 @@ int btrfs_wait_tree_block_writeback(struct extent_buffer *buf)
 }
 
 struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
-                                     u32 blocksize, u64 parent_transid)
+                                     u64 parent_transid)
 {
        struct extent_buffer *buf = NULL;
        int ret;
 
-       buf = btrfs_find_create_tree_block(root, bytenr, blocksize);
+       buf = btrfs_find_create_tree_block(root, bytenr, root->nodesize);
        if (!buf)
                return NULL;
 
@@ -1183,7 +1194,7 @@ static struct btrfs_subvolume_writers *btrfs_alloc_subvolume_writers(void)
        if (!writers)
                return ERR_PTR(-ENOMEM);
 
-       ret = percpu_counter_init(&writers->counter, 0);
+       ret = percpu_counter_init(&writers->counter, 0, GFP_KERNEL);
        if (ret < 0) {
                kfree(writers);
                return ERR_PTR(ret);
@@ -1200,16 +1211,14 @@ btrfs_free_subvolume_writers(struct btrfs_subvolume_writers *writers)
        kfree(writers);
 }
 
-static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
-                        u32 stripesize, struct btrfs_root *root,
-                        struct btrfs_fs_info *fs_info,
+static void __setup_root(u32 nodesize, u32 sectorsize, u32 stripesize,
+                        struct btrfs_root *root, struct btrfs_fs_info *fs_info,
                         u64 objectid)
 {
        root->node = NULL;
        root->commit_root = NULL;
        root->sectorsize = sectorsize;
        root->nodesize = nodesize;
-       root->leafsize = leafsize;
        root->stripesize = stripesize;
        root->state = 0;
        root->orphan_cleanup_state = 0;
@@ -1295,7 +1304,7 @@ struct btrfs_root *btrfs_alloc_dummy_root(void)
        root = btrfs_alloc_root(NULL);
        if (!root)
                return ERR_PTR(-ENOMEM);
-       __setup_root(4096, 4096, 4096, 4096, root, NULL, 1);
+       __setup_root(4096, 4096, 4096, root, NULL, 1);
        set_bit(BTRFS_ROOT_DUMMY_ROOT, &root->state);
        root->alloc_bytenr = 0;
 
@@ -1318,15 +1327,13 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
        if (!root)
                return ERR_PTR(-ENOMEM);
 
-       __setup_root(tree_root->nodesize, tree_root->leafsize,
-                    tree_root->sectorsize, tree_root->stripesize,
-                    root, fs_info, objectid);
+       __setup_root(tree_root->nodesize, tree_root->sectorsize,
+               tree_root->stripesize, root, fs_info, objectid);
        root->root_key.objectid = objectid;
        root->root_key.type = BTRFS_ROOT_ITEM_KEY;
        root->root_key.offset = 0;
 
-       leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
-                                     0, objectid, NULL, 0, 0, 0);
+       leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0);
        if (IS_ERR(leaf)) {
                ret = PTR_ERR(leaf);
                leaf = NULL;
@@ -1396,9 +1403,9 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
        if (!root)
                return ERR_PTR(-ENOMEM);
 
-       __setup_root(tree_root->nodesize, tree_root->leafsize,
-                    tree_root->sectorsize, tree_root->stripesize,
-                    root, fs_info, BTRFS_TREE_LOG_OBJECTID);
+       __setup_root(tree_root->nodesize, tree_root->sectorsize,
+                    tree_root->stripesize, root, fs_info,
+                    BTRFS_TREE_LOG_OBJECTID);
 
        root->root_key.objectid = BTRFS_TREE_LOG_OBJECTID;
        root->root_key.type = BTRFS_ROOT_ITEM_KEY;
@@ -1413,9 +1420,8 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
         * updated (along with back refs to the log tree).
         */
 
-       leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
-                                     BTRFS_TREE_LOG_OBJECTID, NULL,
-                                     0, 0, 0);
+       leaf = btrfs_alloc_tree_block(trans, root, 0, BTRFS_TREE_LOG_OBJECTID,
+                       NULL, 0, 0, 0);
        if (IS_ERR(leaf)) {
                kfree(root);
                return ERR_CAST(leaf);
@@ -1465,7 +1471,7 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
        btrfs_set_stack_inode_generation(inode_item, 1);
        btrfs_set_stack_inode_size(inode_item, 3);
        btrfs_set_stack_inode_nlink(inode_item, 1);
-       btrfs_set_stack_inode_nbytes(inode_item, root->leafsize);
+       btrfs_set_stack_inode_nbytes(inode_item, root->nodesize);
        btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
 
        btrfs_set_root_node(&log_root->root_item, log_root->node);
@@ -1485,7 +1491,6 @@ static struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
        struct btrfs_fs_info *fs_info = tree_root->fs_info;
        struct btrfs_path *path;
        u64 generation;
-       u32 blocksize;
        int ret;
 
        path = btrfs_alloc_path();
@@ -1498,9 +1503,8 @@ static struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
                goto alloc_fail;
        }
 
-       __setup_root(tree_root->nodesize, tree_root->leafsize,
-                    tree_root->sectorsize, tree_root->stripesize,
-                    root, fs_info, key->objectid);
+       __setup_root(tree_root->nodesize, tree_root->sectorsize,
+               tree_root->stripesize, root, fs_info, key->objectid);
 
        ret = btrfs_find_root(tree_root, key, path,
                              &root->root_item, &root->root_key);
@@ -1511,9 +1515,8 @@ static struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
        }
 
        generation = btrfs_root_generation(&root->root_item);
-       blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
        root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
-                                    blocksize, generation);
+                                    generation);
        if (!root->node) {
                ret = -ENOMEM;
                goto find_fail;
@@ -1573,8 +1576,8 @@ int btrfs_init_fs_root(struct btrfs_root *root)
        root->subv_writers = writers;
 
        btrfs_init_free_ino_ctl(root);
-       spin_lock_init(&root->cache_lock);
-       init_waitqueue_head(&root->cache_wait);
+       spin_lock_init(&root->ino_cache_lock);
+       init_waitqueue_head(&root->ino_cache_wait);
 
        ret = get_anon_bdev(&root->anon_dev);
        if (ret)
@@ -1708,10 +1711,6 @@ static int btrfs_congested_fn(void *congested_data, int bdi_bits)
        return ret;
 }
 
-/*
- * If this fails, caller must call bdi_destroy() to get rid of the
- * bdi again.
- */
 static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi)
 {
        int err;
@@ -1734,16 +1733,16 @@ static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi)
 static void end_workqueue_fn(struct btrfs_work *work)
 {
        struct bio *bio;
-       struct end_io_wq *end_io_wq;
+       struct btrfs_end_io_wq *end_io_wq;
        int error;
 
-       end_io_wq = container_of(work, struct end_io_wq, work);
+       end_io_wq = container_of(work, struct btrfs_end_io_wq, work);
        bio = end_io_wq->bio;
 
        error = end_io_wq->error;
        bio->bi_private = end_io_wq->private;
        bio->bi_end_io = end_io_wq->end_io;
-       kfree(end_io_wq);
+       kmem_cache_free(btrfs_end_io_wq_cache, end_io_wq);
        bio_endio_nodec(bio, error);
 }
 
@@ -1772,6 +1771,7 @@ static int cleaner_kthread(void *arg)
                }
 
                btrfs_run_delayed_iputs(root);
+               btrfs_delete_unused_bgs(root->fs_info);
                again = btrfs_clean_one_deleted_snapshot(root);
                mutex_unlock(&root->fs_info->cleaner_mutex);
 
@@ -2063,6 +2063,7 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
        btrfs_destroy_workqueue(fs_info->endio_workers);
        btrfs_destroy_workqueue(fs_info->endio_meta_workers);
        btrfs_destroy_workqueue(fs_info->endio_raid56_workers);
+       btrfs_destroy_workqueue(fs_info->endio_repair_workers);
        btrfs_destroy_workqueue(fs_info->rmw_workers);
        btrfs_destroy_workqueue(fs_info->endio_meta_write_workers);
        btrfs_destroy_workqueue(fs_info->endio_write_workers);
@@ -2143,8 +2144,6 @@ int open_ctree(struct super_block *sb,
 {
        u32 sectorsize;
        u32 nodesize;
-       u32 leafsize;
-       u32 blocksize;
        u32 stripesize;
        u64 generation;
        u64 features;
@@ -2188,7 +2187,7 @@ int open_ctree(struct super_block *sb,
                goto fail_srcu;
        }
 
-       ret = percpu_counter_init(&fs_info->dirty_metadata_bytes, 0);
+       ret = percpu_counter_init(&fs_info->dirty_metadata_bytes, 0, GFP_KERNEL);
        if (ret) {
                err = ret;
                goto fail_bdi;
@@ -2196,13 +2195,13 @@ int open_ctree(struct super_block *sb,
        fs_info->dirty_metadata_batch = PAGE_CACHE_SIZE *
                                        (1 + ilog2(nr_cpu_ids));
 
-       ret = percpu_counter_init(&fs_info->delalloc_bytes, 0);
+       ret = percpu_counter_init(&fs_info->delalloc_bytes, 0, GFP_KERNEL);
        if (ret) {
                err = ret;
                goto fail_dirty_metadata_bytes;
        }
 
-       ret = percpu_counter_init(&fs_info->bio_counter, 0);
+       ret = percpu_counter_init(&fs_info->bio_counter, 0, GFP_KERNEL);
        if (ret) {
                err = ret;
                goto fail_delalloc_bytes;
@@ -2233,6 +2232,7 @@ int open_ctree(struct super_block *sb,
        spin_lock_init(&fs_info->super_lock);
        spin_lock_init(&fs_info->qgroup_op_lock);
        spin_lock_init(&fs_info->buffer_lock);
+       spin_lock_init(&fs_info->unused_bgs_lock);
        rwlock_init(&fs_info->tree_mod_log_lock);
        mutex_init(&fs_info->reloc_mutex);
        mutex_init(&fs_info->delalloc_root_mutex);
@@ -2242,6 +2242,7 @@ int open_ctree(struct super_block *sb,
        INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
        INIT_LIST_HEAD(&fs_info->space_info);
        INIT_LIST_HEAD(&fs_info->tree_mod_seq_list);
+       INIT_LIST_HEAD(&fs_info->unused_bgs);
        btrfs_mapping_init(&fs_info->mapping_tree);
        btrfs_init_block_rsv(&fs_info->global_block_rsv,
                             BTRFS_BLOCK_RSV_GLOBAL);
@@ -2260,7 +2261,7 @@ int open_ctree(struct super_block *sb,
        atomic_set(&fs_info->qgroup_op_seq, 0);
        atomic64_set(&fs_info->tree_mod_seq, 0);
        fs_info->sb = sb;
-       fs_info->max_inline = 8192 * 1024;
+       fs_info->max_inline = BTRFS_DEFAULT_MAX_INLINE;
        fs_info->metadata_ratio = 0;
        fs_info->defrag_inodes = RB_ROOT;
        fs_info->free_chunk_space = 0;
@@ -2389,7 +2390,7 @@ int open_ctree(struct super_block *sb,
                goto fail_alloc;
        }
 
-       __setup_root(4096, 4096, 4096, 4096, tree_root,
+       __setup_root(4096, 4096, 4096, tree_root,
                     fs_info, BTRFS_ROOT_TREE_OBJECTID);
 
        invalidate_bdev(fs_devices->latest_bdev);
@@ -2469,19 +2470,22 @@ int open_ctree(struct super_block *sb,
                goto fail_alloc;
        }
 
-       if (btrfs_super_leafsize(disk_super) !=
+       /*
+        * Leafsize and nodesize were always equal, this is only a sanity check.
+        */
+       if (le32_to_cpu(disk_super->__unused_leafsize) !=
            btrfs_super_nodesize(disk_super)) {
                printk(KERN_ERR "BTRFS: couldn't mount because metadata "
                       "blocksizes don't match.  node %d leaf %d\n",
                       btrfs_super_nodesize(disk_super),
-                      btrfs_super_leafsize(disk_super));
+                      le32_to_cpu(disk_super->__unused_leafsize));
                err = -EINVAL;
                goto fail_alloc;
        }
-       if (btrfs_super_leafsize(disk_super) > BTRFS_MAX_METADATA_BLOCKSIZE) {
+       if (btrfs_super_nodesize(disk_super) > BTRFS_MAX_METADATA_BLOCKSIZE) {
                printk(KERN_ERR "BTRFS: couldn't mount because metadata "
                       "blocksize (%d) was too large\n",
-                      btrfs_super_leafsize(disk_super));
+                      btrfs_super_nodesize(disk_super));
                err = -EINVAL;
                goto fail_alloc;
        }
@@ -2498,17 +2502,16 @@ int open_ctree(struct super_block *sb,
         * flag our filesystem as having big metadata blocks if
         * they are bigger than the page size
         */
-       if (btrfs_super_leafsize(disk_super) > PAGE_CACHE_SIZE) {
+       if (btrfs_super_nodesize(disk_super) > PAGE_CACHE_SIZE) {
                if (!(features & BTRFS_FEATURE_INCOMPAT_BIG_METADATA))
                        printk(KERN_INFO "BTRFS: flagging fs with big metadata feature\n");
                features |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
        }
 
        nodesize = btrfs_super_nodesize(disk_super);
-       leafsize = btrfs_super_leafsize(disk_super);
        sectorsize = btrfs_super_sectorsize(disk_super);
        stripesize = btrfs_super_stripesize(disk_super);
-       fs_info->dirty_metadata_batch = leafsize * (1 + ilog2(nr_cpu_ids));
+       fs_info->dirty_metadata_batch = nodesize * (1 + ilog2(nr_cpu_ids));
        fs_info->delalloc_batch = sectorsize * 512 * (1 + ilog2(nr_cpu_ids));
 
        /*
@@ -2516,7 +2519,7 @@ int open_ctree(struct super_block *sb,
         * extent buffers for the same range.  It leads to corruptions
         */
        if ((features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS) &&
-           (sectorsize != leafsize)) {
+           (sectorsize != nodesize)) {
                printk(KERN_WARNING "BTRFS: unequal leaf/node/sector sizes "
                                "are not allowed for mixed block groups on %s\n",
                                sb->s_id);
@@ -2579,6 +2582,8 @@ int open_ctree(struct super_block *sb,
                btrfs_alloc_workqueue("endio-meta-write", flags, max_active, 2);
        fs_info->endio_raid56_workers =
                btrfs_alloc_workqueue("endio-raid56", flags, max_active, 4);
+       fs_info->endio_repair_workers =
+               btrfs_alloc_workqueue("endio-repair", flags, 1, 0);
        fs_info->rmw_workers =
                btrfs_alloc_workqueue("rmw", flags, max_active, 2);
        fs_info->endio_write_workers =
@@ -2600,11 +2605,12 @@ int open_ctree(struct super_block *sb,
              fs_info->submit_workers && fs_info->flush_workers &&
              fs_info->endio_workers && fs_info->endio_meta_workers &&
              fs_info->endio_meta_write_workers &&
+             fs_info->endio_repair_workers &&
              fs_info->endio_write_workers && fs_info->endio_raid56_workers &&
              fs_info->endio_freespace_worker && fs_info->rmw_workers &&
              fs_info->caching_workers && fs_info->readahead_workers &&
              fs_info->fixup_workers && fs_info->delayed_workers &&
-             fs_info->fixup_workers && fs_info->extent_workers &&
+             fs_info->extent_workers &&
              fs_info->qgroup_rescan_workers)) {
                err = -ENOMEM;
                goto fail_sb_buffer;
@@ -2615,7 +2621,6 @@ int open_ctree(struct super_block *sb,
                                    4 * 1024 * 1024 / PAGE_CACHE_SIZE);
 
        tree_root->nodesize = nodesize;
-       tree_root->leafsize = leafsize;
        tree_root->sectorsize = sectorsize;
        tree_root->stripesize = stripesize;
 
@@ -2642,16 +2647,14 @@ int open_ctree(struct super_block *sb,
                goto fail_sb_buffer;
        }
 
-       blocksize = btrfs_level_size(tree_root,
-                                    btrfs_super_chunk_root_level(disk_super));
        generation = btrfs_super_chunk_root_generation(disk_super);
 
-       __setup_root(nodesize, leafsize, sectorsize, stripesize,
-                    chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
+       __setup_root(nodesize, sectorsize, stripesize, chunk_root,
+                    fs_info, BTRFS_CHUNK_TREE_OBJECTID);
 
        chunk_root->node = read_tree_block(chunk_root,
                                           btrfs_super_chunk_root(disk_super),
-                                          blocksize, generation);
+                                          generation);
        if (!chunk_root->node ||
            !test_bit(EXTENT_BUFFER_UPTODATE, &chunk_root->node->bflags)) {
                printk(KERN_WARNING "BTRFS: failed to read chunk root on %s\n",
@@ -2684,13 +2687,11 @@ int open_ctree(struct super_block *sb,
        }
 
 retry_root_backup:
-       blocksize = btrfs_level_size(tree_root,
-                                    btrfs_super_root_level(disk_super));
        generation = btrfs_super_generation(disk_super);
 
        tree_root->node = read_tree_block(tree_root,
                                          btrfs_super_root(disk_super),
-                                         blocksize, generation);
+                                         generation);
        if (!tree_root->node ||
            !test_bit(EXTENT_BUFFER_UPTODATE, &tree_root->node->bflags)) {
                printk(KERN_WARNING "BTRFS: failed to read tree root on %s\n",
@@ -2859,9 +2860,6 @@ retry_root_backup:
                        err = -EIO;
                        goto fail_qgroup;
                }
-               blocksize =
-                    btrfs_level_size(tree_root,
-                                     btrfs_super_log_root_level(disk_super));
 
                log_tree_root = btrfs_alloc_root(fs_info);
                if (!log_tree_root) {
@@ -2869,11 +2867,10 @@ retry_root_backup:
                        goto fail_qgroup;
                }
 
-               __setup_root(nodesize, leafsize, sectorsize, stripesize,
+               __setup_root(nodesize, sectorsize, stripesize,
                             log_tree_root, fs_info, BTRFS_TREE_LOG_OBJECTID);
 
                log_tree_root->node = read_tree_block(tree_root, bytenr,
-                                                     blocksize,
                                                      generation + 1);
                if (!log_tree_root->node ||
                    !extent_buffer_uptodate(log_tree_root->node)) {
@@ -2980,6 +2977,8 @@ retry_root_backup:
                fs_info->update_uuid_tree_gen = 1;
        }
 
+       fs_info->open = 1;
+
        return 0;
 
 fail_qgroup:
@@ -3139,7 +3138,8 @@ static int write_dev_supers(struct btrfs_device *device,
 
        for (i = 0; i < max_mirrors; i++) {
                bytenr = btrfs_sb_offset(i);
-               if (bytenr + BTRFS_SUPER_INFO_SIZE >= device->total_bytes)
+               if (bytenr + BTRFS_SUPER_INFO_SIZE >=
+                   device->commit_total_bytes)
                        break;
 
                if (wait) {
@@ -3456,8 +3456,9 @@ static int write_all_supers(struct btrfs_root *root, int max_mirrors)
                btrfs_set_stack_device_type(dev_item, dev->type);
                btrfs_set_stack_device_id(dev_item, dev->devid);
                btrfs_set_stack_device_total_bytes(dev_item,
-                                                  dev->disk_total_bytes);
-               btrfs_set_stack_device_bytes_used(dev_item, dev->bytes_used);
+                                                  dev->commit_total_bytes);
+               btrfs_set_stack_device_bytes_used(dev_item,
+                                                 dev->commit_bytes_used);
                btrfs_set_stack_device_io_align(dev_item, dev->io_align);
                btrfs_set_stack_device_io_width(dev_item, dev->io_width);
                btrfs_set_stack_device_sector_size(dev_item, dev->sector_size);
@@ -3532,7 +3533,7 @@ void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
 
 static void free_fs_root(struct btrfs_root *root)
 {
-       iput(root->cache_inode);
+       iput(root->ino_cache_inode);
        WARN_ON(!RB_EMPTY_ROOT(&root->inode_tree));
        btrfs_free_block_rsv(root, root->orphan_block_rsv);
        root->orphan_block_rsv = NULL;
@@ -3623,7 +3624,7 @@ int btrfs_commit_super(struct btrfs_root *root)
        return btrfs_commit_transaction(trans, root);
 }
 
-int close_ctree(struct btrfs_root *root)
+void close_ctree(struct btrfs_root *root)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        int ret;
@@ -3689,6 +3690,7 @@ int close_ctree(struct btrfs_root *root)
        invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
        btrfs_stop_all_workers(fs_info);
 
+       fs_info->open = 0;
        free_root_pointers(fs_info, 1);
 
        iput(fs_info->btree_inode);
@@ -3711,8 +3713,6 @@ int close_ctree(struct btrfs_root *root)
 
        btrfs_free_block_rsv(root, root->orphan_block_rsv);
        root->orphan_block_rsv = NULL;
-
-       return 0;
 }
 
 int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid,
@@ -3814,10 +3814,73 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid)
 static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
                              int read_only)
 {
+       struct btrfs_super_block *sb = fs_info->super_copy;
+       int ret = 0;
+
+       if (sb->root_level > BTRFS_MAX_LEVEL) {
+               printk(KERN_ERR "BTRFS: tree_root level too big: %d > %d\n",
+                               sb->root_level, BTRFS_MAX_LEVEL);
+               ret = -EINVAL;
+       }
+       if (sb->chunk_root_level > BTRFS_MAX_LEVEL) {
+               printk(KERN_ERR "BTRFS: chunk_root level too big: %d > %d\n",
+                               sb->chunk_root_level, BTRFS_MAX_LEVEL);
+               ret = -EINVAL;
+       }
+       if (sb->log_root_level > BTRFS_MAX_LEVEL) {
+               printk(KERN_ERR "BTRFS: log_root level too big: %d > %d\n",
+                               sb->log_root_level, BTRFS_MAX_LEVEL);
+               ret = -EINVAL;
+       }
+
        /*
-        * Placeholder for checks
+        * The common minimum, we don't know if we can trust the nodesize/sectorsize
+        * items yet, they'll be verified later. Issue just a warning.
         */
-       return 0;
+       if (!IS_ALIGNED(sb->root, 4096))
+               printk(KERN_WARNING "BTRFS: tree_root block unaligned: %llu\n",
+                               sb->root);
+       if (!IS_ALIGNED(sb->chunk_root, 4096))
+               printk(KERN_WARNING "BTRFS: tree_root block unaligned: %llu\n",
+                               sb->chunk_root);
+       if (!IS_ALIGNED(sb->log_root, 4096))
+               printk(KERN_WARNING "BTRFS: tree_root block unaligned: %llu\n",
+                               sb->log_root);
+
+       if (memcmp(fs_info->fsid, sb->dev_item.fsid, BTRFS_UUID_SIZE) != 0) {
+               printk(KERN_ERR "BTRFS: dev_item UUID does not match fsid: %pU != %pU\n",
+                               fs_info->fsid, sb->dev_item.fsid);
+               ret = -EINVAL;
+       }
+
+       /*
+        * Hint to catch really bogus numbers, bitflips or so, more exact checks are
+        * done later
+        */
+       if (sb->num_devices > (1UL << 31))
+               printk(KERN_WARNING "BTRFS: suspicious number of devices: %llu\n",
+                               sb->num_devices);
+
+       if (sb->bytenr != BTRFS_SUPER_INFO_OFFSET) {
+               printk(KERN_ERR "BTRFS: super offset mismatch %llu != %u\n",
+                               sb->bytenr, BTRFS_SUPER_INFO_OFFSET);
+               ret = -EINVAL;
+       }
+
+       /*
+        * The generation is a global counter, we'll trust it more than the others
+        * but it's still possible that it's the one that's wrong.
+        */
+       if (sb->generation < sb->chunk_root_generation)
+               printk(KERN_WARNING
+                       "BTRFS: suspicious: generation < chunk_root_generation: %llu < %llu\n",
+                       sb->generation, sb->chunk_root_generation);
+       if (sb->generation < sb->cache_generation && sb->cache_generation != (u64)-1)
+               printk(KERN_WARNING
+                       "BTRFS: suspicious: generation < cache_generation: %llu < %llu\n",
+                       sb->generation, sb->cache_generation);
+
+       return ret;
 }
 
 static void btrfs_error_commit_super(struct btrfs_root *root)
@@ -4009,9 +4072,8 @@ static int btrfs_destroy_marked_extents(struct btrfs_root *root,
 
                clear_extent_bits(dirty_pages, start, end, mark, GFP_NOFS);
                while (start <= end) {
-                       eb = btrfs_find_tree_block(root, start,
-                                                  root->leafsize);
-                       start += root->leafsize;
+                       eb = btrfs_find_tree_block(root, start);
+                       start += root->nodesize;
                        if (!eb)
                                continue;
                        wait_on_extent_buffer_writeback(eb);
index 23ce3ceba0a975d5544eb35f0dfd306aff87ad0c..414651821fb3b38a62df3a72fa44877f66b2e602 100644 (file)
 #define BTRFS_SUPER_MIRROR_MAX  3
 #define BTRFS_SUPER_MIRROR_SHIFT 12
 
-enum {
+enum btrfs_wq_endio_type {
        BTRFS_WQ_ENDIO_DATA = 0,
        BTRFS_WQ_ENDIO_METADATA = 1,
        BTRFS_WQ_ENDIO_FREE_SPACE = 2,
        BTRFS_WQ_ENDIO_RAID56 = 3,
+       BTRFS_WQ_ENDIO_DIO_REPAIR = 4,
 };
 
 static inline u64 btrfs_sb_offset(int mirror)
@@ -44,9 +45,8 @@ struct btrfs_device;
 struct btrfs_fs_devices;
 
 struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
-                                     u32 blocksize, u64 parent_transid);
-int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
-                        u64 parent_transid);
+                                     u64 parent_transid);
+void readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize);
 int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, u32 blocksize,
                         int mirror_num, struct extent_buffer **eb);
 struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
@@ -56,13 +56,13 @@ void clean_tree_block(struct btrfs_trans_handle *trans,
 int open_ctree(struct super_block *sb,
               struct btrfs_fs_devices *fs_devices,
               char *options);
-int close_ctree(struct btrfs_root *root);
+void close_ctree(struct btrfs_root *root);
 int write_ctree_super(struct btrfs_trans_handle *trans,
                      struct btrfs_root *root, int max_mirrors);
 struct buffer_head *btrfs_read_dev_super(struct block_device *bdev);
 int btrfs_commit_super(struct btrfs_root *root);
 struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
-                                           u64 bytenr, u32 blocksize);
+                                           u64 bytenr);
 struct btrfs_root *btrfs_read_fs_root(struct btrfs_root *tree_root,
                                      struct btrfs_key *location);
 int btrfs_init_fs_root(struct btrfs_root *root);
@@ -119,7 +119,7 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid);
 u32 btrfs_csum_data(char *data, u32 seed, size_t len);
 void btrfs_csum_final(u32 crc, char *result);
 int btrfs_bio_wq_end_io(struct btrfs_fs_info *info, struct bio *bio,
-                       int metadata);
+                       enum btrfs_wq_endio_type metadata);
 int btrfs_wq_submit_bio(struct btrfs_fs_info *fs_info, struct inode *inode,
                        int rw, struct bio *bio, int mirror_num,
                        unsigned long bio_flags, u64 bio_offset,
@@ -141,6 +141,8 @@ int btree_lock_page_hook(struct page *page, void *data,
                                void (*flush_fn)(void *));
 int btrfs_calc_num_tolerated_disk_barrier_failures(
        struct btrfs_fs_info *fs_info);
+int __init btrfs_end_io_wq_init(void);
+void btrfs_end_io_wq_exit(void);
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 void btrfs_init_lockdep(void);
index 41422a3de8ed03f66c7341d8289c1626097a4ff7..37d164540c3a25c469d831899e05f9cc5eccb9d1 100644 (file)
@@ -70,7 +70,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
                return ERR_PTR(-ESTALE);
 
        key.objectid = root_objectid;
-       btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+       key.type = BTRFS_ROOT_ITEM_KEY;
        key.offset = (u64)-1;
 
        index = srcu_read_lock(&fs_info->subvol_srcu);
@@ -82,7 +82,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
        }
 
        key.objectid = objectid;
-       btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
+       key.type = BTRFS_INODE_ITEM_KEY;
        key.offset = 0;
 
        inode = btrfs_iget(sb, &key, root, NULL);
index 3efe1c3877bf34c4643ce99fb90d52fedda74d06..d565895710126f813fd8a6c88c520ffe0fcffd3c 100644 (file)
@@ -491,7 +491,7 @@ next:
                                                          key.objectid);
                        if (key.type == BTRFS_METADATA_ITEM_KEY)
                                last = key.objectid +
-                                       fs_info->tree_root->leafsize;
+                                       fs_info->tree_root->nodesize;
                        else
                                last = key.objectid + key.offset;
 
@@ -765,7 +765,7 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
         * different
         */
        if (metadata && !btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) {
-               offset = root->leafsize;
+               offset = root->nodesize;
                metadata = 0;
        }
 
@@ -799,13 +799,13 @@ again:
                                              path->slots[0]);
                        if (key.objectid == bytenr &&
                            key.type == BTRFS_EXTENT_ITEM_KEY &&
-                           key.offset == root->leafsize)
+                           key.offset == root->nodesize)
                                ret = 0;
                }
                if (ret) {
                        key.objectid = bytenr;
                        key.type = BTRFS_EXTENT_ITEM_KEY;
-                       key.offset = root->leafsize;
+                       key.offset = root->nodesize;
                        btrfs_release_path(path);
                        goto again;
                }
@@ -2651,7 +2651,7 @@ int btrfs_check_space_for_delayed_refs(struct btrfs_trans_handle *trans,
        num_bytes = btrfs_calc_trans_metadata_size(root, 1);
        num_heads = heads_to_leaves(root, num_heads);
        if (num_heads > 1)
-               num_bytes += (num_heads - 1) * root->leafsize;
+               num_bytes += (num_heads - 1) * root->nodesize;
        num_bytes <<= 1;
        global_rsv = &root->fs_info->global_block_rsv;
 
@@ -3073,10 +3073,10 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
        int (*process_func)(struct btrfs_trans_handle *, struct btrfs_root *,
                            u64, u64, u64, u64, u64, u64, int);
 
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
-       if (unlikely(test_bit(BTRFS_ROOT_DUMMY_ROOT, &root->state)))
+
+       if (btrfs_test_is_dummy_root(root))
                return 0;
-#endif
+
        ref_root = btrfs_header_owner(buf);
        nritems = btrfs_header_nritems(buf);
        level = btrfs_header_level(buf);
@@ -3097,7 +3097,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
        for (i = 0; i < nritems; i++) {
                if (level == 0) {
                        btrfs_item_key_to_cpu(buf, &key, i);
-                       if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
+                       if (key.type != BTRFS_EXTENT_DATA_KEY)
                                continue;
                        fi = btrfs_item_ptr(buf, i,
                                            struct btrfs_file_extent_item);
@@ -3117,7 +3117,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
                                goto fail;
                } else {
                        bytenr = btrfs_node_blockptr(buf, i);
-                       num_bytes = btrfs_level_size(root, level - 1);
+                       num_bytes = root->nodesize;
                        ret = process_func(trans, root, bytenr, num_bytes,
                                           parent, ref_root, level - 1, 0,
                                           1);
@@ -3494,7 +3494,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        if (!found)
                return -ENOMEM;
 
-       ret = percpu_counter_init(&found->total_bytes_pinned, 0);
+       ret = percpu_counter_init(&found->total_bytes_pinned, 0, GFP_KERNEL);
        if (ret) {
                kfree(found);
                return ret;
@@ -4343,11 +4343,21 @@ static inline int need_do_async_reclaim(struct btrfs_space_info *space_info,
 }
 
 static int btrfs_need_do_async_reclaim(struct btrfs_space_info *space_info,
-                                      struct btrfs_fs_info *fs_info)
+                                      struct btrfs_fs_info *fs_info,
+                                      int flush_state)
 {
        u64 used;
 
        spin_lock(&space_info->lock);
+       /*
+        * We run out of space and have not got any free space via flush_space,
+        * so don't bother doing async reclaim.
+        */
+       if (flush_state > COMMIT_TRANS && space_info->full) {
+               spin_unlock(&space_info->lock);
+               return 0;
+       }
+
        used = space_info->bytes_used + space_info->bytes_reserved +
               space_info->bytes_pinned + space_info->bytes_readonly +
               space_info->bytes_may_use;
@@ -4380,11 +4390,12 @@ static void btrfs_async_reclaim_metadata_space(struct work_struct *work)
                flush_space(fs_info->fs_root, space_info, to_reclaim,
                            to_reclaim, flush_state);
                flush_state++;
-               if (!btrfs_need_do_async_reclaim(space_info, fs_info))
+               if (!btrfs_need_do_async_reclaim(space_info, fs_info,
+                                                flush_state))
                        return;
        } while (flush_state <= COMMIT_TRANS);
 
-       if (btrfs_need_do_async_reclaim(space_info, fs_info))
+       if (btrfs_need_do_async_reclaim(space_info, fs_info, flush_state))
                queue_work(system_unbound_wq, work);
 }
 
@@ -4502,7 +4513,13 @@ again:
                space_info->flush = 1;
        } else if (!ret && space_info->flags & BTRFS_BLOCK_GROUP_METADATA) {
                used += orig_bytes;
-               if (need_do_async_reclaim(space_info, root->fs_info, used) &&
+               /*
+                * We will do the space reservation dance during log replay,
+                * which means we won't have fs_info->fs_root set, so don't do
+                * the async reclaim as we will panic.
+                */
+               if (!root->fs_info->log_root_recovering &&
+                   need_do_async_reclaim(space_info, root->fs_info, used) &&
                    !work_busy(&root->fs_info->async_reclaim_work))
                        queue_work(system_unbound_wq,
                                   &root->fs_info->async_reclaim_work);
@@ -4839,7 +4856,7 @@ static u64 calc_global_metadata_size(struct btrfs_fs_info *fs_info)
        if (num_bytes * 3 > meta_used)
                num_bytes = div64_u64(meta_used, 3);
 
-       return ALIGN(num_bytes, fs_info->extent_root->leafsize << 10);
+       return ALIGN(num_bytes, fs_info->extent_root->nodesize << 10);
 }
 
 static void update_global_block_rsv(struct btrfs_fs_info *fs_info)
@@ -4988,7 +5005,7 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root,
 
        if (root->fs_info->quota_enabled) {
                /* One for parent inode, two for dir entries */
-               num_bytes = 3 * root->leafsize;
+               num_bytes = 3 * root->nodesize;
                ret = btrfs_qgroup_reserve(root, num_bytes);
                if (ret)
                        return ret;
@@ -5176,7 +5193,7 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
 
        if (root->fs_info->quota_enabled) {
                ret = btrfs_qgroup_reserve(root, num_bytes +
-                                          nr_extents * root->leafsize);
+                                          nr_extents * root->nodesize);
                if (ret)
                        goto out_fail;
        }
@@ -5185,7 +5202,7 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
        if (unlikely(ret)) {
                if (root->fs_info->quota_enabled)
                        btrfs_qgroup_free(root, num_bytes +
-                                               nr_extents * root->leafsize);
+                                               nr_extents * root->nodesize);
                goto out_fail;
        }
 
@@ -5301,7 +5318,7 @@ void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes)
                                      btrfs_ino(inode), to_free, 0);
        if (root->fs_info->quota_enabled) {
                btrfs_qgroup_free(root, num_bytes +
-                                       dropped * root->leafsize);
+                                       dropped * root->nodesize);
        }
 
        btrfs_block_rsv_release(root, &root->fs_info->delalloc_block_rsv,
@@ -5422,6 +5439,20 @@ static int update_block_group(struct btrfs_root *root,
                        spin_unlock(&cache->space_info->lock);
                } else {
                        old_val -= num_bytes;
+
+                       /*
+                        * No longer have used bytes in this block group, queue
+                        * it for deletion.
+                        */
+                       if (old_val == 0) {
+                               spin_lock(&info->unused_bgs_lock);
+                               if (list_empty(&cache->bg_list)) {
+                                       btrfs_get_block_group(cache);
+                                       list_add_tail(&cache->bg_list,
+                                                     &info->unused_bgs);
+                               }
+                               spin_unlock(&info->unused_bgs_lock);
+                       }
                        btrfs_set_block_group_used(&cache->item, old_val);
                        cache->pinned += num_bytes;
                        cache->space_info->bytes_pinned += num_bytes;
@@ -6233,10 +6264,9 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root,
        int ret;
        struct btrfs_fs_info *fs_info = root->fs_info;
 
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
-       if (unlikely(test_bit(BTRFS_ROOT_DUMMY_ROOT, &root->state)))
+       if (btrfs_test_is_dummy_root(root))
                return 0;
-#endif
+
        add_pinned_bytes(root->fs_info, num_bytes, owner, root_objectid);
 
        /*
@@ -6263,14 +6293,6 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root,
        return ret;
 }
 
-static u64 stripe_align(struct btrfs_root *root,
-                       struct btrfs_block_group_cache *cache,
-                       u64 val, u64 num_bytes)
-{
-       u64 ret = ALIGN(val, root->stripesize);
-       return ret;
-}
-
 /*
  * when we wait for progress in the block group caching, its because
  * our allocation attempt failed at least once.  So, we must sleep
@@ -6464,7 +6486,7 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
        bool have_caching_bg = false;
 
        WARN_ON(num_bytes < root->sectorsize);
-       btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
+       ins->type = BTRFS_EXTENT_ITEM_KEY;
        ins->objectid = 0;
        ins->offset = 0;
 
@@ -6751,8 +6773,7 @@ unclustered_alloc:
                        goto loop;
                }
 checks:
-               search_start = stripe_align(root, block_group,
-                                           offset, num_bytes);
+               search_start = ALIGN(offset, root->stripesize);
 
                /* move on to the next group */
                if (search_start + num_bytes >
@@ -7077,7 +7098,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        path = btrfs_alloc_path();
        if (!path) {
                btrfs_free_and_pin_reserved_extent(root, ins->objectid,
-                                                  root->leafsize);
+                                                  root->nodesize);
                return -ENOMEM;
        }
 
@@ -7086,7 +7107,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
                                      ins, size);
        if (ret) {
                btrfs_free_and_pin_reserved_extent(root, ins->objectid,
-                                                  root->leafsize);
+                                                  root->nodesize);
                btrfs_free_path(path);
                return ret;
        }
@@ -7101,7 +7122,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
 
        if (skinny_metadata) {
                iref = (struct btrfs_extent_inline_ref *)(extent_item + 1);
-               num_bytes = root->leafsize;
+               num_bytes = root->nodesize;
        } else {
                block_info = (struct btrfs_tree_block_info *)(extent_item + 1);
                btrfs_set_tree_block_key(leaf, block_info, key);
@@ -7131,14 +7152,14 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
                        return ret;
        }
 
-       ret = update_block_group(root, ins->objectid, root->leafsize, 1);
+       ret = update_block_group(root, ins->objectid, root->nodesize, 1);
        if (ret) { /* -ENOENT, logic error */
                btrfs_err(fs_info, "update block group failed for %llu %llu",
                        ins->objectid, ins->offset);
                BUG();
        }
 
-       trace_btrfs_reserved_extent_alloc(root, ins->objectid, root->leafsize);
+       trace_btrfs_reserved_extent_alloc(root, ins->objectid, root->nodesize);
        return ret;
 }
 
@@ -7213,17 +7234,19 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
        btrfs_set_buffer_uptodate(buf);
 
        if (root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID) {
+               buf->log_index = root->log_transid % 2;
                /*
                 * we allow two log transactions at a time, use different
                 * EXENT bit to differentiate dirty pages.
                 */
-               if (root->log_transid % 2 == 0)
+               if (buf->log_index == 0)
                        set_extent_dirty(&root->dirty_log_pages, buf->start,
                                        buf->start + buf->len - 1, GFP_NOFS);
                else
                        set_extent_new(&root->dirty_log_pages, buf->start,
                                        buf->start + buf->len - 1, GFP_NOFS);
        } else {
+               buf->log_index = -1;
                set_extent_dirty(&trans->transaction->dirty_pages, buf->start,
                         buf->start + buf->len - 1, GFP_NOFS);
        }
@@ -7300,8 +7323,8 @@ static void unuse_block_rsv(struct btrfs_fs_info *fs_info,
  *
  * returns the tree buffer or NULL.
  */
-struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
-                                       struct btrfs_root *root, u32 blocksize,
+struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
+                                       struct btrfs_root *root,
                                        u64 parent, u64 root_objectid,
                                        struct btrfs_disk_key *key, int level,
                                        u64 hint, u64 empty_size)
@@ -7311,18 +7334,18 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
        struct extent_buffer *buf;
        u64 flags = 0;
        int ret;
+       u32 blocksize = root->nodesize;
        bool skinny_metadata = btrfs_fs_incompat(root->fs_info,
                                                 SKINNY_METADATA);
 
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
-       if (unlikely(test_bit(BTRFS_ROOT_DUMMY_ROOT, &root->state))) {
+       if (btrfs_test_is_dummy_root(root)) {
                buf = btrfs_init_new_buffer(trans, root, root->alloc_bytenr,
                                            blocksize, level);
                if (!IS_ERR(buf))
                        root->alloc_bytenr += blocksize;
                return buf;
        }
-#endif
+
        block_rsv = use_block_rsv(trans, root, blocksize);
        if (IS_ERR(block_rsv))
                return ERR_CAST(block_rsv);
@@ -7417,7 +7440,7 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
 
        eb = path->nodes[wc->level];
        nritems = btrfs_header_nritems(eb);
-       blocksize = btrfs_level_size(root, wc->level - 1);
+       blocksize = root->nodesize;
 
        for (slot = path->slots[wc->level]; slot < nritems; slot++) {
                if (nread >= wc->reada_count)
@@ -7464,10 +7487,7 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
                                continue;
                }
 reada:
-               ret = readahead_tree_block(root, bytenr, blocksize,
-                                          generation);
-               if (ret)
-                       break;
+               readahead_tree_block(root, bytenr, blocksize);
                nread++;
        }
        wc->reada_slot = slot;
@@ -7626,7 +7646,6 @@ walk_down:
        level = root_level;
        while (level >= 0) {
                if (path->nodes[level] == NULL) {
-                       int child_bsize = root->nodesize;
                        int parent_slot;
                        u64 child_gen;
                        u64 child_bytenr;
@@ -7638,8 +7657,7 @@ walk_down:
                        child_bytenr = btrfs_node_blockptr(eb, parent_slot);
                        child_gen = btrfs_node_ptr_generation(eb, parent_slot);
 
-                       eb = read_tree_block(root, child_bytenr, child_bsize,
-                                            child_gen);
+                       eb = read_tree_block(root, child_bytenr, child_gen);
                        if (!eb || !extent_buffer_uptodate(eb)) {
                                ret = -EIO;
                                goto out;
@@ -7655,7 +7673,7 @@ walk_down:
                        ret = btrfs_qgroup_record_ref(trans, root->fs_info,
                                                root->objectid,
                                                child_bytenr,
-                                               child_bsize,
+                                               root->nodesize,
                                                BTRFS_QGROUP_OPER_SUB_SUBTREE,
                                                0);
                        if (ret)
@@ -7806,9 +7824,9 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
        }
 
        bytenr = btrfs_node_blockptr(path->nodes[level], path->slots[level]);
-       blocksize = btrfs_level_size(root, level - 1);
+       blocksize = root->nodesize;
 
-       next = btrfs_find_tree_block(root, bytenr, blocksize);
+       next = btrfs_find_tree_block(root, bytenr);
        if (!next) {
                next = btrfs_find_create_tree_block(root, bytenr, blocksize);
                if (!next)
@@ -7870,7 +7888,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
        if (!next) {
                if (reada && level == 1)
                        reada_walk_down(trans, root, wc, path);
-               next = read_tree_block(root, bytenr, blocksize, generation);
+               next = read_tree_block(root, bytenr, generation);
                if (!next || !extent_buffer_uptodate(next)) {
                        free_extent_buffer(next);
                        return -EIO;
@@ -8853,6 +8871,16 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
        }
        up_write(&info->commit_root_sem);
 
+       spin_lock(&info->unused_bgs_lock);
+       while (!list_empty(&info->unused_bgs)) {
+               block_group = list_first_entry(&info->unused_bgs,
+                                              struct btrfs_block_group_cache,
+                                              bg_list);
+               list_del_init(&block_group->bg_list);
+               btrfs_put_block_group(block_group);
+       }
+       spin_unlock(&info->unused_bgs_lock);
+
        spin_lock(&info->block_group_cache_lock);
        while ((n = rb_last(&info->block_group_cache_tree)) != NULL) {
                block_group = rb_entry(n, struct btrfs_block_group_cache,
@@ -8987,7 +9015,7 @@ btrfs_create_block_group_cache(struct btrfs_root *root, u64 start, u64 size)
        init_rwsem(&cache->data_rwsem);
        INIT_LIST_HEAD(&cache->list);
        INIT_LIST_HEAD(&cache->cluster_list);
-       INIT_LIST_HEAD(&cache->new_bg_list);
+       INIT_LIST_HEAD(&cache->bg_list);
        btrfs_init_free_space_ctl(cache);
 
        return cache;
@@ -9009,7 +9037,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
        root = info->extent_root;
        key.objectid = 0;
        key.offset = 0;
-       btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY);
+       key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -9128,8 +9156,18 @@ int btrfs_read_block_groups(struct btrfs_root *root)
                __link_block_group(space_info, cache);
 
                set_avail_alloc_bits(root->fs_info, cache->flags);
-               if (btrfs_chunk_readonly(root, cache->key.objectid))
+               if (btrfs_chunk_readonly(root, cache->key.objectid)) {
                        set_block_group_ro(cache, 1);
+               } else if (btrfs_block_group_used(&cache->item) == 0) {
+                       spin_lock(&info->unused_bgs_lock);
+                       /* Should always be true but just in case. */
+                       if (list_empty(&cache->bg_list)) {
+                               btrfs_get_block_group(cache);
+                               list_add_tail(&cache->bg_list,
+                                             &info->unused_bgs);
+                       }
+                       spin_unlock(&info->unused_bgs_lock);
+               }
        }
 
        list_for_each_entry_rcu(space_info, &root->fs_info->space_info, list) {
@@ -9170,10 +9208,8 @@ void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans,
        struct btrfs_key key;
        int ret = 0;
 
-       list_for_each_entry_safe(block_group, tmp, &trans->new_bgs,
-                                new_bg_list) {
-               list_del_init(&block_group->new_bg_list);
-
+       list_for_each_entry_safe(block_group, tmp, &trans->new_bgs, bg_list) {
+               list_del_init(&block_group->bg_list);
                if (ret)
                        continue;
 
@@ -9259,7 +9295,7 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
 
        __link_block_group(cache->space_info, cache);
 
-       list_add_tail(&cache->new_bg_list, &trans->new_bgs);
+       list_add_tail(&cache->bg_list, &trans->new_bgs);
 
        set_avail_alloc_bits(extent_root->fs_info, type);
 
@@ -9413,8 +9449,6 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
 
        memcpy(&key, &block_group->key, sizeof(key));
 
-       btrfs_clear_space_info_full(root->fs_info);
-
        btrfs_put_block_group(block_group);
        btrfs_put_block_group(block_group);
 
@@ -9430,6 +9464,101 @@ out:
        return ret;
 }
 
+/*
+ * Process the unused_bgs list and remove any that don't have any allocated
+ * space inside of them.
+ */
+void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_block_group_cache *block_group;
+       struct btrfs_space_info *space_info;
+       struct btrfs_root *root = fs_info->extent_root;
+       struct btrfs_trans_handle *trans;
+       int ret = 0;
+
+       if (!fs_info->open)
+               return;
+
+       spin_lock(&fs_info->unused_bgs_lock);
+       while (!list_empty(&fs_info->unused_bgs)) {
+               u64 start, end;
+
+               block_group = list_first_entry(&fs_info->unused_bgs,
+                                              struct btrfs_block_group_cache,
+                                              bg_list);
+               space_info = block_group->space_info;
+               list_del_init(&block_group->bg_list);
+               if (ret || btrfs_mixed_space_info(space_info)) {
+                       btrfs_put_block_group(block_group);
+                       continue;
+               }
+               spin_unlock(&fs_info->unused_bgs_lock);
+
+               /* Don't want to race with allocators so take the groups_sem */
+               down_write(&space_info->groups_sem);
+               spin_lock(&block_group->lock);
+               if (block_group->reserved ||
+                   btrfs_block_group_used(&block_group->item) ||
+                   block_group->ro) {
+                       /*
+                        * We want to bail if we made new allocations or have
+                        * outstanding allocations in this block group.  We do
+                        * the ro check in case balance is currently acting on
+                        * this block group.
+                        */
+                       spin_unlock(&block_group->lock);
+                       up_write(&space_info->groups_sem);
+                       goto next;
+               }
+               spin_unlock(&block_group->lock);
+
+               /* We don't want to force the issue, only flip if it's ok. */
+               ret = set_block_group_ro(block_group, 0);
+               up_write(&space_info->groups_sem);
+               if (ret < 0) {
+                       ret = 0;
+                       goto next;
+               }
+
+               /*
+                * Want to do this before we do anything else so we can recover
+                * properly if we fail to join the transaction.
+                */
+               trans = btrfs_join_transaction(root);
+               if (IS_ERR(trans)) {
+                       btrfs_set_block_group_rw(root, block_group);
+                       ret = PTR_ERR(trans);
+                       goto next;
+               }
+
+               /*
+                * We could have pending pinned extents for this block group,
+                * just delete them, we don't care about them anymore.
+                */
+               start = block_group->key.objectid;
+               end = start + block_group->key.offset - 1;
+               clear_extent_bits(&fs_info->freed_extents[0], start, end,
+                                 EXTENT_DIRTY, GFP_NOFS);
+               clear_extent_bits(&fs_info->freed_extents[1], start, end,
+                                 EXTENT_DIRTY, GFP_NOFS);
+
+               /* Reset pinned so btrfs_put_block_group doesn't complain */
+               block_group->pinned = 0;
+
+               /*
+                * Btrfs_remove_chunk will abort the transaction if things go
+                * horribly wrong.
+                */
+               ret = btrfs_remove_chunk(trans, root,
+                                        block_group->key.objectid);
+               btrfs_end_transaction(trans, root);
+next:
+               btrfs_put_block_group(block_group);
+               spin_lock(&fs_info->unused_bgs_lock);
+       }
+       spin_unlock(&fs_info->unused_bgs_lock);
+}
+
 int btrfs_init_space_info(struct btrfs_fs_info *fs_info)
 {
        struct btrfs_space_info *space_info;
@@ -9561,7 +9690,7 @@ void btrfs_end_nocow_write(struct btrfs_root *root)
 
 int btrfs_start_nocow_write(struct btrfs_root *root)
 {
-       if (unlikely(atomic_read(&root->will_be_snapshoted)))
+       if (atomic_read(&root->will_be_snapshoted))
                return 0;
 
        percpu_counter_inc(&root->subv_writers->counter);
@@ -9569,7 +9698,7 @@ int btrfs_start_nocow_write(struct btrfs_root *root)
         * Make sure counter is updated before we check for snapshot creation.
         */
        smp_mb();
-       if (unlikely(atomic_read(&root->will_be_snapshoted))) {
+       if (atomic_read(&root->will_be_snapshoted)) {
                btrfs_end_nocow_write(root);
                return 0;
        }
index af0359dcf337dbfec421b49fedbb8b5d0ecbf8b8..bf3f424e0013c17d3a47047e5c7301abba93bfac 100644 (file)
@@ -25,6 +25,11 @@ static struct kmem_cache *extent_state_cache;
 static struct kmem_cache *extent_buffer_cache;
 static struct bio_set *btrfs_bioset;
 
+static inline bool extent_state_in_tree(const struct extent_state *state)
+{
+       return !RB_EMPTY_NODE(&state->rb_node);
+}
+
 #ifdef CONFIG_BTRFS_DEBUG
 static LIST_HEAD(buffers);
 static LIST_HEAD(states);
@@ -59,9 +64,9 @@ void btrfs_leak_debug_check(void)
 
        while (!list_empty(&states)) {
                state = list_entry(states.next, struct extent_state, leak_list);
-               printk(KERN_ERR "BTRFS: state leak: start %llu end %llu "
-                      "state %lu in tree %p refs %d\n",
-                      state->start, state->end, state->state, state->tree,
+               pr_err("BTRFS: state leak: start %llu end %llu state %lu in tree %d refs %d\n",
+                      state->start, state->end, state->state,
+                      extent_state_in_tree(state),
                       atomic_read(&state->refs));
                list_del(&state->leak_list);
                kmem_cache_free(extent_state_cache, state);
@@ -209,7 +214,7 @@ static struct extent_state *alloc_extent_state(gfp_t mask)
                return state;
        state->state = 0;
        state->private = 0;
-       state->tree = NULL;
+       RB_CLEAR_NODE(&state->rb_node);
        btrfs_leak_debug_add(&state->leak_list, &states);
        atomic_set(&state->refs, 1);
        init_waitqueue_head(&state->wq);
@@ -222,7 +227,7 @@ void free_extent_state(struct extent_state *state)
        if (!state)
                return;
        if (atomic_dec_and_test(&state->refs)) {
-               WARN_ON(state->tree);
+               WARN_ON(extent_state_in_tree(state));
                btrfs_leak_debug_del(&state->leak_list);
                trace_free_extent_state(state, _RET_IP_);
                kmem_cache_free(extent_state_cache, state);
@@ -371,8 +376,8 @@ static void merge_state(struct extent_io_tree *tree,
                    other->state == state->state) {
                        merge_cb(tree, state, other);
                        state->start = other->start;
-                       other->tree = NULL;
                        rb_erase(&other->rb_node, &tree->state);
+                       RB_CLEAR_NODE(&other->rb_node);
                        free_extent_state(other);
                }
        }
@@ -383,8 +388,8 @@ static void merge_state(struct extent_io_tree *tree,
                    other->state == state->state) {
                        merge_cb(tree, state, other);
                        state->end = other->end;
-                       other->tree = NULL;
                        rb_erase(&other->rb_node, &tree->state);
+                       RB_CLEAR_NODE(&other->rb_node);
                        free_extent_state(other);
                }
        }
@@ -442,7 +447,6 @@ static int insert_state(struct extent_io_tree *tree,
                       found->start, found->end, start, end);
                return -EEXIST;
        }
-       state->tree = tree;
        merge_state(tree, state);
        return 0;
 }
@@ -486,7 +490,6 @@ static int split_state(struct extent_io_tree *tree, struct extent_state *orig,
                free_extent_state(prealloc);
                return -EEXIST;
        }
-       prealloc->tree = tree;
        return 0;
 }
 
@@ -524,9 +527,9 @@ static struct extent_state *clear_state_bit(struct extent_io_tree *tree,
                wake_up(&state->wq);
        if (state->state == 0) {
                next = next_state(state);
-               if (state->tree) {
+               if (extent_state_in_tree(state)) {
                        rb_erase(&state->rb_node, &tree->state);
-                       state->tree = NULL;
+                       RB_CLEAR_NODE(&state->rb_node);
                        free_extent_state(state);
                } else {
                        WARN_ON(1);
@@ -606,8 +609,8 @@ again:
                        cached_state = NULL;
                }
 
-               if (cached && cached->tree && cached->start <= start &&
-                   cached->end > start) {
+               if (cached && extent_state_in_tree(cached) &&
+                   cached->start <= start && cached->end > start) {
                        if (clear)
                                atomic_dec(&cached->refs);
                        state = cached;
@@ -843,7 +846,7 @@ again:
        if (cached_state && *cached_state) {
                state = *cached_state;
                if (state->start <= start && state->end > start &&
-                   state->tree) {
+                   extent_state_in_tree(state)) {
                        node = &state->rb_node;
                        goto hit_next;
                }
@@ -1069,7 +1072,7 @@ again:
        if (cached_state && *cached_state) {
                state = *cached_state;
                if (state->start <= start && state->end > start &&
-                   state->tree) {
+                   extent_state_in_tree(state)) {
                        node = &state->rb_node;
                        goto hit_next;
                }
@@ -1459,7 +1462,7 @@ int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
        spin_lock(&tree->lock);
        if (cached_state && *cached_state) {
                state = *cached_state;
-               if (state->end == start - 1 && state->tree) {
+               if (state->end == start - 1 && extent_state_in_tree(state)) {
                        n = rb_next(&state->rb_node);
                        while (n) {
                                state = rb_entry(n, struct extent_state,
@@ -1905,7 +1908,7 @@ int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end,
        int bitset = 0;
 
        spin_lock(&tree->lock);
-       if (cached && cached->tree && cached->start <= start &&
+       if (cached && extent_state_in_tree(cached) && cached->start <= start &&
            cached->end > start)
                node = &cached->rb_node;
        else
@@ -1959,27 +1962,7 @@ static void check_page_uptodate(struct extent_io_tree *tree, struct page *page)
                SetPageUptodate(page);
 }
 
-/*
- * When IO fails, either with EIO or csum verification fails, we
- * try other mirrors that might have a good copy of the data.  This
- * io_failure_record is used to record state as we go through all the
- * mirrors.  If another mirror has good data, the page is set up to date
- * and things continue.  If a good mirror can't be found, the original
- * bio end_io callback is called to indicate things have failed.
- */
-struct io_failure_record {
-       struct page *page;
-       u64 start;
-       u64 len;
-       u64 logical;
-       unsigned long bio_flags;
-       int this_mirror;
-       int failed_mirror;
-       int in_validation;
-};
-
-static int free_io_failure(struct inode *inode, struct io_failure_record *rec,
-                               int did_repair)
+int free_io_failure(struct inode *inode, struct io_failure_record *rec)
 {
        int ret;
        int err = 0;
@@ -2012,10 +1995,10 @@ static int free_io_failure(struct inode *inode, struct io_failure_record *rec,
  * currently, there can be no more than two copies of every data bit. thus,
  * exactly one rewrite is required.
  */
-int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start,
-                       u64 length, u64 logical, struct page *page,
-                       int mirror_num)
+int repair_io_failure(struct inode *inode, u64 start, u64 length, u64 logical,
+                     struct page *page, unsigned int pg_offset, int mirror_num)
 {
+       struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
        struct bio *bio;
        struct btrfs_device *dev;
        u64 map_length = 0;
@@ -2053,7 +2036,7 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start,
                return -EIO;
        }
        bio->bi_bdev = dev->bdev;
-       bio_add_page(bio, page, length, start - page_offset(page));
+       bio_add_page(bio, page, length, pg_offset);
 
        if (btrfsic_submit_bio_wait(WRITE_SYNC, bio)) {
                /* try to remap that extent elsewhere? */
@@ -2063,10 +2046,9 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start,
        }
 
        printk_ratelimited_in_rcu(KERN_INFO
-                       "BTRFS: read error corrected: ino %lu off %llu "
-                   "(dev %s sector %llu)\n", page->mapping->host->i_ino,
-                   start, rcu_str_deref(dev->name), sector);
-
+                                 "BTRFS: read error corrected: ino %llu off %llu (dev %s sector %llu)\n",
+                                 btrfs_ino(inode), start,
+                                 rcu_str_deref(dev->name), sector);
        bio_put(bio);
        return 0;
 }
@@ -2082,9 +2064,11 @@ int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
                return -EROFS;
 
        for (i = 0; i < num_pages; i++) {
-               struct page *p = extent_buffer_page(eb, i);
-               ret = repair_io_failure(root->fs_info, start, PAGE_CACHE_SIZE,
-                                       start, p, mirror_num);
+               struct page *p = eb->pages[i];
+
+               ret = repair_io_failure(root->fs_info->btree_inode, start,
+                                       PAGE_CACHE_SIZE, start, p,
+                                       start - page_offset(p), mirror_num);
                if (ret)
                        break;
                start += PAGE_CACHE_SIZE;
@@ -2097,16 +2081,15 @@ int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
  * each time an IO finishes, we do a fast check in the IO failure tree
  * to see if we need to process or clean up an io_failure_record
  */
-static int clean_io_failure(u64 start, struct page *page)
+int clean_io_failure(struct inode *inode, u64 start, struct page *page,
+                    unsigned int pg_offset)
 {
        u64 private;
        u64 private_failure;
        struct io_failure_record *failrec;
-       struct inode *inode = page->mapping->host;
        struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
        struct extent_state *state;
        int num_copies;
-       int did_repair = 0;
        int ret;
 
        private = 0;
@@ -2127,7 +2110,6 @@ static int clean_io_failure(u64 start, struct page *page)
                /* there was no real error, just free the record */
                pr_debug("clean_io_failure: freeing dummy error at %llu\n",
                         failrec->start);
-               did_repair = 1;
                goto out;
        }
        if (fs_info->sb->s_flags & MS_RDONLY)
@@ -2144,55 +2126,70 @@ static int clean_io_failure(u64 start, struct page *page)
                num_copies = btrfs_num_copies(fs_info, failrec->logical,
                                              failrec->len);
                if (num_copies > 1)  {
-                       ret = repair_io_failure(fs_info, start, failrec->len,
-                                               failrec->logical, page,
-                                               failrec->failed_mirror);
-                       did_repair = !ret;
+                       repair_io_failure(inode, start, failrec->len,
+                                         failrec->logical, page,
+                                         pg_offset, failrec->failed_mirror);
                }
-               ret = 0;
        }
 
 out:
-       if (!ret)
-               ret = free_io_failure(inode, failrec, did_repair);
+       free_io_failure(inode, failrec);
 
-       return ret;
+       return 0;
 }
 
 /*
- * this is a generic handler for readpage errors (default
- * readpage_io_failed_hook). if other copies exist, read those and write back
- * good data to the failed position. does not investigate in remapping the
- * failed extent elsewhere, hoping the device will be smart enough to do this as
- * needed
+ * Can be called when
+ * - hold extent lock
+ * - under ordered extent
+ * - the inode is freeing
  */
+void btrfs_free_io_failure_record(struct inode *inode, u64 start, u64 end)
+{
+       struct extent_io_tree *failure_tree = &BTRFS_I(inode)->io_failure_tree;
+       struct io_failure_record *failrec;
+       struct extent_state *state, *next;
 
-static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
-                             struct page *page, u64 start, u64 end,
-                             int failed_mirror)
+       if (RB_EMPTY_ROOT(&failure_tree->state))
+               return;
+
+       spin_lock(&failure_tree->lock);
+       state = find_first_extent_bit_state(failure_tree, start, EXTENT_DIRTY);
+       while (state) {
+               if (state->start > end)
+                       break;
+
+               ASSERT(state->end <= end);
+
+               next = next_state(state);
+
+               failrec = (struct io_failure_record *)state->private;
+               free_extent_state(state);
+               kfree(failrec);
+
+               state = next;
+       }
+       spin_unlock(&failure_tree->lock);
+}
+
+int btrfs_get_io_failure_record(struct inode *inode, u64 start, u64 end,
+                               struct io_failure_record **failrec_ret)
 {
-       struct io_failure_record *failrec = NULL;
+       struct io_failure_record *failrec;
        u64 private;
        struct extent_map *em;
-       struct inode *inode = page->mapping->host;
        struct extent_io_tree *failure_tree = &BTRFS_I(inode)->io_failure_tree;
        struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree;
        struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
-       struct bio *bio;
-       struct btrfs_io_bio *btrfs_failed_bio;
-       struct btrfs_io_bio *btrfs_bio;
-       int num_copies;
        int ret;
-       int read_mode;
        u64 logical;
 
-       BUG_ON(failed_bio->bi_rw & REQ_WRITE);
-
        ret = get_state_private(failure_tree, start, &private);
        if (ret) {
                failrec = kzalloc(sizeof(*failrec), GFP_NOFS);
                if (!failrec)
                        return -ENOMEM;
+
                failrec->start = start;
                failrec->len = end - start + 1;
                failrec->this_mirror = 0;
@@ -2212,11 +2209,11 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
                        em = NULL;
                }
                read_unlock(&em_tree->lock);
-
                if (!em) {
                        kfree(failrec);
                        return -EIO;
                }
+
                logical = start - em->start;
                logical = em->block_start + logical;
                if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
@@ -2225,8 +2222,10 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
                        extent_set_compress_type(&failrec->bio_flags,
                                                 em->compress_type);
                }
-               pr_debug("bio_readpage_error: (new) logical=%llu, start=%llu, "
-                        "len=%llu\n", logical, start, failrec->len);
+
+               pr_debug("Get IO Failure Record: (new) logical=%llu, start=%llu, len=%llu\n",
+                        logical, start, failrec->len);
+
                failrec->logical = logical;
                free_extent_map(em);
 
@@ -2246,8 +2245,7 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
                }
        } else {
                failrec = (struct io_failure_record *)(unsigned long)private;
-               pr_debug("bio_readpage_error: (found) logical=%llu, "
-                        "start=%llu, len=%llu, validation=%d\n",
+               pr_debug("Get IO Failure Record: (found) logical=%llu, start=%llu, len=%llu, validation=%d\n",
                         failrec->logical, failrec->start, failrec->len,
                         failrec->in_validation);
                /*
@@ -2256,6 +2254,17 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
                 * clean_io_failure() clean all those errors at once.
                 */
        }
+
+       *failrec_ret = failrec;
+
+       return 0;
+}
+
+int btrfs_check_repairable(struct inode *inode, struct bio *failed_bio,
+                          struct io_failure_record *failrec, int failed_mirror)
+{
+       int num_copies;
+
        num_copies = btrfs_num_copies(BTRFS_I(inode)->root->fs_info,
                                      failrec->logical, failrec->len);
        if (num_copies == 1) {
@@ -2264,10 +2273,9 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
                 * all the retry and error correction code that follows. no
                 * matter what the error is, it is very likely to persist.
                 */
-               pr_debug("bio_readpage_error: cannot repair, num_copies=%d, next_mirror %d, failed_mirror %d\n",
+               pr_debug("Check Repairable: cannot repair, num_copies=%d, next_mirror %d, failed_mirror %d\n",
                         num_copies, failrec->this_mirror, failed_mirror);
-               free_io_failure(inode, failrec, 0);
-               return -EIO;
+               return 0;
        }
 
        /*
@@ -2287,7 +2295,6 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
                BUG_ON(failrec->in_validation);
                failrec->in_validation = 1;
                failrec->this_mirror = failed_mirror;
-               read_mode = READ_SYNC | REQ_FAILFAST_DEV;
        } else {
                /*
                 * we're ready to fulfill a) and b) alongside. get a good copy
@@ -2303,25 +2310,36 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
                failrec->this_mirror++;
                if (failrec->this_mirror == failed_mirror)
                        failrec->this_mirror++;
-               read_mode = READ_SYNC;
        }
 
        if (failrec->this_mirror > num_copies) {
-               pr_debug("bio_readpage_error: (fail) num_copies=%d, next_mirror %d, failed_mirror %d\n",
+               pr_debug("Check Repairable: (fail) num_copies=%d, next_mirror %d, failed_mirror %d\n",
                         num_copies, failrec->this_mirror, failed_mirror);
-               free_io_failure(inode, failrec, 0);
-               return -EIO;
+               return 0;
        }
 
+       return 1;
+}
+
+
+struct bio *btrfs_create_repair_bio(struct inode *inode, struct bio *failed_bio,
+                                   struct io_failure_record *failrec,
+                                   struct page *page, int pg_offset, int icsum,
+                                   bio_end_io_t *endio_func, void *data)
+{
+       struct bio *bio;
+       struct btrfs_io_bio *btrfs_failed_bio;
+       struct btrfs_io_bio *btrfs_bio;
+
        bio = btrfs_io_bio_alloc(GFP_NOFS, 1);
-       if (!bio) {
-               free_io_failure(inode, failrec, 0);
-               return -EIO;
-       }
-       bio->bi_end_io = failed_bio->bi_end_io;
+       if (!bio)
+               return NULL;
+
+       bio->bi_end_io = endio_func;
        bio->bi_iter.bi_sector = failrec->logical >> 9;
        bio->bi_bdev = BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev;
        bio->bi_iter.bi_size = 0;
+       bio->bi_private = data;
 
        btrfs_failed_bio = btrfs_io_bio(failed_bio);
        if (btrfs_failed_bio->csum) {
@@ -2330,21 +2348,73 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
 
                btrfs_bio = btrfs_io_bio(bio);
                btrfs_bio->csum = btrfs_bio->csum_inline;
-               phy_offset >>= inode->i_sb->s_blocksize_bits;
-               phy_offset *= csum_size;
-               memcpy(btrfs_bio->csum, btrfs_failed_bio->csum + phy_offset,
+               icsum *= csum_size;
+               memcpy(btrfs_bio->csum, btrfs_failed_bio->csum + icsum,
                       csum_size);
        }
 
-       bio_add_page(bio, page, failrec->len, start - page_offset(page));
+       bio_add_page(bio, page, failrec->len, pg_offset);
+
+       return bio;
+}
+
+/*
+ * this is a generic handler for readpage errors (default
+ * readpage_io_failed_hook). if other copies exist, read those and write back
+ * good data to the failed position. does not investigate in remapping the
+ * failed extent elsewhere, hoping the device will be smart enough to do this as
+ * needed
+ */
+
+static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
+                             struct page *page, u64 start, u64 end,
+                             int failed_mirror)
+{
+       struct io_failure_record *failrec;
+       struct inode *inode = page->mapping->host;
+       struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree;
+       struct bio *bio;
+       int read_mode;
+       int ret;
+
+       BUG_ON(failed_bio->bi_rw & REQ_WRITE);
+
+       ret = btrfs_get_io_failure_record(inode, start, end, &failrec);
+       if (ret)
+               return ret;
+
+       ret = btrfs_check_repairable(inode, failed_bio, failrec, failed_mirror);
+       if (!ret) {
+               free_io_failure(inode, failrec);
+               return -EIO;
+       }
+
+       if (failed_bio->bi_vcnt > 1)
+               read_mode = READ_SYNC | REQ_FAILFAST_DEV;
+       else
+               read_mode = READ_SYNC;
+
+       phy_offset >>= inode->i_sb->s_blocksize_bits;
+       bio = btrfs_create_repair_bio(inode, failed_bio, failrec, page,
+                                     start - page_offset(page),
+                                     (int)phy_offset, failed_bio->bi_end_io,
+                                     NULL);
+       if (!bio) {
+               free_io_failure(inode, failrec);
+               return -EIO;
+       }
 
-       pr_debug("bio_readpage_error: submitting new read[%#x] to "
-                "this_mirror=%d, num_copies=%d, in_validation=%d\n", read_mode,
-                failrec->this_mirror, num_copies, failrec->in_validation);
+       pr_debug("Repair Read Error: submitting new read[%#x] to this_mirror=%d, in_validation=%d\n",
+                read_mode, failrec->this_mirror, failrec->in_validation);
 
        ret = tree->ops->submit_bio_hook(inode, read_mode, bio,
                                         failrec->this_mirror,
                                         failrec->bio_flags, 0);
+       if (ret) {
+               free_io_failure(inode, failrec);
+               bio_put(bio);
+       }
+
        return ret;
 }
 
@@ -2469,7 +2539,7 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
                struct inode *inode = page->mapping->host;
 
                pr_debug("end_bio_extent_readpage: bi_sector=%llu, err=%d, "
-                        "mirror=%lu\n", (u64)bio->bi_iter.bi_sector, err,
+                        "mirror=%u\n", (u64)bio->bi_iter.bi_sector, err,
                         io_bio->mirror_num);
                tree = &BTRFS_I(inode)->io_tree;
 
@@ -2503,7 +2573,7 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
                        if (ret)
                                uptodate = 0;
                        else
-                               clean_io_failure(start, page);
+                               clean_io_failure(inode, start, page, 0);
                }
 
                if (likely(uptodate))
@@ -2540,12 +2610,12 @@ readpage_ok:
                if (likely(uptodate)) {
                        loff_t i_size = i_size_read(inode);
                        pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
-                       unsigned offset;
+                       unsigned off;
 
                        /* Zero out the end if this page straddles i_size */
-                       offset = i_size & (PAGE_CACHE_SIZE-1);
-                       if (page->index == end_index && offset)
-                               zero_user_segment(page, offset, PAGE_CACHE_SIZE);
+                       off = i_size & (PAGE_CACHE_SIZE-1);
+                       if (page->index == end_index && off)
+                               zero_user_segment(page, off, PAGE_CACHE_SIZE);
                        SetPageUptodate(page);
                } else {
                        ClearPageUptodate(page);
@@ -2618,9 +2688,18 @@ btrfs_bio_alloc(struct block_device *bdev, u64 first_sector, int nr_vecs,
 
 struct bio *btrfs_bio_clone(struct bio *bio, gfp_t gfp_mask)
 {
-       return bio_clone_bioset(bio, gfp_mask, btrfs_bioset);
-}
+       struct btrfs_io_bio *btrfs_bio;
+       struct bio *new;
 
+       new = bio_clone_bioset(bio, gfp_mask, btrfs_bioset);
+       if (new) {
+               btrfs_bio = btrfs_io_bio(new);
+               btrfs_bio->csum = NULL;
+               btrfs_bio->csum_allocated = NULL;
+               btrfs_bio->end_io = NULL;
+       }
+       return new;
+}
 
 /* this also allocates from the btrfs_bioset */
 struct bio *btrfs_io_bio_alloc(gfp_t gfp_mask, unsigned int nr_iovecs)
@@ -3501,7 +3580,7 @@ lock_extent_buffer_for_io(struct extent_buffer *eb,
 
        num_pages = num_extent_pages(eb->start, eb->len);
        for (i = 0; i < num_pages; i++) {
-               struct page *p = extent_buffer_page(eb, i);
+               struct page *p = eb->pages[i];
 
                if (!trylock_page(p)) {
                        if (!flush) {
@@ -3522,6 +3601,68 @@ static void end_extent_buffer_writeback(struct extent_buffer *eb)
        wake_up_bit(&eb->bflags, EXTENT_BUFFER_WRITEBACK);
 }
 
+static void set_btree_ioerr(struct page *page)
+{
+       struct extent_buffer *eb = (struct extent_buffer *)page->private;
+       struct btrfs_inode *btree_ino = BTRFS_I(eb->fs_info->btree_inode);
+
+       SetPageError(page);
+       if (test_and_set_bit(EXTENT_BUFFER_WRITE_ERR, &eb->bflags))
+               return;
+
+       /*
+        * If writeback for a btree extent that doesn't belong to a log tree
+        * failed, increment the counter transaction->eb_write_errors.
+        * We do this because while the transaction is running and before it's
+        * committing (when we call filemap_fdata[write|wait]_range against
+        * the btree inode), we might have
+        * btree_inode->i_mapping->a_ops->writepages() called by the VM - if it
+        * returns an error or an error happens during writeback, when we're
+        * committing the transaction we wouldn't know about it, since the pages
+        * can be no longer dirty nor marked anymore for writeback (if a
+        * subsequent modification to the extent buffer didn't happen before the
+        * transaction commit), which makes filemap_fdata[write|wait]_range not
+        * able to find the pages tagged with SetPageError at transaction
+        * commit time. So if this happens we must abort the transaction,
+        * otherwise we commit a super block with btree roots that point to
+        * btree nodes/leafs whose content on disk is invalid - either garbage
+        * or the content of some node/leaf from a past generation that got
+        * cowed or deleted and is no longer valid.
+        *
+        * Note: setting AS_EIO/AS_ENOSPC in the btree inode's i_mapping would
+        * not be enough - we need to distinguish between log tree extents vs
+        * non-log tree extents, and the next filemap_fdatawait_range() call
+        * will catch and clear such errors in the mapping - and that call might
+        * be from a log sync and not from a transaction commit. Also, checking
+        * for the eb flag EXTENT_BUFFER_WRITE_ERR at transaction commit time is
+        * not done and would not be reliable - the eb might have been released
+        * from memory and reading it back again means that flag would not be
+        * set (since it's a runtime flag, not persisted on disk).
+        *
+        * Using the flags below in the btree inode also makes us achieve the
+        * goal of AS_EIO/AS_ENOSPC when writepages() returns success, started
+        * writeback for all dirty pages and before filemap_fdatawait_range()
+        * is called, the writeback for all dirty pages had already finished
+        * with errors - because we were not using AS_EIO/AS_ENOSPC,
+        * filemap_fdatawait_range() would return success, as it could not know
+        * that writeback errors happened (the pages were no longer tagged for
+        * writeback).
+        */
+       switch (eb->log_index) {
+       case -1:
+               set_bit(BTRFS_INODE_BTREE_ERR, &btree_ino->runtime_flags);
+               break;
+       case 0:
+               set_bit(BTRFS_INODE_BTREE_LOG1_ERR, &btree_ino->runtime_flags);
+               break;
+       case 1:
+               set_bit(BTRFS_INODE_BTREE_LOG2_ERR, &btree_ino->runtime_flags);
+               break;
+       default:
+               BUG(); /* unexpected, logic error */
+       }
+}
+
 static void end_bio_extent_buffer_writepage(struct bio *bio, int err)
 {
        struct bio_vec *bvec;
@@ -3535,10 +3676,9 @@ static void end_bio_extent_buffer_writepage(struct bio *bio, int err)
                BUG_ON(!eb);
                done = atomic_dec_and_test(&eb->io_pages);
 
-               if (err || test_bit(EXTENT_BUFFER_IOERR, &eb->bflags)) {
-                       set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
+               if (err || test_bit(EXTENT_BUFFER_WRITE_ERR, &eb->bflags)) {
                        ClearPageUptodate(page);
-                       SetPageError(page);
+                       set_btree_ioerr(page);
                }
 
                end_page_writeback(page);
@@ -3565,14 +3705,14 @@ static noinline_for_stack int write_one_eb(struct extent_buffer *eb,
        int rw = (epd->sync_io ? WRITE_SYNC : WRITE) | REQ_META;
        int ret = 0;
 
-       clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
+       clear_bit(EXTENT_BUFFER_WRITE_ERR, &eb->bflags);
        num_pages = num_extent_pages(eb->start, eb->len);
        atomic_set(&eb->io_pages, num_pages);
        if (btrfs_header_owner(eb) == BTRFS_TREE_LOG_OBJECTID)
                bio_flags = EXTENT_BIO_TREE_LOG;
 
        for (i = 0; i < num_pages; i++) {
-               struct page *p = extent_buffer_page(eb, i);
+               struct page *p = eb->pages[i];
 
                clear_page_dirty_for_io(p);
                set_page_writeback(p);
@@ -3582,8 +3722,8 @@ static noinline_for_stack int write_one_eb(struct extent_buffer *eb,
                                         0, epd->bio_flags, bio_flags);
                epd->bio_flags = bio_flags;
                if (ret) {
-                       set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
-                       SetPageError(p);
+                       set_btree_ioerr(p);
+                       end_page_writeback(p);
                        if (atomic_sub_and_test(num_pages - i, &eb->io_pages))
                                end_extent_buffer_writeback(eb);
                        ret = -EIO;
@@ -3596,7 +3736,8 @@ static noinline_for_stack int write_one_eb(struct extent_buffer *eb,
 
        if (unlikely(ret)) {
                for (; i < num_pages; i++) {
-                       struct page *p = extent_buffer_page(eb, i);
+                       struct page *p = eb->pages[i];
+                       clear_page_dirty_for_io(p);
                        unlock_page(p);
                }
        }
@@ -4166,19 +4307,6 @@ static struct extent_map *get_extent_skip_holes(struct inode *inode,
        return NULL;
 }
 
-static noinline int count_ext_ref(u64 inum, u64 offset, u64 root_id, void *ctx)
-{
-       unsigned long cnt = *((unsigned long *)ctx);
-
-       cnt++;
-       *((unsigned long *)ctx) = cnt;
-
-       /* Now we're sure that the extent is shared. */
-       if (cnt > 1)
-               return 1;
-       return 0;
-}
-
 int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                __u64 start, __u64 len, get_extent_t *get_extent)
 {
@@ -4195,6 +4323,7 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
        struct extent_map *em = NULL;
        struct extent_state *cached_state = NULL;
        struct btrfs_path *path;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
        int end = 0;
        u64 em_start = 0;
        u64 em_len = 0;
@@ -4215,8 +4344,8 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
         * lookup the last file extent.  We're not using i_size here
         * because there might be preallocation past i_size
         */
-       ret = btrfs_lookup_file_extent(NULL, BTRFS_I(inode)->root,
-                                      path, btrfs_ino(inode), -1, 0);
+       ret = btrfs_lookup_file_extent(NULL, root, path, btrfs_ino(inode), -1,
+                                      0);
        if (ret < 0) {
                btrfs_free_path(path);
                return ret;
@@ -4224,7 +4353,7 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
        WARN_ON(!ret);
        path->slots[0]--;
        btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]);
-       found_type = btrfs_key_type(&found_key);
+       found_type = found_key.type;
 
        /* No extents, but there might be delalloc bits */
        if (found_key.objectid != btrfs_ino(inode) ||
@@ -4309,25 +4438,27 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                } else if (em->block_start == EXTENT_MAP_DELALLOC) {
                        flags |= (FIEMAP_EXTENT_DELALLOC |
                                  FIEMAP_EXTENT_UNKNOWN);
-               } else {
-                       unsigned long ref_cnt = 0;
+               } else if (fieinfo->fi_extents_max) {
+                       u64 bytenr = em->block_start -
+                               (em->start - em->orig_start);
 
                        disko = em->block_start + offset_in_extent;
 
                        /*
                         * As btrfs supports shared space, this information
                         * can be exported to userspace tools via
-                        * flag FIEMAP_EXTENT_SHARED.
+                        * flag FIEMAP_EXTENT_SHARED.  If fi_extents_max == 0
+                        * then we're just getting a count and we can skip the
+                        * lookup stuff.
                         */
-                       ret = iterate_inodes_from_logical(
-                                       em->block_start,
-                                       BTRFS_I(inode)->root->fs_info,
-                                       path, count_ext_ref, &ref_cnt);
-                       if (ret < 0 && ret != -ENOENT)
+                       ret = btrfs_check_shared(NULL, root->fs_info,
+                                                root->objectid,
+                                                btrfs_ino(inode), bytenr);
+                       if (ret < 0)
                                goto out_free;
-
-                       if (ref_cnt > 1)
+                       if (ret)
                                flags |= FIEMAP_EXTENT_SHARED;
+                       ret = 0;
                }
                if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags))
                        flags |= FIEMAP_EXTENT_ENCODED;
@@ -4381,24 +4512,21 @@ int extent_buffer_under_io(struct extent_buffer *eb)
 /*
  * Helper for releasing extent buffer page.
  */
-static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
-                                               unsigned long start_idx)
+static void btrfs_release_extent_buffer_page(struct extent_buffer *eb)
 {
        unsigned long index;
-       unsigned long num_pages;
        struct page *page;
        int mapped = !test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags);
 
        BUG_ON(extent_buffer_under_io(eb));
 
-       num_pages = num_extent_pages(eb->start, eb->len);
-       index = start_idx + num_pages;
-       if (start_idx >= index)
+       index = num_extent_pages(eb->start, eb->len);
+       if (index == 0)
                return;
 
        do {
                index--;
-               page = extent_buffer_page(eb, index);
+               page = eb->pages[index];
                if (page && mapped) {
                        spin_lock(&page->mapping->private_lock);
                        /*
@@ -4429,7 +4557,7 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
                        /* One for when we alloced the page */
                        page_cache_release(page);
                }
-       } while (index != start_idx);
+       } while (index != 0);
 }
 
 /*
@@ -4437,7 +4565,7 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
  */
 static inline void btrfs_release_extent_buffer(struct extent_buffer *eb)
 {
-       btrfs_release_extent_buffer_page(eb, 0);
+       btrfs_release_extent_buffer_page(eb);
        __free_extent_buffer(eb);
 }
 
@@ -4580,7 +4708,8 @@ static void mark_extent_buffer_accessed(struct extent_buffer *eb,
 
        num_pages = num_extent_pages(eb->start, eb->len);
        for (i = 0; i < num_pages; i++) {
-               struct page *p = extent_buffer_page(eb, i);
+               struct page *p = eb->pages[i];
+
                if (p != accessed)
                        mark_page_accessed(p);
        }
@@ -4749,7 +4878,7 @@ again:
         */
        SetPageChecked(eb->pages[0]);
        for (i = 1; i < num_pages; i++) {
-               p = extent_buffer_page(eb, i);
+               p = eb->pages[i];
                ClearPageChecked(p);
                unlock_page(p);
        }
@@ -4794,7 +4923,7 @@ static int release_extent_buffer(struct extent_buffer *eb)
                }
 
                /* Should be safe to release our pages at this point */
-               btrfs_release_extent_buffer_page(eb, 0);
+               btrfs_release_extent_buffer_page(eb);
                call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu);
                return 1;
        }
@@ -4860,7 +4989,7 @@ void clear_extent_buffer_dirty(struct extent_buffer *eb)
        num_pages = num_extent_pages(eb->start, eb->len);
 
        for (i = 0; i < num_pages; i++) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
                if (!PageDirty(page))
                        continue;
 
@@ -4896,7 +5025,7 @@ int set_extent_buffer_dirty(struct extent_buffer *eb)
        WARN_ON(!test_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags));
 
        for (i = 0; i < num_pages; i++)
-               set_page_dirty(extent_buffer_page(eb, i));
+               set_page_dirty(eb->pages[i]);
        return was_dirty;
 }
 
@@ -4909,7 +5038,7 @@ int clear_extent_buffer_uptodate(struct extent_buffer *eb)
        clear_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
        num_pages = num_extent_pages(eb->start, eb->len);
        for (i = 0; i < num_pages; i++) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
                if (page)
                        ClearPageUptodate(page);
        }
@@ -4925,7 +5054,7 @@ int set_extent_buffer_uptodate(struct extent_buffer *eb)
        set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
        num_pages = num_extent_pages(eb->start, eb->len);
        for (i = 0; i < num_pages; i++) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
                SetPageUptodate(page);
        }
        return 0;
@@ -4965,7 +5094,7 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
 
        num_pages = num_extent_pages(eb->start, eb->len);
        for (i = start_i; i < num_pages; i++) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
                if (wait == WAIT_NONE) {
                        if (!trylock_page(page))
                                goto unlock_exit;
@@ -4984,11 +5113,11 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
                goto unlock_exit;
        }
 
-       clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
+       clear_bit(EXTENT_BUFFER_READ_ERR, &eb->bflags);
        eb->read_mirror = 0;
        atomic_set(&eb->io_pages, num_reads);
        for (i = start_i; i < num_pages; i++) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
                if (!PageUptodate(page)) {
                        ClearPageError(page);
                        err = __extent_read_full_page(tree, page,
@@ -5013,7 +5142,7 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
                return ret;
 
        for (i = start_i; i < num_pages; i++) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
                wait_on_page_locked(page);
                if (!PageUptodate(page))
                        ret = -EIO;
@@ -5024,7 +5153,7 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
 unlock_exit:
        i = start_i;
        while (locked_pages > 0) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
                i++;
                unlock_page(page);
                locked_pages--;
@@ -5050,7 +5179,7 @@ void read_extent_buffer(struct extent_buffer *eb, void *dstv,
        offset = (start_offset + start) & (PAGE_CACHE_SIZE - 1);
 
        while (len > 0) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
 
                cur = min(len, (PAGE_CACHE_SIZE - offset));
                kaddr = page_address(page);
@@ -5082,7 +5211,7 @@ int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dstv,
        offset = (start_offset + start) & (PAGE_CACHE_SIZE - 1);
 
        while (len > 0) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
 
                cur = min(len, (PAGE_CACHE_SIZE - offset));
                kaddr = page_address(page);
@@ -5131,7 +5260,7 @@ int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
                return -EINVAL;
        }
 
-       p = extent_buffer_page(eb, i);
+       p = eb->pages[i];
        kaddr = page_address(p);
        *map = kaddr + offset;
        *map_len = PAGE_CACHE_SIZE - offset;
@@ -5157,7 +5286,7 @@ int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv,
        offset = (start_offset + start) & (PAGE_CACHE_SIZE - 1);
 
        while (len > 0) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
 
                cur = min(len, (PAGE_CACHE_SIZE - offset));
 
@@ -5191,7 +5320,7 @@ void write_extent_buffer(struct extent_buffer *eb, const void *srcv,
        offset = (start_offset + start) & (PAGE_CACHE_SIZE - 1);
 
        while (len > 0) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
                WARN_ON(!PageUptodate(page));
 
                cur = min(len, PAGE_CACHE_SIZE - offset);
@@ -5221,7 +5350,7 @@ void memset_extent_buffer(struct extent_buffer *eb, char c,
        offset = (start_offset + start) & (PAGE_CACHE_SIZE - 1);
 
        while (len > 0) {
-               page = extent_buffer_page(eb, i);
+               page = eb->pages[i];
                WARN_ON(!PageUptodate(page));
 
                cur = min(len, PAGE_CACHE_SIZE - offset);
@@ -5252,7 +5381,7 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
                (PAGE_CACHE_SIZE - 1);
 
        while (len > 0) {
-               page = extent_buffer_page(dst, i);
+               page = dst->pages[i];
                WARN_ON(!PageUptodate(page));
 
                cur = min(len, (unsigned long)(PAGE_CACHE_SIZE - offset));
@@ -5330,8 +5459,7 @@ void memcpy_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
                cur = min_t(unsigned long, cur,
                        (unsigned long)(PAGE_CACHE_SIZE - dst_off_in_page));
 
-               copy_pages(extent_buffer_page(dst, dst_i),
-                          extent_buffer_page(dst, src_i),
+               copy_pages(dst->pages[dst_i], dst->pages[src_i],
                           dst_off_in_page, src_off_in_page, cur);
 
                src_offset += cur;
@@ -5377,8 +5505,7 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
 
                cur = min_t(unsigned long, len, src_off_in_page + 1);
                cur = min(cur, dst_off_in_page + 1);
-               copy_pages(extent_buffer_page(dst, dst_i),
-                          extent_buffer_page(dst, src_i),
+               copy_pages(dst->pages[dst_i], dst->pages[src_i],
                           dst_off_in_page - cur + 1,
                           src_off_in_page - cur + 1, cur);
 
index ccc264e7bde12760035dacfd40cafb25b663de4e..6d4b938be98678660cc51fb55bd51a5688279ed8 100644 (file)
@@ -11,8 +11,6 @@
 #define EXTENT_NEW (1 << 4)
 #define EXTENT_DELALLOC (1 << 5)
 #define EXTENT_DEFRAG (1 << 6)
-#define EXTENT_DEFRAG_DONE (1 << 7)
-#define EXTENT_BUFFER_FILLED (1 << 8)
 #define EXTENT_BOUNDARY (1 << 9)
 #define EXTENT_NODATASUM (1 << 10)
 #define EXTENT_DO_ACCOUNTING (1 << 11)
 
 /* these are bit numbers for test/set bit */
 #define EXTENT_BUFFER_UPTODATE 0
-#define EXTENT_BUFFER_BLOCKING 1
 #define EXTENT_BUFFER_DIRTY 2
 #define EXTENT_BUFFER_CORRUPT 3
 #define EXTENT_BUFFER_READAHEAD 4      /* this got triggered by readahead */
 #define EXTENT_BUFFER_TREE_REF 5
 #define EXTENT_BUFFER_STALE 6
 #define EXTENT_BUFFER_WRITEBACK 7
-#define EXTENT_BUFFER_IOERR 8
+#define EXTENT_BUFFER_READ_ERR 8        /* read IO error */
 #define EXTENT_BUFFER_DUMMY 9
 #define EXTENT_BUFFER_IN_TREE 10
+#define EXTENT_BUFFER_WRITE_ERR 11    /* write IO error */
 
 /* these are flags for extent_clear_unlock_delalloc */
 #define PAGE_UNLOCK            (1 << 0)
@@ -57,7 +55,6 @@
  * map has page->private set to one.
  */
 #define EXTENT_PAGE_PRIVATE 1
-#define EXTENT_PAGE_PRIVATE_FIRST_PAGE 3
 
 struct extent_state;
 struct btrfs_root;
@@ -108,7 +105,6 @@ struct extent_state {
        struct rb_node rb_node;
 
        /* ADD NEW ELEMENTS AFTER THIS */
-       struct extent_io_tree *tree;
        wait_queue_head_t wq;
        atomic_t refs;
        unsigned long state;
@@ -126,8 +122,6 @@ struct extent_state {
 struct extent_buffer {
        u64 start;
        unsigned long len;
-       unsigned long map_start;
-       unsigned long map_len;
        unsigned long bflags;
        struct btrfs_fs_info *fs_info;
        spinlock_t refs_lock;
@@ -144,7 +138,9 @@ struct extent_buffer {
        atomic_t blocking_readers;
        atomic_t spinning_readers;
        atomic_t spinning_writers;
-       int lock_nested;
+       short lock_nested;
+       /* >= 0 if eb belongs to a log tree, -1 otherwise */
+       short log_index;
 
        /* protects write locks */
        rwlock_t lock;
@@ -286,12 +282,6 @@ static inline unsigned long num_extent_pages(u64 start, u64 len)
                (start >> PAGE_CACHE_SHIFT);
 }
 
-static inline struct page *extent_buffer_page(struct extent_buffer *eb,
-                                             unsigned long i)
-{
-       return eb->pages[i];
-}
-
 static inline void extent_buffer_get(struct extent_buffer *eb)
 {
        atomic_inc(&eb->refs);
@@ -341,18 +331,50 @@ struct bio *btrfs_bio_clone(struct bio *bio, gfp_t gfp_mask);
 
 struct btrfs_fs_info;
 
-int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start,
-                       u64 length, u64 logical, struct page *page,
-                       int mirror_num);
+int repair_io_failure(struct inode *inode, u64 start, u64 length, u64 logical,
+                     struct page *page, unsigned int pg_offset,
+                     int mirror_num);
+int clean_io_failure(struct inode *inode, u64 start, struct page *page,
+                    unsigned int pg_offset);
 int end_extent_writepage(struct page *page, int err, u64 start, u64 end);
 int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
                         int mirror_num);
+
+/*
+ * When IO fails, either with EIO or csum verification fails, we
+ * try other mirrors that might have a good copy of the data.  This
+ * io_failure_record is used to record state as we go through all the
+ * mirrors.  If another mirror has good data, the page is set up to date
+ * and things continue.  If a good mirror can't be found, the original
+ * bio end_io callback is called to indicate things have failed.
+ */
+struct io_failure_record {
+       struct page *page;
+       u64 start;
+       u64 len;
+       u64 logical;
+       unsigned long bio_flags;
+       int this_mirror;
+       int failed_mirror;
+       int in_validation;
+};
+
+void btrfs_free_io_failure_record(struct inode *inode, u64 start, u64 end);
+int btrfs_get_io_failure_record(struct inode *inode, u64 start, u64 end,
+                               struct io_failure_record **failrec_ret);
+int btrfs_check_repairable(struct inode *inode, struct bio *failed_bio,
+                          struct io_failure_record *failrec, int fail_mirror);
+struct bio *btrfs_create_repair_bio(struct inode *inode, struct bio *failed_bio,
+                                   struct io_failure_record *failrec,
+                                   struct page *page, int pg_offset, int icsum,
+                                   bio_end_io_t *endio_func, void *data);
+int free_io_failure(struct inode *inode, struct io_failure_record *rec);
 #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
 noinline u64 find_lock_delalloc_range(struct inode *inode,
                                      struct extent_io_tree *tree,
                                      struct page *locked_page, u64 *start,
                                      u64 *end, u64 max_bytes);
+#endif
 struct extent_buffer *alloc_test_extent_buffer(struct btrfs_fs_info *fs_info,
                                               u64 start, unsigned long len);
 #endif
-#endif
index 54c84daec9b515f73f8f1178a1e4ea9485d20e3f..783a94355efd0be2acd75d1a84b3860ec7a28740 100644 (file)
@@ -55,7 +55,7 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
                return -ENOMEM;
        file_key.objectid = objectid;
        file_key.offset = pos;
-       btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
+       file_key.type = BTRFS_EXTENT_DATA_KEY;
 
        path->leave_spinning = 1;
        ret = btrfs_insert_empty_item(trans, root, path, &file_key,
@@ -100,7 +100,7 @@ btrfs_lookup_csum(struct btrfs_trans_handle *trans,
 
        file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
        file_key.offset = bytenr;
-       btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY);
+       file_key.type = BTRFS_EXTENT_CSUM_KEY;
        ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow);
        if (ret < 0)
                goto fail;
@@ -111,7 +111,7 @@ btrfs_lookup_csum(struct btrfs_trans_handle *trans,
                        goto fail;
                path->slots[0]--;
                btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-               if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY)
+               if (found_key.type != BTRFS_EXTENT_CSUM_KEY)
                        goto fail;
 
                csum_offset = (bytenr - found_key.offset) >>
@@ -148,7 +148,7 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
 
        file_key.objectid = objectid;
        file_key.offset = offset;
-       btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
+       file_key.type = BTRFS_EXTENT_DATA_KEY;
        ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow);
        return ret;
 }
@@ -299,19 +299,9 @@ int btrfs_lookup_bio_sums(struct btrfs_root *root, struct inode *inode,
 }
 
 int btrfs_lookup_bio_sums_dio(struct btrfs_root *root, struct inode *inode,
-                             struct btrfs_dio_private *dip, struct bio *bio,
-                             u64 offset)
+                             struct bio *bio, u64 offset)
 {
-       int len = (bio->bi_iter.bi_sector << 9) - dip->disk_bytenr;
-       u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
-       int ret;
-
-       len >>= inode->i_sb->s_blocksize_bits;
-       len *= csum_size;
-
-       ret = __btrfs_lookup_bio_sums(root, inode, bio, offset,
-                                     (u32 *)(dip->csum + len), 1);
-       return ret;
+       return __btrfs_lookup_bio_sums(root, inode, bio, offset, NULL, 1);
 }
 
 int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
@@ -329,8 +319,8 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
        u64 csum_end;
        u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
 
-       ASSERT(start == ALIGN(start, root->sectorsize) &&
-              (end + 1) == ALIGN(end + 1, root->sectorsize));
+       ASSERT(IS_ALIGNED(start, root->sectorsize) &&
+              IS_ALIGNED(end + 1, root->sectorsize));
 
        path = btrfs_alloc_path();
        if (!path)
@@ -720,7 +710,7 @@ again:
        bytenr = sums->bytenr + total_bytes;
        file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
        file_key.offset = bytenr;
-       btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY);
+       file_key.type = BTRFS_EXTENT_CSUM_KEY;
 
        item = btrfs_lookup_csum(trans, root, path, bytenr, 1);
        if (!IS_ERR(item)) {
@@ -790,7 +780,7 @@ again:
        csum_offset = (bytenr - found_key.offset) >>
                        root->fs_info->sb->s_blocksize_bits;
 
-       if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY ||
+       if (found_key.type != BTRFS_EXTENT_CSUM_KEY ||
            found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID ||
            csum_offset >= MAX_CSUM_ITEMS(root, csum_size)) {
                goto insert;
index ff1cc0399b9a206c127b4707b6dcd69d455d8231..a18ceabd99a87978f785e2ef0b6318eab3b80ab9 100644 (file)
@@ -299,7 +299,7 @@ static int __btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info,
 
        /* get the inode */
        key.objectid = defrag->root;
-       btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+       key.type = BTRFS_ROOT_ITEM_KEY;
        key.offset = (u64)-1;
 
        index = srcu_read_lock(&fs_info->subvol_srcu);
@@ -311,7 +311,7 @@ static int __btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info,
        }
 
        key.objectid = defrag->ino;
-       btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
+       key.type = BTRFS_INODE_ITEM_KEY;
        key.offset = 0;
        inode = btrfs_iget(fs_info->sb, &key, inode_root, NULL);
        if (IS_ERR(inode)) {
@@ -452,7 +452,7 @@ static noinline int btrfs_copy_from_user(loff_t pos, int num_pages,
                if (unlikely(copied == 0))
                        break;
 
-               if (unlikely(copied < PAGE_CACHE_SIZE - offset)) {
+               if (copied < PAGE_CACHE_SIZE - offset) {
                        offset += copied;
                } else {
                        pg++;
@@ -1481,9 +1481,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
        bool force_page_uptodate = false;
        bool need_unlock;
 
-       nrptrs = min((iov_iter_count(i) + PAGE_CACHE_SIZE - 1) /
-                    PAGE_CACHE_SIZE, PAGE_CACHE_SIZE /
-                    (sizeof(struct page *)));
+       nrptrs = min(DIV_ROUND_UP(iov_iter_count(i), PAGE_CACHE_SIZE),
+                       PAGE_CACHE_SIZE / (sizeof(struct page *)));
        nrptrs = min(nrptrs, current->nr_dirtied_pause - current->nr_dirtied);
        nrptrs = max(nrptrs, 8);
        pages = kmalloc(nrptrs * sizeof(struct page *), GFP_KERNEL);
@@ -1497,8 +1496,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                size_t write_bytes = min(iov_iter_count(i),
                                         nrptrs * (size_t)PAGE_CACHE_SIZE -
                                         offset);
-               size_t num_pages = (write_bytes + offset +
-                                   PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+               size_t num_pages = DIV_ROUND_UP(write_bytes + offset,
+                                               PAGE_CACHE_SIZE);
                size_t reserve_bytes;
                size_t dirty_pages;
                size_t copied;
@@ -1526,9 +1525,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                                 * our prealloc extent may be smaller than
                                 * write_bytes, so scale down.
                                 */
-                               num_pages = (write_bytes + offset +
-                                            PAGE_CACHE_SIZE - 1) >>
-                                       PAGE_CACHE_SHIFT;
+                               num_pages = DIV_ROUND_UP(write_bytes + offset,
+                                                        PAGE_CACHE_SIZE);
                                reserve_bytes = num_pages << PAGE_CACHE_SHIFT;
                                ret = 0;
                        } else {
@@ -1590,9 +1588,8 @@ again:
                        dirty_pages = 0;
                } else {
                        force_page_uptodate = false;
-                       dirty_pages = (copied + offset +
-                                      PAGE_CACHE_SIZE - 1) >>
-                                      PAGE_CACHE_SHIFT;
+                       dirty_pages = DIV_ROUND_UP(copied + offset,
+                                                  PAGE_CACHE_SIZE);
                }
 
                /*
@@ -1653,7 +1650,7 @@ again:
                cond_resched();
 
                balance_dirty_pages_ratelimited(inode->i_mapping);
-               if (dirty_pages < (root->leafsize >> PAGE_CACHE_SHIFT) + 1)
+               if (dirty_pages < (root->nodesize >> PAGE_CACHE_SHIFT) + 1)
                        btrfs_btree_balance_dirty(root);
 
                pos += copied;
@@ -1795,7 +1792,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
        if (sync)
                atomic_inc(&BTRFS_I(inode)->sync_writers);
 
-       if (unlikely(file->f_flags & O_DIRECT)) {
+       if (file->f_flags & O_DIRECT) {
                num_written = __btrfs_direct_write(iocb, from, pos);
        } else {
                num_written = __btrfs_buffered_write(file, from, pos);
@@ -1852,6 +1849,20 @@ int btrfs_release_file(struct inode *inode, struct file *filp)
        return 0;
 }
 
+static int start_ordered_ops(struct inode *inode, loff_t start, loff_t end)
+{
+       int ret;
+
+       atomic_inc(&BTRFS_I(inode)->sync_writers);
+       ret = filemap_fdatawrite_range(inode->i_mapping, start, end);
+       if (!ret && test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
+                            &BTRFS_I(inode)->runtime_flags))
+               ret = filemap_fdatawrite_range(inode->i_mapping, start, end);
+       atomic_dec(&BTRFS_I(inode)->sync_writers);
+
+       return ret;
+}
+
 /*
  * fsync call for both files and directories.  This logs the inode into
  * the tree log instead of forcing full commits whenever possible.
@@ -1881,30 +1892,64 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
         * multi-task, and make the performance up.  See
         * btrfs_wait_ordered_range for an explanation of the ASYNC check.
         */
-       atomic_inc(&BTRFS_I(inode)->sync_writers);
-       ret = filemap_fdatawrite_range(inode->i_mapping, start, end);
-       if (!ret && test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
-                            &BTRFS_I(inode)->runtime_flags))
-               ret = filemap_fdatawrite_range(inode->i_mapping, start, end);
-       atomic_dec(&BTRFS_I(inode)->sync_writers);
+       ret = start_ordered_ops(inode, start, end);
        if (ret)
                return ret;
 
        mutex_lock(&inode->i_mutex);
-
-       /*
-        * We flush the dirty pages again to avoid some dirty pages in the
-        * range being left.
-        */
        atomic_inc(&root->log_batch);
        full_sync = test_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
                             &BTRFS_I(inode)->runtime_flags);
+       /*
+        * We might have have had more pages made dirty after calling
+        * start_ordered_ops and before acquiring the inode's i_mutex.
+        */
        if (full_sync) {
+               /*
+                * For a full sync, we need to make sure any ordered operations
+                * start and finish before we start logging the inode, so that
+                * all extents are persisted and the respective file extent
+                * items are in the fs/subvol btree.
+                */
                ret = btrfs_wait_ordered_range(inode, start, end - start + 1);
-               if (ret) {
-                       mutex_unlock(&inode->i_mutex);
-                       goto out;
-               }
+       } else {
+               /*
+                * Start any new ordered operations before starting to log the
+                * inode. We will wait for them to finish in btrfs_sync_log().
+                *
+                * Right before acquiring the inode's mutex, we might have new
+                * writes dirtying pages, which won't immediately start the
+                * respective ordered operations - that is done through the
+                * fill_delalloc callbacks invoked from the writepage and
+                * writepages address space operations. So make sure we start
+                * all ordered operations before starting to log our inode. Not
+                * doing this means that while logging the inode, writeback
+                * could start and invoke writepage/writepages, which would call
+                * the fill_delalloc callbacks (cow_file_range,
+                * submit_compressed_extents). These callbacks add first an
+                * extent map to the modified list of extents and then create
+                * the respective ordered operation, which means in
+                * tree-log.c:btrfs_log_inode() we might capture all existing
+                * ordered operations (with btrfs_get_logged_extents()) before
+                * the fill_delalloc callback adds its ordered operation, and by
+                * the time we visit the modified list of extent maps (with
+                * btrfs_log_changed_extents()), we see and process the extent
+                * map they created. We then use the extent map to construct a
+                * file extent item for logging without waiting for the
+                * respective ordered operation to finish - this file extent
+                * item points to a disk location that might not have yet been
+                * written to, containing random data - so after a crash a log
+                * replay will make our inode have file extent items that point
+                * to disk locations containing invalid data, as we returned
+                * success to userspace without waiting for the respective
+                * ordered operation to finish, because it wasn't captured by
+                * btrfs_get_logged_extents().
+                */
+               ret = start_ordered_ops(inode, start, end);
+       }
+       if (ret) {
+               mutex_unlock(&inode->i_mutex);
+               goto out;
        }
        atomic_inc(&root->log_batch);
 
@@ -1984,6 +2029,25 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
         */
        mutex_unlock(&inode->i_mutex);
 
+       /*
+        * If any of the ordered extents had an error, just return it to user
+        * space, so that the application knows some writes didn't succeed and
+        * can take proper action (retry for e.g.). Blindly committing the
+        * transaction in this case, would fool userspace that everything was
+        * successful. And we also want to make sure our log doesn't contain
+        * file extent items pointing to extents that weren't fully written to -
+        * just like in the non fast fsync path, where we check for the ordered
+        * operation's error flag before writing to the log tree and return -EIO
+        * if any of them had this flag set (btrfs_wait_ordered_range) -
+        * therefore we need to check for errors in the ordered operations,
+        * which are indicated by ctx.io_err.
+        */
+       if (ctx.io_err) {
+               btrfs_end_transaction(trans, root);
+               ret = ctx.io_err;
+               goto out;
+       }
+
        if (ret != BTRFS_NO_LOG_SYNC) {
                if (!ret) {
                        ret = btrfs_sync_log(trans, root, &ctx);
@@ -2621,23 +2685,28 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct extent_map *em = NULL;
        struct extent_state *cached_state = NULL;
-       u64 lockstart = *offset;
-       u64 lockend = i_size_read(inode);
-       u64 start = *offset;
-       u64 len = i_size_read(inode);
+       u64 lockstart;
+       u64 lockend;
+       u64 start;
+       u64 len;
        int ret = 0;
 
-       lockend = max_t(u64, root->sectorsize, lockend);
+       if (inode->i_size == 0)
+               return -ENXIO;
+
+       /*
+        * *offset can be negative, in this case we start finding DATA/HOLE from
+        * the very start of the file.
+        */
+       start = max_t(loff_t, 0, *offset);
+
+       lockstart = round_down(start, root->sectorsize);
+       lockend = round_up(i_size_read(inode), root->sectorsize);
        if (lockend <= lockstart)
                lockend = lockstart + root->sectorsize;
-
        lockend--;
        len = lockend - lockstart + 1;
 
-       len = max_t(u64, len, root->sectorsize);
-       if (inode->i_size == 0)
-               return -ENXIO;
-
        lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0,
                         &cached_state);
 
index 2b0a627cb5f94e414d3c9751f5e8e384e39c42f9..33848196550e4984eff6a577242e7710f70b0c82 100644 (file)
@@ -279,8 +279,7 @@ static int io_ctl_init(struct io_ctl *io_ctl, struct inode *inode,
        int num_pages;
        int check_crcs = 0;
 
-       num_pages = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
-                   PAGE_CACHE_SHIFT;
+       num_pages = DIV_ROUND_UP(i_size_read(inode), PAGE_CACHE_SIZE);
 
        if (btrfs_ino(inode) != BTRFS_FREE_INO_OBJECTID)
                check_crcs = 1;
@@ -1998,6 +1997,128 @@ static bool try_merge_free_space(struct btrfs_free_space_ctl *ctl,
        return merged;
 }
 
+static bool steal_from_bitmap_to_end(struct btrfs_free_space_ctl *ctl,
+                                    struct btrfs_free_space *info,
+                                    bool update_stat)
+{
+       struct btrfs_free_space *bitmap;
+       unsigned long i;
+       unsigned long j;
+       const u64 end = info->offset + info->bytes;
+       const u64 bitmap_offset = offset_to_bitmap(ctl, end);
+       u64 bytes;
+
+       bitmap = tree_search_offset(ctl, bitmap_offset, 1, 0);
+       if (!bitmap)
+               return false;
+
+       i = offset_to_bit(bitmap->offset, ctl->unit, end);
+       j = find_next_zero_bit(bitmap->bitmap, BITS_PER_BITMAP, i);
+       if (j == i)
+               return false;
+       bytes = (j - i) * ctl->unit;
+       info->bytes += bytes;
+
+       if (update_stat)
+               bitmap_clear_bits(ctl, bitmap, end, bytes);
+       else
+               __bitmap_clear_bits(ctl, bitmap, end, bytes);
+
+       if (!bitmap->bytes)
+               free_bitmap(ctl, bitmap);
+
+       return true;
+}
+
+static bool steal_from_bitmap_to_front(struct btrfs_free_space_ctl *ctl,
+                                      struct btrfs_free_space *info,
+                                      bool update_stat)
+{
+       struct btrfs_free_space *bitmap;
+       u64 bitmap_offset;
+       unsigned long i;
+       unsigned long j;
+       unsigned long prev_j;
+       u64 bytes;
+
+       bitmap_offset = offset_to_bitmap(ctl, info->offset);
+       /* If we're on a boundary, try the previous logical bitmap. */
+       if (bitmap_offset == info->offset) {
+               if (info->offset == 0)
+                       return false;
+               bitmap_offset = offset_to_bitmap(ctl, info->offset - 1);
+       }
+
+       bitmap = tree_search_offset(ctl, bitmap_offset, 1, 0);
+       if (!bitmap)
+               return false;
+
+       i = offset_to_bit(bitmap->offset, ctl->unit, info->offset) - 1;
+       j = 0;
+       prev_j = (unsigned long)-1;
+       for_each_clear_bit_from(j, bitmap->bitmap, BITS_PER_BITMAP) {
+               if (j > i)
+                       break;
+               prev_j = j;
+       }
+       if (prev_j == i)
+               return false;
+
+       if (prev_j == (unsigned long)-1)
+               bytes = (i + 1) * ctl->unit;
+       else
+               bytes = (i - prev_j) * ctl->unit;
+
+       info->offset -= bytes;
+       info->bytes += bytes;
+
+       if (update_stat)
+               bitmap_clear_bits(ctl, bitmap, info->offset, bytes);
+       else
+               __bitmap_clear_bits(ctl, bitmap, info->offset, bytes);
+
+       if (!bitmap->bytes)
+               free_bitmap(ctl, bitmap);
+
+       return true;
+}
+
+/*
+ * We prefer always to allocate from extent entries, both for clustered and
+ * non-clustered allocation requests. So when attempting to add a new extent
+ * entry, try to see if there's adjacent free space in bitmap entries, and if
+ * there is, migrate that space from the bitmaps to the extent.
+ * Like this we get better chances of satisfying space allocation requests
+ * because we attempt to satisfy them based on a single cache entry, and never
+ * on 2 or more entries - even if the entries represent a contiguous free space
+ * region (e.g. 1 extent entry + 1 bitmap entry starting where the extent entry
+ * ends).
+ */
+static void steal_from_bitmap(struct btrfs_free_space_ctl *ctl,
+                             struct btrfs_free_space *info,
+                             bool update_stat)
+{
+       /*
+        * Only work with disconnected entries, as we can change their offset,
+        * and must be extent entries.
+        */
+       ASSERT(!info->bitmap);
+       ASSERT(RB_EMPTY_NODE(&info->offset_index));
+
+       if (ctl->total_bitmaps > 0) {
+               bool stole_end;
+               bool stole_front = false;
+
+               stole_end = steal_from_bitmap_to_end(ctl, info, update_stat);
+               if (ctl->total_bitmaps > 0)
+                       stole_front = steal_from_bitmap_to_front(ctl, info,
+                                                                update_stat);
+
+               if (stole_end || stole_front)
+                       try_merge_free_space(ctl, info, update_stat);
+       }
+}
+
 int __btrfs_add_free_space(struct btrfs_free_space_ctl *ctl,
                           u64 offset, u64 bytes)
 {
@@ -2010,6 +2131,7 @@ int __btrfs_add_free_space(struct btrfs_free_space_ctl *ctl,
 
        info->offset = offset;
        info->bytes = bytes;
+       RB_CLEAR_NODE(&info->offset_index);
 
        spin_lock(&ctl->tree_lock);
 
@@ -2029,6 +2151,14 @@ int __btrfs_add_free_space(struct btrfs_free_space_ctl *ctl,
                goto out;
        }
 link:
+       /*
+        * Only steal free space from adjacent bitmaps if we're sure we're not
+        * going to add the new free space to existing bitmap entries - because
+        * that would mean unnecessary work that would be reverted. Therefore
+        * attempt to steal space from bitmaps if we're adding an extent entry.
+        */
+       steal_from_bitmap(ctl, info, true);
+
        ret = link_free_space(ctl, info);
        if (ret)
                kmem_cache_free(btrfs_free_space_cachep, info);
@@ -2205,10 +2335,13 @@ __btrfs_return_cluster_to_free_space(
                entry = rb_entry(node, struct btrfs_free_space, offset_index);
                node = rb_next(&entry->offset_index);
                rb_erase(&entry->offset_index, &cluster->root);
+               RB_CLEAR_NODE(&entry->offset_index);
 
                bitmap = (entry->bitmap != NULL);
-               if (!bitmap)
+               if (!bitmap) {
                        try_merge_free_space(ctl, entry, false);
+                       steal_from_bitmap(ctl, entry, false);
+               }
                tree_insert_offset(&ctl->free_space_offset,
                                   entry->offset, &entry->offset_index, bitmap);
        }
@@ -3033,10 +3166,10 @@ struct inode *lookup_free_ino_inode(struct btrfs_root *root,
 {
        struct inode *inode = NULL;
 
-       spin_lock(&root->cache_lock);
-       if (root->cache_inode)
-               inode = igrab(root->cache_inode);
-       spin_unlock(&root->cache_lock);
+       spin_lock(&root->ino_cache_lock);
+       if (root->ino_cache_inode)
+               inode = igrab(root->ino_cache_inode);
+       spin_unlock(&root->ino_cache_lock);
        if (inode)
                return inode;
 
@@ -3044,10 +3177,10 @@ struct inode *lookup_free_ino_inode(struct btrfs_root *root,
        if (IS_ERR(inode))
                return inode;
 
-       spin_lock(&root->cache_lock);
+       spin_lock(&root->ino_cache_lock);
        if (!btrfs_fs_closing(root->fs_info))
-               root->cache_inode = igrab(inode);
-       spin_unlock(&root->cache_lock);
+               root->ino_cache_inode = igrab(inode);
+       spin_unlock(&root->ino_cache_lock);
 
        return inode;
 }
@@ -3176,6 +3309,7 @@ again:
                map = NULL;
                add_new_bitmap(ctl, info, offset);
                bitmap_info = info;
+               info = NULL;
        }
 
        bytes_added = add_bytes_to_bitmap(ctl, bitmap_info, offset, bytes);
@@ -3186,6 +3320,8 @@ again:
        if (bytes)
                goto again;
 
+       if (info)
+               kmem_cache_free(btrfs_free_space_cachep, info);
        if (map)
                kfree(map);
        return 0;
@@ -3260,6 +3396,7 @@ have_info:
                        goto have_info;
                }
 
+               ret = 0;
                goto out;
        }
 
index 85889aa82c62dd8d05d9c8a01cff08f86afc4d05..64f15bb30a81dbf9ada217d4e0c09cd74fddb197 100644 (file)
@@ -20,10 +20,8 @@ static struct crypto_shash *tfm;
 int __init btrfs_hash_init(void)
 {
        tfm = crypto_alloc_shash("crc32c", 0, 0);
-       if (IS_ERR(tfm))
-               return PTR_ERR(tfm);
 
-       return 0;
+       return PTR_ERR_OR_ZERO(tfm);
 }
 
 void btrfs_hash_exit(void)
index 2be38df703c9b095f35ed5e887fa2c37c7fb0e7c..8ffa4783cbf438ed182e6f94e39711ded5207558 100644 (file)
@@ -135,7 +135,7 @@ static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
        u32 item_size;
 
        key.objectid = inode_objectid;
-       btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY);
+       key.type = BTRFS_INODE_EXTREF_KEY;
        key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
 
        path = btrfs_alloc_path();
@@ -209,7 +209,7 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
 
        key.objectid = inode_objectid;
        key.offset = ref_objectid;
-       btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY);
+       key.type = BTRFS_INODE_REF_KEY;
 
        path = btrfs_alloc_path();
        if (!path)
@@ -337,7 +337,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
 
        key.objectid = inode_objectid;
        key.offset = ref_objectid;
-       btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY);
+       key.type = BTRFS_INODE_REF_KEY;
 
        path = btrfs_alloc_path();
        if (!path)
@@ -400,7 +400,7 @@ int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans,
        struct btrfs_key key;
        int ret;
        key.objectid = objectid;
-       btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
+       key.type = BTRFS_INODE_ITEM_KEY;
        key.offset = 0;
 
        ret = btrfs_insert_empty_item(trans, root, path, &key,
@@ -420,13 +420,13 @@ int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root
        struct btrfs_key found_key;
 
        ret = btrfs_search_slot(trans, root, location, path, ins_len, cow);
-       if (ret > 0 && btrfs_key_type(location) == BTRFS_ROOT_ITEM_KEY &&
+       if (ret > 0 && location->type == BTRFS_ROOT_ITEM_KEY &&
            location->offset == (u64)-1 && path->slots[0] != 0) {
                slot = path->slots[0] - 1;
                leaf = path->nodes[0];
                btrfs_item_key_to_cpu(leaf, &found_key, slot);
                if (found_key.objectid == location->objectid &&
-                   btrfs_key_type(&found_key) == btrfs_key_type(location)) {
+                   found_key.type == location->type) {
                        path->slots[0]--;
                        return 0;
                }
index 888fbe19079fd7589484feb089cfa88dcafa6a89..83d646bd2e4b90f34bd597b8ba6f6244071e48f1 100644 (file)
@@ -87,7 +87,7 @@ again:
                                 */
                                btrfs_item_key_to_cpu(leaf, &key, 0);
                                btrfs_release_path(path);
-                               root->cache_progress = last;
+                               root->ino_cache_progress = last;
                                up_read(&fs_info->commit_root_sem);
                                schedule_timeout(1);
                                goto again;
@@ -106,7 +106,7 @@ again:
                if (last != (u64)-1 && last + 1 != key.objectid) {
                        __btrfs_add_free_space(ctl, last + 1,
                                               key.objectid - last - 1);
-                       wake_up(&root->cache_wait);
+                       wake_up(&root->ino_cache_wait);
                }
 
                last = key.objectid;
@@ -119,14 +119,14 @@ next:
                                       root->highest_objectid - last - 1);
        }
 
-       spin_lock(&root->cache_lock);
-       root->cached = BTRFS_CACHE_FINISHED;
-       spin_unlock(&root->cache_lock);
+       spin_lock(&root->ino_cache_lock);
+       root->ino_cache_state = BTRFS_CACHE_FINISHED;
+       spin_unlock(&root->ino_cache_lock);
 
-       root->cache_progress = (u64)-1;
+       root->ino_cache_progress = (u64)-1;
        btrfs_unpin_free_ino(root);
 out:
-       wake_up(&root->cache_wait);
+       wake_up(&root->ino_cache_wait);
        up_read(&fs_info->commit_root_sem);
 
        btrfs_free_path(path);
@@ -144,20 +144,20 @@ static void start_caching(struct btrfs_root *root)
        if (!btrfs_test_opt(root, INODE_MAP_CACHE))
                return;
 
-       spin_lock(&root->cache_lock);
-       if (root->cached != BTRFS_CACHE_NO) {
-               spin_unlock(&root->cache_lock);
+       spin_lock(&root->ino_cache_lock);
+       if (root->ino_cache_state != BTRFS_CACHE_NO) {
+               spin_unlock(&root->ino_cache_lock);
                return;
        }
 
-       root->cached = BTRFS_CACHE_STARTED;
-       spin_unlock(&root->cache_lock);
+       root->ino_cache_state = BTRFS_CACHE_STARTED;
+       spin_unlock(&root->ino_cache_lock);
 
        ret = load_free_ino_cache(root->fs_info, root);
        if (ret == 1) {
-               spin_lock(&root->cache_lock);
-               root->cached = BTRFS_CACHE_FINISHED;
-               spin_unlock(&root->cache_lock);
+               spin_lock(&root->ino_cache_lock);
+               root->ino_cache_state = BTRFS_CACHE_FINISHED;
+               spin_unlock(&root->ino_cache_lock);
                return;
        }
 
@@ -196,11 +196,11 @@ again:
 
        start_caching(root);
 
-       wait_event(root->cache_wait,
-                  root->cached == BTRFS_CACHE_FINISHED ||
+       wait_event(root->ino_cache_wait,
+                  root->ino_cache_state == BTRFS_CACHE_FINISHED ||
                   root->free_ino_ctl->free_space > 0);
 
-       if (root->cached == BTRFS_CACHE_FINISHED &&
+       if (root->ino_cache_state == BTRFS_CACHE_FINISHED &&
            root->free_ino_ctl->free_space == 0)
                return -ENOSPC;
        else
@@ -214,17 +214,17 @@ void btrfs_return_ino(struct btrfs_root *root, u64 objectid)
        if (!btrfs_test_opt(root, INODE_MAP_CACHE))
                return;
 again:
-       if (root->cached == BTRFS_CACHE_FINISHED) {
+       if (root->ino_cache_state == BTRFS_CACHE_FINISHED) {
                __btrfs_add_free_space(pinned, objectid, 1);
        } else {
                down_write(&root->fs_info->commit_root_sem);
-               spin_lock(&root->cache_lock);
-               if (root->cached == BTRFS_CACHE_FINISHED) {
-                       spin_unlock(&root->cache_lock);
+               spin_lock(&root->ino_cache_lock);
+               if (root->ino_cache_state == BTRFS_CACHE_FINISHED) {
+                       spin_unlock(&root->ino_cache_lock);
                        up_write(&root->fs_info->commit_root_sem);
                        goto again;
                }
-               spin_unlock(&root->cache_lock);
+               spin_unlock(&root->ino_cache_lock);
 
                start_caching(root);
 
@@ -235,10 +235,10 @@ again:
 }
 
 /*
- * When a transaction is committed, we'll move those inode numbers which
- * are smaller than root->cache_progress from pinned tree to free_ino tree,
- * and others will just be dropped, because the commit root we were
- * searching has changed.
+ * When a transaction is committed, we'll move those inode numbers which are
+ * smaller than root->ino_cache_progress from pinned tree to free_ino tree, and
+ * others will just be dropped, because the commit root we were searching has
+ * changed.
  *
  * Must be called with root->fs_info->commit_root_sem held
  */
@@ -261,10 +261,10 @@ void btrfs_unpin_free_ino(struct btrfs_root *root)
                info = rb_entry(n, struct btrfs_free_space, offset_index);
                BUG_ON(info->bitmap); /* Logic error */
 
-               if (info->offset > root->cache_progress)
+               if (info->offset > root->ino_cache_progress)
                        goto free;
-               else if (info->offset + info->bytes > root->cache_progress)
-                       count = root->cache_progress - info->offset + 1;
+               else if (info->offset + info->bytes > root->ino_cache_progress)
+                       count = root->ino_cache_progress - info->offset + 1;
                else
                        count = info->bytes;
 
@@ -462,13 +462,13 @@ again:
                }
        }
 
-       spin_lock(&root->cache_lock);
-       if (root->cached != BTRFS_CACHE_FINISHED) {
+       spin_lock(&root->ino_cache_lock);
+       if (root->ino_cache_state != BTRFS_CACHE_FINISHED) {
                ret = -1;
-               spin_unlock(&root->cache_lock);
+               spin_unlock(&root->ino_cache_lock);
                goto out_put;
        }
-       spin_unlock(&root->cache_lock);
+       spin_unlock(&root->ino_cache_lock);
 
        spin_lock(&ctl->tree_lock);
        prealloc = sizeof(struct btrfs_free_space) * ctl->free_extents;
index 016c403bfe7e4241b33c6b6c3e4101ba5ea993b2..fc9c0439caa314e131e1c23c384884b71bf84fb1 100644 (file)
@@ -153,7 +153,7 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
 
                key.objectid = btrfs_ino(inode);
                key.offset = start;
-               btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
+               key.type = BTRFS_EXTENT_DATA_KEY;
 
                datasize = btrfs_file_extent_calc_inline_size(cur_size);
                path->leave_spinning = 1;
@@ -249,8 +249,8 @@ static noinline int cow_file_range_inline(struct btrfs_root *root,
                data_len = compressed_size;
 
        if (start > 0 ||
-           actual_end >= PAGE_CACHE_SIZE ||
-           data_len >= BTRFS_MAX_INLINE_DATA_SIZE(root) ||
+           actual_end > PAGE_CACHE_SIZE ||
+           data_len > BTRFS_MAX_INLINE_DATA_SIZE(root) ||
            (!compressed_size &&
            (actual_end & (root->sectorsize - 1)) == 0) ||
            end + 1 < isize ||
@@ -348,6 +348,23 @@ static noinline int add_async_extent(struct async_cow *cow,
        return 0;
 }
 
+static inline int inode_need_compress(struct inode *inode)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+
+       /* force compress */
+       if (btrfs_test_opt(root, FORCE_COMPRESS))
+               return 1;
+       /* bad compression ratios */
+       if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS)
+               return 0;
+       if (btrfs_test_opt(root, COMPRESS) ||
+           BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS ||
+           BTRFS_I(inode)->force_compress)
+               return 1;
+       return 0;
+}
+
 /*
  * we create compressed extents in two phases.  The first
  * phase compresses a range of pages that have already been
@@ -444,10 +461,7 @@ again:
         * inode has not been flagged as nocompress.  This flag can
         * change at any time if we discover bad compression ratios.
         */
-       if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&
-           (btrfs_test_opt(root, COMPRESS) ||
-            (BTRFS_I(inode)->force_compress) ||
-            (BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS))) {
+       if (inode_need_compress(inode)) {
                WARN_ON(pages);
                pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
                if (!pages) {
@@ -1094,7 +1108,8 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
                async_cow->locked_page = locked_page;
                async_cow->start = start;
 
-               if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS)
+               if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS &&
+                   !btrfs_test_opt(root, FORCE_COMPRESS))
                        cur_end = end;
                else
                        cur_end = min(end, start + 512 * 1024 - 1);
@@ -1445,6 +1460,26 @@ error:
        return ret;
 }
 
+static inline int need_force_cow(struct inode *inode, u64 start, u64 end)
+{
+
+       if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW) &&
+           !(BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC))
+               return 0;
+
+       /*
+        * @defrag_bytes is a hint value, no spinlock held here,
+        * if is not zero, it means the file is defragging.
+        * Force cow if given extent needs to be defragged.
+        */
+       if (BTRFS_I(inode)->defrag_bytes &&
+           test_range_bit(&BTRFS_I(inode)->io_tree, start, end,
+                          EXTENT_DEFRAG, 0, NULL))
+               return 1;
+
+       return 0;
+}
+
 /*
  * extent_io.c call back to do delayed allocation processing
  */
@@ -1453,17 +1488,15 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
                              unsigned long *nr_written)
 {
        int ret;
-       struct btrfs_root *root = BTRFS_I(inode)->root;
+       int force_cow = need_force_cow(inode, start, end);
 
-       if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW) {
+       if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW && !force_cow) {
                ret = run_delalloc_nocow(inode, locked_page, start, end,
                                         page_started, 1, nr_written);
-       } else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC) {
+       } else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC && !force_cow) {
                ret = run_delalloc_nocow(inode, locked_page, start, end,
                                         page_started, 0, nr_written);
-       } else if (!btrfs_test_opt(root, COMPRESS) &&
-                  !(BTRFS_I(inode)->force_compress) &&
-                  !(BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS)) {
+       } else if (!inode_need_compress(inode)) {
                ret = cow_file_range(inode, locked_page, start, end,
                                      page_started, nr_written, 1);
        } else {
@@ -1555,6 +1588,8 @@ static void btrfs_set_bit_hook(struct inode *inode,
                               struct extent_state *state, unsigned long *bits)
 {
 
+       if ((*bits & EXTENT_DEFRAG) && !(*bits & EXTENT_DELALLOC))
+               WARN_ON(1);
        /*
         * set_bit and clear bit hooks normally require _irqsave/restore
         * but in this case, we are only testing for the DELALLOC
@@ -1577,6 +1612,8 @@ static void btrfs_set_bit_hook(struct inode *inode,
                                     root->fs_info->delalloc_batch);
                spin_lock(&BTRFS_I(inode)->lock);
                BTRFS_I(inode)->delalloc_bytes += len;
+               if (*bits & EXTENT_DEFRAG)
+                       BTRFS_I(inode)->defrag_bytes += len;
                if (do_list && !test_bit(BTRFS_INODE_IN_DELALLOC_LIST,
                                         &BTRFS_I(inode)->runtime_flags))
                        btrfs_add_delalloc_inodes(root, inode);
@@ -1591,6 +1628,13 @@ static void btrfs_clear_bit_hook(struct inode *inode,
                                 struct extent_state *state,
                                 unsigned long *bits)
 {
+       u64 len = state->end + 1 - state->start;
+
+       spin_lock(&BTRFS_I(inode)->lock);
+       if ((state->state & EXTENT_DEFRAG) && (*bits & EXTENT_DEFRAG))
+               BTRFS_I(inode)->defrag_bytes -= len;
+       spin_unlock(&BTRFS_I(inode)->lock);
+
        /*
         * set_bit and clear bit hooks normally require _irqsave/restore
         * but in this case, we are only testing for the DELALLOC
@@ -1598,7 +1642,6 @@ static void btrfs_clear_bit_hook(struct inode *inode,
         */
        if ((state->state & EXTENT_DELALLOC) && (*bits & EXTENT_DELALLOC)) {
                struct btrfs_root *root = BTRFS_I(inode)->root;
-               u64 len = state->end + 1 - state->start;
                bool do_list = !btrfs_is_free_space_inode(inode);
 
                if (*bits & EXTENT_FIRST_DELALLOC) {
@@ -2660,6 +2703,10 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
                goto out;
        }
 
+       btrfs_free_io_failure_record(inode, ordered_extent->file_offset,
+                                    ordered_extent->file_offset +
+                                    ordered_extent->len - 1);
+
        if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered_extent->flags)) {
                truncated = true;
                logical_len = ordered_extent->truncated_len;
@@ -2856,6 +2903,40 @@ static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
        return 0;
 }
 
+static int __readpage_endio_check(struct inode *inode,
+                                 struct btrfs_io_bio *io_bio,
+                                 int icsum, struct page *page,
+                                 int pgoff, u64 start, size_t len)
+{
+       char *kaddr;
+       u32 csum_expected;
+       u32 csum = ~(u32)0;
+       static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
+                                     DEFAULT_RATELIMIT_BURST);
+
+       csum_expected = *(((u32 *)io_bio->csum) + icsum);
+
+       kaddr = kmap_atomic(page);
+       csum = btrfs_csum_data(kaddr + pgoff, csum,  len);
+       btrfs_csum_final(csum, (char *)&csum);
+       if (csum != csum_expected)
+               goto zeroit;
+
+       kunmap_atomic(kaddr);
+       return 0;
+zeroit:
+       if (__ratelimit(&_rs))
+               btrfs_info(BTRFS_I(inode)->root->fs_info,
+                          "csum failed ino %llu off %llu csum %u expected csum %u",
+                          btrfs_ino(inode), start, csum, csum_expected);
+       memset(kaddr + pgoff, 1, len);
+       flush_dcache_page(page);
+       kunmap_atomic(kaddr);
+       if (csum_expected == 0)
+               return 0;
+       return -EIO;
+}
+
 /*
  * when reads are done, we need to check csums to verify the data is correct
  * if there's a match, we allow the bio to finish.  If not, the code in
@@ -2868,20 +2949,15 @@ static int btrfs_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
        size_t offset = start - page_offset(page);
        struct inode *inode = page->mapping->host;
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
-       char *kaddr;
        struct btrfs_root *root = BTRFS_I(inode)->root;
-       u32 csum_expected;
-       u32 csum = ~(u32)0;
-       static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
-                                     DEFAULT_RATELIMIT_BURST);
 
        if (PageChecked(page)) {
                ClearPageChecked(page);
-               goto good;
+               return 0;
        }
 
        if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
-               goto good;
+               return 0;
 
        if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID &&
            test_range_bit(io_tree, start, end, EXTENT_NODATASUM, 1, NULL)) {
@@ -2891,28 +2967,8 @@ static int btrfs_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
        }
 
        phy_offset >>= inode->i_sb->s_blocksize_bits;
-       csum_expected = *(((u32 *)io_bio->csum) + phy_offset);
-
-       kaddr = kmap_atomic(page);
-       csum = btrfs_csum_data(kaddr + offset, csum,  end - start + 1);
-       btrfs_csum_final(csum, (char *)&csum);
-       if (csum != csum_expected)
-               goto zeroit;
-
-       kunmap_atomic(kaddr);
-good:
-       return 0;
-
-zeroit:
-       if (__ratelimit(&_rs))
-               btrfs_info(root->fs_info, "csum failed ino %llu off %llu csum %u expected csum %u",
-                       btrfs_ino(page->mapping->host), start, csum, csum_expected);
-       memset(kaddr + offset, 1, end - start + 1);
-       flush_dcache_page(page);
-       kunmap_atomic(kaddr);
-       if (csum_expected == 0)
-               return 0;
-       return -EIO;
+       return __readpage_endio_check(inode, io_bio, phy_offset, page, offset,
+                                     start, (size_t)(end - start + 1));
 }
 
 struct delayed_iput {
@@ -3159,7 +3215,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
        path->reada = -1;
 
        key.objectid = BTRFS_ORPHAN_OBJECTID;
-       btrfs_set_key_type(&key, BTRFS_ORPHAN_ITEM_KEY);
+       key.type = BTRFS_ORPHAN_ITEM_KEY;
        key.offset = (u64)-1;
 
        while (1) {
@@ -3186,7 +3242,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
                /* make sure the item matches what we want */
                if (found_key.objectid != BTRFS_ORPHAN_OBJECTID)
                        break;
-               if (btrfs_key_type(&found_key) != BTRFS_ORPHAN_ITEM_KEY)
+               if (found_key.type != BTRFS_ORPHAN_ITEM_KEY)
                        break;
 
                /* release the path since we're done with it */
@@ -3662,7 +3718,8 @@ noinline int btrfs_update_inode(struct btrfs_trans_handle *trans,
         * without delay
         */
        if (!btrfs_is_free_space_inode(inode)
-           && root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID) {
+           && root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID
+           && !root->fs_info->log_root_recovering) {
                btrfs_update_root_times(trans, root);
 
                ret = btrfs_delayed_update_inode(trans, root, inode);
@@ -4085,7 +4142,7 @@ search_again:
                fi = NULL;
                leaf = path->nodes[0];
                btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-               found_type = btrfs_key_type(&found_key);
+               found_type = found_key.type;
 
                if (found_key.objectid != ino)
                        break;
@@ -4747,6 +4804,8 @@ void btrfs_evict_inode(struct inode *inode)
        /* do we really want it for ->i_nlink > 0 and zero btrfs_root_refs? */
        btrfs_wait_ordered_range(inode, 0, (u64)-1);
 
+       btrfs_free_io_failure_record(inode, 0, (u64)-1);
+
        if (root->fs_info->log_root_recovering) {
                BUG_ON(test_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
                                 &BTRFS_I(inode)->runtime_flags));
@@ -5331,7 +5390,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
                btrfs_get_delayed_items(inode, &ins_list, &del_list);
        }
 
-       btrfs_set_key_type(&key, key_type);
+       key.type = key_type;
        key.offset = ctx->pos;
        key.objectid = btrfs_ino(inode);
 
@@ -5356,7 +5415,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
 
                if (found_key.objectid != key.objectid)
                        break;
-               if (btrfs_key_type(&found_key) != key_type)
+               if (found_key.type != key_type)
                        break;
                if (found_key.offset < ctx->pos)
                        goto next;
@@ -5568,7 +5627,7 @@ static int btrfs_set_inode_index_count(struct inode *inode)
        int ret;
 
        key.objectid = btrfs_ino(inode);
-       btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
+       key.type = BTRFS_DIR_INDEX_KEY;
        key.offset = (u64)-1;
 
        path = btrfs_alloc_path();
@@ -5600,7 +5659,7 @@ static int btrfs_set_inode_index_count(struct inode *inode)
        btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
 
        if (found_key.objectid != btrfs_ino(inode) ||
-           btrfs_key_type(&found_key) != BTRFS_DIR_INDEX_KEY) {
+           found_key.type != BTRFS_DIR_INDEX_KEY) {
                BTRFS_I(inode)->index_cnt = 2;
                goto out;
        }
@@ -5718,7 +5777,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags);
 
        key[0].objectid = objectid;
-       btrfs_set_key_type(&key[0], BTRFS_INODE_ITEM_KEY);
+       key[0].type = BTRFS_INODE_ITEM_KEY;
        key[0].offset = 0;
 
        sizes[0] = sizeof(struct btrfs_inode_item);
@@ -5731,7 +5790,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
                 * add more hard links than can fit in the ref item.
                 */
                key[1].objectid = objectid;
-               btrfs_set_key_type(&key[1], BTRFS_INODE_REF_KEY);
+               key[1].type = BTRFS_INODE_REF_KEY;
                key[1].offset = ref_objectid;
 
                sizes[1] = name_len + sizeof(*ref);
@@ -5740,7 +5799,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        location = &BTRFS_I(inode)->location;
        location->objectid = objectid;
        location->offset = 0;
-       btrfs_set_key_type(location, BTRFS_INODE_ITEM_KEY);
+       location->type = BTRFS_INODE_ITEM_KEY;
 
        ret = btrfs_insert_inode_locked(inode);
        if (ret < 0)
@@ -5832,7 +5891,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
                memcpy(&key, &BTRFS_I(inode)->root->root_key, sizeof(key));
        } else {
                key.objectid = ino;
-               btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
+               key.type = BTRFS_INODE_ITEM_KEY;
                key.offset = 0;
        }
 
@@ -6191,21 +6250,60 @@ out_fail_inode:
        goto out_fail;
 }
 
+/* Find next extent map of a given extent map, caller needs to ensure locks */
+static struct extent_map *next_extent_map(struct extent_map *em)
+{
+       struct rb_node *next;
+
+       next = rb_next(&em->rb_node);
+       if (!next)
+               return NULL;
+       return container_of(next, struct extent_map, rb_node);
+}
+
+static struct extent_map *prev_extent_map(struct extent_map *em)
+{
+       struct rb_node *prev;
+
+       prev = rb_prev(&em->rb_node);
+       if (!prev)
+               return NULL;
+       return container_of(prev, struct extent_map, rb_node);
+}
+
 /* helper for btfs_get_extent.  Given an existing extent in the tree,
+ * the existing extent is the nearest extent to map_start,
  * and an extent that you want to insert, deal with overlap and insert
- * the new extent into the tree.
+ * the best fitted new extent into the tree.
  */
 static int merge_extent_mapping(struct extent_map_tree *em_tree,
                                struct extent_map *existing,
                                struct extent_map *em,
                                u64 map_start)
 {
+       struct extent_map *prev;
+       struct extent_map *next;
+       u64 start;
+       u64 end;
        u64 start_diff;
 
        BUG_ON(map_start < em->start || map_start >= extent_map_end(em));
-       start_diff = map_start - em->start;
-       em->start = map_start;
-       em->len = existing->start - em->start;
+
+       if (existing->start > map_start) {
+               next = existing;
+               prev = prev_extent_map(next);
+       } else {
+               prev = existing;
+               next = next_extent_map(prev);
+       }
+
+       start = prev ? extent_map_end(prev) : em->start;
+       start = max_t(u64, start, em->start);
+       end = next ? next->start : extent_map_end(em);
+       end = min_t(u64, end, extent_map_end(em));
+       start_diff = start - em->start;
+       em->start = start;
+       em->len = end - start;
        if (em->block_start < EXTENT_MAP_LAST_BYTE &&
            !test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
                em->block_start += start_diff;
@@ -6333,7 +6431,7 @@ again:
                              struct btrfs_file_extent_item);
        /* are we inside the extent that was found? */
        btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-       found_type = btrfs_key_type(&found_key);
+       found_type = found_key.type;
        if (found_key.objectid != objectid ||
            found_type != BTRFS_EXTENT_DATA_KEY) {
                /*
@@ -6482,25 +6580,21 @@ insert:
 
                ret = 0;
 
-               existing = lookup_extent_mapping(em_tree, start, len);
-               if (existing && (existing->start > start ||
-                   existing->start + existing->len <= start)) {
+               existing = search_extent_mapping(em_tree, start, len);
+               /*
+                * existing will always be non-NULL, since there must be
+                * extent causing the -EEXIST.
+                */
+               if (start >= extent_map_end(existing) ||
+                   start <= existing->start) {
+                       /*
+                        * The existing extent map is the one nearest to
+                        * the [start, start + len) range which overlaps
+                        */
+                       err = merge_extent_mapping(em_tree, existing,
+                                                  em, start);
                        free_extent_map(existing);
-                       existing = NULL;
-               }
-               if (!existing) {
-                       existing = lookup_extent_mapping(em_tree, em->start,
-                                                        em->len);
-                       if (existing) {
-                               err = merge_extent_mapping(em_tree, existing,
-                                                          em, start);
-                               free_extent_map(existing);
-                               if (err) {
-                                       free_extent_map(em);
-                                       em = NULL;
-                               }
-                       } else {
-                               err = -EIO;
+                       if (err) {
                                free_extent_map(em);
                                em = NULL;
                        }
@@ -7112,8 +7206,10 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
                                                       block_start, len,
                                                       orig_block_len,
                                                       ram_bytes, type);
-                               if (IS_ERR(em))
+                               if (IS_ERR(em)) {
+                                       ret = PTR_ERR(em);
                                        goto unlock_err;
+                               }
                        }
 
                        ret = btrfs_add_ordered_extent_dio(inode, start,
@@ -7188,45 +7284,277 @@ unlock_err:
        return ret;
 }
 
-static void btrfs_endio_direct_read(struct bio *bio, int err)
+static inline int submit_dio_repair_bio(struct inode *inode, struct bio *bio,
+                                       int rw, int mirror_num)
 {
-       struct btrfs_dio_private *dip = bio->bi_private;
-       struct bio_vec *bvec;
-       struct inode *inode = dip->inode;
        struct btrfs_root *root = BTRFS_I(inode)->root;
-       struct bio *dio_bio;
-       u32 *csums = (u32 *)dip->csum;
+       int ret;
+
+       BUG_ON(rw & REQ_WRITE);
+
+       bio_get(bio);
+
+       ret = btrfs_bio_wq_end_io(root->fs_info, bio,
+                                 BTRFS_WQ_ENDIO_DIO_REPAIR);
+       if (ret)
+               goto err;
+
+       ret = btrfs_map_bio(root, rw, bio, mirror_num, 0);
+err:
+       bio_put(bio);
+       return ret;
+}
+
+static int btrfs_check_dio_repairable(struct inode *inode,
+                                     struct bio *failed_bio,
+                                     struct io_failure_record *failrec,
+                                     int failed_mirror)
+{
+       int num_copies;
+
+       num_copies = btrfs_num_copies(BTRFS_I(inode)->root->fs_info,
+                                     failrec->logical, failrec->len);
+       if (num_copies == 1) {
+               /*
+                * we only have a single copy of the data, so don't bother with
+                * all the retry and error correction code that follows. no
+                * matter what the error is, it is very likely to persist.
+                */
+               pr_debug("Check DIO Repairable: cannot repair, num_copies=%d, next_mirror %d, failed_mirror %d\n",
+                        num_copies, failrec->this_mirror, failed_mirror);
+               return 0;
+       }
+
+       failrec->failed_mirror = failed_mirror;
+       failrec->this_mirror++;
+       if (failrec->this_mirror == failed_mirror)
+               failrec->this_mirror++;
+
+       if (failrec->this_mirror > num_copies) {
+               pr_debug("Check DIO Repairable: (fail) num_copies=%d, next_mirror %d, failed_mirror %d\n",
+                        num_copies, failrec->this_mirror, failed_mirror);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int dio_read_error(struct inode *inode, struct bio *failed_bio,
+                         struct page *page, u64 start, u64 end,
+                         int failed_mirror, bio_end_io_t *repair_endio,
+                         void *repair_arg)
+{
+       struct io_failure_record *failrec;
+       struct bio *bio;
+       int isector;
+       int read_mode;
+       int ret;
+
+       BUG_ON(failed_bio->bi_rw & REQ_WRITE);
+
+       ret = btrfs_get_io_failure_record(inode, start, end, &failrec);
+       if (ret)
+               return ret;
+
+       ret = btrfs_check_dio_repairable(inode, failed_bio, failrec,
+                                        failed_mirror);
+       if (!ret) {
+               free_io_failure(inode, failrec);
+               return -EIO;
+       }
+
+       if (failed_bio->bi_vcnt > 1)
+               read_mode = READ_SYNC | REQ_FAILFAST_DEV;
+       else
+               read_mode = READ_SYNC;
+
+       isector = start - btrfs_io_bio(failed_bio)->logical;
+       isector >>= inode->i_sb->s_blocksize_bits;
+       bio = btrfs_create_repair_bio(inode, failed_bio, failrec, page,
+                                     0, isector, repair_endio, repair_arg);
+       if (!bio) {
+               free_io_failure(inode, failrec);
+               return -EIO;
+       }
+
+       btrfs_debug(BTRFS_I(inode)->root->fs_info,
+                   "Repair DIO Read Error: submitting new dio read[%#x] to this_mirror=%d, in_validation=%d\n",
+                   read_mode, failrec->this_mirror, failrec->in_validation);
+
+       ret = submit_dio_repair_bio(inode, bio, read_mode,
+                                   failrec->this_mirror);
+       if (ret) {
+               free_io_failure(inode, failrec);
+               bio_put(bio);
+       }
+
+       return ret;
+}
+
+struct btrfs_retry_complete {
+       struct completion done;
+       struct inode *inode;
+       u64 start;
+       int uptodate;
+};
+
+static void btrfs_retry_endio_nocsum(struct bio *bio, int err)
+{
+       struct btrfs_retry_complete *done = bio->bi_private;
+       struct bio_vec *bvec;
+       int i;
+
+       if (err)
+               goto end;
+
+       done->uptodate = 1;
+       bio_for_each_segment_all(bvec, bio, i)
+               clean_io_failure(done->inode, done->start, bvec->bv_page, 0);
+end:
+       complete(&done->done);
+       bio_put(bio);
+}
+
+static int __btrfs_correct_data_nocsum(struct inode *inode,
+                                      struct btrfs_io_bio *io_bio)
+{
+       struct bio_vec *bvec;
+       struct btrfs_retry_complete done;
        u64 start;
        int i;
+       int ret;
+
+       start = io_bio->logical;
+       done.inode = inode;
+
+       bio_for_each_segment_all(bvec, &io_bio->bio, i) {
+try_again:
+               done.uptodate = 0;
+               done.start = start;
+               init_completion(&done.done);
+
+               ret = dio_read_error(inode, &io_bio->bio, bvec->bv_page, start,
+                                    start + bvec->bv_len - 1,
+                                    io_bio->mirror_num,
+                                    btrfs_retry_endio_nocsum, &done);
+               if (ret)
+                       return ret;
+
+               wait_for_completion(&done.done);
+
+               if (!done.uptodate) {
+                       /* We might have another mirror, so try again */
+                       goto try_again;
+               }
+
+               start += bvec->bv_len;
+       }
+
+       return 0;
+}
+
+static void btrfs_retry_endio(struct bio *bio, int err)
+{
+       struct btrfs_retry_complete *done = bio->bi_private;
+       struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+       struct bio_vec *bvec;
+       int uptodate;
+       int ret;
+       int i;
+
+       if (err)
+               goto end;
 
-       start = dip->logical_offset;
+       uptodate = 1;
        bio_for_each_segment_all(bvec, bio, i) {
-               if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
-                       struct page *page = bvec->bv_page;
-                       char *kaddr;
-                       u32 csum = ~(u32)0;
-                       unsigned long flags;
-
-                       local_irq_save(flags);
-                       kaddr = kmap_atomic(page);
-                       csum = btrfs_csum_data(kaddr + bvec->bv_offset,
-                                              csum, bvec->bv_len);
-                       btrfs_csum_final(csum, (char *)&csum);
-                       kunmap_atomic(kaddr);
-                       local_irq_restore(flags);
-
-                       flush_dcache_page(bvec->bv_page);
-                       if (csum != csums[i]) {
-                               btrfs_err(root->fs_info, "csum failed ino %llu off %llu csum %u expected csum %u",
-                                         btrfs_ino(inode), start, csum,
-                                         csums[i]);
-                               err = -EIO;
-                       }
+               ret = __readpage_endio_check(done->inode, io_bio, i,
+                                            bvec->bv_page, 0,
+                                            done->start, bvec->bv_len);
+               if (!ret)
+                       clean_io_failure(done->inode, done->start,
+                                        bvec->bv_page, 0);
+               else
+                       uptodate = 0;
+       }
+
+       done->uptodate = uptodate;
+end:
+       complete(&done->done);
+       bio_put(bio);
+}
+
+static int __btrfs_subio_endio_read(struct inode *inode,
+                                   struct btrfs_io_bio *io_bio, int err)
+{
+       struct bio_vec *bvec;
+       struct btrfs_retry_complete done;
+       u64 start;
+       u64 offset = 0;
+       int i;
+       int ret;
+
+       err = 0;
+       start = io_bio->logical;
+       done.inode = inode;
+
+       bio_for_each_segment_all(bvec, &io_bio->bio, i) {
+               ret = __readpage_endio_check(inode, io_bio, i, bvec->bv_page,
+                                            0, start, bvec->bv_len);
+               if (likely(!ret))
+                       goto next;
+try_again:
+               done.uptodate = 0;
+               done.start = start;
+               init_completion(&done.done);
+
+               ret = dio_read_error(inode, &io_bio->bio, bvec->bv_page, start,
+                                    start + bvec->bv_len - 1,
+                                    io_bio->mirror_num,
+                                    btrfs_retry_endio, &done);
+               if (ret) {
+                       err = ret;
+                       goto next;
                }
 
+               wait_for_completion(&done.done);
+
+               if (!done.uptodate) {
+                       /* We might have another mirror, so try again */
+                       goto try_again;
+               }
+next:
+               offset += bvec->bv_len;
                start += bvec->bv_len;
        }
 
+       return err;
+}
+
+static int btrfs_subio_endio_read(struct inode *inode,
+                                 struct btrfs_io_bio *io_bio, int err)
+{
+       bool skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
+
+       if (skip_csum) {
+               if (unlikely(err))
+                       return __btrfs_correct_data_nocsum(inode, io_bio);
+               else
+                       return 0;
+       } else {
+               return __btrfs_subio_endio_read(inode, io_bio, err);
+       }
+}
+
+static void btrfs_endio_direct_read(struct bio *bio, int err)
+{
+       struct btrfs_dio_private *dip = bio->bi_private;
+       struct inode *inode = dip->inode;
+       struct bio *dio_bio;
+       struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+
+       if (dip->flags & BTRFS_DIO_ORIG_BIO_SUBMITTED)
+               err = btrfs_subio_endio_read(inode, io_bio, err);
+
        unlock_extent(&BTRFS_I(inode)->io_tree, dip->logical_offset,
                      dip->logical_offset + dip->bytes - 1);
        dio_bio = dip->dio_bio;
@@ -7237,6 +7565,9 @@ static void btrfs_endio_direct_read(struct bio *bio, int err)
        if (err)
                clear_bit(BIO_UPTODATE, &dio_bio->bi_flags);
        dio_end_io(dio_bio, err);
+
+       if (io_bio->end_io)
+               io_bio->end_io(io_bio, err);
        bio_put(bio);
 }
 
@@ -7302,12 +7633,17 @@ static void btrfs_end_dio_bio(struct bio *bio, int err)
 {
        struct btrfs_dio_private *dip = bio->bi_private;
 
+       if (err)
+               btrfs_warn(BTRFS_I(dip->inode)->root->fs_info,
+                          "direct IO failed ino %llu rw %lu sector %#Lx len %u err no %d",
+                          btrfs_ino(dip->inode), bio->bi_rw,
+                          (unsigned long long)bio->bi_iter.bi_sector,
+                          bio->bi_iter.bi_size, err);
+
+       if (dip->subio_endio)
+               err = dip->subio_endio(dip->inode, btrfs_io_bio(bio), err);
+
        if (err) {
-               btrfs_err(BTRFS_I(dip->inode)->root->fs_info,
-                         "direct IO failed ino %llu rw %lu sector %#Lx len %u err no %d",
-                     btrfs_ino(dip->inode), bio->bi_rw,
-                     (unsigned long long)bio->bi_iter.bi_sector,
-                     bio->bi_iter.bi_size, err);
                dip->errors = 1;
 
                /*
@@ -7338,6 +7674,38 @@ static struct bio *btrfs_dio_bio_alloc(struct block_device *bdev,
        return btrfs_bio_alloc(bdev, first_sector, nr_vecs, gfp_flags);
 }
 
+static inline int btrfs_lookup_and_bind_dio_csum(struct btrfs_root *root,
+                                                struct inode *inode,
+                                                struct btrfs_dio_private *dip,
+                                                struct bio *bio,
+                                                u64 file_offset)
+{
+       struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+       struct btrfs_io_bio *orig_io_bio = btrfs_io_bio(dip->orig_bio);
+       int ret;
+
+       /*
+        * We load all the csum data we need when we submit
+        * the first bio to reduce the csum tree search and
+        * contention.
+        */
+       if (dip->logical_offset == file_offset) {
+               ret = btrfs_lookup_bio_sums_dio(root, inode, dip->orig_bio,
+                                               file_offset);
+               if (ret)
+                       return ret;
+       }
+
+       if (bio == dip->orig_bio)
+               return 0;
+
+       file_offset -= dip->logical_offset;
+       file_offset >>= inode->i_sb->s_blocksize_bits;
+       io_bio->csum = (u8 *)(((u32 *)orig_io_bio->csum) + file_offset);
+
+       return 0;
+}
+
 static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
                                         int rw, u64 file_offset, int skip_sum,
                                         int async_submit)
@@ -7353,7 +7721,8 @@ static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
        bio_get(bio);
 
        if (!write) {
-               ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
+               ret = btrfs_bio_wq_end_io(root->fs_info, bio,
+                               BTRFS_WQ_ENDIO_DATA);
                if (ret)
                        goto err;
        }
@@ -7376,13 +7745,12 @@ static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
                ret = btrfs_csum_one_bio(root, inode, bio, file_offset, 1);
                if (ret)
                        goto err;
-       } else if (!skip_sum) {
-               ret = btrfs_lookup_bio_sums_dio(root, inode, dip, bio,
-                                               file_offset);
+       } else {
+               ret = btrfs_lookup_and_bind_dio_csum(root, inode, dip, bio,
+                                                    file_offset);
                if (ret)
                        goto err;
        }
-
 map:
        ret = btrfs_map_bio(root, rw, bio, 0, async_submit);
 err:
@@ -7403,7 +7771,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
        u64 submit_len = 0;
        u64 map_length;
        int nr_pages = 0;
-       int ret = 0;
+       int ret;
        int async_submit = 0;
 
        map_length = orig_bio->bi_iter.bi_size;
@@ -7414,6 +7782,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
 
        if (map_length >= orig_bio->bi_iter.bi_size) {
                bio = orig_bio;
+               dip->flags |= BTRFS_DIO_ORIG_BIO_SUBMITTED;
                goto submit;
        }
 
@@ -7430,12 +7799,13 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
 
        bio->bi_private = dip;
        bio->bi_end_io = btrfs_end_dio_bio;
+       btrfs_io_bio(bio)->logical = file_offset;
        atomic_inc(&dip->pending_bios);
 
        while (bvec <= (orig_bio->bi_io_vec + orig_bio->bi_vcnt - 1)) {
-               if (unlikely(map_length < submit_len + bvec->bv_len ||
+               if (map_length < submit_len + bvec->bv_len ||
                    bio_add_page(bio, bvec->bv_page, bvec->bv_len,
-                                bvec->bv_offset) < bvec->bv_len)) {
+                                bvec->bv_offset) < bvec->bv_len) {
                        /*
                         * inc the count before we submit the bio so
                         * we know the end IO handler won't happen before
@@ -7464,6 +7834,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
                                goto out_err;
                        bio->bi_private = dip;
                        bio->bi_end_io = btrfs_end_dio_bio;
+                       btrfs_io_bio(bio)->logical = file_offset;
 
                        map_length = orig_bio->bi_iter.bi_size;
                        ret = btrfs_map_block(root->fs_info, rw,
@@ -7507,11 +7878,10 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio,
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_dio_private *dip;
        struct bio *io_bio;
+       struct btrfs_io_bio *btrfs_bio;
        int skip_sum;
-       int sum_len;
        int write = rw & REQ_WRITE;
        int ret = 0;
-       u16 csum_size;
 
        skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
 
@@ -7521,16 +7891,7 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio,
                goto free_ordered;
        }
 
-       if (!skip_sum && !write) {
-               csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
-               sum_len = dio_bio->bi_iter.bi_size >>
-                       inode->i_sb->s_blocksize_bits;
-               sum_len *= csum_size;
-       } else {
-               sum_len = 0;
-       }
-
-       dip = kmalloc(sizeof(*dip) + sum_len, GFP_NOFS);
+       dip = kzalloc(sizeof(*dip), GFP_NOFS);
        if (!dip) {
                ret = -ENOMEM;
                goto free_io_bio;
@@ -7542,20 +7903,25 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio,
        dip->bytes = dio_bio->bi_iter.bi_size;
        dip->disk_bytenr = (u64)dio_bio->bi_iter.bi_sector << 9;
        io_bio->bi_private = dip;
-       dip->errors = 0;
        dip->orig_bio = io_bio;
        dip->dio_bio = dio_bio;
        atomic_set(&dip->pending_bios, 0);
+       btrfs_bio = btrfs_io_bio(io_bio);
+       btrfs_bio->logical = file_offset;
 
-       if (write)
+       if (write) {
                io_bio->bi_end_io = btrfs_endio_direct_write;
-       else
+       } else {
                io_bio->bi_end_io = btrfs_endio_direct_read;
+               dip->subio_endio = btrfs_subio_endio_read;
+       }
 
        ret = btrfs_submit_direct_hook(rw, dip, skip_sum);
        if (!ret)
                return;
 
+       if (btrfs_bio->end_io)
+               btrfs_bio->end_io(btrfs_bio, ret);
 free_io_bio:
        bio_put(io_bio);
 
@@ -7652,8 +8018,8 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
                ret = btrfs_delalloc_reserve_space(inode, count);
                if (ret)
                        goto out;
-       } else if (unlikely(test_bit(BTRFS_INODE_READDIO_NEED_LOCK,
-                                    &BTRFS_I(inode)->runtime_flags))) {
+       } else if (test_bit(BTRFS_INODE_READDIO_NEED_LOCK,
+                                    &BTRFS_I(inode)->runtime_flags)) {
                inode_dio_done(inode);
                flags = DIO_LOCKING | DIO_SKIP_HOLES;
                wakeup = false;
@@ -8173,6 +8539,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
        ei->last_sub_trans = 0;
        ei->logged_trans = 0;
        ei->delalloc_bytes = 0;
+       ei->defrag_bytes = 0;
        ei->disk_i_size = 0;
        ei->flags = 0;
        ei->csum_bytes = 0;
@@ -8231,6 +8598,7 @@ void btrfs_destroy_inode(struct inode *inode)
        WARN_ON(BTRFS_I(inode)->reserved_extents);
        WARN_ON(BTRFS_I(inode)->delalloc_bytes);
        WARN_ON(BTRFS_I(inode)->csum_bytes);
+       WARN_ON(BTRFS_I(inode)->defrag_bytes);
 
        /*
         * This can happen where we create an inode, but somebody else also
@@ -8646,7 +9014,7 @@ static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput,
                spin_unlock(&root->delalloc_lock);
 
                work = btrfs_alloc_delalloc_work(inode, 0, delay_iput);
-               if (unlikely(!work)) {
+               if (!work) {
                        if (delay_iput)
                                btrfs_add_delayed_iput(inode);
                        else
@@ -8832,7 +9200,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
        }
        key.objectid = btrfs_ino(inode);
        key.offset = 0;
-       btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
+       key.type = BTRFS_EXTENT_DATA_KEY;
        datasize = btrfs_file_extent_calc_inline_size(name_len);
        err = btrfs_insert_empty_item(trans, root, path, &key,
                                      datasize);
index 8a8e29878c34283812f8d990c2432cff94bbab2a..e732274f1afd9c0e7ed8075465c00d1a40a6cfcf 100644 (file)
@@ -332,6 +332,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
                        goto out_drop;
 
        } else {
+               ret = btrfs_set_prop(inode, "btrfs.compression", NULL, 0, 0);
+               if (ret && ret != -ENODATA)
+                       goto out_drop;
                ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
        }
 
@@ -477,8 +480,7 @@ static noinline int create_subvol(struct inode *dir,
        if (ret)
                goto fail;
 
-       leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
-                                     0, objectid, NULL, 0, 0, 0);
+       leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0);
        if (IS_ERR(leaf)) {
                ret = PTR_ERR(leaf);
                goto fail;
@@ -503,7 +505,7 @@ static noinline int create_subvol(struct inode *dir,
        btrfs_set_stack_inode_generation(inode_item, 1);
        btrfs_set_stack_inode_size(inode_item, 3);
        btrfs_set_stack_inode_nlink(inode_item, 1);
-       btrfs_set_stack_inode_nbytes(inode_item, root->leafsize);
+       btrfs_set_stack_inode_nbytes(inode_item, root->nodesize);
        btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
 
        btrfs_set_root_flags(&root_item, 0);
@@ -535,7 +537,7 @@ static noinline int create_subvol(struct inode *dir,
 
        key.objectid = objectid;
        key.offset = 0;
-       btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+       key.type = BTRFS_ROOT_ITEM_KEY;
        ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
                                &root_item);
        if (ret)
@@ -882,7 +884,7 @@ out_unlock:
  * file you want to defrag, we return 0 to let you know to skip this
  * part of the file
  */
-static int check_defrag_in_cache(struct inode *inode, u64 offset, int thresh)
+static int check_defrag_in_cache(struct inode *inode, u64 offset, u32 thresh)
 {
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        struct extent_map *em = NULL;
@@ -917,7 +919,7 @@ static int check_defrag_in_cache(struct inode *inode, u64 offset, int thresh)
  */
 static int find_new_extents(struct btrfs_root *root,
                            struct inode *inode, u64 newer_than,
-                           u64 *off, int thresh)
+                           u64 *off, u32 thresh)
 {
        struct btrfs_path *path;
        struct btrfs_key min_key;
@@ -936,12 +938,9 @@ static int find_new_extents(struct btrfs_root *root,
        min_key.offset = *off;
 
        while (1) {
-               path->keep_locks = 1;
                ret = btrfs_search_forward(root, &min_key, path, newer_than);
                if (ret != 0)
                        goto none;
-               path->keep_locks = 0;
-               btrfs_unlock_up_safe(path, 1);
 process_slot:
                if (min_key.objectid != ino)
                        goto none;
@@ -1029,7 +1028,7 @@ static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em)
        return ret;
 }
 
-static int should_defrag_range(struct inode *inode, u64 start, int thresh,
+static int should_defrag_range(struct inode *inode, u64 start, u32 thresh,
                               u64 *last_len, u64 *skip, u64 *defrag_end,
                               int compress)
 {
@@ -1259,7 +1258,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
        int ret;
        int defrag_count = 0;
        int compress_type = BTRFS_COMPRESS_ZLIB;
-       int extent_thresh = range->extent_thresh;
+       u32 extent_thresh = range->extent_thresh;
        unsigned long max_cluster = (256 * 1024) >> PAGE_CACHE_SHIFT;
        unsigned long cluster = max_cluster;
        u64 new_align = ~((u64)128 * 1024 - 1);
@@ -1335,8 +1334,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
                inode->i_mapping->writeback_index = i;
 
        while (i <= last_index && defrag_count < max_to_defrag &&
-              (i < (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
-               PAGE_CACHE_SHIFT)) {
+              (i < DIV_ROUND_UP(i_size_read(inode), PAGE_CACHE_SIZE))) {
                /*
                 * make sure we stop running if someone unmounts
                 * the FS
@@ -1359,7 +1357,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
                         * the should_defrag function tells us how much to skip
                         * bump our counter by the suggested amount
                         */
-                       next = (skip + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+                       next = DIV_ROUND_UP(skip, PAGE_CACHE_SIZE);
                        i = max(i + 1, next);
                        continue;
                }
@@ -1554,7 +1552,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
                goto out_free;
        }
 
-       old_size = device->total_bytes;
+       old_size = btrfs_device_get_total_bytes(device);
 
        if (mod < 0) {
                if (new_size > old_size) {
@@ -2089,8 +2087,6 @@ static noinline int search_ioctl(struct inode *inode,
        key.type = sk->min_type;
        key.offset = sk->min_offset;
 
-       path->keep_locks = 1;
-
        while (1) {
                ret = btrfs_search_forward(root, &key, path, sk->min_transid);
                if (ret != 0) {
@@ -2526,9 +2522,9 @@ out_unlock:
                ASSERT(dest->send_in_progress == 0);
 
                /* the last ref */
-               if (dest->cache_inode) {
-                       iput(dest->cache_inode);
-                       dest->cache_inode = NULL;
+               if (dest->ino_cache_inode) {
+                       iput(dest->ino_cache_inode);
+                       dest->ino_cache_inode = NULL;
                }
        }
 out_dput:
@@ -2634,6 +2630,9 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg)
        vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
        ret = btrfs_init_new_device(root, vol_args->name);
 
+       if (!ret)
+               btrfs_info(root->fs_info, "disk added %s",vol_args->name);
+
        kfree(vol_args);
 out:
        mutex_unlock(&root->fs_info->volume_mutex);
@@ -2673,6 +2672,9 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
        mutex_unlock(&root->fs_info->volume_mutex);
        atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);
 
+       if (!ret)
+               btrfs_info(root->fs_info, "disk deleted %s",vol_args->name);
+
 out:
        kfree(vol_args);
 err_drop:
@@ -2737,8 +2739,8 @@ static long btrfs_ioctl_dev_info(struct btrfs_root *root, void __user *arg)
        }
 
        di_args->devid = dev->devid;
-       di_args->bytes_used = dev->bytes_used;
-       di_args->total_bytes = dev->total_bytes;
+       di_args->bytes_used = btrfs_device_get_bytes_used(dev);
+       di_args->total_bytes = btrfs_device_get_total_bytes(dev);
        memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid));
        if (dev->name) {
                struct rcu_string *name;
@@ -3164,7 +3166,7 @@ static void clone_update_extent_map(struct inode *inode,
                                        em->start + em->len - 1, 0);
        }
 
-       if (unlikely(ret))
+       if (ret)
                set_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
                        &BTRFS_I(inode)->runtime_flags);
 }
@@ -3199,7 +3201,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
        u64 last_dest_end = destoff;
 
        ret = -ENOMEM;
-       buf = vmalloc(btrfs_level_size(root, 0));
+       buf = vmalloc(root->nodesize);
        if (!buf)
                return ret;
 
@@ -3252,11 +3254,11 @@ process_slot:
                slot = path->slots[0];
 
                btrfs_item_key_to_cpu(leaf, &key, slot);
-               if (btrfs_key_type(&key) > BTRFS_EXTENT_DATA_KEY ||
+               if (key.type > BTRFS_EXTENT_DATA_KEY ||
                    key.objectid != btrfs_ino(src))
                        break;
 
-               if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY) {
+               if (key.type == BTRFS_EXTENT_DATA_KEY) {
                        struct btrfs_file_extent_item *extent;
                        int type;
                        u32 size;
@@ -5283,6 +5285,12 @@ long btrfs_ioctl(struct file *file, unsigned int
                if (ret)
                        return ret;
                ret = btrfs_sync_fs(file->f_dentry->d_sb, 1);
+               /*
+                * The transaction thread may want to do more work,
+                * namely it pokes the cleaner ktread that will start
+                * processing uncleaned subvols.
+                */
+               wake_up_process(root->fs_info->transaction_kthread);
                return ret;
        }
        case BTRFS_IOC_START_SYNC:
index dfad8514f0daa5054378ebae0d739c9a89dd3963..78285f30909edd09f19cc6eb48985d47eed3c565 100644 (file)
@@ -266,8 +266,7 @@ static int lzo_decompress_biovec(struct list_head *ws,
        char *data_in;
        unsigned long page_in_index = 0;
        unsigned long page_out_index = 0;
-       unsigned long total_pages_in = (srclen + PAGE_CACHE_SIZE - 1) /
-                                       PAGE_CACHE_SIZE;
+       unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_CACHE_SIZE);
        unsigned long buf_start;
        unsigned long buf_offset = 0;
        unsigned long bytes;
index 65793edb38ca881a3f82f49101bc4b079574ed4c..47767d5b8f0bacb10613941dcd90259d097ee075 100644 (file)
@@ -27,7 +27,7 @@ int btrfs_insert_orphan_item(struct btrfs_trans_handle *trans,
        int ret = 0;
 
        key.objectid = BTRFS_ORPHAN_OBJECTID;
-       btrfs_set_key_type(&key, BTRFS_ORPHAN_ITEM_KEY);
+       key.type = BTRFS_ORPHAN_ITEM_KEY;
        key.offset = offset;
 
        path = btrfs_alloc_path();
@@ -48,7 +48,7 @@ int btrfs_del_orphan_item(struct btrfs_trans_handle *trans,
        int ret = 0;
 
        key.objectid = BTRFS_ORPHAN_OBJECTID;
-       btrfs_set_key_type(&key, BTRFS_ORPHAN_ITEM_KEY);
+       key.type = BTRFS_ORPHAN_ITEM_KEY;
        key.offset = offset;
 
        path = btrfs_alloc_path();
index 9626b4ad3b9a5a82812c7ced61ed9ce47fd995fb..647ab12fdf5dbcb397e93ee8a958968b2d83ab1e 100644 (file)
@@ -195,7 +195,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
        for (i = 0 ; i < nr ; i++) {
                item = btrfs_item_nr(i);
                btrfs_item_key_to_cpu(l, &key, i);
-               type = btrfs_key_type(&key);
+               type = key.type;
                printk(KERN_INFO "\titem %d key (%llu %u %llu) itemoff %d "
                       "itemsize %d\n",
                        i, key.objectid, type, key.offset,
@@ -336,7 +336,6 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *c)
        for (i = 0; i < nr; i++) {
                struct extent_buffer *next = read_tree_block(root,
                                        btrfs_node_blockptr(c, i),
-                                       btrfs_level_size(root, level - 1),
                                        btrfs_node_ptr_generation(c, i));
                if (btrfs_is_leaf(next) &&
                   level != 1)
index ded5c601d9162a7699a3fa4802a8c9df9bd37722..48b60dbf807fd170593b2e0c7d0a3d1a36f26f58 100644 (file)
@@ -539,10 +539,9 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
        struct extent_buffer *leaf;
        struct btrfs_key key;
 
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
-       if (unlikely(test_bit(BTRFS_ROOT_DUMMY_ROOT, &quota_root->state)))
+       if (btrfs_test_is_dummy_root(quota_root))
                return 0;
-#endif
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -551,9 +550,15 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
        key.type = BTRFS_QGROUP_INFO_KEY;
        key.offset = qgroupid;
 
+       /*
+        * Avoid a transaction abort by catching -EEXIST here. In that
+        * case, we proceed by re-initializing the existing structure
+        * on disk.
+        */
+
        ret = btrfs_insert_empty_item(trans, quota_root, path, &key,
                                      sizeof(*qgroup_info));
-       if (ret)
+       if (ret && ret != -EEXIST)
                goto out;
 
        leaf = path->nodes[0];
@@ -572,7 +577,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
        key.type = BTRFS_QGROUP_LIMIT_KEY;
        ret = btrfs_insert_empty_item(trans, quota_root, path, &key,
                                      sizeof(*qgroup_limit));
-       if (ret)
+       if (ret && ret != -EEXIST)
                goto out;
 
        leaf = path->nodes[0];
@@ -692,10 +697,9 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans,
        int ret;
        int slot;
 
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
-       if (unlikely(test_bit(BTRFS_ROOT_DUMMY_ROOT, &root->state)))
+       if (btrfs_test_is_dummy_root(root))
                return 0;
-#endif
+
        key.objectid = 0;
        key.type = BTRFS_QGROUP_INFO_KEY;
        key.offset = qgroup->qgroupid;
@@ -1335,6 +1339,8 @@ int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans,
        INIT_LIST_HEAD(&oper->elem.list);
        oper->elem.seq = 0;
 
+       trace_btrfs_qgroup_record_ref(oper);
+
        if (type == BTRFS_QGROUP_OPER_SUB_SUBTREE) {
                /*
                 * If any operation for this bytenr/ref_root combo
@@ -2077,6 +2083,8 @@ static int btrfs_qgroup_account(struct btrfs_trans_handle *trans,
 
        ASSERT(is_fstree(oper->ref_root));
 
+       trace_btrfs_qgroup_account(oper);
+
        switch (oper->type) {
        case BTRFS_QGROUP_OPER_ADD_EXCL:
        case BTRFS_QGROUP_OPER_SUB_EXCL:
@@ -2237,7 +2245,6 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
        if (srcid) {
                struct btrfs_root *srcroot;
                struct btrfs_key srckey;
-               int srcroot_level;
 
                srckey.objectid = srcid;
                srckey.type = BTRFS_ROOT_ITEM_KEY;
@@ -2249,8 +2256,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
                }
 
                rcu_read_lock();
-               srcroot_level = btrfs_header_level(srcroot->node);
-               level_size = btrfs_level_size(srcroot, srcroot_level);
+               level_size = srcroot->nodesize;
                rcu_read_unlock();
        }
 
@@ -2566,7 +2572,7 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
                    found.type != BTRFS_METADATA_ITEM_KEY)
                        continue;
                if (found.type == BTRFS_METADATA_ITEM_KEY)
-                       num_bytes = fs_info->extent_root->leafsize;
+                       num_bytes = fs_info->extent_root->nodesize;
                else
                        num_bytes = found.offset;
 
index 0a6b6e4bcbb97a8af56ad6a58aef9f514c3b1132..6a41631cb95988c89e23ffdcb6bdc2466c065b2d 100644 (file)
@@ -912,7 +912,7 @@ static struct page *page_in_rbio(struct btrfs_raid_bio *rbio,
 static unsigned long rbio_nr_pages(unsigned long stripe_len, int nr_stripes)
 {
        unsigned long nr = stripe_len * nr_stripes;
-       return (nr + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       return DIV_ROUND_UP(nr, PAGE_CACHE_SIZE);
 }
 
 /*
@@ -1442,7 +1442,7 @@ static int raid56_rmw_stripe(struct btrfs_raid_bio *rbio)
        struct btrfs_bio *bbio = rbio->bbio;
        struct bio_list bio_list;
        int ret;
-       int nr_pages = (rbio->stripe_len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       int nr_pages = DIV_ROUND_UP(rbio->stripe_len, PAGE_CACHE_SIZE);
        int pagenr;
        int stripe;
        struct bio *bio;
@@ -1725,7 +1725,7 @@ static void __raid_recover_end_io(struct btrfs_raid_bio *rbio)
        int pagenr, stripe;
        void **pointers;
        int faila = -1, failb = -1;
-       int nr_pages = (rbio->stripe_len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       int nr_pages = DIV_ROUND_UP(rbio->stripe_len, PAGE_CACHE_SIZE);
        struct page *page;
        int err;
        int i;
@@ -1940,7 +1940,7 @@ static int __raid56_parity_recover(struct btrfs_raid_bio *rbio)
        struct btrfs_bio *bbio = rbio->bbio;
        struct bio_list bio_list;
        int ret;
-       int nr_pages = (rbio->stripe_len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       int nr_pages = DIV_ROUND_UP(rbio->stripe_len, PAGE_CACHE_SIZE);
        int pagenr;
        int stripe;
        struct bio *bio;
index 20408c6b665ae94e03a152c94bb822bb87fdc63e..b63ae20618fb3f573d7917b088fa58a11f887293 100644 (file)
@@ -347,7 +347,7 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
        if (!re)
                return NULL;
 
-       blocksize = btrfs_level_size(root, level);
+       blocksize = root->nodesize;
        re->logical = logical;
        re->blocksize = blocksize;
        re->top = *top;
index 65245a07275baa37e8089e5e0901c924193d12da..74257d6436adda1b772d8658be41202093ef577a 100644 (file)
@@ -736,7 +736,8 @@ again:
                err = ret;
                goto out;
        }
-       BUG_ON(!ret || !path1->slots[0]);
+       ASSERT(ret);
+       ASSERT(path1->slots[0]);
 
        path1->slots[0]--;
 
@@ -746,10 +747,10 @@ again:
                 * the backref was added previously when processing
                 * backref of type BTRFS_TREE_BLOCK_REF_KEY
                 */
-               BUG_ON(!list_is_singular(&cur->upper));
+               ASSERT(list_is_singular(&cur->upper));
                edge = list_entry(cur->upper.next, struct backref_edge,
                                  list[LOWER]);
-               BUG_ON(!list_empty(&edge->list[UPPER]));
+               ASSERT(list_empty(&edge->list[UPPER]));
                exist = edge->node[UPPER];
                /*
                 * add the upper level block to pending list if we need
@@ -831,7 +832,7 @@ again:
                                        cur->cowonly = 1;
                        }
 #else
-               BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY);
+               ASSERT(key.type != BTRFS_EXTENT_REF_V0_KEY);
                if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) {
 #endif
                        if (key.objectid == key.offset) {
@@ -840,7 +841,7 @@ again:
                                 * backref of this type.
                                 */
                                root = find_reloc_root(rc, cur->bytenr);
-                               BUG_ON(!root);
+                               ASSERT(root);
                                cur->root = root;
                                break;
                        }
@@ -868,7 +869,7 @@ again:
                        } else {
                                upper = rb_entry(rb_node, struct backref_node,
                                                 rb_node);
-                               BUG_ON(!upper->checked);
+                               ASSERT(upper->checked);
                                INIT_LIST_HEAD(&edge->list[UPPER]);
                        }
                        list_add_tail(&edge->list[LOWER], &cur->upper);
@@ -892,7 +893,7 @@ again:
 
                if (btrfs_root_level(&root->root_item) == cur->level) {
                        /* tree root */
-                       BUG_ON(btrfs_root_bytenr(&root->root_item) !=
+                       ASSERT(btrfs_root_bytenr(&root->root_item) ==
                               cur->bytenr);
                        if (should_ignore_root(root))
                                list_add(&cur->list, &useless);
@@ -927,7 +928,7 @@ again:
                need_check = true;
                for (; level < BTRFS_MAX_LEVEL; level++) {
                        if (!path2->nodes[level]) {
-                               BUG_ON(btrfs_root_bytenr(&root->root_item) !=
+                               ASSERT(btrfs_root_bytenr(&root->root_item) ==
                                       lower->bytenr);
                                if (should_ignore_root(root))
                                        list_add(&lower->list, &useless);
@@ -977,12 +978,15 @@ again:
                                        need_check = false;
                                        list_add_tail(&edge->list[UPPER],
                                                      &list);
-                               } else
+                               } else {
+                                       if (upper->checked)
+                                               need_check = true;
                                        INIT_LIST_HEAD(&edge->list[UPPER]);
+                               }
                        } else {
                                upper = rb_entry(rb_node, struct backref_node,
                                                 rb_node);
-                               BUG_ON(!upper->checked);
+                               ASSERT(upper->checked);
                                INIT_LIST_HEAD(&edge->list[UPPER]);
                                if (!upper->owner)
                                        upper->owner = btrfs_header_owner(eb);
@@ -1026,7 +1030,7 @@ next:
         * everything goes well, connect backref nodes and insert backref nodes
         * into the cache.
         */
-       BUG_ON(!node->checked);
+       ASSERT(node->checked);
        cowonly = node->cowonly;
        if (!cowonly) {
                rb_node = tree_insert(&cache->rb_root, node->bytenr,
@@ -1062,8 +1066,21 @@ next:
                        continue;
                }
 
-               BUG_ON(!upper->checked);
-               BUG_ON(cowonly != upper->cowonly);
+               if (!upper->checked) {
+                       /*
+                        * Still want to blow up for developers since this is a
+                        * logic bug.
+                        */
+                       ASSERT(0);
+                       err = -EINVAL;
+                       goto out;
+               }
+               if (cowonly != upper->cowonly) {
+                       ASSERT(0);
+                       err = -EINVAL;
+                       goto out;
+               }
+
                if (!cowonly) {
                        rb_node = tree_insert(&cache->rb_root, upper->bytenr,
                                              &upper->rb_node);
@@ -1086,7 +1103,7 @@ next:
        while (!list_empty(&useless)) {
                upper = list_entry(useless.next, struct backref_node, list);
                list_del_init(&upper->list);
-               BUG_ON(!list_empty(&upper->upper));
+               ASSERT(list_empty(&upper->upper));
                if (upper == node)
                        node = NULL;
                if (upper->lowest) {
@@ -1119,29 +1136,45 @@ out:
        if (err) {
                while (!list_empty(&useless)) {
                        lower = list_entry(useless.next,
-                                          struct backref_node, upper);
-                       list_del_init(&lower->upper);
+                                          struct backref_node, list);
+                       list_del_init(&lower->list);
                }
-               upper = node;
-               INIT_LIST_HEAD(&list);
-               while (upper) {
-                       if (RB_EMPTY_NODE(&upper->rb_node)) {
-                               list_splice_tail(&upper->upper, &list);
-                               free_backref_node(cache, upper);
-                       }
-
-                       if (list_empty(&list))
-                               break;
-
-                       edge = list_entry(list.next, struct backref_edge,
-                                         list[LOWER]);
+               while (!list_empty(&list)) {
+                       edge = list_first_entry(&list, struct backref_edge,
+                                               list[UPPER]);
+                       list_del(&edge->list[UPPER]);
                        list_del(&edge->list[LOWER]);
+                       lower = edge->node[LOWER];
                        upper = edge->node[UPPER];
                        free_backref_edge(cache, edge);
+
+                       /*
+                        * Lower is no longer linked to any upper backref nodes
+                        * and isn't in the cache, we can free it ourselves.
+                        */
+                       if (list_empty(&lower->upper) &&
+                           RB_EMPTY_NODE(&lower->rb_node))
+                               list_add(&lower->list, &useless);
+
+                       if (!RB_EMPTY_NODE(&upper->rb_node))
+                               continue;
+
+                       /* Add this guy's upper edges to the list to proces */
+                       list_for_each_entry(edge, &upper->upper, list[LOWER])
+                               list_add_tail(&edge->list[UPPER], &list);
+                       if (list_empty(&upper->upper))
+                               list_add(&upper->list, &useless);
+               }
+
+               while (!list_empty(&useless)) {
+                       lower = list_entry(useless.next,
+                                          struct backref_node, list);
+                       list_del_init(&lower->list);
+                       free_backref_node(cache, lower);
                }
                return ERR_PTR(err);
        }
-       BUG_ON(node && node->detached);
+       ASSERT(!node || !node->detached);
        return node;
 }
 
@@ -1787,7 +1820,7 @@ again:
                        btrfs_node_key_to_cpu(parent, next_key, slot + 1);
 
                old_bytenr = btrfs_node_blockptr(parent, slot);
-               blocksize = btrfs_level_size(dest, level - 1);
+               blocksize = dest->nodesize;
                old_ptr_gen = btrfs_node_ptr_generation(parent, slot);
 
                if (level <= max_level) {
@@ -1813,8 +1846,7 @@ again:
                                break;
                        }
 
-                       eb = read_tree_block(dest, old_bytenr, blocksize,
-                                            old_ptr_gen);
+                       eb = read_tree_block(dest, old_bytenr, old_ptr_gen);
                        if (!eb || !extent_buffer_uptodate(eb)) {
                                ret = (!eb) ? -ENOMEM : -EIO;
                                free_extent_buffer(eb);
@@ -1944,7 +1976,6 @@ int walk_down_reloc_tree(struct btrfs_root *root, struct btrfs_path *path,
        u64 bytenr;
        u64 ptr_gen = 0;
        u64 last_snapshot;
-       u32 blocksize;
        u32 nritems;
 
        last_snapshot = btrfs_root_last_snapshot(&root->root_item);
@@ -1970,8 +2001,7 @@ int walk_down_reloc_tree(struct btrfs_root *root, struct btrfs_path *path,
                }
 
                bytenr = btrfs_node_blockptr(eb, path->slots[i]);
-               blocksize = btrfs_level_size(root, i - 1);
-               eb = read_tree_block(root, bytenr, blocksize, ptr_gen);
+               eb = read_tree_block(root, bytenr, ptr_gen);
                if (!eb || !extent_buffer_uptodate(eb)) {
                        free_extent_buffer(eb);
                        return -EIO;
@@ -2316,7 +2346,7 @@ void free_reloc_roots(struct list_head *list)
 }
 
 static noinline_for_stack
-int merge_reloc_roots(struct reloc_control *rc)
+void merge_reloc_roots(struct reloc_control *rc)
 {
        struct btrfs_root *root;
        struct btrfs_root *reloc_root;
@@ -2397,7 +2427,6 @@ out:
        }
 
        BUG_ON(!RB_EMPTY_ROOT(&rc->reloc_root_tree.rb_root));
-       return ret;
 }
 
 static void free_block_list(struct rb_root *blocks)
@@ -2544,8 +2573,7 @@ u64 calcu_metadata_size(struct reloc_control *rc,
                        if (next->processed && (reserve || next != node))
                                break;
 
-                       num_bytes += btrfs_level_size(rc->extent_root,
-                                                     next->level);
+                       num_bytes += rc->extent_root->nodesize;
 
                        if (list_empty(&next->upper))
                                break;
@@ -2679,9 +2707,9 @@ static int do_relocation(struct btrfs_trans_handle *trans,
                                goto next;
                }
 
-               blocksize = btrfs_level_size(root, node->level);
+               blocksize = root->nodesize;
                generation = btrfs_node_ptr_generation(upper->eb, slot);
-               eb = read_tree_block(root, bytenr, blocksize, generation);
+               eb = read_tree_block(root, bytenr, generation);
                if (!eb || !extent_buffer_uptodate(eb)) {
                        free_extent_buffer(eb);
                        err = -EIO;
@@ -2789,7 +2817,7 @@ static void __mark_block_processed(struct reloc_control *rc,
        u32 blocksize;
        if (node->level == 0 ||
            in_block_group(node->bytenr, rc->block_group)) {
-               blocksize = btrfs_level_size(rc->extent_root, node->level);
+               blocksize = rc->extent_root->nodesize;
                mark_block_processed(rc, node->bytenr, blocksize);
        }
        node->processed = 1;
@@ -2843,7 +2871,7 @@ static int get_tree_block_key(struct reloc_control *rc,
 
        BUG_ON(block->key_ready);
        eb = read_tree_block(rc->extent_root, block->bytenr,
-                            block->key.objectid, block->key.offset);
+                            block->key.offset);
        if (!eb || !extent_buffer_uptodate(eb)) {
                free_extent_buffer(eb);
                return -EIO;
@@ -2858,20 +2886,6 @@ static int get_tree_block_key(struct reloc_control *rc,
        return 0;
 }
 
-static int reada_tree_block(struct reloc_control *rc,
-                           struct tree_block *block)
-{
-       BUG_ON(block->key_ready);
-       if (block->key.type == BTRFS_METADATA_ITEM_KEY)
-               readahead_tree_block(rc->extent_root, block->bytenr,
-                                    block->key.objectid,
-                                    rc->extent_root->leafsize);
-       else
-               readahead_tree_block(rc->extent_root, block->bytenr,
-                                    block->key.objectid, block->key.offset);
-       return 0;
-}
-
 /*
  * helper function to relocate a tree block
  */
@@ -2951,7 +2965,8 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans,
        while (rb_node) {
                block = rb_entry(rb_node, struct tree_block, rb_node);
                if (!block->key_ready)
-                       reada_tree_block(rc, block);
+                       readahead_tree_block(rc->extent_root, block->bytenr,
+                                       block->key.objectid);
                rb_node = rb_next(rb_node);
        }
 
@@ -3313,7 +3328,7 @@ static int add_tree_block(struct reloc_control *rc,
                return -ENOMEM;
 
        block->bytenr = extent_key->objectid;
-       block->key.objectid = rc->extent_root->leafsize;
+       block->key.objectid = rc->extent_root->nodesize;
        block->key.offset = generation;
        block->level = level;
        block->key_ready = 0;
@@ -3640,7 +3655,7 @@ int add_data_references(struct reloc_control *rc,
        struct btrfs_extent_inline_ref *iref;
        unsigned long ptr;
        unsigned long end;
-       u32 blocksize = btrfs_level_size(rc->extent_root, 0);
+       u32 blocksize = rc->extent_root->nodesize;
        int ret = 0;
        int err = 0;
 
@@ -3783,7 +3798,7 @@ next:
                }
 
                if (key.type == BTRFS_METADATA_ITEM_KEY &&
-                   key.objectid + rc->extent_root->leafsize <=
+                   key.objectid + rc->extent_root->nodesize <=
                    rc->search_start) {
                        path->slots[0]++;
                        goto next;
@@ -3801,7 +3816,7 @@ next:
                                rc->search_start = key.objectid + key.offset;
                        else
                                rc->search_start = key.objectid +
-                                       rc->extent_root->leafsize;
+                                       rc->extent_root->nodesize;
                        memcpy(extent_key, &key, sizeof(key));
                        return 0;
                }
@@ -4096,7 +4111,6 @@ static int __insert_orphan_inode(struct btrfs_trans_handle *trans,
        btrfs_set_inode_flags(leaf, item, BTRFS_INODE_NOCOMPRESS |
                                          BTRFS_INODE_PREALLOC);
        btrfs_mark_buffer_dirty(leaf);
-       btrfs_release_path(path);
 out:
        btrfs_free_path(path);
        return ret;
index f4a41f37be229b555fb2e26993aff7055ee7fff1..efa08311382725c6770f3899033778ba8e3129ac 100644 (file)
@@ -137,7 +137,6 @@ struct scrub_ctx {
        int                     pages_per_rd_bio;
        u32                     sectorsize;
        u32                     nodesize;
-       u32                     leafsize;
 
        int                     is_dev_replace;
        struct scrub_wr_ctx     wr_ctx;
@@ -178,17 +177,12 @@ struct scrub_copy_nocow_ctx {
 struct scrub_warning {
        struct btrfs_path       *path;
        u64                     extent_item_size;
-       char                    *scratch_buf;
-       char                    *msg_buf;
        const char              *errstr;
        sector_t                sector;
        u64                     logical;
        struct btrfs_device     *dev;
-       int                     msg_bufsize;
-       int                     scratch_bufsize;
 };
 
-
 static void scrub_pending_bio_inc(struct scrub_ctx *sctx);
 static void scrub_pending_bio_dec(struct scrub_ctx *sctx);
 static void scrub_pending_trans_workers_inc(struct scrub_ctx *sctx);
@@ -438,7 +432,6 @@ struct scrub_ctx *scrub_setup_ctx(struct btrfs_device *dev, int is_dev_replace)
        }
        sctx->first_free = 0;
        sctx->nodesize = dev->dev_root->nodesize;
-       sctx->leafsize = dev->dev_root->leafsize;
        sctx->sectorsize = dev->dev_root->sectorsize;
        atomic_set(&sctx->bios_in_flight, 0);
        atomic_set(&sctx->workers_pending, 0);
@@ -553,7 +546,6 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock)
        u64 ref_root;
        u32 item_size;
        u8 ref_level;
-       const int bufsize = 4096;
        int ret;
 
        WARN_ON(sblock->page_count < 1);
@@ -561,18 +553,13 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock)
        fs_info = sblock->sctx->dev_root->fs_info;
 
        path = btrfs_alloc_path();
+       if (!path)
+               return;
 
-       swarn.scratch_buf = kmalloc(bufsize, GFP_NOFS);
-       swarn.msg_buf = kmalloc(bufsize, GFP_NOFS);
        swarn.sector = (sblock->pagev[0]->physical) >> 9;
        swarn.logical = sblock->pagev[0]->logical;
        swarn.errstr = errstr;
        swarn.dev = NULL;
-       swarn.msg_bufsize = bufsize;
-       swarn.scratch_bufsize = bufsize;
-
-       if (!path || !swarn.scratch_buf || !swarn.msg_buf)
-               goto out;
 
        ret = extent_from_logical(fs_info, swarn.logical, path, &found_key,
                                  &flags);
@@ -613,8 +600,6 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock)
 
 out:
        btrfs_free_path(path);
-       kfree(swarn.scratch_buf);
-       kfree(swarn.msg_buf);
 }
 
 static int scrub_fixup_readpage(u64 inum, u64 offset, u64 root, void *fixup_ctx)
@@ -681,9 +666,9 @@ static int scrub_fixup_readpage(u64 inum, u64 offset, u64 root, void *fixup_ctx)
                        ret = -EIO;
                        goto out;
                }
-               fs_info = BTRFS_I(inode)->root->fs_info;
-               ret = repair_io_failure(fs_info, offset, PAGE_SIZE,
+               ret = repair_io_failure(inode, offset, PAGE_SIZE,
                                        fixup->logical, page,
+                                       offset - page_offset(page),
                                        fixup->mirror_num);
                unlock_page(page);
                corrected = !ret;
@@ -1361,6 +1346,16 @@ static void scrub_recheck_block(struct btrfs_fs_info *fs_info,
        return;
 }
 
+static inline int scrub_check_fsid(u8 fsid[],
+                                  struct scrub_page *spage)
+{
+       struct btrfs_fs_devices *fs_devices = spage->dev->fs_devices;
+       int ret;
+
+       ret = memcmp(fsid, fs_devices->fsid, BTRFS_UUID_SIZE);
+       return !ret;
+}
+
 static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
                                         struct scrub_block *sblock,
                                         int is_metadata, int have_csum,
@@ -1380,7 +1375,7 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
                h = (struct btrfs_header *)mapped_buffer;
 
                if (sblock->pagev[0]->logical != btrfs_stack_header_bytenr(h) ||
-                   memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE) ||
+                   !scrub_check_fsid(h->fsid, sblock->pagev[0]) ||
                    memcmp(h->chunk_tree_uuid, fs_info->chunk_tree_uuid,
                           BTRFS_UUID_SIZE)) {
                        sblock->header_error = 1;
@@ -1751,14 +1746,13 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock)
        if (sblock->pagev[0]->generation != btrfs_stack_header_generation(h))
                ++fail;
 
-       if (memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE))
+       if (!scrub_check_fsid(h->fsid, sblock->pagev[0]))
                ++fail;
 
        if (memcmp(h->chunk_tree_uuid, fs_info->chunk_tree_uuid,
                   BTRFS_UUID_SIZE))
                ++fail;
 
-       WARN_ON(sctx->nodesize != sctx->leafsize);
        len = sctx->nodesize - BTRFS_CSUM_SIZE;
        mapped_size = PAGE_SIZE - BTRFS_CSUM_SIZE;
        p = ((u8 *)mapped_buffer) + BTRFS_CSUM_SIZE;
@@ -1791,8 +1785,6 @@ static int scrub_checksum_super(struct scrub_block *sblock)
 {
        struct btrfs_super_block *s;
        struct scrub_ctx *sctx = sblock->sctx;
-       struct btrfs_root *root = sctx->dev_root;
-       struct btrfs_fs_info *fs_info = root->fs_info;
        u8 calculated_csum[BTRFS_CSUM_SIZE];
        u8 on_disk_csum[BTRFS_CSUM_SIZE];
        struct page *page;
@@ -1817,7 +1809,7 @@ static int scrub_checksum_super(struct scrub_block *sblock)
        if (sblock->pagev[0]->generation != btrfs_super_generation(s))
                ++fail_gen;
 
-       if (memcmp(s->fsid, fs_info->fsid, BTRFS_UUID_SIZE))
+       if (!scrub_check_fsid(s->fsid, sblock->pagev[0]))
                ++fail_cor;
 
        len = BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE;
@@ -2196,7 +2188,6 @@ static int scrub_extent(struct scrub_ctx *sctx, u64 logical, u64 len,
                sctx->stat.data_bytes_scrubbed += len;
                spin_unlock(&sctx->stat_lock);
        } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
-               WARN_ON(sctx->nodesize != sctx->leafsize);
                blocksize = sctx->nodesize;
                spin_lock(&sctx->stat_lock);
                sctx->stat.tree_extents_scrubbed++;
@@ -2487,7 +2478,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
                        btrfs_item_key_to_cpu(l, &key, slot);
 
                        if (key.type == BTRFS_METADATA_ITEM_KEY)
-                               bytes = root->leafsize;
+                               bytes = root->nodesize;
                        else
                                bytes = key.offset;
 
@@ -2714,7 +2705,7 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
                if (found_key.objectid != scrub_dev->devid)
                        break;
 
-               if (btrfs_key_type(&found_key) != BTRFS_DEV_EXTENT_KEY)
+               if (found_key.type != BTRFS_DEV_EXTENT_KEY)
                        break;
 
                if (found_key.offset >= end)
@@ -2828,11 +2819,16 @@ static noinline_for_stack int scrub_supers(struct scrub_ctx *sctx,
        if (test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state))
                return -EIO;
 
-       gen = root->fs_info->last_trans_committed;
+       /* Seed devices of a new filesystem has their own generation. */
+       if (scrub_dev->fs_devices != root->fs_info->fs_devices)
+               gen = scrub_dev->generation;
+       else
+               gen = root->fs_info->last_trans_committed;
 
        for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
                bytenr = btrfs_sb_offset(i);
-               if (bytenr + BTRFS_SUPER_INFO_SIZE > scrub_dev->total_bytes)
+               if (bytenr + BTRFS_SUPER_INFO_SIZE >
+                   scrub_dev->commit_total_bytes)
                        break;
 
                ret = scrub_pages(sctx, bytenr, BTRFS_SUPER_INFO_SIZE, bytenr,
@@ -2910,17 +2906,6 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
        if (btrfs_fs_closing(fs_info))
                return -EINVAL;
 
-       /*
-        * check some assumptions
-        */
-       if (fs_info->chunk_root->nodesize != fs_info->chunk_root->leafsize) {
-               btrfs_err(fs_info,
-                          "scrub: size assumption nodesize == leafsize (%d == %d) fails",
-                      fs_info->chunk_root->nodesize,
-                      fs_info->chunk_root->leafsize);
-               return -EINVAL;
-       }
-
        if (fs_info->chunk_root->nodesize > BTRFS_STRIPE_LEN) {
                /*
                 * in this case scrub is unable to calculate the checksum
index 6528aa6621819f31fae18eb9d337b1cfd624ba3a..874828dd0a8627d8c57097d17303f6db68f76775 100644 (file)
@@ -515,7 +515,8 @@ static int write_buf(struct file *filp, const void *buf, u32 len, loff_t *off)
        set_fs(KERNEL_DS);
 
        while (pos < len) {
-               ret = vfs_write(filp, (char *)buf + pos, len - pos, off);
+               ret = vfs_write(filp, (__force const char __user *)buf + pos,
+                               len - pos, off);
                /* TODO handle that correctly */
                /*if (ret == -ERESTARTSYS) {
                        continue;
@@ -985,11 +986,13 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
        int num;
        u8 type;
 
-       if (found_key->type == BTRFS_XATTR_ITEM_KEY)
-               buf_len = BTRFS_MAX_XATTR_SIZE(root);
-       else
-               buf_len = PATH_MAX;
-
+       /*
+        * Start with a small buffer (1 page). If later we end up needing more
+        * space, which can happen for xattrs on a fs with a leaf size greater
+        * then the page size, attempt to increase the buffer. Typically xattr
+        * values are small.
+        */
+       buf_len = PATH_MAX;
        buf = kmalloc(buf_len, GFP_NOFS);
        if (!buf) {
                ret = -ENOMEM;
@@ -1016,7 +1019,7 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
                                ret = -ENAMETOOLONG;
                                goto out;
                        }
-                       if (name_len + data_len > buf_len) {
+                       if (name_len + data_len > BTRFS_MAX_XATTR_SIZE(root)) {
                                ret = -E2BIG;
                                goto out;
                        }
@@ -1024,12 +1027,34 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
                        /*
                         * Path too long
                         */
-                       if (name_len + data_len > buf_len) {
+                       if (name_len + data_len > PATH_MAX) {
                                ret = -ENAMETOOLONG;
                                goto out;
                        }
                }
 
+               if (name_len + data_len > buf_len) {
+                       buf_len = name_len + data_len;
+                       if (is_vmalloc_addr(buf)) {
+                               vfree(buf);
+                               buf = NULL;
+                       } else {
+                               char *tmp = krealloc(buf, buf_len,
+                                                    GFP_NOFS | __GFP_NOWARN);
+
+                               if (!tmp)
+                                       kfree(buf);
+                               buf = tmp;
+                       }
+                       if (!buf) {
+                               buf = vmalloc(buf_len);
+                               if (!buf) {
+                                       ret = -ENOMEM;
+                                       goto out;
+                               }
+                       }
+               }
+
                read_extent_buffer(eb, buf, (unsigned long)(di + 1),
                                name_len + data_len);
 
@@ -1050,7 +1075,7 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
        }
 
 out:
-       kfree(buf);
+       kvfree(buf);
        return ret;
 }
 
@@ -3302,7 +3327,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
                if (ret < 0 && ret != -ENOENT) {
                        goto out;
                } else if (ret == -ENOENT) {
-                       ret = 1;
+                       ret = 0;
                        break;
                }
 
@@ -5703,7 +5728,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
                        NULL);
        sort_clone_roots = 1;
 
-       current->journal_info = (void *)BTRFS_SEND_TRANS_STUB;
+       current->journal_info = BTRFS_SEND_TRANS_STUB;
        ret = send_subvol(sctx);
        current->journal_info = NULL;
        if (ret < 0)
index c4124de4435bffed06afc3a76ea6aba49a7c5317..a2b97ef10317081aebaa09aedeaddf4dec71a492 100644 (file)
@@ -60,6 +60,7 @@
 #include "backref.h"
 #include "tests/btrfs-tests.h"
 
+#include "qgroup.h"
 #define CREATE_TRACE_POINTS
 #include <trace/events/btrfs.h>
 
@@ -307,13 +308,7 @@ void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function,
 
 static void btrfs_put_super(struct super_block *sb)
 {
-       (void)close_ctree(btrfs_sb(sb)->tree_root);
-       /* FIXME: need to fix VFS to return error? */
-       /* AV: return it _where_?  ->put_super() can be triggered by any number
-        * of async events, up to and including delivery of SIGKILL to the
-        * last process that kept it busy.  Or segfault in the aforementioned
-        * process...  Whom would you report that to?
-        */
+       close_ctree(btrfs_sb(sb)->tree_root);
 }
 
 enum {
@@ -400,7 +395,6 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
        int ret = 0;
        char *compress_type;
        bool compress_force = false;
-       bool compress = false;
 
        cache_gen = btrfs_super_cache_generation(root->fs_info->super_copy);
        if (cache_gen)
@@ -478,7 +472,6 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
                        /* Fallthrough */
                case Opt_compress:
                case Opt_compress_type:
-                       compress = true;
                        if (token == Opt_compress ||
                            token == Opt_compress_force ||
                            strcmp(args[0].from, "zlib") == 0) {
@@ -508,11 +501,18 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
                                btrfs_set_and_info(root, FORCE_COMPRESS,
                                                   "force %s compression",
                                                   compress_type);
-                       } else if (compress) {
+                       } else {
                                if (!btrfs_test_opt(root, COMPRESS))
                                        btrfs_info(root->fs_info,
                                                   "btrfs: use %s compression",
                                                   compress_type);
+                               /*
+                                * If we remount from compress-force=xxx to
+                                * compress=xxx, we need clear FORCE_COMPRESS
+                                * flag, otherwise, there is no way for users
+                                * to disable forcible compression separately.
+                                */
+                               btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS);
                        }
                        break;
                case Opt_ssd:
@@ -1014,7 +1014,7 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
                seq_puts(seq, ",nodatacow");
        if (btrfs_test_opt(root, NOBARRIER))
                seq_puts(seq, ",nobarrier");
-       if (info->max_inline != 8192 * 1024)
+       if (info->max_inline != BTRFS_DEFAULT_MAX_INLINE)
                seq_printf(seq, ",max_inline=%llu", info->max_inline);
        if (info->alloc_start != 0)
                seq_printf(seq, ",alloc_start=%llu", info->alloc_start);
@@ -1215,6 +1215,56 @@ static struct dentry *mount_subvol(const char *subvol_name, int flags,
        return root;
 }
 
+static int parse_security_options(char *orig_opts,
+                                 struct security_mnt_opts *sec_opts)
+{
+       char *secdata = NULL;
+       int ret = 0;
+
+       secdata = alloc_secdata();
+       if (!secdata)
+               return -ENOMEM;
+       ret = security_sb_copy_data(orig_opts, secdata);
+       if (ret) {
+               free_secdata(secdata);
+               return ret;
+       }
+       ret = security_sb_parse_opts_str(secdata, sec_opts);
+       free_secdata(secdata);
+       return ret;
+}
+
+static int setup_security_options(struct btrfs_fs_info *fs_info,
+                                 struct super_block *sb,
+                                 struct security_mnt_opts *sec_opts)
+{
+       int ret = 0;
+
+       /*
+        * Call security_sb_set_mnt_opts() to check whether new sec_opts
+        * is valid.
+        */
+       ret = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL);
+       if (ret)
+               return ret;
+
+#ifdef CONFIG_SECURITY
+       if (!fs_info->security_opts.num_mnt_opts) {
+               /* first time security setup, copy sec_opts to fs_info */
+               memcpy(&fs_info->security_opts, sec_opts, sizeof(*sec_opts));
+       } else {
+               /*
+                * Since SELinux(the only one supports security_mnt_opts) does
+                * NOT support changing context during remount/mount same sb,
+                * This must be the same or part of the same security options,
+                * just free it.
+                */
+               security_free_mnt_opts(sec_opts);
+       }
+#endif
+       return ret;
+}
+
 /*
  * Find a superblock for the given device / mount point.
  *
@@ -1229,6 +1279,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
        struct dentry *root;
        struct btrfs_fs_devices *fs_devices = NULL;
        struct btrfs_fs_info *fs_info = NULL;
+       struct security_mnt_opts new_sec_opts;
        fmode_t mode = FMODE_READ;
        char *subvol_name = NULL;
        u64 subvol_objectid = 0;
@@ -1251,9 +1302,16 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
                return root;
        }
 
+       security_init_mnt_opts(&new_sec_opts);
+       if (data) {
+               error = parse_security_options(data, &new_sec_opts);
+               if (error)
+                       return ERR_PTR(error);
+       }
+
        error = btrfs_scan_one_device(device_name, mode, fs_type, &fs_devices);
        if (error)
-               return ERR_PTR(error);
+               goto error_sec_opts;
 
        /*
         * Setup a dummy root and fs_info for test/set super.  This is because
@@ -1262,13 +1320,16 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
         * then open_ctree will properly initialize everything later.
         */
        fs_info = kzalloc(sizeof(struct btrfs_fs_info), GFP_NOFS);
-       if (!fs_info)
-               return ERR_PTR(-ENOMEM);
+       if (!fs_info) {
+               error = -ENOMEM;
+               goto error_sec_opts;
+       }
 
        fs_info->fs_devices = fs_devices;
 
        fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS);
        fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS);
+       security_init_mnt_opts(&fs_info->security_opts);
        if (!fs_info->super_copy || !fs_info->super_for_commit) {
                error = -ENOMEM;
                goto error_fs_info;
@@ -1306,8 +1367,19 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
        }
 
        root = !error ? get_default_root(s, subvol_objectid) : ERR_PTR(error);
-       if (IS_ERR(root))
+       if (IS_ERR(root)) {
+               deactivate_locked_super(s);
+               error = PTR_ERR(root);
+               goto error_sec_opts;
+       }
+
+       fs_info = btrfs_sb(s);
+       error = setup_security_options(fs_info, s, &new_sec_opts);
+       if (error) {
+               dput(root);
                deactivate_locked_super(s);
+               goto error_sec_opts;
+       }
 
        return root;
 
@@ -1315,6 +1387,8 @@ error_close_devices:
        btrfs_close_devices(fs_devices);
 error_fs_info:
        free_fs_info(fs_info);
+error_sec_opts:
+       security_free_mnt_opts(&new_sec_opts);
        return ERR_PTR(error);
 }
 
@@ -1396,6 +1470,21 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
        sync_filesystem(sb);
        btrfs_remount_prepare(fs_info);
 
+       if (data) {
+               struct security_mnt_opts new_sec_opts;
+
+               security_init_mnt_opts(&new_sec_opts);
+               ret = parse_security_options(data, &new_sec_opts);
+               if (ret)
+                       goto restore;
+               ret = setup_security_options(fs_info, sb,
+                                            &new_sec_opts);
+               if (ret) {
+                       security_free_mnt_opts(&new_sec_opts);
+                       goto restore;
+               }
+       }
+
        ret = btrfs_parse_options(root, data);
        if (ret) {
                ret = -EINVAL;
@@ -1694,7 +1783,11 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
        int ret;
 
-       /* holding chunk_muext to avoid allocating new chunks */
+       /*
+        * holding chunk_muext to avoid allocating new chunks, holding
+        * device_list_mutex to avoid the device being removed
+        */
+       mutex_lock(&fs_info->fs_devices->device_list_mutex);
        mutex_lock(&fs_info->chunk_mutex);
        rcu_read_lock();
        list_for_each_entry_rcu(found, head, list) {
@@ -1735,11 +1828,13 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        ret = btrfs_calc_avail_data_space(fs_info->tree_root, &total_free_data);
        if (ret) {
                mutex_unlock(&fs_info->chunk_mutex);
+               mutex_unlock(&fs_info->fs_devices->device_list_mutex);
                return ret;
        }
        buf->f_bavail += div_u64(total_free_data, factor);
        buf->f_bavail = buf->f_bavail >> bits;
        mutex_unlock(&fs_info->chunk_mutex);
+       mutex_unlock(&fs_info->fs_devices->device_list_mutex);
 
        buf->f_type = BTRFS_SUPER_MAGIC;
        buf->f_bsize = dentry->d_sb->s_blocksize;
@@ -1769,7 +1864,7 @@ static struct file_system_type btrfs_fs_type = {
        .name           = "btrfs",
        .mount          = btrfs_mount,
        .kill_sb        = btrfs_kill_super,
-       .fs_flags       = FS_REQUIRES_DEV,
+       .fs_flags       = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
 };
 MODULE_ALIAS_FS("btrfs");
 
@@ -1992,12 +2087,16 @@ static int __init init_btrfs_fs(void)
                goto free_auto_defrag;
 
        err = btrfs_prelim_ref_init();
+       if (err)
+               goto free_delayed_ref;
+
+       err = btrfs_end_io_wq_init();
        if (err)
                goto free_prelim_ref;
 
        err = btrfs_interface_init();
        if (err)
-               goto free_delayed_ref;
+               goto free_end_io_wq;
 
        btrfs_init_lockdep();
 
@@ -2015,6 +2114,8 @@ static int __init init_btrfs_fs(void)
 
 unregister_ioctl:
        btrfs_interface_exit();
+free_end_io_wq:
+       btrfs_end_io_wq_exit();
 free_prelim_ref:
        btrfs_prelim_ref_exit();
 free_delayed_ref:
index 12e53556e214c2c26f0a67aadf63b53637178822..b2e7bb4393f65cf17575dadc2686a693d83bd253 100644 (file)
@@ -242,7 +242,7 @@ static ssize_t global_rsv_size_show(struct kobject *kobj,
        struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
        return btrfs_show_u64(&block_rsv->size, &block_rsv->lock, buf);
 }
-BTRFS_ATTR(global_rsv_size, 0444, global_rsv_size_show);
+BTRFS_ATTR(global_rsv_size, global_rsv_size_show);
 
 static ssize_t global_rsv_reserved_show(struct kobject *kobj,
                                        struct kobj_attribute *a, char *buf)
@@ -251,7 +251,7 @@ static ssize_t global_rsv_reserved_show(struct kobject *kobj,
        struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
        return btrfs_show_u64(&block_rsv->reserved, &block_rsv->lock, buf);
 }
-BTRFS_ATTR(global_rsv_reserved, 0444, global_rsv_reserved_show);
+BTRFS_ATTR(global_rsv_reserved, global_rsv_reserved_show);
 
 #define to_space_info(_kobj) container_of(_kobj, struct btrfs_space_info, kobj)
 #define to_raid_kobj(_kobj) container_of(_kobj, struct raid_kobject, kobj)
@@ -306,7 +306,7 @@ static ssize_t btrfs_space_info_show_##field(struct kobject *kobj,  \
        struct btrfs_space_info *sinfo = to_space_info(kobj);           \
        return btrfs_show_u64(&sinfo->field, &sinfo->lock, buf);        \
 }                                                                      \
-BTRFS_ATTR(field, 0444, btrfs_space_info_show_##field)
+BTRFS_ATTR(field, btrfs_space_info_show_##field)
 
 static ssize_t btrfs_space_info_show_total_bytes_pinned(struct kobject *kobj,
                                                       struct kobj_attribute *a,
@@ -325,7 +325,7 @@ SPACE_INFO_ATTR(bytes_reserved);
 SPACE_INFO_ATTR(bytes_may_use);
 SPACE_INFO_ATTR(disk_used);
 SPACE_INFO_ATTR(disk_total);
-BTRFS_ATTR(total_bytes_pinned, 0444, btrfs_space_info_show_total_bytes_pinned);
+BTRFS_ATTR(total_bytes_pinned, btrfs_space_info_show_total_bytes_pinned);
 
 static struct attribute *space_info_attrs[] = {
        BTRFS_ATTR_PTR(flags),
@@ -363,7 +363,8 @@ static ssize_t btrfs_label_show(struct kobject *kobj,
                                struct kobj_attribute *a, char *buf)
 {
        struct btrfs_fs_info *fs_info = to_fs_info(kobj);
-       return snprintf(buf, PAGE_SIZE, "%s\n", fs_info->super_copy->label);
+       char *label = fs_info->super_copy->label;
+       return snprintf(buf, PAGE_SIZE, label[0] ? "%s\n" : "%s", label);
 }
 
 static ssize_t btrfs_label_store(struct kobject *kobj,
@@ -374,8 +375,18 @@ static ssize_t btrfs_label_store(struct kobject *kobj,
        struct btrfs_trans_handle *trans;
        struct btrfs_root *root = fs_info->fs_root;
        int ret;
+       size_t p_len;
 
-       if (len >= BTRFS_LABEL_SIZE)
+       if (fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       /*
+        * p_len is the len until the first occurrence of either
+        * '\n' or '\0'
+        */
+       p_len = strcspn(buf, "\n");
+
+       if (p_len >= BTRFS_LABEL_SIZE)
                return -EINVAL;
 
        trans = btrfs_start_transaction(root, 0);
@@ -383,7 +394,8 @@ static ssize_t btrfs_label_store(struct kobject *kobj,
                return PTR_ERR(trans);
 
        spin_lock(&root->fs_info->super_lock);
-       strcpy(fs_info->super_copy->label, buf);
+       memset(fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE);
+       memcpy(fs_info->super_copy->label, buf, p_len);
        spin_unlock(&root->fs_info->super_lock);
        ret = btrfs_commit_transaction(trans, root);
 
@@ -392,14 +404,7 @@ static ssize_t btrfs_label_store(struct kobject *kobj,
 
        return ret;
 }
-BTRFS_ATTR_RW(label, 0644, btrfs_label_show, btrfs_label_store);
-
-static ssize_t btrfs_no_store(struct kobject *kobj,
-                                struct kobj_attribute *a,
-                                const char *buf, size_t len)
-{
-       return -EPERM;
-}
+BTRFS_ATTR_RW(label, btrfs_label_show, btrfs_label_store);
 
 static ssize_t btrfs_nodesize_show(struct kobject *kobj,
                                struct kobj_attribute *a, char *buf)
@@ -409,7 +414,7 @@ static ssize_t btrfs_nodesize_show(struct kobject *kobj,
        return snprintf(buf, PAGE_SIZE, "%u\n", fs_info->super_copy->nodesize);
 }
 
-BTRFS_ATTR_RW(nodesize, 0444, btrfs_nodesize_show, btrfs_no_store);
+BTRFS_ATTR(nodesize, btrfs_nodesize_show);
 
 static ssize_t btrfs_sectorsize_show(struct kobject *kobj,
                                struct kobj_attribute *a, char *buf)
@@ -419,7 +424,7 @@ static ssize_t btrfs_sectorsize_show(struct kobject *kobj,
        return snprintf(buf, PAGE_SIZE, "%u\n", fs_info->super_copy->sectorsize);
 }
 
-BTRFS_ATTR_RW(sectorsize, 0444, btrfs_sectorsize_show, btrfs_no_store);
+BTRFS_ATTR(sectorsize, btrfs_sectorsize_show);
 
 static ssize_t btrfs_clone_alignment_show(struct kobject *kobj,
                                struct kobj_attribute *a, char *buf)
@@ -429,7 +434,7 @@ static ssize_t btrfs_clone_alignment_show(struct kobject *kobj,
        return snprintf(buf, PAGE_SIZE, "%u\n", fs_info->super_copy->sectorsize);
 }
 
-BTRFS_ATTR_RW(clone_alignment, 0444, btrfs_clone_alignment_show, btrfs_no_store);
+BTRFS_ATTR(clone_alignment, btrfs_clone_alignment_show);
 
 static struct attribute *btrfs_attrs[] = {
        BTRFS_ATTR_PTR(label),
index ac46df37504c45c0bac5196ea02d979677b225f6..f7dd298b3cf6ecbb080924ffd083af7bb68a4e1f 100644 (file)
@@ -20,16 +20,20 @@ enum btrfs_feature_set {
        .store  = _store,                                               \
 }
 
-#define BTRFS_ATTR_RW(_name, _mode, _show, _store)                     \
-static struct kobj_attribute btrfs_attr_##_name =                      \
-                       __INIT_KOBJ_ATTR(_name, _mode, _show, _store)
-#define BTRFS_ATTR(_name, _mode, _show)                                        \
-       BTRFS_ATTR_RW(_name, _mode, _show, NULL)
+#define BTRFS_ATTR_RW(_name, _show, _store)                    \
+       static struct kobj_attribute btrfs_attr_##_name =               \
+                       __INIT_KOBJ_ATTR(_name, 0644, _show, _store)
+
+#define BTRFS_ATTR(_name, _show)                                       \
+       static struct kobj_attribute btrfs_attr_##_name =               \
+                       __INIT_KOBJ_ATTR(_name, 0444, _show, NULL)
+
 #define BTRFS_ATTR_PTR(_name)    (&btrfs_attr_##_name.attr)
 
 #define BTRFS_RAID_ATTR(_name, _show)                                  \
-static struct kobj_attribute btrfs_raid_attr_##_name =                 \
+       static struct kobj_attribute btrfs_raid_attr_##_name =          \
                        __INIT_KOBJ_ATTR(_name, 0444, _show, NULL)
+
 #define BTRFS_RAID_ATTR_PTR(_name)    (&btrfs_raid_attr_##_name.attr)
 
 
index c8d9ddf84c6946d166b4e8dc67fa0ffd24a99b46..2299bfde39eec666fe1b0876733746a76673f5a5 100644 (file)
@@ -40,11 +40,12 @@ static struct btrfs_block_group_cache *init_test_block_group(void)
        cache->key.offset = 1024 * 1024 * 1024;
        cache->key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
        cache->sectorsize = 4096;
+       cache->full_stripe_len = 4096;
 
        spin_lock_init(&cache->lock);
        INIT_LIST_HEAD(&cache->list);
        INIT_LIST_HEAD(&cache->cluster_list);
-       INIT_LIST_HEAD(&cache->new_bg_list);
+       INIT_LIST_HEAD(&cache->bg_list);
 
        btrfs_init_free_space_ctl(cache);
 
@@ -364,6 +365,517 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache)
        return 0;
 }
 
+/* Used by test_steal_space_from_bitmap_to_extent(). */
+static bool test_use_bitmap(struct btrfs_free_space_ctl *ctl,
+                           struct btrfs_free_space *info)
+{
+       return ctl->free_extents > 0;
+}
+
+/* Used by test_steal_space_from_bitmap_to_extent(). */
+static int
+check_num_extents_and_bitmaps(const struct btrfs_block_group_cache *cache,
+                             const int num_extents,
+                             const int num_bitmaps)
+{
+       if (cache->free_space_ctl->free_extents != num_extents) {
+               test_msg("Incorrect # of extent entries in the cache: %d, expected %d\n",
+                        cache->free_space_ctl->free_extents, num_extents);
+               return -EINVAL;
+       }
+       if (cache->free_space_ctl->total_bitmaps != num_bitmaps) {
+               test_msg("Incorrect # of extent entries in the cache: %d, expected %d\n",
+                        cache->free_space_ctl->total_bitmaps, num_bitmaps);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/* Used by test_steal_space_from_bitmap_to_extent(). */
+static int check_cache_empty(struct btrfs_block_group_cache *cache)
+{
+       u64 offset;
+       u64 max_extent_size;
+
+       /*
+        * Now lets confirm that there's absolutely no free space left to
+        * allocate.
+        */
+       if (cache->free_space_ctl->free_space != 0) {
+               test_msg("Cache free space is not 0\n");
+               return -EINVAL;
+       }
+
+       /* And any allocation request, no matter how small, should fail now. */
+       offset = btrfs_find_space_for_alloc(cache, 0, 4096, 0,
+                                           &max_extent_size);
+       if (offset != 0) {
+               test_msg("Space allocation did not fail, returned offset: %llu",
+                        offset);
+               return -EINVAL;
+       }
+
+       /* And no extent nor bitmap entries in the cache anymore. */
+       return check_num_extents_and_bitmaps(cache, 0, 0);
+}
+
+/*
+ * Before we were able to steal free space from a bitmap entry to an extent
+ * entry, we could end up with 2 entries representing a contiguous free space.
+ * One would be an extent entry and the other a bitmap entry. Since in order
+ * to allocate space to a caller we use only 1 entry, we couldn't return that
+ * whole range to the caller if it was requested. This forced the caller to
+ * either assume ENOSPC or perform several smaller space allocations, which
+ * wasn't optimal as they could be spread all over the block group while under
+ * concurrency (extra overhead and fragmentation).
+ *
+ * This stealing approach is benefical, since we always prefer to allocate from
+ * extent entries, both for clustered and non-clustered allocation requests.
+ */
+static int
+test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache)
+{
+       int ret;
+       u64 offset;
+       u64 max_extent_size;
+
+       bool (*use_bitmap_op)(struct btrfs_free_space_ctl *,
+                             struct btrfs_free_space *);
+
+       test_msg("Running space stealing from bitmap to extent\n");
+
+       /*
+        * For this test, we want to ensure we end up with an extent entry
+        * immediately adjacent to a bitmap entry, where the bitmap starts
+        * at an offset where the extent entry ends. We keep adding and
+        * removing free space to reach into this state, but to get there
+        * we need to reach a point where marking new free space doesn't
+        * result in adding new extent entries or merging the new space
+        * with existing extent entries - the space ends up being marked
+        * in an existing bitmap that covers the new free space range.
+        *
+        * To get there, we need to reach the threshold defined set at
+        * cache->free_space_ctl->extents_thresh, which currently is
+        * 256 extents on a x86_64 system at least, and a few other
+        * conditions (check free_space_cache.c). Instead of making the
+        * test much longer and complicated, use a "use_bitmap" operation
+        * that forces use of bitmaps as soon as we have at least 1
+        * extent entry.
+        */
+       use_bitmap_op = cache->free_space_ctl->op->use_bitmap;
+       cache->free_space_ctl->op->use_bitmap = test_use_bitmap;
+
+       /*
+        * Extent entry covering free space range [128Mb - 256Kb, 128Mb - 128Kb[
+        */
+       ret = test_add_free_space_entry(cache, 128 * 1024 * 1024 - 256 * 1024,
+                                       128 * 1024, 0);
+       if (ret) {
+               test_msg("Couldn't add extent entry %d\n", ret);
+               return ret;
+       }
+
+       /* Bitmap entry covering free space range [128Mb + 512Kb, 256Mb[ */
+       ret = test_add_free_space_entry(cache, 128 * 1024 * 1024 + 512 * 1024,
+                                       128 * 1024 * 1024 - 512 * 1024, 1);
+       if (ret) {
+               test_msg("Couldn't add bitmap entry %d\n", ret);
+               return ret;
+       }
+
+       ret = check_num_extents_and_bitmaps(cache, 2, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * Now make only the first 256Kb of the bitmap marked as free, so that
+        * we end up with only the following ranges marked as free space:
+        *
+        * [128Mb - 256Kb, 128Mb - 128Kb[
+        * [128Mb + 512Kb, 128Mb + 768Kb[
+        */
+       ret = btrfs_remove_free_space(cache,
+                                     128 * 1024 * 1024 + 768 * 1024,
+                                     128 * 1024 * 1024 - 768 * 1024);
+       if (ret) {
+               test_msg("Failed to free part of bitmap space %d\n", ret);
+               return ret;
+       }
+
+       /* Confirm that only those 2 ranges are marked as free. */
+       if (!test_check_exists(cache, 128 * 1024 * 1024 - 256 * 1024,
+                              128 * 1024)) {
+               test_msg("Free space range missing\n");
+               return -ENOENT;
+       }
+       if (!test_check_exists(cache, 128 * 1024 * 1024 + 512 * 1024,
+                              256 * 1024)) {
+               test_msg("Free space range missing\n");
+               return -ENOENT;
+       }
+
+       /*
+        * Confirm that the bitmap range [128Mb + 768Kb, 256Mb[ isn't marked
+        * as free anymore.
+        */
+       if (test_check_exists(cache, 128 * 1024 * 1024 + 768 * 1024,
+                             128 * 1024 * 1024 - 768 * 1024)) {
+               test_msg("Bitmap region not removed from space cache\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Confirm that the region [128Mb + 256Kb, 128Mb + 512Kb[, which is
+        * covered by the bitmap, isn't marked as free.
+        */
+       if (test_check_exists(cache, 128 * 1024 * 1024 + 256 * 1024,
+                             256 * 1024)) {
+               test_msg("Invalid bitmap region marked as free\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Confirm that the region [128Mb, 128Mb + 256Kb[, which is covered
+        * by the bitmap too, isn't marked as free either.
+        */
+       if (test_check_exists(cache, 128 * 1024 * 1024,
+                             256 * 1024)) {
+               test_msg("Invalid bitmap region marked as free\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Now lets mark the region [128Mb, 128Mb + 512Kb[ as free too. But,
+        * lets make sure the free space cache marks it as free in the bitmap,
+        * and doesn't insert a new extent entry to represent this region.
+        */
+       ret = btrfs_add_free_space(cache, 128 * 1024 * 1024, 512 * 1024);
+       if (ret) {
+               test_msg("Error adding free space: %d\n", ret);
+               return ret;
+       }
+       /* Confirm the region is marked as free. */
+       if (!test_check_exists(cache, 128 * 1024 * 1024, 512 * 1024)) {
+               test_msg("Bitmap region not marked as free\n");
+               return -ENOENT;
+       }
+
+       /*
+        * Confirm that no new extent entries or bitmap entries were added to
+        * the cache after adding that free space region.
+        */
+       ret = check_num_extents_and_bitmaps(cache, 2, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * Now lets add a small free space region to the right of the previous
+        * one, which is not contiguous with it and is part of the bitmap too.
+        * The goal is to test that the bitmap entry space stealing doesn't
+        * steal this space region.
+        */
+       ret = btrfs_add_free_space(cache, 128 * 1024 * 1024 + 16 * 1024 * 1024,
+                                  4096);
+       if (ret) {
+               test_msg("Error adding free space: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Confirm that no new extent entries or bitmap entries were added to
+        * the cache after adding that free space region.
+        */
+       ret = check_num_extents_and_bitmaps(cache, 2, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * Now mark the region [128Mb - 128Kb, 128Mb[ as free too. This will
+        * expand the range covered by the existing extent entry that represents
+        * the free space [128Mb - 256Kb, 128Mb - 128Kb[.
+        */
+       ret = btrfs_add_free_space(cache, 128 * 1024 * 1024 - 128 * 1024,
+                                  128 * 1024);
+       if (ret) {
+               test_msg("Error adding free space: %d\n", ret);
+               return ret;
+       }
+       /* Confirm the region is marked as free. */
+       if (!test_check_exists(cache, 128 * 1024 * 1024 - 128 * 1024,
+                              128 * 1024)) {
+               test_msg("Extent region not marked as free\n");
+               return -ENOENT;
+       }
+
+       /*
+        * Confirm that our extent entry didn't stole all free space from the
+        * bitmap, because of the small 4Kb free space region.
+        */
+       ret = check_num_extents_and_bitmaps(cache, 2, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * So now we have the range [128Mb - 256Kb, 128Mb + 768Kb[ as free
+        * space. Without stealing bitmap free space into extent entry space,
+        * we would have all this free space represented by 2 entries in the
+        * cache:
+        *
+        * extent entry covering range: [128Mb - 256Kb, 128Mb[
+        * bitmap entry covering range: [128Mb, 128Mb + 768Kb[
+        *
+        * Attempting to allocate the whole free space (1Mb) would fail, because
+        * we can't allocate from multiple entries.
+        * With the bitmap free space stealing, we get a single extent entry
+        * that represents the 1Mb free space, and therefore we're able to
+        * allocate the whole free space at once.
+        */
+       if (!test_check_exists(cache, 128 * 1024 * 1024 - 256 * 1024,
+                              1 * 1024 * 1024)) {
+               test_msg("Expected region not marked as free\n");
+               return -ENOENT;
+       }
+
+       if (cache->free_space_ctl->free_space != (1 * 1024 * 1024 + 4096)) {
+               test_msg("Cache free space is not 1Mb + 4Kb\n");
+               return -EINVAL;
+       }
+
+       offset = btrfs_find_space_for_alloc(cache,
+                                           0, 1 * 1024 * 1024, 0,
+                                           &max_extent_size);
+       if (offset != (128 * 1024 * 1024 - 256 * 1024)) {
+               test_msg("Failed to allocate 1Mb from space cache, returned offset is: %llu\n",
+                        offset);
+               return -EINVAL;
+       }
+
+       /* All that remains is a 4Kb free space region in a bitmap. Confirm. */
+       ret = check_num_extents_and_bitmaps(cache, 1, 1);
+       if (ret)
+               return ret;
+
+       if (cache->free_space_ctl->free_space != 4096) {
+               test_msg("Cache free space is not 4Kb\n");
+               return -EINVAL;
+       }
+
+       offset = btrfs_find_space_for_alloc(cache,
+                                           0, 4096, 0,
+                                           &max_extent_size);
+       if (offset != (128 * 1024 * 1024 + 16 * 1024 * 1024)) {
+               test_msg("Failed to allocate 4Kb from space cache, returned offset is: %llu\n",
+                        offset);
+               return -EINVAL;
+       }
+
+       ret = check_cache_empty(cache);
+       if (ret)
+               return ret;
+
+       __btrfs_remove_free_space_cache(cache->free_space_ctl);
+
+       /*
+        * Now test a similar scenario, but where our extent entry is located
+        * to the right of the bitmap entry, so that we can check that stealing
+        * space from a bitmap to the front of an extent entry works.
+        */
+
+       /*
+        * Extent entry covering free space range [128Mb + 128Kb, 128Mb + 256Kb[
+        */
+       ret = test_add_free_space_entry(cache, 128 * 1024 * 1024 + 128 * 1024,
+                                       128 * 1024, 0);
+       if (ret) {
+               test_msg("Couldn't add extent entry %d\n", ret);
+               return ret;
+       }
+
+       /* Bitmap entry covering free space range [0, 128Mb - 512Kb[ */
+       ret = test_add_free_space_entry(cache, 0,
+                                       128 * 1024 * 1024 - 512 * 1024, 1);
+       if (ret) {
+               test_msg("Couldn't add bitmap entry %d\n", ret);
+               return ret;
+       }
+
+       ret = check_num_extents_and_bitmaps(cache, 2, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * Now make only the last 256Kb of the bitmap marked as free, so that
+        * we end up with only the following ranges marked as free space:
+        *
+        * [128Mb + 128b, 128Mb + 256Kb[
+        * [128Mb - 768Kb, 128Mb - 512Kb[
+        */
+       ret = btrfs_remove_free_space(cache,
+                                     0,
+                                     128 * 1024 * 1024 - 768 * 1024);
+       if (ret) {
+               test_msg("Failed to free part of bitmap space %d\n", ret);
+               return ret;
+       }
+
+       /* Confirm that only those 2 ranges are marked as free. */
+       if (!test_check_exists(cache, 128 * 1024 * 1024 + 128 * 1024,
+                              128 * 1024)) {
+               test_msg("Free space range missing\n");
+               return -ENOENT;
+       }
+       if (!test_check_exists(cache, 128 * 1024 * 1024 - 768 * 1024,
+                              256 * 1024)) {
+               test_msg("Free space range missing\n");
+               return -ENOENT;
+       }
+
+       /*
+        * Confirm that the bitmap range [0, 128Mb - 768Kb[ isn't marked
+        * as free anymore.
+        */
+       if (test_check_exists(cache, 0,
+                             128 * 1024 * 1024 - 768 * 1024)) {
+               test_msg("Bitmap region not removed from space cache\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Confirm that the region [128Mb - 512Kb, 128Mb[, which is
+        * covered by the bitmap, isn't marked as free.
+        */
+       if (test_check_exists(cache, 128 * 1024 * 1024 - 512 * 1024,
+                             512 * 1024)) {
+               test_msg("Invalid bitmap region marked as free\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Now lets mark the region [128Mb - 512Kb, 128Mb[ as free too. But,
+        * lets make sure the free space cache marks it as free in the bitmap,
+        * and doesn't insert a new extent entry to represent this region.
+        */
+       ret = btrfs_add_free_space(cache, 128 * 1024 * 1024 - 512 * 1024,
+                                  512 * 1024);
+       if (ret) {
+               test_msg("Error adding free space: %d\n", ret);
+               return ret;
+       }
+       /* Confirm the region is marked as free. */
+       if (!test_check_exists(cache, 128 * 1024 * 1024 - 512 * 1024,
+                              512 * 1024)) {
+               test_msg("Bitmap region not marked as free\n");
+               return -ENOENT;
+       }
+
+       /*
+        * Confirm that no new extent entries or bitmap entries were added to
+        * the cache after adding that free space region.
+        */
+       ret = check_num_extents_and_bitmaps(cache, 2, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * Now lets add a small free space region to the left of the previous
+        * one, which is not contiguous with it and is part of the bitmap too.
+        * The goal is to test that the bitmap entry space stealing doesn't
+        * steal this space region.
+        */
+       ret = btrfs_add_free_space(cache, 32 * 1024 * 1024, 8192);
+       if (ret) {
+               test_msg("Error adding free space: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Now mark the region [128Mb, 128Mb + 128Kb[ as free too. This will
+        * expand the range covered by the existing extent entry that represents
+        * the free space [128Mb + 128Kb, 128Mb + 256Kb[.
+        */
+       ret = btrfs_add_free_space(cache, 128 * 1024 * 1024, 128 * 1024);
+       if (ret) {
+               test_msg("Error adding free space: %d\n", ret);
+               return ret;
+       }
+       /* Confirm the region is marked as free. */
+       if (!test_check_exists(cache, 128 * 1024 * 1024, 128 * 1024)) {
+               test_msg("Extent region not marked as free\n");
+               return -ENOENT;
+       }
+
+       /*
+        * Confirm that our extent entry didn't stole all free space from the
+        * bitmap, because of the small 8Kb free space region.
+        */
+       ret = check_num_extents_and_bitmaps(cache, 2, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * So now we have the range [128Mb - 768Kb, 128Mb + 256Kb[ as free
+        * space. Without stealing bitmap free space into extent entry space,
+        * we would have all this free space represented by 2 entries in the
+        * cache:
+        *
+        * extent entry covering range: [128Mb, 128Mb + 256Kb[
+        * bitmap entry covering range: [128Mb - 768Kb, 128Mb[
+        *
+        * Attempting to allocate the whole free space (1Mb) would fail, because
+        * we can't allocate from multiple entries.
+        * With the bitmap free space stealing, we get a single extent entry
+        * that represents the 1Mb free space, and therefore we're able to
+        * allocate the whole free space at once.
+        */
+       if (!test_check_exists(cache, 128 * 1024 * 1024 - 768 * 1024,
+                              1 * 1024 * 1024)) {
+               test_msg("Expected region not marked as free\n");
+               return -ENOENT;
+       }
+
+       if (cache->free_space_ctl->free_space != (1 * 1024 * 1024 + 8192)) {
+               test_msg("Cache free space is not 1Mb + 8Kb\n");
+               return -EINVAL;
+       }
+
+       offset = btrfs_find_space_for_alloc(cache,
+                                           0, 1 * 1024 * 1024, 0,
+                                           &max_extent_size);
+       if (offset != (128 * 1024 * 1024 - 768 * 1024)) {
+               test_msg("Failed to allocate 1Mb from space cache, returned offset is: %llu\n",
+                        offset);
+               return -EINVAL;
+       }
+
+       /* All that remains is a 8Kb free space region in a bitmap. Confirm. */
+       ret = check_num_extents_and_bitmaps(cache, 1, 1);
+       if (ret)
+               return ret;
+
+       if (cache->free_space_ctl->free_space != 8192) {
+               test_msg("Cache free space is not 8Kb\n");
+               return -EINVAL;
+       }
+
+       offset = btrfs_find_space_for_alloc(cache,
+                                           0, 8192, 0,
+                                           &max_extent_size);
+       if (offset != (32 * 1024 * 1024)) {
+               test_msg("Failed to allocate 8Kb from space cache, returned offset is: %llu\n",
+                        offset);
+               return -EINVAL;
+       }
+
+       ret = check_cache_empty(cache);
+       if (ret)
+               return ret;
+
+       cache->free_space_ctl->op->use_bitmap = use_bitmap_op;
+       __btrfs_remove_free_space_cache(cache->free_space_ctl);
+
+       return 0;
+}
+
 int btrfs_test_free_space_cache(void)
 {
        struct btrfs_block_group_cache *cache;
@@ -386,6 +898,8 @@ int btrfs_test_free_space_cache(void)
        ret = test_bitmaps_and_extents(cache);
        if (ret)
                goto out;
+
+       ret = test_steal_space_from_bitmap_to_extent(cache);
 out:
        __btrfs_remove_free_space_cache(cache->free_space_ctl);
        kfree(cache->free_space_ctl);
index d89c6d3542cab4c55372954ae97e9e32ec1ba443..dcaae36167288aff33fe679c99b18fc14130188f 100644 (file)
@@ -386,7 +386,7 @@ start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type,
        int ret;
 
        /* Send isn't supposed to start transactions. */
-       ASSERT(current->journal_info != (void *)BTRFS_SEND_TRANS_STUB);
+       ASSERT(current->journal_info != BTRFS_SEND_TRANS_STUB);
 
        if (test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state))
                return ERR_PTR(-EROFS);
@@ -408,7 +408,7 @@ start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type,
        if (num_items > 0 && root != root->fs_info->chunk_root) {
                if (root->fs_info->quota_enabled &&
                    is_fstree(root->root_key.objectid)) {
-                       qgroup_reserved = num_items * root->leafsize;
+                       qgroup_reserved = num_items * root->nodesize;
                        ret = btrfs_qgroup_reserve(root, qgroup_reserved);
                        if (ret)
                                return ERR_PTR(ret);
@@ -418,7 +418,7 @@ start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type,
                /*
                 * Do the reservation for the relocation root creation
                 */
-               if (unlikely(need_reserve_reloc_root(root))) {
+               if (need_reserve_reloc_root(root)) {
                        num_bytes += root->nodesize;
                        reloc_reserved = true;
                }
@@ -609,7 +609,6 @@ int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid)
                if (transid <= root->fs_info->last_trans_committed)
                        goto out;
 
-               ret = -EINVAL;
                /* find specified transaction */
                spin_lock(&root->fs_info->trans_lock);
                list_for_each_entry(t, &root->fs_info->trans_list, list) {
@@ -625,9 +624,16 @@ int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid)
                        }
                }
                spin_unlock(&root->fs_info->trans_lock);
-               /* The specified transaction doesn't exist */
-               if (!cur_trans)
+
+               /*
+                * The specified transaction doesn't exist, or we
+                * raced with btrfs_commit_transaction
+                */
+               if (!cur_trans) {
+                       if (transid > root->fs_info->last_trans_committed)
+                               ret = -EINVAL;
                        goto out;
+               }
        } else {
                /* find newest transaction that is committing | committed */
                spin_lock(&root->fs_info->trans_lock);
@@ -851,6 +857,8 @@ int btrfs_wait_marked_extents(struct btrfs_root *root,
        struct extent_state *cached_state = NULL;
        u64 start = 0;
        u64 end;
+       struct btrfs_inode *btree_ino = BTRFS_I(root->fs_info->btree_inode);
+       bool errors = false;
 
        while (!find_first_extent_bit(dirty_pages, start, &start, &end,
                                      EXTENT_NEED_WAIT, &cached_state)) {
@@ -864,6 +872,26 @@ int btrfs_wait_marked_extents(struct btrfs_root *root,
        }
        if (err)
                werr = err;
+
+       if (root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID) {
+               if ((mark & EXTENT_DIRTY) &&
+                   test_and_clear_bit(BTRFS_INODE_BTREE_LOG1_ERR,
+                                      &btree_ino->runtime_flags))
+                       errors = true;
+
+               if ((mark & EXTENT_NEW) &&
+                   test_and_clear_bit(BTRFS_INODE_BTREE_LOG2_ERR,
+                                      &btree_ino->runtime_flags))
+                       errors = true;
+       } else {
+               if (test_and_clear_bit(BTRFS_INODE_BTREE_ERR,
+                                      &btree_ino->runtime_flags))
+                       errors = true;
+       }
+
+       if (errors && !werr)
+               werr = -EIO;
+
        return werr;
 }
 
@@ -1629,6 +1657,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
 {
        struct btrfs_transaction *cur_trans = trans->transaction;
        struct btrfs_transaction *prev_trans = NULL;
+       struct btrfs_inode *btree_ino = BTRFS_I(root->fs_info->btree_inode);
        int ret;
 
        /* Stop the commit early if ->aborted is set */
@@ -1868,6 +1897,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        memcpy(root->fs_info->super_for_commit, root->fs_info->super_copy,
               sizeof(*root->fs_info->super_copy));
 
+       btrfs_update_commit_device_size(root->fs_info);
+       btrfs_update_commit_device_bytes_used(root, cur_trans);
+
+       clear_bit(BTRFS_INODE_BTREE_LOG1_ERR, &btree_ino->runtime_flags);
+       clear_bit(BTRFS_INODE_BTREE_LOG2_ERR, &btree_ino->runtime_flags);
+
        spin_lock(&root->fs_info->trans_lock);
        cur_trans->state = TRANS_STATE_UNBLOCKED;
        root->fs_info->running_transaction = NULL;
@@ -1981,9 +2016,6 @@ int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root)
                ret = btrfs_drop_snapshot(root, NULL, 0, 0);
        else
                ret = btrfs_drop_snapshot(root, NULL, 1, 0);
-       /*
-        * If we encounter a transaction abort during snapshot cleaning, we
-        * don't want to crash here
-        */
+
        return (ret < 0) ? 0 : 1;
 }
index 579be51b27e5ee0970feb4fb21ddf54a1d621a12..d8f40e1a5d2dc1b6dfd9ca24526eb7de2bd189f0 100644 (file)
@@ -79,7 +79,7 @@ struct btrfs_transaction {
 #define TRANS_EXTWRITERS       (__TRANS_USERSPACE | __TRANS_START |    \
                                 __TRANS_ATTACH)
 
-#define BTRFS_SEND_TRANS_STUB  1
+#define BTRFS_SEND_TRANS_STUB  ((void *)1)
 
 struct btrfs_trans_handle {
        u64 transid;
index d0262ceb85e10f2fe57237a9e3b52751c4012074..1475979e5718ab8727ad47f8c52898257c76b0ed 100644 (file)
@@ -97,7 +97,8 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root, struct inode *inode,
                           int inode_only,
                           const loff_t start,
-                          const loff_t end);
+                          const loff_t end,
+                          struct btrfs_log_ctx *ctx);
 static int link_to_fixup_dir(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root,
                             struct btrfs_path *path, u64 objectid);
@@ -1498,7 +1499,7 @@ static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans,
                return -EIO;
 
        key.objectid = BTRFS_TREE_LOG_FIXUP_OBJECTID;
-       btrfs_set_key_type(&key, BTRFS_ORPHAN_ITEM_KEY);
+       key.type = BTRFS_ORPHAN_ITEM_KEY;
        key.offset = objectid;
 
        ret = btrfs_insert_empty_item(trans, root, path, &key, 0);
@@ -1637,6 +1638,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
            found_key.type == log_key.type &&
            found_key.offset == log_key.offset &&
            btrfs_dir_type(path->nodes[0], dst_di) == log_type) {
+               update_size = false;
                goto out;
        }
 
@@ -2157,7 +2159,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
 
                bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
                ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
-               blocksize = btrfs_level_size(root, *level - 1);
+               blocksize = root->nodesize;
 
                parent = path->nodes[*level];
                root_owner = btrfs_header_owner(parent);
@@ -2983,8 +2985,6 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
        min_key.type = key_type;
        min_key.offset = min_offset;
 
-       path->keep_locks = 1;
-
        ret = btrfs_search_forward(root, &min_key, path, trans->transid);
 
        /*
@@ -3364,7 +3364,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
                 * or deletes of this inode don't have to relog the inode
                 * again
                 */
-               if (btrfs_key_type(ins_keys + i) == BTRFS_EXTENT_DATA_KEY &&
+               if (ins_keys[i].type == BTRFS_EXTENT_DATA_KEY &&
                    !skip_csum) {
                        int found_type;
                        extent = btrfs_item_ptr(src, start_slot + i,
@@ -3573,107 +3573,33 @@ static int extent_cmp(void *priv, struct list_head *a, struct list_head *b)
        return 0;
 }
 
-static int log_one_extent(struct btrfs_trans_handle *trans,
-                         struct inode *inode, struct btrfs_root *root,
-                         struct extent_map *em, struct btrfs_path *path,
-                         struct list_head *logged_list)
+static int wait_ordered_extents(struct btrfs_trans_handle *trans,
+                               struct inode *inode,
+                               struct btrfs_root *root,
+                               const struct extent_map *em,
+                               const struct list_head *logged_list,
+                               bool *ordered_io_error)
 {
-       struct btrfs_root *log = root->log_root;
-       struct btrfs_file_extent_item *fi;
-       struct extent_buffer *leaf;
        struct btrfs_ordered_extent *ordered;
-       struct list_head ordered_sums;
-       struct btrfs_map_token token;
-       struct btrfs_key key;
+       struct btrfs_root *log = root->log_root;
        u64 mod_start = em->mod_start;
        u64 mod_len = em->mod_len;
+       const bool skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
        u64 csum_offset;
        u64 csum_len;
-       u64 extent_offset = em->start - em->orig_start;
-       u64 block_len;
-       int ret;
-       bool skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
-       int extent_inserted = 0;
-
-       INIT_LIST_HEAD(&ordered_sums);
-       btrfs_init_map_token(&token);
-
-       ret = __btrfs_drop_extents(trans, log, inode, path, em->start,
-                                  em->start + em->len, NULL, 0, 1,
-                                  sizeof(*fi), &extent_inserted);
-       if (ret)
-               return ret;
-
-       if (!extent_inserted) {
-               key.objectid = btrfs_ino(inode);
-               key.type = BTRFS_EXTENT_DATA_KEY;
-               key.offset = em->start;
-
-               ret = btrfs_insert_empty_item(trans, log, path, &key,
-                                             sizeof(*fi));
-               if (ret)
-                       return ret;
-       }
-       leaf = path->nodes[0];
-       fi = btrfs_item_ptr(leaf, path->slots[0],
-                           struct btrfs_file_extent_item);
-
-       btrfs_set_token_file_extent_generation(leaf, fi, em->generation,
-                                              &token);
-       if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) {
-               skip_csum = true;
-               btrfs_set_token_file_extent_type(leaf, fi,
-                                                BTRFS_FILE_EXTENT_PREALLOC,
-                                                &token);
-       } else {
-               btrfs_set_token_file_extent_type(leaf, fi,
-                                                BTRFS_FILE_EXTENT_REG,
-                                                &token);
-               if (em->block_start == EXTENT_MAP_HOLE)
-                       skip_csum = true;
-       }
-
-       block_len = max(em->block_len, em->orig_block_len);
-       if (em->compress_type != BTRFS_COMPRESS_NONE) {
-               btrfs_set_token_file_extent_disk_bytenr(leaf, fi,
-                                                       em->block_start,
-                                                       &token);
-               btrfs_set_token_file_extent_disk_num_bytes(leaf, fi, block_len,
-                                                          &token);
-       } else if (em->block_start < EXTENT_MAP_LAST_BYTE) {
-               btrfs_set_token_file_extent_disk_bytenr(leaf, fi,
-                                                       em->block_start -
-                                                       extent_offset, &token);
-               btrfs_set_token_file_extent_disk_num_bytes(leaf, fi, block_len,
-                                                          &token);
-       } else {
-               btrfs_set_token_file_extent_disk_bytenr(leaf, fi, 0, &token);
-               btrfs_set_token_file_extent_disk_num_bytes(leaf, fi, 0,
-                                                          &token);
-       }
-
-       btrfs_set_token_file_extent_offset(leaf, fi,
-                                          em->start - em->orig_start,
-                                          &token);
-       btrfs_set_token_file_extent_num_bytes(leaf, fi, em->len, &token);
-       btrfs_set_token_file_extent_ram_bytes(leaf, fi, em->ram_bytes, &token);
-       btrfs_set_token_file_extent_compression(leaf, fi, em->compress_type,
-                                               &token);
-       btrfs_set_token_file_extent_encryption(leaf, fi, 0, &token);
-       btrfs_set_token_file_extent_other_encoding(leaf, fi, 0, &token);
-       btrfs_mark_buffer_dirty(leaf);
+       LIST_HEAD(ordered_sums);
+       int ret = 0;
 
-       btrfs_release_path(path);
-       if (ret) {
-               return ret;
-       }
+       *ordered_io_error = false;
 
-       if (skip_csum)
+       if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags) ||
+           em->block_start == EXTENT_MAP_HOLE)
                return 0;
 
        /*
-        * First check and see if our csums are on our outstanding ordered
-        * extents.
+        * Wait far any ordered extent that covers our extent map. If it
+        * finishes without an error, first check and see if our csums are on
+        * our outstanding ordered extents.
         */
        list_for_each_entry(ordered, logged_list, log_list) {
                struct btrfs_ordered_sum *sum;
@@ -3685,6 +3611,24 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
                    mod_start + mod_len <= ordered->file_offset)
                        continue;
 
+               if (!test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags) &&
+                   !test_bit(BTRFS_ORDERED_IOERR, &ordered->flags) &&
+                   !test_bit(BTRFS_ORDERED_DIRECT, &ordered->flags)) {
+                       const u64 start = ordered->file_offset;
+                       const u64 end = ordered->file_offset + ordered->len - 1;
+
+                       WARN_ON(ordered->inode != inode);
+                       filemap_fdatawrite_range(inode->i_mapping, start, end);
+               }
+
+               wait_event(ordered->wait,
+                          (test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags) ||
+                           test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)));
+
+               if (test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)) {
+                       *ordered_io_error = true;
+                       break;
+               }
                /*
                 * We are going to copy all the csums on this ordered extent, so
                 * go ahead and adjust mod_start and mod_len in case this
@@ -3716,6 +3660,9 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
                        }
                }
 
+               if (skip_csum)
+                       continue;
+
                /*
                 * To keep us from looping for the above case of an ordered
                 * extent that falls inside of the logged extent.
@@ -3733,18 +3680,16 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
                list_for_each_entry(sum, &ordered->list, list) {
                        ret = btrfs_csum_file_blocks(trans, log, sum);
                        if (ret)
-                               goto unlocked;
+                               break;
                }
-
        }
-unlocked:
 
-       if (!mod_len || ret)
+       if (*ordered_io_error || !mod_len || ret || skip_csum)
                return ret;
 
        if (em->compress_type) {
                csum_offset = 0;
-               csum_len = block_len;
+               csum_len = max(em->block_len, em->orig_block_len);
        } else {
                csum_offset = mod_start - em->start;
                csum_len = mod_len;
@@ -3771,11 +3716,106 @@ unlocked:
        return ret;
 }
 
+static int log_one_extent(struct btrfs_trans_handle *trans,
+                         struct inode *inode, struct btrfs_root *root,
+                         const struct extent_map *em,
+                         struct btrfs_path *path,
+                         const struct list_head *logged_list,
+                         struct btrfs_log_ctx *ctx)
+{
+       struct btrfs_root *log = root->log_root;
+       struct btrfs_file_extent_item *fi;
+       struct extent_buffer *leaf;
+       struct btrfs_map_token token;
+       struct btrfs_key key;
+       u64 extent_offset = em->start - em->orig_start;
+       u64 block_len;
+       int ret;
+       int extent_inserted = 0;
+       bool ordered_io_err = false;
+
+       ret = wait_ordered_extents(trans, inode, root, em, logged_list,
+                                  &ordered_io_err);
+       if (ret)
+               return ret;
+
+       if (ordered_io_err) {
+               ctx->io_err = -EIO;
+               return 0;
+       }
+
+       btrfs_init_map_token(&token);
+
+       ret = __btrfs_drop_extents(trans, log, inode, path, em->start,
+                                  em->start + em->len, NULL, 0, 1,
+                                  sizeof(*fi), &extent_inserted);
+       if (ret)
+               return ret;
+
+       if (!extent_inserted) {
+               key.objectid = btrfs_ino(inode);
+               key.type = BTRFS_EXTENT_DATA_KEY;
+               key.offset = em->start;
+
+               ret = btrfs_insert_empty_item(trans, log, path, &key,
+                                             sizeof(*fi));
+               if (ret)
+                       return ret;
+       }
+       leaf = path->nodes[0];
+       fi = btrfs_item_ptr(leaf, path->slots[0],
+                           struct btrfs_file_extent_item);
+
+       btrfs_set_token_file_extent_generation(leaf, fi, em->generation,
+                                              &token);
+       if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
+               btrfs_set_token_file_extent_type(leaf, fi,
+                                                BTRFS_FILE_EXTENT_PREALLOC,
+                                                &token);
+       else
+               btrfs_set_token_file_extent_type(leaf, fi,
+                                                BTRFS_FILE_EXTENT_REG,
+                                                &token);
+
+       block_len = max(em->block_len, em->orig_block_len);
+       if (em->compress_type != BTRFS_COMPRESS_NONE) {
+               btrfs_set_token_file_extent_disk_bytenr(leaf, fi,
+                                                       em->block_start,
+                                                       &token);
+               btrfs_set_token_file_extent_disk_num_bytes(leaf, fi, block_len,
+                                                          &token);
+       } else if (em->block_start < EXTENT_MAP_LAST_BYTE) {
+               btrfs_set_token_file_extent_disk_bytenr(leaf, fi,
+                                                       em->block_start -
+                                                       extent_offset, &token);
+               btrfs_set_token_file_extent_disk_num_bytes(leaf, fi, block_len,
+                                                          &token);
+       } else {
+               btrfs_set_token_file_extent_disk_bytenr(leaf, fi, 0, &token);
+               btrfs_set_token_file_extent_disk_num_bytes(leaf, fi, 0,
+                                                          &token);
+       }
+
+       btrfs_set_token_file_extent_offset(leaf, fi, extent_offset, &token);
+       btrfs_set_token_file_extent_num_bytes(leaf, fi, em->len, &token);
+       btrfs_set_token_file_extent_ram_bytes(leaf, fi, em->ram_bytes, &token);
+       btrfs_set_token_file_extent_compression(leaf, fi, em->compress_type,
+                                               &token);
+       btrfs_set_token_file_extent_encryption(leaf, fi, 0, &token);
+       btrfs_set_token_file_extent_other_encoding(leaf, fi, 0, &token);
+       btrfs_mark_buffer_dirty(leaf);
+
+       btrfs_release_path(path);
+
+       return ret;
+}
+
 static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
                                     struct btrfs_root *root,
                                     struct inode *inode,
                                     struct btrfs_path *path,
-                                    struct list_head *logged_list)
+                                    struct list_head *logged_list,
+                                    struct btrfs_log_ctx *ctx)
 {
        struct extent_map *em, *n;
        struct list_head extents;
@@ -3833,7 +3873,8 @@ process:
 
                write_unlock(&tree->lock);
 
-               ret = log_one_extent(trans, inode, root, em, path, logged_list);
+               ret = log_one_extent(trans, inode, root, em, path, logged_list,
+                                    ctx);
                write_lock(&tree->lock);
                clear_em_logging(tree, em);
                free_extent_map(em);
@@ -3863,7 +3904,8 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root, struct inode *inode,
                           int inode_only,
                           const loff_t start,
-                          const loff_t end)
+                          const loff_t end,
+                          struct btrfs_log_ctx *ctx)
 {
        struct btrfs_path *path;
        struct btrfs_path *dst_path;
@@ -3964,7 +4006,6 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                err = ret;
                goto out_unlock;
        }
-       path->keep_locks = 1;
 
        while (1) {
                ins_nr = 0;
@@ -4049,7 +4090,7 @@ log_extents:
        btrfs_release_path(dst_path);
        if (fast_search) {
                ret = btrfs_log_changed_extents(trans, root, inode, dst_path,
-                                               &logged_list);
+                                               &logged_list, ctx);
                if (ret) {
                        err = ret;
                        goto out_unlock;
@@ -4239,7 +4280,7 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
        if (ret)
                goto end_no_trans;
 
-       ret = btrfs_log_inode(trans, root, inode, inode_only, start, end);
+       ret = btrfs_log_inode(trans, root, inode, inode_only, start, end, ctx);
        if (ret)
                goto end_trans;
 
@@ -4268,7 +4309,7 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
                if (BTRFS_I(inode)->generation >
                    root->fs_info->last_trans_committed) {
                        ret = btrfs_log_inode(trans, root, inode, inode_only,
-                                             0, LLONG_MAX);
+                                             0, LLONG_MAX, ctx);
                        if (ret)
                                goto end_trans;
                }
@@ -4360,7 +4401,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
 again:
        key.objectid = BTRFS_TREE_LOG_OBJECTID;
        key.offset = (u64)-1;
-       btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+       key.type = BTRFS_ROOT_ITEM_KEY;
 
        while (1) {
                ret = btrfs_search_slot(NULL, log_root_tree, &key, path, 0, 0);
index e2e798ae7cd7c6c989a6633fe7dae682ab4a5148..154990c26dcbc9d63508228b9125182b7168b80e 100644 (file)
@@ -28,6 +28,7 @@
 struct btrfs_log_ctx {
        int log_ret;
        int log_transid;
+       int io_err;
        struct list_head list;
 };
 
@@ -35,6 +36,7 @@ static inline void btrfs_init_log_ctx(struct btrfs_log_ctx *ctx)
 {
        ctx->log_ret = 0;
        ctx->log_transid = 0;
+       ctx->io_err = 0;
        INIT_LIST_HEAD(&ctx->list);
 }
 
index f6a4c03ee7d8fc1cf3aedc3f2b8edf0007992ff7..778282944530f6baba8a27037e191d28922421a1 100644 (file)
@@ -279,7 +279,6 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info,
        key.offset = 0;
 
 again_search_slot:
-       path->keep_locks = 1;
        ret = btrfs_search_forward(root, &key, path, 0);
        if (ret) {
                if (ret > 0)
index 2c2d6d1d8eee929fc910a82cd17fbec79803cbb2..d47289c715c814f47c7842349afc942640310238 100644 (file)
@@ -50,7 +50,7 @@ static void __btrfs_reset_dev_stats(struct btrfs_device *dev);
 static void btrfs_dev_stat_print_on_error(struct btrfs_device *dev);
 static void btrfs_dev_stat_print_on_load(struct btrfs_device *device);
 
-static DEFINE_MUTEX(uuid_mutex);
+DEFINE_MUTEX(uuid_mutex);
 static LIST_HEAD(fs_uuids);
 
 static void lock_chunks(struct btrfs_root *root)
@@ -74,6 +74,7 @@ static struct btrfs_fs_devices *__alloc_fs_devices(void)
        mutex_init(&fs_devs->device_list_mutex);
 
        INIT_LIST_HEAD(&fs_devs->devices);
+       INIT_LIST_HEAD(&fs_devs->resized_devices);
        INIT_LIST_HEAD(&fs_devs->alloc_list);
        INIT_LIST_HEAD(&fs_devs->list);
 
@@ -154,11 +155,13 @@ static struct btrfs_device *__alloc_device(void)
 
        INIT_LIST_HEAD(&dev->dev_list);
        INIT_LIST_HEAD(&dev->dev_alloc_list);
+       INIT_LIST_HEAD(&dev->resized_list);
 
        spin_lock_init(&dev->io_lock);
 
        spin_lock_init(&dev->reada_lock);
        atomic_set(&dev->reada_in_flight, 0);
+       atomic_set(&dev->dev_stats_ccnt, 0);
        INIT_RADIX_TREE(&dev->reada_zones, GFP_NOFS & ~__GFP_WAIT);
        INIT_RADIX_TREE(&dev->reada_extents, GFP_NOFS & ~__GFP_WAIT);
 
@@ -474,14 +477,13 @@ static noinline int device_list_add(const char *path,
                        return PTR_ERR(fs_devices);
 
                list_add(&fs_devices->list, &fs_uuids);
-               fs_devices->latest_devid = devid;
-               fs_devices->latest_trans = found_transid;
 
                device = NULL;
        } else {
                device = __find_device(&fs_devices->devices, devid,
                                       disk_super->dev_item.uuid);
        }
+
        if (!device) {
                if (fs_devices->opened)
                        return -EBUSY;
@@ -565,10 +567,6 @@ static noinline int device_list_add(const char *path,
        if (!fs_devices->opened)
                device->generation = found_transid;
 
-       if (found_transid > fs_devices->latest_trans) {
-               fs_devices->latest_devid = devid;
-               fs_devices->latest_trans = found_transid;
-       }
        *fs_devices_ret = fs_devices;
 
        return ret;
@@ -584,8 +582,7 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
        if (IS_ERR(fs_devices))
                return fs_devices;
 
-       fs_devices->latest_devid = orig->latest_devid;
-       fs_devices->latest_trans = orig->latest_trans;
+       mutex_lock(&orig->device_list_mutex);
        fs_devices->total_devices = orig->total_devices;
 
        /* We have held the volume lock, it is safe to get the devices. */
@@ -614,8 +611,10 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
                device->fs_devices = fs_devices;
                fs_devices->num_devices++;
        }
+       mutex_unlock(&orig->device_list_mutex);
        return fs_devices;
 error:
+       mutex_unlock(&orig->device_list_mutex);
        free_fs_devices(fs_devices);
        return ERR_PTR(-ENOMEM);
 }
@@ -624,10 +623,7 @@ void btrfs_close_extra_devices(struct btrfs_fs_info *fs_info,
                               struct btrfs_fs_devices *fs_devices, int step)
 {
        struct btrfs_device *device, *next;
-
-       struct block_device *latest_bdev = NULL;
-       u64 latest_devid = 0;
-       u64 latest_transid = 0;
+       struct btrfs_device *latest_dev = NULL;
 
        mutex_lock(&uuid_mutex);
 again:
@@ -635,11 +631,9 @@ again:
        list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
                if (device->in_fs_metadata) {
                        if (!device->is_tgtdev_for_dev_replace &&
-                           (!latest_transid ||
-                            device->generation > latest_transid)) {
-                               latest_devid = device->devid;
-                               latest_transid = device->generation;
-                               latest_bdev = device->bdev;
+                           (!latest_dev ||
+                            device->generation > latest_dev->generation)) {
+                               latest_dev = device;
                        }
                        continue;
                }
@@ -681,9 +675,7 @@ again:
                goto again;
        }
 
-       fs_devices->latest_bdev = latest_bdev;
-       fs_devices->latest_devid = latest_devid;
-       fs_devices->latest_trans = latest_transid;
+       fs_devices->latest_bdev = latest_dev->bdev;
 
        mutex_unlock(&uuid_mutex);
 }
@@ -732,8 +724,6 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
                        fs_devices->rw_devices--;
                }
 
-               if (device->can_discard)
-                       fs_devices->num_can_discard--;
                if (device->missing)
                        fs_devices->missing_devices--;
 
@@ -798,11 +788,9 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
        struct block_device *bdev;
        struct list_head *head = &fs_devices->devices;
        struct btrfs_device *device;
-       struct block_device *latest_bdev = NULL;
+       struct btrfs_device *latest_dev = NULL;
        struct buffer_head *bh;
        struct btrfs_super_block *disk_super;
-       u64 latest_devid = 0;
-       u64 latest_transid = 0;
        u64 devid;
        int seeding = 1;
        int ret = 0;
@@ -830,11 +818,9 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
                        goto error_brelse;
 
                device->generation = btrfs_super_generation(disk_super);
-               if (!latest_transid || device->generation > latest_transid) {
-                       latest_devid = devid;
-                       latest_transid = device->generation;
-                       latest_bdev = bdev;
-               }
+               if (!latest_dev ||
+                   device->generation > latest_dev->generation)
+                       latest_dev = device;
 
                if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_SEEDING) {
                        device->writeable = 0;
@@ -844,10 +830,8 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
                }
 
                q = bdev_get_queue(bdev);
-               if (blk_queue_discard(q)) {
+               if (blk_queue_discard(q))
                        device->can_discard = 1;
-                       fs_devices->num_can_discard++;
-               }
 
                device->bdev = bdev;
                device->in_fs_metadata = 0;
@@ -877,9 +861,7 @@ error_brelse:
        }
        fs_devices->seeding = seeding;
        fs_devices->opened = 1;
-       fs_devices->latest_bdev = latest_bdev;
-       fs_devices->latest_devid = latest_devid;
-       fs_devices->latest_trans = latest_transid;
+       fs_devices->latest_bdev = latest_dev->bdev;
        fs_devices->total_rw_bytes = 0;
 out:
        return ret;
@@ -1053,7 +1035,7 @@ int btrfs_account_dev_extents_size(struct btrfs_device *device, u64 start,
                if (key.objectid > device->devid)
                        break;
 
-               if (btrfs_key_type(&key) != BTRFS_DEV_EXTENT_KEY)
+               if (key.type != BTRFS_DEV_EXTENT_KEY)
                        goto next;
 
                dev_extent = btrfs_item_ptr(l, slot, struct btrfs_dev_extent);
@@ -1205,7 +1187,7 @@ again:
                if (key.objectid > device->devid)
                        break;
 
-               if (btrfs_key_type(&key) != BTRFS_DEV_EXTENT_KEY)
+               if (key.type != BTRFS_DEV_EXTENT_KEY)
                        goto next;
 
                if (key.offset > search_start) {
@@ -1284,7 +1266,7 @@ out:
 
 static int btrfs_free_dev_extent(struct btrfs_trans_handle *trans,
                          struct btrfs_device *device,
-                         u64 start)
+                         u64 start, u64 *dev_extent_len)
 {
        int ret;
        struct btrfs_path *path;
@@ -1326,13 +1308,8 @@ again:
                goto out;
        }
 
-       if (device->bytes_used > 0) {
-               u64 len = btrfs_dev_extent_length(leaf, extent);
-               device->bytes_used -= len;
-               spin_lock(&root->fs_info->free_chunk_lock);
-               root->fs_info->free_chunk_space += len;
-               spin_unlock(&root->fs_info->free_chunk_lock);
-       }
+       *dev_extent_len = btrfs_dev_extent_length(leaf, extent);
+
        ret = btrfs_del_item(trans, root, path);
        if (ret) {
                btrfs_error(root->fs_info, ret,
@@ -1482,8 +1459,10 @@ static int btrfs_add_device(struct btrfs_trans_handle *trans,
        btrfs_set_device_io_align(leaf, dev_item, device->io_align);
        btrfs_set_device_io_width(leaf, dev_item, device->io_width);
        btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
-       btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
-       btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
+       btrfs_set_device_total_bytes(leaf, dev_item,
+                                    btrfs_device_get_disk_total_bytes(device));
+       btrfs_set_device_bytes_used(leaf, dev_item,
+                                   btrfs_device_get_bytes_used(device));
        btrfs_set_device_group(leaf, dev_item, 0);
        btrfs_set_device_seek_speed(leaf, dev_item, 0);
        btrfs_set_device_bandwidth(leaf, dev_item, 0);
@@ -1539,7 +1518,6 @@ static int btrfs_rm_dev_item(struct btrfs_root *root,
        key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
        key.type = BTRFS_DEV_ITEM_KEY;
        key.offset = device->devid;
-       lock_chunks(root);
 
        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
        if (ret < 0)
@@ -1555,7 +1533,6 @@ static int btrfs_rm_dev_item(struct btrfs_root *root,
                goto out;
 out:
        btrfs_free_path(path);
-       unlock_chunks(root);
        btrfs_commit_transaction(trans, root);
        return ret;
 }
@@ -1671,8 +1648,8 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
        if (device->writeable) {
                lock_chunks(root);
                list_del_init(&device->dev_alloc_list);
+               device->fs_devices->rw_devices--;
                unlock_chunks(root);
-               root->fs_info->fs_devices->rw_devices--;
                clear_super = true;
        }
 
@@ -1691,11 +1668,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
        if (ret)
                goto error_undo;
 
-       spin_lock(&root->fs_info->free_chunk_lock);
-       root->fs_info->free_chunk_space = device->total_bytes -
-               device->bytes_used;
-       spin_unlock(&root->fs_info->free_chunk_lock);
-
        device->in_fs_metadata = 0;
        btrfs_scrub_cancel_dev(root->fs_info, device);
 
@@ -1749,9 +1721,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
                        fs_devices = fs_devices->seed;
                }
                cur_devices->seed = NULL;
-               lock_chunks(root);
                __btrfs_close_devices(cur_devices);
-               unlock_chunks(root);
                free_fs_devices(cur_devices);
        }
 
@@ -1824,8 +1794,8 @@ error_undo:
                lock_chunks(root);
                list_add(&device->dev_alloc_list,
                         &root->fs_info->fs_devices->alloc_list);
+               device->fs_devices->rw_devices++;
                unlock_chunks(root);
-               root->fs_info->fs_devices->rw_devices++;
        }
        goto error_brelse;
 }
@@ -1833,29 +1803,57 @@ error_undo:
 void btrfs_rm_dev_replace_srcdev(struct btrfs_fs_info *fs_info,
                                 struct btrfs_device *srcdev)
 {
+       struct btrfs_fs_devices *fs_devices;
+
        WARN_ON(!mutex_is_locked(&fs_info->fs_devices->device_list_mutex));
 
+       /*
+        * in case of fs with no seed, srcdev->fs_devices will point
+        * to fs_devices of fs_info. However when the dev being replaced is
+        * a seed dev it will point to the seed's local fs_devices. In short
+        * srcdev will have its correct fs_devices in both the cases.
+        */
+       fs_devices = srcdev->fs_devices;
+
        list_del_rcu(&srcdev->dev_list);
        list_del_rcu(&srcdev->dev_alloc_list);
-       fs_info->fs_devices->num_devices--;
-       if (srcdev->missing) {
-               fs_info->fs_devices->missing_devices--;
-               fs_info->fs_devices->rw_devices++;
-       }
-       if (srcdev->can_discard)
-               fs_info->fs_devices->num_can_discard--;
-       if (srcdev->bdev) {
-               fs_info->fs_devices->open_devices--;
+       fs_devices->num_devices--;
+       if (srcdev->missing)
+               fs_devices->missing_devices--;
 
-               /*
-                * zero out the old super if it is not writable
-                * (e.g. seed device)
-                */
-               if (srcdev->writeable)
-                       btrfs_scratch_superblock(srcdev);
+       if (srcdev->writeable) {
+               fs_devices->rw_devices--;
+               /* zero out the old super if it is writable */
+               btrfs_scratch_superblock(srcdev);
        }
 
+       if (srcdev->bdev)
+               fs_devices->open_devices--;
+
        call_rcu(&srcdev->rcu, free_device);
+
+       /*
+        * unless fs_devices is seed fs, num_devices shouldn't go
+        * zero
+        */
+       BUG_ON(!fs_devices->num_devices && !fs_devices->seeding);
+
+       /* if this is no devs we rather delete the fs_devices */
+       if (!fs_devices->num_devices) {
+               struct btrfs_fs_devices *tmp_fs_devices;
+
+               tmp_fs_devices = fs_info->fs_devices;
+               while (tmp_fs_devices) {
+                       if (tmp_fs_devices->seed == fs_devices) {
+                               tmp_fs_devices->seed = fs_devices->seed;
+                               break;
+                       }
+                       tmp_fs_devices = tmp_fs_devices->seed;
+               }
+               fs_devices->seed = NULL;
+               __btrfs_close_devices(fs_devices);
+               free_fs_devices(fs_devices);
+       }
 }
 
 void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
@@ -1863,6 +1861,7 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
 {
        struct btrfs_device *next_device;
 
+       mutex_lock(&uuid_mutex);
        WARN_ON(!tgtdev);
        mutex_lock(&fs_info->fs_devices->device_list_mutex);
        if (tgtdev->bdev) {
@@ -1870,8 +1869,6 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
                fs_info->fs_devices->open_devices--;
        }
        fs_info->fs_devices->num_devices--;
-       if (tgtdev->can_discard)
-               fs_info->fs_devices->num_can_discard++;
 
        next_device = list_entry(fs_info->fs_devices->devices.next,
                                 struct btrfs_device, dev_list);
@@ -1884,6 +1881,7 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
        call_rcu(&tgtdev->rcu, free_device);
 
        mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+       mutex_unlock(&uuid_mutex);
 }
 
 static int btrfs_find_device_by_path(struct btrfs_root *root, char *device_path,
@@ -1982,17 +1980,17 @@ static int btrfs_prepare_sprout(struct btrfs_root *root)
        mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
        list_splice_init_rcu(&fs_devices->devices, &seed_devices->devices,
                              synchronize_rcu);
+       list_for_each_entry(device, &seed_devices->devices, dev_list)
+               device->fs_devices = seed_devices;
 
+       lock_chunks(root);
        list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list);
-       list_for_each_entry(device, &seed_devices->devices, dev_list) {
-               device->fs_devices = seed_devices;
-       }
+       unlock_chunks(root);
 
        fs_devices->seeding = 0;
        fs_devices->num_devices = 0;
        fs_devices->open_devices = 0;
        fs_devices->missing_devices = 0;
-       fs_devices->num_can_discard = 0;
        fs_devices->rotating = 0;
        fs_devices->seed = seed_devices;
 
@@ -2092,7 +2090,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        struct list_head *devices;
        struct super_block *sb = root->fs_info->sb;
        struct rcu_string *name;
-       u64 total_bytes;
+       u64 tmp;
        int seeding_dev = 0;
        int ret = 0;
 
@@ -2148,8 +2146,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
                goto error;
        }
 
-       lock_chunks(root);
-
        q = bdev_get_queue(bdev);
        if (blk_queue_discard(q))
                device->can_discard = 1;
@@ -2160,6 +2156,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        device->sector_size = root->sectorsize;
        device->total_bytes = i_size_read(bdev->bd_inode);
        device->disk_total_bytes = device->total_bytes;
+       device->commit_total_bytes = device->total_bytes;
        device->dev_root = root->fs_info->dev_root;
        device->bdev = bdev;
        device->in_fs_metadata = 1;
@@ -2177,6 +2174,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        device->fs_devices = root->fs_info->fs_devices;
 
        mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
+       lock_chunks(root);
        list_add_rcu(&device->dev_list, &root->fs_info->fs_devices->devices);
        list_add(&device->dev_alloc_list,
                 &root->fs_info->fs_devices->alloc_list);
@@ -2184,8 +2182,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        root->fs_info->fs_devices->open_devices++;
        root->fs_info->fs_devices->rw_devices++;
        root->fs_info->fs_devices->total_devices++;
-       if (device->can_discard)
-               root->fs_info->fs_devices->num_can_discard++;
        root->fs_info->fs_devices->total_rw_bytes += device->total_bytes;
 
        spin_lock(&root->fs_info->free_chunk_lock);
@@ -2195,26 +2191,45 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        if (!blk_queue_nonrot(bdev_get_queue(bdev)))
                root->fs_info->fs_devices->rotating = 1;
 
-       total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy);
+       tmp = btrfs_super_total_bytes(root->fs_info->super_copy);
        btrfs_set_super_total_bytes(root->fs_info->super_copy,
-                                   total_bytes + device->total_bytes);
+                                   tmp + device->total_bytes);
 
-       total_bytes = btrfs_super_num_devices(root->fs_info->super_copy);
+       tmp = btrfs_super_num_devices(root->fs_info->super_copy);
        btrfs_set_super_num_devices(root->fs_info->super_copy,
-                                   total_bytes + 1);
+                                   tmp + 1);
 
        /* add sysfs device entry */
        btrfs_kobj_add_device(root->fs_info, device);
 
+       /*
+        * we've got more storage, clear any full flags on the space
+        * infos
+        */
+       btrfs_clear_space_info_full(root->fs_info);
+
+       unlock_chunks(root);
        mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
 
        if (seeding_dev) {
-               char fsid_buf[BTRFS_UUID_UNPARSED_SIZE];
+               lock_chunks(root);
                ret = init_first_rw_device(trans, root, device);
+               unlock_chunks(root);
                if (ret) {
                        btrfs_abort_transaction(trans, root, ret);
                        goto error_trans;
                }
+       }
+
+       ret = btrfs_add_device(trans, root, device);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto error_trans;
+       }
+
+       if (seeding_dev) {
+               char fsid_buf[BTRFS_UUID_UNPARSED_SIZE];
+
                ret = btrfs_finish_sprout(trans, root);
                if (ret) {
                        btrfs_abort_transaction(trans, root, ret);
@@ -2228,21 +2243,8 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
                                                root->fs_info->fsid);
                if (kobject_rename(&root->fs_info->super_kobj, fsid_buf))
                        goto error_trans;
-       } else {
-               ret = btrfs_add_device(trans, root, device);
-               if (ret) {
-                       btrfs_abort_transaction(trans, root, ret);
-                       goto error_trans;
-               }
        }
 
-       /*
-        * we've got more storage, clear any full flags on the space
-        * infos
-        */
-       btrfs_clear_space_info_full(root->fs_info);
-
-       unlock_chunks(root);
        root->fs_info->num_tolerated_disk_barrier_failures =
                btrfs_calc_num_tolerated_disk_barrier_failures(root->fs_info);
        ret = btrfs_commit_transaction(trans, root);
@@ -2274,7 +2276,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        return ret;
 
 error_trans:
-       unlock_chunks(root);
        btrfs_end_transaction(trans, root);
        rcu_string_free(device->name);
        btrfs_kobj_rm_device(root->fs_info, device);
@@ -2289,6 +2290,7 @@ error:
 }
 
 int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
+                                 struct btrfs_device *srcdev,
                                  struct btrfs_device **device_out)
 {
        struct request_queue *q;
@@ -2301,24 +2303,38 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
        int ret = 0;
 
        *device_out = NULL;
-       if (fs_info->fs_devices->seeding)
+       if (fs_info->fs_devices->seeding) {
+               btrfs_err(fs_info, "the filesystem is a seed filesystem!");
                return -EINVAL;
+       }
 
        bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL,
                                  fs_info->bdev_holder);
-       if (IS_ERR(bdev))
+       if (IS_ERR(bdev)) {
+               btrfs_err(fs_info, "target device %s is invalid!", device_path);
                return PTR_ERR(bdev);
+       }
 
        filemap_write_and_wait(bdev->bd_inode->i_mapping);
 
        devices = &fs_info->fs_devices->devices;
        list_for_each_entry(device, devices, dev_list) {
                if (device->bdev == bdev) {
+                       btrfs_err(fs_info, "target device is in the filesystem!");
                        ret = -EEXIST;
                        goto error;
                }
        }
 
+
+       if (i_size_read(bdev->bd_inode) <
+           btrfs_device_get_total_bytes(srcdev)) {
+               btrfs_err(fs_info, "target device is smaller than source device!");
+               ret = -EINVAL;
+               goto error;
+       }
+
+
        device = btrfs_alloc_device(NULL, &devid, NULL);
        if (IS_ERR(device)) {
                ret = PTR_ERR(device);
@@ -2342,8 +2358,12 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
        device->io_width = root->sectorsize;
        device->io_align = root->sectorsize;
        device->sector_size = root->sectorsize;
-       device->total_bytes = i_size_read(bdev->bd_inode);
-       device->disk_total_bytes = device->total_bytes;
+       device->total_bytes = btrfs_device_get_total_bytes(srcdev);
+       device->disk_total_bytes = btrfs_device_get_disk_total_bytes(srcdev);
+       device->bytes_used = btrfs_device_get_bytes_used(srcdev);
+       ASSERT(list_empty(&srcdev->resized_list));
+       device->commit_total_bytes = srcdev->commit_total_bytes;
+       device->commit_bytes_used = device->bytes_used;
        device->dev_root = fs_info->dev_root;
        device->bdev = bdev;
        device->in_fs_metadata = 1;
@@ -2355,8 +2375,6 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
        list_add(&device->dev_list, &fs_info->fs_devices->devices);
        fs_info->fs_devices->num_devices++;
        fs_info->fs_devices->open_devices++;
-       if (device->can_discard)
-               fs_info->fs_devices->num_can_discard++;
        mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
 
        *device_out = device;
@@ -2415,8 +2433,10 @@ static noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
        btrfs_set_device_io_align(leaf, dev_item, device->io_align);
        btrfs_set_device_io_width(leaf, dev_item, device->io_width);
        btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
-       btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
-       btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
+       btrfs_set_device_total_bytes(leaf, dev_item,
+                                    btrfs_device_get_disk_total_bytes(device));
+       btrfs_set_device_bytes_used(leaf, dev_item,
+                                   btrfs_device_get_bytes_used(device));
        btrfs_mark_buffer_dirty(leaf);
 
 out:
@@ -2424,40 +2444,44 @@ out:
        return ret;
 }
 
-static int __btrfs_grow_device(struct btrfs_trans_handle *trans,
+int btrfs_grow_device(struct btrfs_trans_handle *trans,
                      struct btrfs_device *device, u64 new_size)
 {
        struct btrfs_super_block *super_copy =
                device->dev_root->fs_info->super_copy;
-       u64 old_total = btrfs_super_total_bytes(super_copy);
-       u64 diff = new_size - device->total_bytes;
+       struct btrfs_fs_devices *fs_devices;
+       u64 old_total;
+       u64 diff;
 
        if (!device->writeable)
                return -EACCES;
+
+       lock_chunks(device->dev_root);
+       old_total = btrfs_super_total_bytes(super_copy);
+       diff = new_size - device->total_bytes;
+
        if (new_size <= device->total_bytes ||
-           device->is_tgtdev_for_dev_replace)
+           device->is_tgtdev_for_dev_replace) {
+               unlock_chunks(device->dev_root);
                return -EINVAL;
+       }
+
+       fs_devices = device->dev_root->fs_info->fs_devices;
 
        btrfs_set_super_total_bytes(super_copy, old_total + diff);
        device->fs_devices->total_rw_bytes += diff;
 
-       device->total_bytes = new_size;
-       device->disk_total_bytes = new_size;
+       btrfs_device_set_total_bytes(device, new_size);
+       btrfs_device_set_disk_total_bytes(device, new_size);
        btrfs_clear_space_info_full(device->dev_root->fs_info);
+       if (list_empty(&device->resized_list))
+               list_add_tail(&device->resized_list,
+                             &fs_devices->resized_devices);
+       unlock_chunks(device->dev_root);
 
        return btrfs_update_device(trans, device);
 }
 
-int btrfs_grow_device(struct btrfs_trans_handle *trans,
-                     struct btrfs_device *device, u64 new_size)
-{
-       int ret;
-       lock_chunks(device->dev_root);
-       ret = __btrfs_grow_device(trans, device, new_size);
-       unlock_chunks(device->dev_root);
-       return ret;
-}
-
 static int btrfs_free_chunk(struct btrfs_trans_handle *trans,
                            struct btrfs_root *root,
                            u64 chunk_tree, u64 chunk_objectid,
@@ -2509,6 +2533,7 @@ static int btrfs_del_sys_chunk(struct btrfs_root *root, u64 chunk_objectid, u64
        u32 cur;
        struct btrfs_key key;
 
+       lock_chunks(root);
        array_size = btrfs_super_sys_array_size(super_copy);
 
        ptr = super_copy->sys_chunk_array;
@@ -2538,79 +2563,95 @@ static int btrfs_del_sys_chunk(struct btrfs_root *root, u64 chunk_objectid, u64
                        cur += len;
                }
        }
+       unlock_chunks(root);
        return ret;
 }
 
-static int btrfs_relocate_chunk(struct btrfs_root *root,
-                        u64 chunk_tree, u64 chunk_objectid,
-                        u64 chunk_offset)
+int btrfs_remove_chunk(struct btrfs_trans_handle *trans,
+                      struct btrfs_root *root, u64 chunk_offset)
 {
        struct extent_map_tree *em_tree;
-       struct btrfs_root *extent_root;
-       struct btrfs_trans_handle *trans;
        struct extent_map *em;
+       struct btrfs_root *extent_root = root->fs_info->extent_root;
        struct map_lookup *map;
-       int ret;
-       int i;
+       u64 dev_extent_len = 0;
+       u64 chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+       u64 chunk_tree = root->fs_info->chunk_root->objectid;
+       int i, ret = 0;
 
+       /* Just in case */
        root = root->fs_info->chunk_root;
-       extent_root = root->fs_info->extent_root;
        em_tree = &root->fs_info->mapping_tree.map_tree;
 
-       ret = btrfs_can_relocate(extent_root, chunk_offset);
-       if (ret)
-               return -ENOSPC;
-
-       /* step one, relocate all the extents inside this chunk */
-       ret = btrfs_relocate_block_group(extent_root, chunk_offset);
-       if (ret)
-               return ret;
-
-       trans = btrfs_start_transaction(root, 0);
-       if (IS_ERR(trans)) {
-               ret = PTR_ERR(trans);
-               btrfs_std_error(root->fs_info, ret);
-               return ret;
-       }
-
-       lock_chunks(root);
-
-       /*
-        * step two, delete the device extents and the
-        * chunk tree entries
-        */
        read_lock(&em_tree->lock);
        em = lookup_extent_mapping(em_tree, chunk_offset, 1);
        read_unlock(&em_tree->lock);
 
-       BUG_ON(!em || em->start > chunk_offset ||
-              em->start + em->len < chunk_offset);
+       if (!em || em->start > chunk_offset ||
+           em->start + em->len < chunk_offset) {
+               /*
+                * This is a logic error, but we don't want to just rely on the
+                * user having built with ASSERT enabled, so if ASSERT doens't
+                * do anything we still error out.
+                */
+               ASSERT(0);
+               if (em)
+                       free_extent_map(em);
+               return -EINVAL;
+       }
        map = (struct map_lookup *)em->bdev;
 
        for (i = 0; i < map->num_stripes; i++) {
-               ret = btrfs_free_dev_extent(trans, map->stripes[i].dev,
-                                           map->stripes[i].physical);
-               BUG_ON(ret);
+               struct btrfs_device *device = map->stripes[i].dev;
+               ret = btrfs_free_dev_extent(trans, device,
+                                           map->stripes[i].physical,
+                                           &dev_extent_len);
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out;
+               }
+
+               if (device->bytes_used > 0) {
+                       lock_chunks(root);
+                       btrfs_device_set_bytes_used(device,
+                                       device->bytes_used - dev_extent_len);
+                       spin_lock(&root->fs_info->free_chunk_lock);
+                       root->fs_info->free_chunk_space += dev_extent_len;
+                       spin_unlock(&root->fs_info->free_chunk_lock);
+                       btrfs_clear_space_info_full(root->fs_info);
+                       unlock_chunks(root);
+               }
 
                if (map->stripes[i].dev) {
                        ret = btrfs_update_device(trans, map->stripes[i].dev);
-                       BUG_ON(ret);
+                       if (ret) {
+                               btrfs_abort_transaction(trans, root, ret);
+                               goto out;
+                       }
                }
        }
        ret = btrfs_free_chunk(trans, root, chunk_tree, chunk_objectid,
                               chunk_offset);
-
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto out;
+       }
 
        trace_btrfs_chunk_free(root, map, chunk_offset, em->len);
 
        if (map->type & BTRFS_BLOCK_GROUP_SYSTEM) {
                ret = btrfs_del_sys_chunk(root, chunk_objectid, chunk_offset);
-               BUG_ON(ret);
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out;
+               }
        }
 
        ret = btrfs_remove_block_group(trans, extent_root, chunk_offset);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, extent_root, ret);
+               goto out;
+       }
 
        write_lock(&em_tree->lock);
        remove_extent_mapping(em_tree, em);
@@ -2618,12 +2659,46 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
 
        /* once for the tree */
        free_extent_map(em);
+out:
        /* once for us */
        free_extent_map(em);
+       return ret;
+}
 
-       unlock_chunks(root);
+static int btrfs_relocate_chunk(struct btrfs_root *root,
+                        u64 chunk_tree, u64 chunk_objectid,
+                        u64 chunk_offset)
+{
+       struct btrfs_root *extent_root;
+       struct btrfs_trans_handle *trans;
+       int ret;
+
+       root = root->fs_info->chunk_root;
+       extent_root = root->fs_info->extent_root;
+
+       ret = btrfs_can_relocate(extent_root, chunk_offset);
+       if (ret)
+               return -ENOSPC;
+
+       /* step one, relocate all the extents inside this chunk */
+       ret = btrfs_relocate_block_group(extent_root, chunk_offset);
+       if (ret)
+               return ret;
+
+       trans = btrfs_start_transaction(root, 0);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               btrfs_std_error(root->fs_info, ret);
+               return ret;
+       }
+
+       /*
+        * step two, delete the device extents and the
+        * chunk tree entries
+        */
+       ret = btrfs_remove_chunk(trans, root, chunk_offset);
        btrfs_end_transaction(trans, root);
-       return 0;
+       return ret;
 }
 
 static int btrfs_relocate_sys_chunks(struct btrfs_root *root)
@@ -2676,8 +2751,8 @@ again:
                                                   found_key.offset);
                        if (ret == -ENOSPC)
                                failed++;
-                       else if (ret)
-                               BUG();
+                       else
+                               BUG_ON(ret);
                }
 
                if (found_key.offset == 0)
@@ -3084,11 +3159,12 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info)
        /* step one make some room on all the devices */
        devices = &fs_info->fs_devices->devices;
        list_for_each_entry(device, devices, dev_list) {
-               old_size = device->total_bytes;
+               old_size = btrfs_device_get_total_bytes(device);
                size_to_free = div_factor(old_size, 1);
                size_to_free = min(size_to_free, (u64)1 * 1024 * 1024);
                if (!device->writeable ||
-                   device->total_bytes - device->bytes_used > size_to_free ||
+                   btrfs_device_get_total_bytes(device) -
+                   btrfs_device_get_bytes_used(device) > size_to_free ||
                    device->is_tgtdev_for_dev_replace)
                        continue;
 
@@ -3643,8 +3719,6 @@ static int btrfs_uuid_scan_kthread(void *data)
        max_key.type = BTRFS_ROOT_ITEM_KEY;
        max_key.offset = (u64)-1;
 
-       path->keep_locks = 1;
-
        while (1) {
                ret = btrfs_search_forward(root, &key, path, 0);
                if (ret) {
@@ -3896,8 +3970,8 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
        struct btrfs_key key;
        struct btrfs_super_block *super_copy = root->fs_info->super_copy;
        u64 old_total = btrfs_super_total_bytes(super_copy);
-       u64 old_size = device->total_bytes;
-       u64 diff = device->total_bytes - new_size;
+       u64 old_size = btrfs_device_get_total_bytes(device);
+       u64 diff = old_size - new_size;
 
        if (device->is_tgtdev_for_dev_replace)
                return -EINVAL;
@@ -3910,7 +3984,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
 
        lock_chunks(root);
 
-       device->total_bytes = new_size;
+       btrfs_device_set_total_bytes(device, new_size);
        if (device->writeable) {
                device->fs_devices->total_rw_bytes -= diff;
                spin_lock(&root->fs_info->free_chunk_lock);
@@ -3976,7 +4050,7 @@ again:
                ret = -ENOSPC;
                lock_chunks(root);
 
-               device->total_bytes = old_size;
+               btrfs_device_set_total_bytes(device, old_size);
                if (device->writeable)
                        device->fs_devices->total_rw_bytes += diff;
                spin_lock(&root->fs_info->free_chunk_lock);
@@ -3994,18 +4068,17 @@ again:
        }
 
        lock_chunks(root);
+       btrfs_device_set_disk_total_bytes(device, new_size);
+       if (list_empty(&device->resized_list))
+               list_add_tail(&device->resized_list,
+                             &root->fs_info->fs_devices->resized_devices);
 
-       device->disk_total_bytes = new_size;
-       /* Now btrfs_update_device() will change the on-disk size. */
-       ret = btrfs_update_device(trans, device);
-       if (ret) {
-               unlock_chunks(root);
-               btrfs_end_transaction(trans, root);
-               goto done;
-       }
        WARN_ON(diff > old_total);
        btrfs_set_super_total_bytes(super_copy, old_total - diff);
        unlock_chunks(root);
+
+       /* Now btrfs_update_device() will change the on-disk size. */
+       ret = btrfs_update_device(trans, device);
        btrfs_end_transaction(trans, root);
 done:
        btrfs_free_path(path);
@@ -4021,10 +4094,13 @@ static int btrfs_add_system_chunk(struct btrfs_root *root,
        u32 array_size;
        u8 *ptr;
 
+       lock_chunks(root);
        array_size = btrfs_super_sys_array_size(super_copy);
        if (array_size + item_size + sizeof(disk_key)
-                       > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE)
+                       > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
+               unlock_chunks(root);
                return -EFBIG;
+       }
 
        ptr = super_copy->sys_chunk_array + array_size;
        btrfs_cpu_key_to_disk(&disk_key, key);
@@ -4033,6 +4109,8 @@ static int btrfs_add_system_chunk(struct btrfs_root *root,
        memcpy(ptr, chunk, item_size);
        item_size += sizeof(disk_key);
        btrfs_set_super_sys_array_size(super_copy, array_size + item_size);
+       unlock_chunks(root);
+
        return 0;
 }
 
@@ -4402,6 +4480,16 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
        if (ret)
                goto error_del_extent;
 
+       for (i = 0; i < map->num_stripes; i++) {
+               num_bytes = map->stripes[i].dev->bytes_used + stripe_size;
+               btrfs_device_set_bytes_used(map->stripes[i].dev, num_bytes);
+       }
+
+       spin_lock(&extent_root->fs_info->free_chunk_lock);
+       extent_root->fs_info->free_chunk_space -= (stripe_size *
+                                                  map->num_stripes);
+       spin_unlock(&extent_root->fs_info->free_chunk_lock);
+
        free_extent_map(em);
        check_raid56_incompat_flag(extent_root->fs_info, type);
 
@@ -4473,7 +4561,6 @@ int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans,
                device = map->stripes[i].dev;
                dev_offset = map->stripes[i].physical;
 
-               device->bytes_used += stripe_size;
                ret = btrfs_update_device(trans, device);
                if (ret)
                        goto out;
@@ -4486,11 +4573,6 @@ int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans,
                        goto out;
        }
 
-       spin_lock(&extent_root->fs_info->free_chunk_lock);
-       extent_root->fs_info->free_chunk_space -= (stripe_size *
-                                                  map->num_stripes);
-       spin_unlock(&extent_root->fs_info->free_chunk_lock);
-
        stripe = &chunk->stripe;
        for (i = 0; i < map->num_stripes; i++) {
                device = map->stripes[i].dev;
@@ -4570,16 +4652,25 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
        alloc_profile = btrfs_get_alloc_profile(fs_info->chunk_root, 0);
        ret = __btrfs_alloc_chunk(trans, extent_root, sys_chunk_offset,
                                  alloc_profile);
-       if (ret) {
-               btrfs_abort_transaction(trans, root, ret);
-               goto out;
+       return ret;
+}
+
+static inline int btrfs_chunk_max_errors(struct map_lookup *map)
+{
+       int max_errors;
+
+       if (map->type & (BTRFS_BLOCK_GROUP_RAID1 |
+                        BTRFS_BLOCK_GROUP_RAID10 |
+                        BTRFS_BLOCK_GROUP_RAID5 |
+                        BTRFS_BLOCK_GROUP_DUP)) {
+               max_errors = 1;
+       } else if (map->type & BTRFS_BLOCK_GROUP_RAID6) {
+               max_errors = 2;
+       } else {
+               max_errors = 0;
        }
 
-       ret = btrfs_add_device(trans, fs_info->chunk_root, device);
-       if (ret)
-               btrfs_abort_transaction(trans, root, ret);
-out:
-       return ret;
+       return max_errors;
 }
 
 int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
@@ -4588,6 +4679,7 @@ int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
        struct map_lookup *map;
        struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
        int readonly = 0;
+       int miss_ndevs = 0;
        int i;
 
        read_lock(&map_tree->map_tree.lock);
@@ -4596,18 +4688,27 @@ int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
        if (!em)
                return 1;
 
-       if (btrfs_test_opt(root, DEGRADED)) {
-               free_extent_map(em);
-               return 0;
-       }
-
        map = (struct map_lookup *)em->bdev;
        for (i = 0; i < map->num_stripes; i++) {
+               if (map->stripes[i].dev->missing) {
+                       miss_ndevs++;
+                       continue;
+               }
+
                if (!map->stripes[i].dev->writeable) {
                        readonly = 1;
-                       break;
+                       goto end;
                }
        }
+
+       /*
+        * If the number of missing devices is larger than max errors,
+        * we can not write the data into that chunk successfully, so
+        * set it readonly.
+        */
+       if (miss_ndevs > btrfs_chunk_max_errors(map))
+               readonly = 1;
+end:
        free_extent_map(em);
        return readonly;
 }
@@ -5008,6 +5109,8 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
                        num_stripes = min_t(u64, map->num_stripes,
                                            stripe_nr_end - stripe_nr_orig);
                stripe_index = do_div(stripe_nr, map->num_stripes);
+               if (!(rw & (REQ_WRITE | REQ_DISCARD | REQ_GET_READ_MIRRORS)))
+                       mirror_num = 1;
        } else if (map->type & BTRFS_BLOCK_GROUP_RAID1) {
                if (rw & (REQ_WRITE | REQ_DISCARD | REQ_GET_READ_MIRRORS))
                        num_stripes = map->num_stripes;
@@ -5111,6 +5214,9 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
                        /* We distribute the parity blocks across stripes */
                        tmp = stripe_nr + stripe_index;
                        stripe_index = do_div(tmp, map->num_stripes);
+                       if (!(rw & (REQ_WRITE | REQ_DISCARD |
+                                   REQ_GET_READ_MIRRORS)) && mirror_num <= 1)
+                               mirror_num = 1;
                }
        } else {
                /*
@@ -5218,16 +5324,8 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
                }
        }
 
-       if (rw & (REQ_WRITE | REQ_GET_READ_MIRRORS)) {
-               if (map->type & (BTRFS_BLOCK_GROUP_RAID1 |
-                                BTRFS_BLOCK_GROUP_RAID10 |
-                                BTRFS_BLOCK_GROUP_RAID5 |
-                                BTRFS_BLOCK_GROUP_DUP)) {
-                       max_errors = 1;
-               } else if (map->type & BTRFS_BLOCK_GROUP_RAID6) {
-                       max_errors = 2;
-               }
-       }
+       if (rw & (REQ_WRITE | REQ_GET_READ_MIRRORS))
+               max_errors = btrfs_chunk_max_errors(map);
 
        if (dev_replace_is_ongoing && (rw & (REQ_WRITE | REQ_DISCARD)) &&
            dev_replace->tgtdev != NULL) {
@@ -5610,8 +5708,8 @@ static void submit_stripe_bio(struct btrfs_root *root, struct btrfs_bio *bbio,
                name = rcu_dereference(dev->name);
                pr_debug("btrfs_map_bio: rw %d, sector=%llu, dev=%lu "
                         "(%s id %llu), size=%u\n", rw,
-                        (u64)bio->bi_sector, (u_long)dev->bdev->bd_dev,
-                        name->str, dev->devid, bio->bi_size);
+                        (u64)bio->bi_iter.bi_sector, (u_long)dev->bdev->bd_dev,
+                        name->str, dev->devid, bio->bi_iter.bi_size);
                rcu_read_unlock();
        }
 #endif
@@ -5789,10 +5887,10 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
 }
 
 static struct btrfs_device *add_missing_dev(struct btrfs_root *root,
+                                           struct btrfs_fs_devices *fs_devices,
                                            u64 devid, u8 *dev_uuid)
 {
        struct btrfs_device *device;
-       struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
 
        device = btrfs_alloc_device(NULL, &devid, dev_uuid);
        if (IS_ERR(device))
@@ -5929,7 +6027,8 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
                }
                if (!map->stripes[i].dev) {
                        map->stripes[i].dev =
-                               add_missing_dev(root, devid, uuid);
+                               add_missing_dev(root, root->fs_info->fs_devices,
+                                               devid, uuid);
                        if (!map->stripes[i].dev) {
                                free_extent_map(em);
                                return -EIO;
@@ -5956,7 +6055,9 @@ static void fill_device_from_item(struct extent_buffer *leaf,
        device->devid = btrfs_device_id(leaf, dev_item);
        device->disk_total_bytes = btrfs_device_total_bytes(leaf, dev_item);
        device->total_bytes = device->disk_total_bytes;
+       device->commit_total_bytes = device->disk_total_bytes;
        device->bytes_used = btrfs_device_bytes_used(leaf, dev_item);
+       device->commit_bytes_used = device->bytes_used;
        device->type = btrfs_device_type(leaf, dev_item);
        device->io_align = btrfs_device_io_align(leaf, dev_item);
        device->io_width = btrfs_device_io_width(leaf, dev_item);
@@ -5968,7 +6069,8 @@ static void fill_device_from_item(struct extent_buffer *leaf,
        read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
 }
 
-static int open_seed_devices(struct btrfs_root *root, u8 *fsid)
+static struct btrfs_fs_devices *open_seed_devices(struct btrfs_root *root,
+                                                 u8 *fsid)
 {
        struct btrfs_fs_devices *fs_devices;
        int ret;
@@ -5977,49 +6079,56 @@ static int open_seed_devices(struct btrfs_root *root, u8 *fsid)
 
        fs_devices = root->fs_info->fs_devices->seed;
        while (fs_devices) {
-               if (!memcmp(fs_devices->fsid, fsid, BTRFS_UUID_SIZE)) {
-                       ret = 0;
-                       goto out;
-               }
+               if (!memcmp(fs_devices->fsid, fsid, BTRFS_UUID_SIZE))
+                       return fs_devices;
+
                fs_devices = fs_devices->seed;
        }
 
        fs_devices = find_fsid(fsid);
        if (!fs_devices) {
-               ret = -ENOENT;
-               goto out;
+               if (!btrfs_test_opt(root, DEGRADED))
+                       return ERR_PTR(-ENOENT);
+
+               fs_devices = alloc_fs_devices(fsid);
+               if (IS_ERR(fs_devices))
+                       return fs_devices;
+
+               fs_devices->seeding = 1;
+               fs_devices->opened = 1;
+               return fs_devices;
        }
 
        fs_devices = clone_fs_devices(fs_devices);
-       if (IS_ERR(fs_devices)) {
-               ret = PTR_ERR(fs_devices);
-               goto out;
-       }
+       if (IS_ERR(fs_devices))
+               return fs_devices;
 
        ret = __btrfs_open_devices(fs_devices, FMODE_READ,
                                   root->fs_info->bdev_holder);
        if (ret) {
                free_fs_devices(fs_devices);
+               fs_devices = ERR_PTR(ret);
                goto out;
        }
 
        if (!fs_devices->seeding) {
                __btrfs_close_devices(fs_devices);
                free_fs_devices(fs_devices);
-               ret = -EINVAL;
+               fs_devices = ERR_PTR(-EINVAL);
                goto out;
        }
 
        fs_devices->seed = root->fs_info->fs_devices->seed;
        root->fs_info->fs_devices->seed = fs_devices;
 out:
-       return ret;
+       return fs_devices;
 }
 
 static int read_one_dev(struct btrfs_root *root,
                        struct extent_buffer *leaf,
                        struct btrfs_dev_item *dev_item)
 {
+       struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
        struct btrfs_device *device;
        u64 devid;
        int ret;
@@ -6033,31 +6142,48 @@ static int read_one_dev(struct btrfs_root *root,
                           BTRFS_UUID_SIZE);
 
        if (memcmp(fs_uuid, root->fs_info->fsid, BTRFS_UUID_SIZE)) {
-               ret = open_seed_devices(root, fs_uuid);
-               if (ret && !btrfs_test_opt(root, DEGRADED))
-                       return ret;
+               fs_devices = open_seed_devices(root, fs_uuid);
+               if (IS_ERR(fs_devices))
+                       return PTR_ERR(fs_devices);
        }
 
        device = btrfs_find_device(root->fs_info, devid, dev_uuid, fs_uuid);
-       if (!device || !device->bdev) {
+       if (!device) {
                if (!btrfs_test_opt(root, DEGRADED))
                        return -EIO;
 
-               if (!device) {
-                       btrfs_warn(root->fs_info, "devid %llu missing", devid);
-                       device = add_missing_dev(root, devid, dev_uuid);
-                       if (!device)
-                               return -ENOMEM;
-               } else if (!device->missing) {
+               btrfs_warn(root->fs_info, "devid %llu missing", devid);
+               device = add_missing_dev(root, fs_devices, devid, dev_uuid);
+               if (!device)
+                       return -ENOMEM;
+       } else {
+               if (!device->bdev && !btrfs_test_opt(root, DEGRADED))
+                       return -EIO;
+
+               if(!device->bdev && !device->missing) {
                        /*
                         * this happens when a device that was properly setup
                         * in the device info lists suddenly goes bad.
                         * device->bdev is NULL, and so we have to set
                         * device->missing to one here
                         */
-                       root->fs_info->fs_devices->missing_devices++;
+                       device->fs_devices->missing_devices++;
                        device->missing = 1;
                }
+
+               /* Move the device to its own fs_devices */
+               if (device->fs_devices != fs_devices) {
+                       ASSERT(device->missing);
+
+                       list_move(&device->dev_list, &fs_devices->devices);
+                       device->fs_devices->num_devices--;
+                       fs_devices->num_devices++;
+
+                       device->fs_devices->missing_devices--;
+                       fs_devices->missing_devices++;
+
+                       device->fs_devices = fs_devices;
+               }
        }
 
        if (device->fs_devices != root->fs_info->fs_devices) {
@@ -6373,16 +6499,18 @@ int btrfs_run_dev_stats(struct btrfs_trans_handle *trans,
        struct btrfs_root *dev_root = fs_info->dev_root;
        struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
        struct btrfs_device *device;
+       int stats_cnt;
        int ret = 0;
 
        mutex_lock(&fs_devices->device_list_mutex);
        list_for_each_entry(device, &fs_devices->devices, dev_list) {
-               if (!device->dev_stats_valid || !device->dev_stats_dirty)
+               if (!device->dev_stats_valid || !btrfs_dev_stats_dirty(device))
                        continue;
 
+               stats_cnt = atomic_read(&device->dev_stats_ccnt);
                ret = update_dev_stat_item(trans, dev_root, device);
                if (!ret)
-                       device->dev_stats_dirty = 0;
+                       atomic_sub(stats_cnt, &device->dev_stats_ccnt);
        }
        mutex_unlock(&fs_devices->device_list_mutex);
 
@@ -6481,3 +6609,51 @@ int btrfs_scratch_superblock(struct btrfs_device *device)
 
        return 0;
 }
+
+/*
+ * Update the size of all devices, which is used for writing out the
+ * super blocks.
+ */
+void btrfs_update_commit_device_size(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+       struct btrfs_device *curr, *next;
+
+       if (list_empty(&fs_devices->resized_devices))
+               return;
+
+       mutex_lock(&fs_devices->device_list_mutex);
+       lock_chunks(fs_info->dev_root);
+       list_for_each_entry_safe(curr, next, &fs_devices->resized_devices,
+                                resized_list) {
+               list_del_init(&curr->resized_list);
+               curr->commit_total_bytes = curr->disk_total_bytes;
+       }
+       unlock_chunks(fs_info->dev_root);
+       mutex_unlock(&fs_devices->device_list_mutex);
+}
+
+/* Must be invoked during the transaction commit */
+void btrfs_update_commit_device_bytes_used(struct btrfs_root *root,
+                                       struct btrfs_transaction *transaction)
+{
+       struct extent_map *em;
+       struct map_lookup *map;
+       struct btrfs_device *dev;
+       int i;
+
+       if (list_empty(&transaction->pending_chunks))
+               return;
+
+       /* In order to kick the device replace finish process */
+       lock_chunks(root);
+       list_for_each_entry(em, &transaction->pending_chunks, list) {
+               map = (struct map_lookup *)em->bdev;
+
+               for (i = 0; i < map->num_stripes; i++) {
+                       dev = map->stripes[i].dev;
+                       dev->commit_bytes_used = dev->bytes_used;
+               }
+       }
+       unlock_chunks(root);
+}
index 2aaa00c4781637f508302829ed51e3ae05402c84..08980fa2303916ee08c0f2b6bd30a6b9a6f0a711 100644 (file)
@@ -24,6 +24,8 @@
 #include <linux/btrfs.h>
 #include "async-thread.h"
 
+extern struct mutex uuid_mutex;
+
 #define BTRFS_STRIPE_LEN       (64 * 1024)
 
 struct buffer_head;
@@ -32,41 +34,59 @@ struct btrfs_pending_bios {
        struct bio *tail;
 };
 
+/*
+ * Use sequence counter to get consistent device stat data on
+ * 32-bit processors.
+ */
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+#include <linux/seqlock.h>
+#define __BTRFS_NEED_DEVICE_DATA_ORDERED
+#define btrfs_device_data_ordered_init(device) \
+       seqcount_init(&device->data_seqcount)
+#else
+#define btrfs_device_data_ordered_init(device) do { } while (0)
+#endif
+
 struct btrfs_device {
        struct list_head dev_list;
        struct list_head dev_alloc_list;
        struct btrfs_fs_devices *fs_devices;
+
        struct btrfs_root *dev_root;
 
+       struct rcu_string *name;
+
+       u64 generation;
+
+       spinlock_t io_lock ____cacheline_aligned;
+       int running_pending;
        /* regular prio bios */
        struct btrfs_pending_bios pending_bios;
        /* WRITE_SYNC bios */
        struct btrfs_pending_bios pending_sync_bios;
 
-       u64 generation;
-       int running_pending;
+       struct block_device *bdev;
+
+       /* the mode sent to blkdev_get */
+       fmode_t mode;
+
        int writeable;
        int in_fs_metadata;
        int missing;
        int can_discard;
        int is_tgtdev_for_dev_replace;
 
-       spinlock_t io_lock;
-       /* the mode sent to blkdev_get */
-       fmode_t mode;
-
-       struct block_device *bdev;
-
-
-       struct rcu_string *name;
+#ifdef __BTRFS_NEED_DEVICE_DATA_ORDERED
+       seqcount_t data_seqcount;
+#endif
 
        /* the internal btrfs device id */
        u64 devid;
 
-       /* size of the device */
+       /* size of the device in memory */
        u64 total_bytes;
 
-       /* size of the disk */
+       /* size of the device on disk */
        u64 disk_total_bytes;
 
        /* bytes used */
@@ -83,10 +103,26 @@ struct btrfs_device {
        /* minimal io size for this device */
        u32 sector_size;
 
-
        /* physical drive uuid (or lvm uuid) */
        u8 uuid[BTRFS_UUID_SIZE];
 
+       /*
+        * size of the device on the current transaction
+        *
+        * This variant is update when committing the transaction,
+        * and protected by device_list_mutex
+        */
+       u64 commit_total_bytes;
+
+       /* bytes used on the current transaction */
+       u64 commit_bytes_used;
+       /*
+        * used to manage the device which is resized
+        *
+        * It is protected by chunk_lock.
+        */
+       struct list_head resized_list;
+
        /* for sending down flush barriers */
        int nobarriers;
        struct bio *flush_bio;
@@ -107,26 +143,90 @@ struct btrfs_device {
        struct radix_tree_root reada_zones;
        struct radix_tree_root reada_extents;
 
-
        /* disk I/O failure stats. For detailed description refer to
         * enum btrfs_dev_stat_values in ioctl.h */
        int dev_stats_valid;
-       int dev_stats_dirty; /* counters need to be written to disk */
+
+       /* Counter to record the change of device stats */
+       atomic_t dev_stats_ccnt;
        atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX];
 };
 
+/*
+ * If we read those variants at the context of their own lock, we needn't
+ * use the following helpers, reading them directly is safe.
+ */
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+#define BTRFS_DEVICE_GETSET_FUNCS(name)                                        \
+static inline u64                                                      \
+btrfs_device_get_##name(const struct btrfs_device *dev)                        \
+{                                                                      \
+       u64 size;                                                       \
+       unsigned int seq;                                               \
+                                                                       \
+       do {                                                            \
+               seq = read_seqcount_begin(&dev->data_seqcount);         \
+               size = dev->name;                                       \
+       } while (read_seqcount_retry(&dev->data_seqcount, seq));        \
+       return size;                                                    \
+}                                                                      \
+                                                                       \
+static inline void                                                     \
+btrfs_device_set_##name(struct btrfs_device *dev, u64 size)            \
+{                                                                      \
+       preempt_disable();                                              \
+       write_seqcount_begin(&dev->data_seqcount);                      \
+       dev->name = size;                                               \
+       write_seqcount_end(&dev->data_seqcount);                        \
+       preempt_enable();                                               \
+}
+#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT)
+#define BTRFS_DEVICE_GETSET_FUNCS(name)                                        \
+static inline u64                                                      \
+btrfs_device_get_##name(const struct btrfs_device *dev)                        \
+{                                                                      \
+       u64 size;                                                       \
+                                                                       \
+       preempt_disable();                                              \
+       size = dev->name;                                               \
+       preempt_enable();                                               \
+       return size;                                                    \
+}                                                                      \
+                                                                       \
+static inline void                                                     \
+btrfs_device_set_##name(struct btrfs_device *dev, u64 size)            \
+{                                                                      \
+       preempt_disable();                                              \
+       dev->name = size;                                               \
+       preempt_enable();                                               \
+}
+#else
+#define BTRFS_DEVICE_GETSET_FUNCS(name)                                        \
+static inline u64                                                      \
+btrfs_device_get_##name(const struct btrfs_device *dev)                        \
+{                                                                      \
+       return dev->name;                                               \
+}                                                                      \
+                                                                       \
+static inline void                                                     \
+btrfs_device_set_##name(struct btrfs_device *dev, u64 size)            \
+{                                                                      \
+       dev->name = size;                                               \
+}
+#endif
+
+BTRFS_DEVICE_GETSET_FUNCS(total_bytes);
+BTRFS_DEVICE_GETSET_FUNCS(disk_total_bytes);
+BTRFS_DEVICE_GETSET_FUNCS(bytes_used);
+
 struct btrfs_fs_devices {
        u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
 
-       /* the device with this id has the most recent copy of the super */
-       u64 latest_devid;
-       u64 latest_trans;
        u64 num_devices;
        u64 open_devices;
        u64 rw_devices;
        u64 missing_devices;
        u64 total_rw_bytes;
-       u64 num_can_discard;
        u64 total_devices;
        struct block_device *latest_bdev;
 
@@ -139,6 +239,7 @@ struct btrfs_fs_devices {
        struct mutex device_list_mutex;
        struct list_head devices;
 
+       struct list_head resized_devices;
        /* devices not currently being allocated */
        struct list_head alloc_list;
        struct list_head list;
@@ -167,8 +268,9 @@ struct btrfs_fs_devices {
  */
 typedef void (btrfs_io_bio_end_io_t) (struct btrfs_io_bio *bio, int err);
 struct btrfs_io_bio {
-       unsigned long mirror_num;
-       unsigned long stripe_index;
+       unsigned int mirror_num;
+       unsigned int stripe_index;
+       u64 logical;
        u8 *csum;
        u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE];
        u8 *csum_allocated;
@@ -325,6 +427,7 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
 int btrfs_shrink_device(struct btrfs_device *device, u64 new_size);
 int btrfs_init_new_device(struct btrfs_root *root, char *path);
 int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
+                                 struct btrfs_device *srcdev,
                                  struct btrfs_device **device_out);
 int btrfs_balance(struct btrfs_balance_control *bctl,
                  struct btrfs_ioctl_balance_args *bargs);
@@ -360,11 +463,20 @@ unsigned long btrfs_full_stripe_len(struct btrfs_root *root,
 int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans,
                                struct btrfs_root *extent_root,
                                u64 chunk_offset, u64 chunk_size);
+int btrfs_remove_chunk(struct btrfs_trans_handle *trans,
+                      struct btrfs_root *root, u64 chunk_offset);
+
+static inline int btrfs_dev_stats_dirty(struct btrfs_device *dev)
+{
+       return atomic_read(&dev->dev_stats_ccnt);
+}
+
 static inline void btrfs_dev_stat_inc(struct btrfs_device *dev,
                                      int index)
 {
        atomic_inc(dev->dev_stat_values + index);
-       dev->dev_stats_dirty = 1;
+       smp_mb__before_atomic();
+       atomic_inc(&dev->dev_stats_ccnt);
 }
 
 static inline int btrfs_dev_stat_read(struct btrfs_device *dev,
@@ -379,7 +491,8 @@ static inline int btrfs_dev_stat_read_and_reset(struct btrfs_device *dev,
        int ret;
 
        ret = atomic_xchg(dev->dev_stat_values + index, 0);
-       dev->dev_stats_dirty = 1;
+       smp_mb__before_atomic();
+       atomic_inc(&dev->dev_stats_ccnt);
        return ret;
 }
 
@@ -387,7 +500,8 @@ static inline void btrfs_dev_stat_set(struct btrfs_device *dev,
                                      int index, unsigned long val)
 {
        atomic_set(dev->dev_stat_values + index, val);
-       dev->dev_stats_dirty = 1;
+       smp_mb__before_atomic();
+       atomic_inc(&dev->dev_stats_ccnt);
 }
 
 static inline void btrfs_dev_stat_reset(struct btrfs_device *dev,
@@ -395,4 +509,8 @@ static inline void btrfs_dev_stat_reset(struct btrfs_device *dev,
 {
        btrfs_dev_stat_set(dev, index, 0);
 }
+
+void btrfs_update_commit_device_size(struct btrfs_fs_info *fs_info);
+void btrfs_update_commit_device_bytes_used(struct btrfs_root *root,
+                                       struct btrfs_transaction *transaction);
 #endif
index ad8328d797ea9910c21f0ecd5db6051d347a4571..dcf20131fbe49c25348463dffedffaeaabf0f607 100644 (file)
@@ -237,7 +237,7 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
         * first xattr that we find and walk forward
         */
        key.objectid = btrfs_ino(inode);
-       btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
+       key.type = BTRFS_XATTR_ITEM_KEY;
        key.offset = 0;
 
        path = btrfs_alloc_path();
@@ -273,7 +273,7 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
                /* check to make sure this item is what we want */
                if (found_key.objectid != key.objectid)
                        break;
-               if (btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY)
+               if (found_key.type != BTRFS_XATTR_ITEM_KEY)
                        break;
 
                di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
index b67d8fc81277675edb3fdb7beb9b8c4db2c919ad..759fa4e2de8fec28d3f6448456e1e12cec1add17 100644 (file)
@@ -33,8 +33,7 @@
 #include "compression.h"
 
 struct workspace {
-       z_stream inf_strm;
-       z_stream def_strm;
+       z_stream strm;
        char *buf;
        struct list_head list;
 };
@@ -43,8 +42,7 @@ static void zlib_free_workspace(struct list_head *ws)
 {
        struct workspace *workspace = list_entry(ws, struct workspace, list);
 
-       vfree(workspace->def_strm.workspace);
-       vfree(workspace->inf_strm.workspace);
+       vfree(workspace->strm.workspace);
        kfree(workspace->buf);
        kfree(workspace);
 }
@@ -52,17 +50,17 @@ static void zlib_free_workspace(struct list_head *ws)
 static struct list_head *zlib_alloc_workspace(void)
 {
        struct workspace *workspace;
+       int workspacesize;
 
        workspace = kzalloc(sizeof(*workspace), GFP_NOFS);
        if (!workspace)
                return ERR_PTR(-ENOMEM);
 
-       workspace->def_strm.workspace = vmalloc(zlib_deflate_workspacesize(
-                                               MAX_WBITS, MAX_MEM_LEVEL));
-       workspace->inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
+       workspacesize = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
+                       zlib_inflate_workspacesize());
+       workspace->strm.workspace = vmalloc(workspacesize);
        workspace->buf = kmalloc(PAGE_CACHE_SIZE, GFP_NOFS);
-       if (!workspace->def_strm.workspace ||
-           !workspace->inf_strm.workspace || !workspace->buf)
+       if (!workspace->strm.workspace || !workspace->buf)
                goto fail;
 
        INIT_LIST_HEAD(&workspace->list);
@@ -96,14 +94,14 @@ static int zlib_compress_pages(struct list_head *ws,
        *total_out = 0;
        *total_in = 0;
 
-       if (Z_OK != zlib_deflateInit(&workspace->def_strm, 3)) {
+       if (Z_OK != zlib_deflateInit(&workspace->strm, 3)) {
                printk(KERN_WARNING "BTRFS: deflateInit failed\n");
                ret = -EIO;
                goto out;
        }
 
-       workspace->def_strm.total_in = 0;
-       workspace->def_strm.total_out = 0;
+       workspace->strm.total_in = 0;
+       workspace->strm.total_out = 0;
 
        in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT);
        data_in = kmap(in_page);
@@ -117,25 +115,25 @@ static int zlib_compress_pages(struct list_head *ws,
        pages[0] = out_page;
        nr_pages = 1;
 
-       workspace->def_strm.next_in = data_in;
-       workspace->def_strm.next_out = cpage_out;
-       workspace->def_strm.avail_out = PAGE_CACHE_SIZE;
-       workspace->def_strm.avail_in = min(len, PAGE_CACHE_SIZE);
+       workspace->strm.next_in = data_in;
+       workspace->strm.next_out = cpage_out;
+       workspace->strm.avail_out = PAGE_CACHE_SIZE;
+       workspace->strm.avail_in = min(len, PAGE_CACHE_SIZE);
 
-       while (workspace->def_strm.total_in < len) {
-               ret = zlib_deflate(&workspace->def_strm, Z_SYNC_FLUSH);
+       while (workspace->strm.total_in < len) {
+               ret = zlib_deflate(&workspace->strm, Z_SYNC_FLUSH);
                if (ret != Z_OK) {
                        printk(KERN_DEBUG "BTRFS: deflate in loop returned %d\n",
                               ret);
-                       zlib_deflateEnd(&workspace->def_strm);
+                       zlib_deflateEnd(&workspace->strm);
                        ret = -EIO;
                        goto out;
                }
 
                /* we're making it bigger, give up */
-               if (workspace->def_strm.total_in > 8192 &&
-                   workspace->def_strm.total_in <
-                   workspace->def_strm.total_out) {
+               if (workspace->strm.total_in > 8192 &&
+                   workspace->strm.total_in <
+                   workspace->strm.total_out) {
                        ret = -E2BIG;
                        goto out;
                }
@@ -143,7 +141,7 @@ static int zlib_compress_pages(struct list_head *ws,
                 * before the total_in so we will pull in a new page for
                 * the stream end if required
                 */
-               if (workspace->def_strm.avail_out == 0) {
+               if (workspace->strm.avail_out == 0) {
                        kunmap(out_page);
                        if (nr_pages == nr_dest_pages) {
                                out_page = NULL;
@@ -158,19 +156,19 @@ static int zlib_compress_pages(struct list_head *ws,
                        cpage_out = kmap(out_page);
                        pages[nr_pages] = out_page;
                        nr_pages++;
-                       workspace->def_strm.avail_out = PAGE_CACHE_SIZE;
-                       workspace->def_strm.next_out = cpage_out;
+                       workspace->strm.avail_out = PAGE_CACHE_SIZE;
+                       workspace->strm.next_out = cpage_out;
                }
                /* we're all done */
-               if (workspace->def_strm.total_in >= len)
+               if (workspace->strm.total_in >= len)
                        break;
 
                /* we've read in a full page, get a new one */
-               if (workspace->def_strm.avail_in == 0) {
-                       if (workspace->def_strm.total_out > max_out)
+               if (workspace->strm.avail_in == 0) {
+                       if (workspace->strm.total_out > max_out)
                                break;
 
-                       bytes_left = len - workspace->def_strm.total_in;
+                       bytes_left = len - workspace->strm.total_in;
                        kunmap(in_page);
                        page_cache_release(in_page);
 
@@ -178,28 +176,28 @@ static int zlib_compress_pages(struct list_head *ws,
                        in_page = find_get_page(mapping,
                                                start >> PAGE_CACHE_SHIFT);
                        data_in = kmap(in_page);
-                       workspace->def_strm.avail_in = min(bytes_left,
+                       workspace->strm.avail_in = min(bytes_left,
                                                           PAGE_CACHE_SIZE);
-                       workspace->def_strm.next_in = data_in;
+                       workspace->strm.next_in = data_in;
                }
        }
-       workspace->def_strm.avail_in = 0;
-       ret = zlib_deflate(&workspace->def_strm, Z_FINISH);
-       zlib_deflateEnd(&workspace->def_strm);
+       workspace->strm.avail_in = 0;
+       ret = zlib_deflate(&workspace->strm, Z_FINISH);
+       zlib_deflateEnd(&workspace->strm);
 
        if (ret != Z_STREAM_END) {
                ret = -EIO;
                goto out;
        }
 
-       if (workspace->def_strm.total_out >= workspace->def_strm.total_in) {
+       if (workspace->strm.total_out >= workspace->strm.total_in) {
                ret = -E2BIG;
                goto out;
        }
 
        ret = 0;
-       *total_out = workspace->def_strm.total_out;
-       *total_in = workspace->def_strm.total_in;
+       *total_out = workspace->strm.total_out;
+       *total_in = workspace->strm.total_in;
 out:
        *out_pages = nr_pages;
        if (out_page)
@@ -225,19 +223,18 @@ static int zlib_decompress_biovec(struct list_head *ws, struct page **pages_in,
        size_t total_out = 0;
        unsigned long page_in_index = 0;
        unsigned long page_out_index = 0;
-       unsigned long total_pages_in = (srclen + PAGE_CACHE_SIZE - 1) /
-                                       PAGE_CACHE_SIZE;
+       unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_CACHE_SIZE);
        unsigned long buf_start;
        unsigned long pg_offset;
 
        data_in = kmap(pages_in[page_in_index]);
-       workspace->inf_strm.next_in = data_in;
-       workspace->inf_strm.avail_in = min_t(size_t, srclen, PAGE_CACHE_SIZE);
-       workspace->inf_strm.total_in = 0;
+       workspace->strm.next_in = data_in;
+       workspace->strm.avail_in = min_t(size_t, srclen, PAGE_CACHE_SIZE);
+       workspace->strm.total_in = 0;
 
-       workspace->inf_strm.total_out = 0;
-       workspace->inf_strm.next_out = workspace->buf;
-       workspace->inf_strm.avail_out = PAGE_CACHE_SIZE;
+       workspace->strm.total_out = 0;
+       workspace->strm.next_out = workspace->buf;
+       workspace->strm.avail_out = PAGE_CACHE_SIZE;
        pg_offset = 0;
 
        /* If it's deflate, and it's got no preset dictionary, then
@@ -247,21 +244,21 @@ static int zlib_decompress_biovec(struct list_head *ws, struct page **pages_in,
            !(((data_in[0]<<8) + data_in[1]) % 31)) {
 
                wbits = -((data_in[0] >> 4) + 8);
-               workspace->inf_strm.next_in += 2;
-               workspace->inf_strm.avail_in -= 2;
+               workspace->strm.next_in += 2;
+               workspace->strm.avail_in -= 2;
        }
 
-       if (Z_OK != zlib_inflateInit2(&workspace->inf_strm, wbits)) {
+       if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) {
                printk(KERN_WARNING "BTRFS: inflateInit failed\n");
                return -EIO;
        }
-       while (workspace->inf_strm.total_in < srclen) {
-               ret = zlib_inflate(&workspace->inf_strm, Z_NO_FLUSH);
+       while (workspace->strm.total_in < srclen) {
+               ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH);
                if (ret != Z_OK && ret != Z_STREAM_END)
                        break;
 
                buf_start = total_out;
-               total_out = workspace->inf_strm.total_out;
+               total_out = workspace->strm.total_out;
 
                /* we didn't make progress in this inflate call, we're done */
                if (buf_start == total_out)
@@ -276,10 +273,10 @@ static int zlib_decompress_biovec(struct list_head *ws, struct page **pages_in,
                        goto done;
                }
 
-               workspace->inf_strm.next_out = workspace->buf;
-               workspace->inf_strm.avail_out = PAGE_CACHE_SIZE;
+               workspace->strm.next_out = workspace->buf;
+               workspace->strm.avail_out = PAGE_CACHE_SIZE;
 
-               if (workspace->inf_strm.avail_in == 0) {
+               if (workspace->strm.avail_in == 0) {
                        unsigned long tmp;
                        kunmap(pages_in[page_in_index]);
                        page_in_index++;
@@ -288,9 +285,9 @@ static int zlib_decompress_biovec(struct list_head *ws, struct page **pages_in,
                                break;
                        }
                        data_in = kmap(pages_in[page_in_index]);
-                       workspace->inf_strm.next_in = data_in;
-                       tmp = srclen - workspace->inf_strm.total_in;
-                       workspace->inf_strm.avail_in = min(tmp,
+                       workspace->strm.next_in = data_in;
+                       tmp = srclen - workspace->strm.total_in;
+                       workspace->strm.avail_in = min(tmp,
                                                           PAGE_CACHE_SIZE);
                }
        }
@@ -299,7 +296,7 @@ static int zlib_decompress_biovec(struct list_head *ws, struct page **pages_in,
        else
                ret = 0;
 done:
-       zlib_inflateEnd(&workspace->inf_strm);
+       zlib_inflateEnd(&workspace->strm);
        if (data_in)
                kunmap(pages_in[page_in_index]);
        return ret;
@@ -317,13 +314,13 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in,
        unsigned long total_out = 0;
        char *kaddr;
 
-       workspace->inf_strm.next_in = data_in;
-       workspace->inf_strm.avail_in = srclen;
-       workspace->inf_strm.total_in = 0;
+       workspace->strm.next_in = data_in;
+       workspace->strm.avail_in = srclen;
+       workspace->strm.total_in = 0;
 
-       workspace->inf_strm.next_out = workspace->buf;
-       workspace->inf_strm.avail_out = PAGE_CACHE_SIZE;
-       workspace->inf_strm.total_out = 0;
+       workspace->strm.next_out = workspace->buf;
+       workspace->strm.avail_out = PAGE_CACHE_SIZE;
+       workspace->strm.total_out = 0;
        /* If it's deflate, and it's got no preset dictionary, then
           we can tell zlib to skip the adler32 check. */
        if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
@@ -331,11 +328,11 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in,
            !(((data_in[0]<<8) + data_in[1]) % 31)) {
 
                wbits = -((data_in[0] >> 4) + 8);
-               workspace->inf_strm.next_in += 2;
-               workspace->inf_strm.avail_in -= 2;
+               workspace->strm.next_in += 2;
+               workspace->strm.avail_in -= 2;
        }
 
-       if (Z_OK != zlib_inflateInit2(&workspace->inf_strm, wbits)) {
+       if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) {
                printk(KERN_WARNING "BTRFS: inflateInit failed\n");
                return -EIO;
        }
@@ -346,12 +343,12 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in,
                unsigned long bytes;
                unsigned long pg_offset = 0;
 
-               ret = zlib_inflate(&workspace->inf_strm, Z_NO_FLUSH);
+               ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH);
                if (ret != Z_OK && ret != Z_STREAM_END)
                        break;
 
                buf_start = total_out;
-               total_out = workspace->inf_strm.total_out;
+               total_out = workspace->strm.total_out;
 
                if (total_out == buf_start) {
                        ret = -EIO;
@@ -377,8 +374,8 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in,
                pg_offset += bytes;
                bytes_left -= bytes;
 next:
-               workspace->inf_strm.next_out = workspace->buf;
-               workspace->inf_strm.avail_out = PAGE_CACHE_SIZE;
+               workspace->strm.next_out = workspace->buf;
+               workspace->strm.avail_out = PAGE_CACHE_SIZE;
        }
 
        if (ret != Z_STREAM_END && bytes_left != 0)
@@ -386,7 +383,7 @@ next:
        else
                ret = 0;
 
-       zlib_inflateEnd(&workspace->inf_strm);
+       zlib_inflateEnd(&workspace->strm);
        return ret;
 }
 
index 3588a80854b27acb18d185d437662d03668eddae..44c14a87750e7780ecb34468e60ed77b61628bf3 100644 (file)
@@ -1253,7 +1253,7 @@ static struct buffer_head *__bread_slow(struct buffer_head *bh)
  * a local interrupt disable for that.
  */
 
-#define BH_LRU_SIZE    8
+#define BH_LRU_SIZE    16
 
 struct bh_lru {
        struct buffer_head *bhs[BH_LRU_SIZE];
@@ -2956,7 +2956,7 @@ static void end_bio_bh_io_sync(struct bio *bio, int err)
 
 /*
  * This allows us to do IO even on the odd last sectors
- * of a device, even if the bh block size is some multiple
+ * of a device, even if the block size is some multiple
  * of the physical sector size.
  *
  * We'll just truncate the bio to the size of the device,
@@ -2966,10 +2966,11 @@ static void end_bio_bh_io_sync(struct bio *bio, int err)
  * errors, this only handles the "we need to be able to
  * do IO at the final sector" case.
  */
-static void guard_bh_eod(int rw, struct bio *bio, struct buffer_head *bh)
+void guard_bio_eod(int rw, struct bio *bio)
 {
        sector_t maxsector;
-       unsigned bytes;
+       struct bio_vec *bvec = &bio->bi_io_vec[bio->bi_vcnt - 1];
+       unsigned truncated_bytes;
 
        maxsector = i_size_read(bio->bi_bdev->bd_inode) >> 9;
        if (!maxsector)
@@ -2984,23 +2985,20 @@ static void guard_bh_eod(int rw, struct bio *bio, struct buffer_head *bh)
                return;
 
        maxsector -= bio->bi_iter.bi_sector;
-       bytes = bio->bi_iter.bi_size;
-       if (likely((bytes >> 9) <= maxsector))
+       if (likely((bio->bi_iter.bi_size >> 9) <= maxsector))
                return;
 
-       /* Uhhuh. We've got a bh that straddles the device size! */
-       bytes = maxsector << 9;
+       /* Uhhuh. We've got a bio that straddles the device size! */
+       truncated_bytes = bio->bi_iter.bi_size - (maxsector << 9);
 
        /* Truncate the bio.. */
-       bio->bi_iter.bi_size bytes;
-       bio->bi_io_vec[0].bv_len = bytes;
+       bio->bi_iter.bi_size -= truncated_bytes;
+       bvec->bv_len -= truncated_bytes;
 
        /* ..and clear the end of the buffer for reads */
        if ((rw & RW_MASK) == READ) {
-               void *kaddr = kmap_atomic(bh->b_page);
-               memset(kaddr + bh_offset(bh) + bytes, 0, bh->b_size - bytes);
-               kunmap_atomic(kaddr);
-               flush_dcache_page(bh->b_page);
+               zero_user(bvec->bv_page, bvec->bv_offset + bvec->bv_len,
+                               truncated_bytes);
        }
 }
 
@@ -3041,7 +3039,7 @@ int _submit_bh(int rw, struct buffer_head *bh, unsigned long bio_flags)
        bio->bi_flags |= bio_flags;
 
        /* Take care of bh's that straddle the end of the device */
-       guard_bh_eod(rw, bio, bh);
+       guard_bio_eod(rw, bio);
 
        if (buffer_meta(bh))
                rw |= REQ_META;
index 889b9845575079517e92ee387fe5c863751c3848..9d7996e8e7932bc5ad386c894e7955975394490c 100644 (file)
@@ -813,7 +813,8 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
        return generic_file_llseek(file, offset, whence);
 }
 
-static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
+static int
+cifs_setlease(struct file *file, long arg, struct file_lock **lease, void **priv)
 {
        /*
         * Note that this is called by vfs setlease with i_lock held to
@@ -829,7 +830,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
        if (arg == F_UNLCK ||
            ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) ||
            ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode))))
-               return generic_setlease(file, arg, lease);
+               return generic_setlease(file, arg, lease, priv);
        else if (tlink_tcon(cfile->tlink)->local_lease &&
                 !CIFS_CACHE_READ(CIFS_I(inode)))
                /*
@@ -840,7 +841,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
                 * knows that the file won't be changed on the server by anyone
                 * else.
                 */
-               return generic_setlease(file, arg, lease);
+               return generic_setlease(file, arg, lease, priv);
        else
                return -EAGAIN;
 }
index f704458ea5f59a78d8b1121508cc9f22acfe6e45..e0ab3a93eeff01b42ec4ffaeb48160bcd0fc9ad6 100644 (file)
@@ -30,7 +30,7 @@ struct plock_op {
 
 struct plock_xop {
        struct plock_op xop;
-       void *callback;
+       int (*callback)(struct file_lock *fl, int result);
        void *fl;
        void *file;
        struct file_lock flc;
@@ -190,7 +190,7 @@ static int dlm_plock_callback(struct plock_op *op)
        struct file *file;
        struct file_lock *fl;
        struct file_lock *flc;
-       int (*notify)(void *, void *, int) = NULL;
+       int (*notify)(struct file_lock *fl, int result) = NULL;
        struct plock_xop *xop = (struct plock_xop *)op;
        int rv = 0;
 
@@ -209,7 +209,7 @@ static int dlm_plock_callback(struct plock_op *op)
        notify = xop->callback;
 
        if (op->info.rv) {
-               notify(fl, NULL, op->info.rv);
+               notify(fl, op->info.rv);
                goto out;
        }
 
@@ -228,7 +228,7 @@ static int dlm_plock_callback(struct plock_op *op)
                          (unsigned long long)op->info.number, file, fl);
        }
 
-       rv = notify(fl, NULL, 0);
+       rv = notify(fl, 0);
        if (rv) {
                /* XXX: We need to cancel the fs lock here: */
                log_print("dlm_plock_callback: lock granted after lock request "
index db0fad3269c0395f230c39cc0fcd6c9975f6d633..b4b6ab9873ae420a61cf800224253f7f91a62653 100644 (file)
@@ -229,8 +229,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
        if (rc) {
                printk(KERN_ERR "%s: Error attempting to initialize "
                        "the lower file for the dentry with name "
-                       "[%s]; rc = [%d]\n", __func__,
-                       ecryptfs_dentry->d_name.name, rc);
+                       "[%pd]; rc = [%d]\n", __func__,
+                       ecryptfs_dentry, rc);
                goto out_free;
        }
        if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_ACCMODE)
index d4a9431ec73ce0cb215fc3b6e52727b4059f9528..1686dc2da9fd7627df7fea9035262ca900b5676e 100644 (file)
@@ -53,9 +53,7 @@ static void unlock_dir(struct dentry *dir)
 
 static int ecryptfs_inode_test(struct inode *inode, void *lower_inode)
 {
-       if (ecryptfs_inode_to_lower(inode) == (struct inode *)lower_inode)
-               return 1;
-       return 0;
+       return ecryptfs_inode_to_lower(inode) == lower_inode;
 }
 
 static int ecryptfs_inode_set(struct inode *inode, void *opaque)
@@ -192,12 +190,6 @@ ecryptfs_do_create(struct inode *directory_inode,
 
        lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
        lower_dir_dentry = lock_parent(lower_dentry);
-       if (IS_ERR(lower_dir_dentry)) {
-               ecryptfs_printk(KERN_ERR, "Error locking directory of "
-                               "dentry\n");
-               inode = ERR_CAST(lower_dir_dentry);
-               goto out;
-       }
        rc = vfs_create(lower_dir_dentry->d_inode, lower_dentry, mode, true);
        if (rc) {
                printk(KERN_ERR "%s: Failure to create dentry in lower fs; "
@@ -215,7 +207,6 @@ ecryptfs_do_create(struct inode *directory_inode,
        fsstack_copy_inode_size(directory_inode, lower_dir_dentry->d_inode);
 out_lock:
        unlock_dir(lower_dir_dentry);
-out:
        return inode;
 }
 
@@ -250,8 +241,8 @@ int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,
        if (rc) {
                printk(KERN_ERR "%s: Error attempting to initialize "
                        "the lower file for the dentry with name "
-                       "[%s]; rc = [%d]\n", __func__,
-                       ecryptfs_dentry->d_name.name, rc);
+                       "[%pd]; rc = [%d]\n", __func__,
+                       ecryptfs_dentry, rc);
                goto out;
        }
        rc = ecryptfs_write_metadata(ecryptfs_dentry, ecryptfs_inode);
@@ -313,8 +304,8 @@ static int ecryptfs_i_size_read(struct dentry *dentry, struct inode *inode)
        if (rc) {
                printk(KERN_ERR "%s: Error attempting to initialize "
                        "the lower file for the dentry with name "
-                       "[%s]; rc = [%d]\n", __func__,
-                       dentry->d_name.name, rc);
+                       "[%pd]; rc = [%d]\n", __func__,
+                       dentry, rc);
                return rc;
        }
 
@@ -418,8 +409,8 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
        if (IS_ERR(lower_dentry)) {
                rc = PTR_ERR(lower_dentry);
                ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
-                               "[%d] on lower_dentry = [%s]\n", __func__, rc,
-                               ecryptfs_dentry->d_name.name);
+                               "[%d] on lower_dentry = [%pd]\n", __func__, rc,
+                               ecryptfs_dentry);
                goto out;
        }
        if (lower_dentry->d_inode)
@@ -1039,7 +1030,7 @@ ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
        }
 
        rc = vfs_setxattr(lower_dentry, name, value, size, flags);
-       if (!rc)
+       if (!rc && dentry->d_inode)
                fsstack_copy_attr_all(dentry->d_inode, lower_dentry->d_inode);
 out:
        return rc;
index 4725a07f003cf3279fa81813748442afc24a053c..635e8e16a5b7692b1d45b7629d404beeafdb8567 100644 (file)
@@ -26,7 +26,6 @@
  */
 
 #include <linux/string.h>
-#include <linux/syscalls.h>
 #include <linux/pagemap.h>
 #include <linux/key.h>
 #include <linux/random.h>
@@ -1846,7 +1845,6 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
                                        "(Tag 11 not allowed by itself)\n");
                        rc = -EIO;
                        goto out_wipe_list;
-                       break;
                default:
                        ecryptfs_printk(KERN_DEBUG, "No packet at offset [%zd] "
                                        "of the file header; hex value of "
index e57380e5f6bd78fb983b10e292919d29eba19abc..286f10b0363b15f05a561565f27caa45a46ee9fd 100644 (file)
@@ -434,8 +434,7 @@ void ecryptfs_release_messaging(void)
                mutex_lock(&ecryptfs_msg_ctx_lists_mux);
                for (i = 0; i < ecryptfs_message_buf_len; i++) {
                        mutex_lock(&ecryptfs_msg_ctx_arr[i].mux);
-                       if (ecryptfs_msg_ctx_arr[i].msg)
-                               kfree(ecryptfs_msg_ctx_arr[i].msg);
+                       kfree(ecryptfs_msg_ctx_arr[i].msg);
                        mutex_unlock(&ecryptfs_msg_ctx_arr[i].mux);
                }
                kfree(ecryptfs_msg_ctx_arr);
index b88edc05c2300871ea70ed16b6c42ece6b3dcf2f..170dc41e8bf4418f8de7840c12d46c6f6483e4eb 100644 (file)
@@ -1067,14 +1067,14 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
        ext2_rsv_window_add(sb, &sbi->s_rsv_window_head);
 
        err = percpu_counter_init(&sbi->s_freeblocks_counter,
-                               ext2_count_free_blocks(sb));
+                               ext2_count_free_blocks(sb), GFP_KERNEL);
        if (!err) {
                err = percpu_counter_init(&sbi->s_freeinodes_counter,
-                               ext2_count_free_inodes(sb));
+                               ext2_count_free_inodes(sb), GFP_KERNEL);
        }
        if (!err) {
                err = percpu_counter_init(&sbi->s_dirs_counter,
-                               ext2_count_dirs(sb));
+                               ext2_count_dirs(sb), GFP_KERNEL);
        }
        if (err) {
                ext2_msg(sb, KERN_ERR, "error: insufficient memory");
index e85ff15a060e7dcb220c4170f8455f7d1c96cba2..fc3cdcf24aedab36e7d31b01bbdc251d8b064e75 100644 (file)
@@ -237,6 +237,8 @@ struct ext3_new_group_data {
 #define EXT3_IOC32_GETVERSION_OLD      FS_IOC32_GETVERSION
 #define EXT3_IOC32_SETVERSION_OLD      FS_IOC32_SETVERSION
 
+/* Number of supported quota types */
+#define EXT3_MAXQUOTAS 2
 
 /*
  *  Mount options
@@ -248,7 +250,7 @@ struct ext3_mount_options {
        unsigned long s_commit_interval;
 #ifdef CONFIG_QUOTA
        int s_jquota_fmt;
-       char *s_qf_names[MAXQUOTAS];
+       char *s_qf_names[EXT3_MAXQUOTAS];
 #endif
 };
 
@@ -669,7 +671,7 @@ struct ext3_sb_info {
        unsigned long s_commit_interval;
        struct block_device *journal_bdev;
 #ifdef CONFIG_QUOTA
-       char *s_qf_names[MAXQUOTAS];            /* Names of quota files with journalled quota */
+       char *s_qf_names[EXT3_MAXQUOTAS];       /* Names of quota files with journalled quota */
        int s_jquota_fmt;                       /* Format of quota to use */
 #endif
 };
@@ -1183,9 +1185,9 @@ extern const struct inode_operations ext3_fast_symlink_inode_operations;
 #define EXT3_QUOTA_INIT_BLOCKS(sb) 0
 #define EXT3_QUOTA_DEL_BLOCKS(sb) 0
 #endif
-#define EXT3_MAXQUOTAS_TRANS_BLOCKS(sb) (MAXQUOTAS*EXT3_QUOTA_TRANS_BLOCKS(sb))
-#define EXT3_MAXQUOTAS_INIT_BLOCKS(sb) (MAXQUOTAS*EXT3_QUOTA_INIT_BLOCKS(sb))
-#define EXT3_MAXQUOTAS_DEL_BLOCKS(sb) (MAXQUOTAS*EXT3_QUOTA_DEL_BLOCKS(sb))
+#define EXT3_MAXQUOTAS_TRANS_BLOCKS(sb) (EXT3_MAXQUOTAS*EXT3_QUOTA_TRANS_BLOCKS(sb))
+#define EXT3_MAXQUOTAS_INIT_BLOCKS(sb) (EXT3_MAXQUOTAS*EXT3_QUOTA_INIT_BLOCKS(sb))
+#define EXT3_MAXQUOTAS_DEL_BLOCKS(sb) (EXT3_MAXQUOTAS*EXT3_QUOTA_DEL_BLOCKS(sb))
 
 int
 ext3_mark_iloc_dirty(handle_t *handle,
index 622e8824902432e5aae886ce68dca464f504fda3..7015db0bafd1b0c3179e6a9eb8de8c1e06465751 100644 (file)
@@ -441,7 +441,7 @@ static void ext3_put_super (struct super_block * sb)
        percpu_counter_destroy(&sbi->s_dirs_counter);
        brelse(sbi->s_sbh);
 #ifdef CONFIG_QUOTA
-       for (i = 0; i < MAXQUOTAS; i++)
+       for (i = 0; i < EXT3_MAXQUOTAS; i++)
                kfree(sbi->s_qf_names[i]);
 #endif
 
@@ -1555,7 +1555,7 @@ static void ext3_orphan_cleanup (struct super_block * sb,
        /* Needed for iput() to work correctly and not trash data */
        sb->s_flags |= MS_ACTIVE;
        /* Turn on quotas so that they are updated correctly */
-       for (i = 0; i < MAXQUOTAS; i++) {
+       for (i = 0; i < EXT3_MAXQUOTAS; i++) {
                if (EXT3_SB(sb)->s_qf_names[i]) {
                        int ret = ext3_quota_on_mount(sb, i);
                        if (ret < 0)
@@ -1606,7 +1606,7 @@ static void ext3_orphan_cleanup (struct super_block * sb,
                       PLURAL(nr_truncates));
 #ifdef CONFIG_QUOTA
        /* Turn quotas off */
-       for (i = 0; i < MAXQUOTAS; i++) {
+       for (i = 0; i < EXT3_MAXQUOTAS; i++) {
                if (sb_dqopt(sb)->files[i])
                        dquot_quota_off(sb, i);
        }
@@ -2039,14 +2039,14 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
                goto failed_mount2;
        }
        err = percpu_counter_init(&sbi->s_freeblocks_counter,
-                       ext3_count_free_blocks(sb));
+                       ext3_count_free_blocks(sb), GFP_KERNEL);
        if (!err) {
                err = percpu_counter_init(&sbi->s_freeinodes_counter,
-                               ext3_count_free_inodes(sb));
+                               ext3_count_free_inodes(sb), GFP_KERNEL);
        }
        if (!err) {
                err = percpu_counter_init(&sbi->s_dirs_counter,
-                               ext3_count_dirs(sb));
+                               ext3_count_dirs(sb), GFP_KERNEL);
        }
        if (err) {
                ext3_msg(sb, KERN_ERR, "error: insufficient memory");
@@ -2139,7 +2139,7 @@ failed_mount2:
        kfree(sbi->s_group_desc);
 failed_mount:
 #ifdef CONFIG_QUOTA
-       for (i = 0; i < MAXQUOTAS; i++)
+       for (i = 0; i < EXT3_MAXQUOTAS; i++)
                kfree(sbi->s_qf_names[i]);
 #endif
        ext3_blkdev_remove(sbi);
@@ -2659,7 +2659,7 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
        old_opts.s_commit_interval = sbi->s_commit_interval;
 #ifdef CONFIG_QUOTA
        old_opts.s_jquota_fmt = sbi->s_jquota_fmt;
-       for (i = 0; i < MAXQUOTAS; i++)
+       for (i = 0; i < EXT3_MAXQUOTAS; i++)
                if (sbi->s_qf_names[i]) {
                        old_opts.s_qf_names[i] = kstrdup(sbi->s_qf_names[i],
                                                         GFP_KERNEL);
@@ -2763,7 +2763,7 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
        }
 #ifdef CONFIG_QUOTA
        /* Release old quota file names */
-       for (i = 0; i < MAXQUOTAS; i++)
+       for (i = 0; i < EXT3_MAXQUOTAS; i++)
                kfree(old_opts.s_qf_names[i]);
 #endif
        if (enable_quota)
@@ -2777,7 +2777,7 @@ restore_opts:
        sbi->s_commit_interval = old_opts.s_commit_interval;
 #ifdef CONFIG_QUOTA
        sbi->s_jquota_fmt = old_opts.s_jquota_fmt;
-       for (i = 0; i < MAXQUOTAS; i++) {
+       for (i = 0; i < EXT3_MAXQUOTAS; i++) {
                kfree(sbi->s_qf_names[i]);
                sbi->s_qf_names[i] = old_opts.s_qf_names[i];
        }
index 0b28b36e7915ccf2e440c8782c618473f31d9576..05c159218bc267431ee4e7a865a0e20a5867c949 100644 (file)
@@ -3892,7 +3892,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        /* Register extent status tree shrinker */
        ext4_es_register_shrinker(sbi);
 
-       if ((err = percpu_counter_init(&sbi->s_extent_cache_cnt, 0)) != 0) {
+       err = percpu_counter_init(&sbi->s_extent_cache_cnt, 0, GFP_KERNEL);
+       if (err) {
                ext4_msg(sb, KERN_ERR, "insufficient memory");
                goto failed_mount3;
        }
@@ -4106,17 +4107,20 @@ no_journal:
        block = ext4_count_free_clusters(sb);
        ext4_free_blocks_count_set(sbi->s_es, 
                                   EXT4_C2B(sbi, block));
-       err = percpu_counter_init(&sbi->s_freeclusters_counter, block);
+       err = percpu_counter_init(&sbi->s_freeclusters_counter, block,
+                                 GFP_KERNEL);
        if (!err) {
                unsigned long freei = ext4_count_free_inodes(sb);
                sbi->s_es->s_free_inodes_count = cpu_to_le32(freei);
-               err = percpu_counter_init(&sbi->s_freeinodes_counter, freei);
+               err = percpu_counter_init(&sbi->s_freeinodes_counter, freei,
+                                         GFP_KERNEL);
        }
        if (!err)
                err = percpu_counter_init(&sbi->s_dirs_counter,
-                                         ext4_count_dirs(sb));
+                                         ext4_count_dirs(sb), GFP_KERNEL);
        if (!err)
-               err = percpu_counter_init(&sbi->s_dirtyclusters_counter, 0);
+               err = percpu_counter_init(&sbi->s_dirtyclusters_counter, 0,
+                                         GFP_KERNEL);
        if (err) {
                ext4_msg(sb, KERN_ERR, "insufficient memory");
                goto failed_mount6;
index 22d1c3df61acfa61ae957ede4aedfa01a3e9ab7a..99d440a4a6ba259e5bd7ec6b167dbedb2637ac5d 100644 (file)
@@ -98,26 +98,19 @@ static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
        write_unlock_irq(&filp->f_owner.lock);
 }
 
-int __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
+void __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
                int force)
 {
-       int err;
-
-       err = security_file_set_fowner(filp);
-       if (err)
-               return err;
-
+       security_file_set_fowner(filp);
        f_modown(filp, pid, type, force);
-       return 0;
 }
 EXPORT_SYMBOL(__f_setown);
 
-int f_setown(struct file *filp, unsigned long arg, int force)
+void f_setown(struct file *filp, unsigned long arg, int force)
 {
        enum pid_type type;
        struct pid *pid;
        int who = arg;
-       int result;
        type = PIDTYPE_PID;
        if (who < 0) {
                type = PIDTYPE_PGID;
@@ -125,9 +118,8 @@ int f_setown(struct file *filp, unsigned long arg, int force)
        }
        rcu_read_lock();
        pid = find_vpid(who);
-       result = __f_setown(filp, pid, type, force);
+       __f_setown(filp, pid, type, force);
        rcu_read_unlock();
-       return result;
 }
 EXPORT_SYMBOL(f_setown);
 
@@ -181,7 +173,7 @@ static int f_setown_ex(struct file *filp, unsigned long arg)
        if (owner.pid && !pid)
                ret = -ESRCH;
        else
-               ret = __f_setown(filp, pid, type, 1);
+                __f_setown(filp, pid, type, 1);
        rcu_read_unlock();
 
        return ret;
@@ -302,7 +294,8 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
                force_successful_syscall_return();
                break;
        case F_SETOWN:
-               err = f_setown(filp, arg, 1);
+               f_setown(filp, arg, 1);
+               err = 0;
                break;
        case F_GETOWN_EX:
                err = f_getown_ex(filp, arg);
index 385bfd31512a17f4e4c6869a3ee8f32c456cd327..0bab12b2046009e0dd53fcc1a834c0a153b1f7cd 100644 (file)
@@ -331,5 +331,5 @@ void __init files_init(unsigned long mempages)
 
        n = (mempages * (PAGE_SIZE / 1024)) / 10;
        files_stat.max_files = max_t(unsigned long, n, NR_FILE);
-       percpu_counter_init(&nr_files, 0);
+       percpu_counter_init(&nr_files, 0, GFP_KERNEL);
 } 
index 1a349f9a9685a88c7dc67141ed0cee0c2374fec9..5d4261ff5d23ac98e83498e17fdcd3a7b7fa0d06 100644 (file)
@@ -2100,8 +2100,13 @@ int gfs2_diradd_alloc_required(struct inode *inode, const struct qstr *name,
        }
        if (IS_ERR(dent))
                return PTR_ERR(dent);
-       da->bh = bh;
-       da->dent = dent;
+
+       if (da->save_loc) {
+               da->bh = bh;
+               da->dent = dent;
+       } else {
+               brelse(bh);
+       }
        return 0;
 }
 
index 126c65dda0284080eb71a2eec146782745c8ee63..e1b309c24dab3417c513daf58bab3dd6f73ea884 100644 (file)
@@ -23,6 +23,7 @@ struct gfs2_diradd {
        unsigned nr_blocks;
        struct gfs2_dirent *dent;
        struct buffer_head *bh;
+       int save_loc;
 };
 
 extern struct inode *gfs2_dir_search(struct inode *dir,
index 7f4ed3daa38c4a190132c1c1fa6ce1b1877e0b94..80dd44dca028f97ec343e3c87727be806174094b 100644 (file)
@@ -913,26 +913,6 @@ out_uninit:
 
 #ifdef CONFIG_GFS2_FS_LOCKING_DLM
 
-/**
- * gfs2_setlease - acquire/release a file lease
- * @file: the file pointer
- * @arg: lease type
- * @fl: file lock
- *
- * We don't currently have a way to enforce a lease across the whole
- * cluster; until we do, disable leases (by just returning -EINVAL),
- * unless the administrator has requested purely local locking.
- *
- * Locking: called under i_lock
- *
- * Returns: errno
- */
-
-static int gfs2_setlease(struct file *file, long arg, struct file_lock **fl)
-{
-       return -EINVAL;
-}
-
 /**
  * gfs2_lock - acquire/release a posix lock on a file
  * @file: the file pointer
@@ -1078,7 +1058,7 @@ const struct file_operations gfs2_file_fops = {
        .flock          = gfs2_flock,
        .splice_read    = generic_file_splice_read,
        .splice_write   = iter_file_splice_write,
-       .setlease       = gfs2_setlease,
+       .setlease       = simple_nosetlease,
        .fallocate      = gfs2_fallocate,
 };
 
index 7f513b1ceb2c7adc024b2dee16321a5c4d06b733..8f0c19d1d9439e3827ffd649e7f7d43f096afc89 100644 (file)
@@ -811,7 +811,7 @@ void gfs2_holder_init(struct gfs2_glock *gl, unsigned int state, unsigned flags,
 {
        INIT_LIST_HEAD(&gh->gh_list);
        gh->gh_gl = gl;
-       gh->gh_ip = (unsigned long)__builtin_return_address(0);
+       gh->gh_ip = _RET_IP_;
        gh->gh_owner_pid = get_pid(task_pid(current));
        gh->gh_state = state;
        gh->gh_flags = flags;
@@ -835,7 +835,7 @@ void gfs2_holder_reinit(unsigned int state, unsigned flags, struct gfs2_holder *
        gh->gh_state = state;
        gh->gh_flags = flags;
        gh->gh_iflags = 0;
-       gh->gh_ip = (unsigned long)__builtin_return_address(0);
+       gh->gh_ip = _RET_IP_;
        if (gh->gh_owner_pid)
                put_pid(gh->gh_owner_pid);
        gh->gh_owner_pid = get_pid(task_pid(current));
index 2ffc67dce87f268d0b5824414927c6a1bae90513..1cc0bba6313f21216202d81f8fc1bb283496003a 100644 (file)
@@ -93,7 +93,7 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
          * tr->alloced is not set since the transaction structure is
          * on the stack */
        tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64));
-       tr.tr_ip = (unsigned long)__builtin_return_address(0);
+       tr.tr_ip = _RET_IP_;
        sb_start_intwrite(sdp->sd_vfs);
        if (gfs2_log_reserve(sdp, tr.tr_reserved) < 0) {
                sb_end_intwrite(sdp->sd_vfs);
index fc8ac2ee0667c8d66082aa48f05f9acee452a8ac..fcf42eadb69ceaea5644d93e673a82cb2a265f44 100644 (file)
@@ -600,7 +600,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
        int error, free_vfs_inode = 0;
        u32 aflags = 0;
        unsigned blocks = 1;
-       struct gfs2_diradd da = { .bh = NULL, };
+       struct gfs2_diradd da = { .bh = NULL, .save_loc = 1, };
 
        if (!name->len || name->len > GFS2_FNAMESIZE)
                return -ENAMETOOLONG;
@@ -672,6 +672,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
        inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
        gfs2_set_inode_blocks(inode, 1);
        munge_mode_uid_gid(dip, inode);
+       check_and_update_goal(dip);
        ip->i_goal = dip->i_goal;
        ip->i_diskflags = 0;
        ip->i_eattr = 0;
@@ -899,7 +900,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_holder ghs[2];
        struct buffer_head *dibh;
-       struct gfs2_diradd da = { .bh = NULL, };
+       struct gfs2_diradd da = { .bh = NULL, .save_loc = 1, };
        int error;
 
        if (S_ISDIR(inode->i_mode))
@@ -1337,7 +1338,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
        struct gfs2_rgrpd *nrgd;
        unsigned int num_gh;
        int dir_rename = 0;
-       struct gfs2_diradd da = { .nr_blocks = 0, };
+       struct gfs2_diradd da = { .nr_blocks = 0, .save_loc = 0, };
        unsigned int x;
        int error;
 
index f4cb9c0d6bbdce4bdfc4a82b20aa7aa2ac119829..7474c413ffd1e2c8da3d4396f3bbc31a0175805d 100644 (file)
@@ -577,6 +577,13 @@ struct gfs2_rgrpd *gfs2_rgrpd_get_next(struct gfs2_rgrpd *rgd)
        return rgd;
 }
 
+void check_and_update_goal(struct gfs2_inode *ip)
+{
+       struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
+       if (!ip->i_goal || gfs2_blk2rgrpd(sdp, ip->i_goal, 1) == NULL)
+               ip->i_goal = ip->i_no_addr;
+}
+
 void gfs2_free_clones(struct gfs2_rgrpd *rgd)
 {
        int x;
@@ -1910,6 +1917,7 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, const struct gfs2_alloc_parms *a
        } else if (ip->i_rgd && rgrp_contains_block(ip->i_rgd, ip->i_goal)) {
                rs->rs_rbm.rgd = begin = ip->i_rgd;
        } else {
+               check_and_update_goal(ip);
                rs->rs_rbm.rgd = begin = gfs2_blk2rgrpd(sdp, ip->i_goal, 1);
        }
        if (S_ISDIR(ip->i_inode.i_mode) && (ap->aflags & GFS2_AF_ORLOV))
@@ -2089,7 +2097,7 @@ static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart,
                                     u32 blen, unsigned char new_state)
 {
        struct gfs2_rbm rbm;
-       struct gfs2_bitmap *bi;
+       struct gfs2_bitmap *bi, *bi_prev = NULL;
 
        rbm.rgd = gfs2_blk2rgrpd(sdp, bstart, 1);
        if (!rbm.rgd) {
@@ -2098,18 +2106,22 @@ static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart,
                return NULL;
        }
 
+       gfs2_rbm_from_block(&rbm, bstart);
        while (blen--) {
-               gfs2_rbm_from_block(&rbm, bstart);
                bi = rbm_bi(&rbm);
-               bstart++;
-               if (!bi->bi_clone) {
-                       bi->bi_clone = kmalloc(bi->bi_bh->b_size,
-                                              GFP_NOFS | __GFP_NOFAIL);
-                       memcpy(bi->bi_clone + bi->bi_offset,
-                              bi->bi_bh->b_data + bi->bi_offset, bi->bi_len);
+               if (bi != bi_prev) {
+                       if (!bi->bi_clone) {
+                               bi->bi_clone = kmalloc(bi->bi_bh->b_size,
+                                                     GFP_NOFS | __GFP_NOFAIL);
+                               memcpy(bi->bi_clone + bi->bi_offset,
+                                      bi->bi_bh->b_data + bi->bi_offset,
+                                      bi->bi_len);
+                       }
+                       gfs2_trans_add_meta(rbm.rgd->rd_gl, bi->bi_bh);
+                       bi_prev = bi;
                }
-               gfs2_trans_add_meta(rbm.rgd->rd_gl, bi->bi_bh);
                gfs2_setbit(&rbm, false, new_state);
+               gfs2_rbm_incr(&rbm);
        }
 
        return rbm.rgd;
index 463ab2e95d1cb94cafd6e31118ae77000754bd71..5d8f085f7adea18f9acce4ea5ed7f9e15527a7d0 100644 (file)
@@ -80,4 +80,5 @@ static inline bool gfs2_rs_active(struct gfs2_blkreserv *rs)
        return rs && !RB_EMPTY_NODE(&rs->rs_node);
 }
 
+extern void check_and_update_goal(struct gfs2_inode *ip);
 #endif /* __RGRP_DOT_H__ */
index 0546ab4e28e8a3dcbcef555f8574e068b531531c..42bfd33619793fb5bdda021ba9a7b9a027585e86 100644 (file)
@@ -44,7 +44,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
        if (!tr)
                return -ENOMEM;
 
-       tr->tr_ip = (unsigned long)__builtin_return_address(0);
+       tr->tr_ip = _RET_IP_;
        tr->tr_blocks = blocks;
        tr->tr_revokes = revokes;
        tr->tr_reserved = 1;
index e325b4f9c799669ebfa02e0ee143231e98510ba6..b2623200107b3d0065a5492cfa8b8929734caa3b 100644 (file)
@@ -34,6 +34,11 @@ static inline int __sync_blockdev(struct block_device *bdev, int wait)
 }
 #endif
 
+/*
+ * buffer.c
+ */
+extern void guard_bio_eod(int rw, struct bio *bio);
+
 /*
  * char_dev.c
  */
index 88e3e00e2eca0d72aa2bc2af350682d26ca7c7ca..171d2846f2a319088f6e43fff1dc0030d3ce6550 100644 (file)
@@ -1075,3 +1075,21 @@ struct inode *alloc_anon_inode(struct super_block *s)
        return inode;
 }
 EXPORT_SYMBOL(alloc_anon_inode);
+
+/**
+ * simple_nosetlease - generic helper for prohibiting leases
+ * @filp: file pointer
+ * @arg: type of lease to obtain
+ * @flp: new lease supplied for insertion
+ * @priv: private data for lm_setup operation
+ *
+ * Generic helper for filesystems that do not wish to allow leases to be set.
+ * All arguments are ignored and it just returns -EINVAL.
+ */
+int
+simple_nosetlease(struct file *filp, long arg, struct file_lock **flp,
+                 void **priv)
+{
+       return -EINVAL;
+}
+EXPORT_SYMBOL(simple_nosetlease);
index ab798a88ec1d52347afe05d4b8ed2eff13151de1..13db95f54176ec0864732177392889c389df2b57 100644 (file)
@@ -245,7 +245,6 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
        block->b_daemon = rqstp->rq_server;
        block->b_host   = host;
        block->b_file   = file;
-       block->b_fl = NULL;
        file->f_count++;
 
        /* Add to file's list of blocks */
@@ -295,7 +294,6 @@ static void nlmsvc_free_block(struct kref *kref)
        nlmsvc_freegrantargs(block->b_call);
        nlmsvc_release_call(block->b_call);
        nlm_release_file(block->b_file);
-       kfree(block->b_fl);
        kfree(block);
 }
 
@@ -508,7 +506,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
                struct nlm_host *host, struct nlm_lock *lock,
                struct nlm_lock *conflock, struct nlm_cookie *cookie)
 {
-       struct nlm_block        *block = NULL;
        int                     error;
        __be32                  ret;
 
@@ -519,63 +516,26 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
                                (long long)lock->fl.fl_start,
                                (long long)lock->fl.fl_end);
 
-       /* Get existing block (in case client is busy-waiting) */
-       block = nlmsvc_lookup_block(file, lock);
-
-       if (block == NULL) {
-               struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
-
-               if (conf == NULL)
-                       return nlm_granted;
-               block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
-               if (block == NULL) {
-                       kfree(conf);
-                       return nlm_granted;
-               }
-               block->b_fl = conf;
-       }
-       if (block->b_flags & B_QUEUED) {
-               dprintk("lockd: nlmsvc_testlock deferred block %p flags %d fl %p\n",
-                       block, block->b_flags, block->b_fl);
-               if (block->b_flags & B_TIMED_OUT) {
-                       nlmsvc_unlink_block(block);
-                       ret = nlm_lck_denied;
-                       goto out;
-               }
-               if (block->b_flags & B_GOT_CALLBACK) {
-                       nlmsvc_unlink_block(block);
-                       if (block->b_fl != NULL
-                                       && block->b_fl->fl_type != F_UNLCK) {
-                               lock->fl = *block->b_fl;
-                               goto conf_lock;
-                       } else {
-                               ret = nlm_granted;
-                               goto out;
-                       }
-               }
-               ret = nlm_drop_reply;
-               goto out;
-       }
-
        if (locks_in_grace(SVC_NET(rqstp))) {
                ret = nlm_lck_denied_grace_period;
                goto out;
        }
+
        error = vfs_test_lock(file->f_file, &lock->fl);
-       if (error == FILE_LOCK_DEFERRED) {
-               ret = nlmsvc_defer_lock_rqst(rqstp, block);
-               goto out;
-       }
        if (error) {
+               /* We can't currently deal with deferred test requests */
+               if (error == FILE_LOCK_DEFERRED)
+                       WARN_ON_ONCE(1);
+
                ret = nlm_lck_denied_nolocks;
                goto out;
        }
+
        if (lock->fl.fl_type == F_UNLCK) {
                ret = nlm_granted;
                goto out;
        }
 
-conf_lock:
        dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
                lock->fl.fl_type, (long long)lock->fl.fl_start,
                (long long)lock->fl.fl_end);
@@ -586,10 +546,9 @@ conf_lock:
        conflock->fl.fl_type = lock->fl.fl_type;
        conflock->fl.fl_start = lock->fl.fl_start;
        conflock->fl.fl_end = lock->fl.fl_end;
+       locks_release_private(&lock->fl);
        ret = nlm_lck_denied;
 out:
-       if (block)
-               nlmsvc_release_block(block);
        return ret;
 }
 
@@ -660,29 +619,22 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l
  * This is a callback from the filesystem for VFS file lock requests.
  * It will be used if lm_grant is defined and the filesystem can not
  * respond to the request immediately.
- * For GETLK request it will copy the reply to the nlm_block.
  * For SETLK or SETLKW request it will get the local posix lock.
  * In all cases it will move the block to the head of nlm_blocked q where
  * nlmsvc_retry_blocked() can send back a reply for SETLKW or revisit the
  * deferred rpc for GETLK and SETLK.
  */
 static void
-nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf,
-                            int result)
+nlmsvc_update_deferred_block(struct nlm_block *block, int result)
 {
        block->b_flags |= B_GOT_CALLBACK;
        if (result == 0)
                block->b_granted = 1;
        else
                block->b_flags |= B_TIMED_OUT;
-       if (conf) {
-               if (block->b_fl)
-                       __locks_copy_lock(block->b_fl, conf);
-       }
 }
 
-static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
-                                       int result)
+static int nlmsvc_grant_deferred(struct file_lock *fl, int result)
 {
        struct nlm_block *block;
        int rc = -ENOENT;
@@ -697,7 +649,7 @@ static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
                                        rc = -ENOLCK;
                                        break;
                                }
-                               nlmsvc_update_deferred_block(block, conf, result);
+                               nlmsvc_update_deferred_block(block, result);
                        } else if (result == 0)
                                block->b_granted = 1;
 
index bb08857f90b56cb2b51a77b3f69acfef33a8fd9c..735b8d3fa78c92bf746aff05475be1fa2a82abd6 100644 (file)
@@ -230,8 +230,12 @@ void locks_release_private(struct file_lock *fl)
                        fl->fl_ops->fl_release_private(fl);
                fl->fl_ops = NULL;
        }
-       fl->fl_lmops = NULL;
 
+       if (fl->fl_lmops) {
+               if (fl->fl_lmops->lm_put_owner)
+                       fl->fl_lmops->lm_put_owner(fl);
+               fl->fl_lmops = NULL;
+       }
 }
 EXPORT_SYMBOL_GPL(locks_release_private);
 
@@ -267,21 +271,10 @@ void locks_init_lock(struct file_lock *fl)
 
 EXPORT_SYMBOL(locks_init_lock);
 
-static void locks_copy_private(struct file_lock *new, struct file_lock *fl)
-{
-       if (fl->fl_ops) {
-               if (fl->fl_ops->fl_copy_lock)
-                       fl->fl_ops->fl_copy_lock(new, fl);
-               new->fl_ops = fl->fl_ops;
-       }
-       if (fl->fl_lmops)
-               new->fl_lmops = fl->fl_lmops;
-}
-
 /*
  * Initialize a new lock from an existing file_lock structure.
  */
-void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl)
+void locks_copy_conflock(struct file_lock *new, struct file_lock *fl)
 {
        new->fl_owner = fl->fl_owner;
        new->fl_pid = fl->fl_pid;
@@ -290,22 +283,30 @@ void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl)
        new->fl_type = fl->fl_type;
        new->fl_start = fl->fl_start;
        new->fl_end = fl->fl_end;
+       new->fl_lmops = fl->fl_lmops;
        new->fl_ops = NULL;
-       new->fl_lmops = NULL;
+
+       if (fl->fl_lmops) {
+               if (fl->fl_lmops->lm_get_owner)
+                       fl->fl_lmops->lm_get_owner(new, fl);
+       }
 }
-EXPORT_SYMBOL(__locks_copy_lock);
+EXPORT_SYMBOL(locks_copy_conflock);
 
 void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
 {
        /* "new" must be a freshly-initialized lock */
        WARN_ON_ONCE(new->fl_ops);
 
-       __locks_copy_lock(new, fl);
+       locks_copy_conflock(new, fl);
+
        new->fl_file = fl->fl_file;
        new->fl_ops = fl->fl_ops;
-       new->fl_lmops = fl->fl_lmops;
 
-       locks_copy_private(new, fl);
+       if (fl->fl_ops) {
+               if (fl->fl_ops->fl_copy_lock)
+                       fl->fl_ops->fl_copy_lock(new, fl);
+       }
 }
 
 EXPORT_SYMBOL(locks_copy_lock);
@@ -325,17 +326,18 @@ static inline int flock_translate_cmd(int cmd) {
 }
 
 /* Fill in a file_lock structure with an appropriate FLOCK lock. */
-static int flock_make_lock(struct file *filp, struct file_lock **lock,
-               unsigned int cmd)
+static struct file_lock *
+flock_make_lock(struct file *filp, unsigned int cmd)
 {
        struct file_lock *fl;
        int type = flock_translate_cmd(cmd);
+
        if (type < 0)
-               return type;
+               return ERR_PTR(type);
        
        fl = locks_alloc_lock();
        if (fl == NULL)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        fl->fl_file = filp;
        fl->fl_owner = filp;
@@ -344,8 +346,7 @@ static int flock_make_lock(struct file *filp, struct file_lock **lock,
        fl->fl_type = type;
        fl->fl_end = OFFSET_MAX;
        
-       *lock = fl;
-       return 0;
+       return fl;
 }
 
 static int assign_type(struct file_lock *fl, long type)
@@ -426,14 +427,34 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
 }
 
 /* default lease lock manager operations */
-static void lease_break_callback(struct file_lock *fl)
+static bool
+lease_break_callback(struct file_lock *fl)
 {
        kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG);
+       return false;
+}
+
+static void
+lease_setup(struct file_lock *fl, void **priv)
+{
+       struct file *filp = fl->fl_file;
+       struct fasync_struct *fa = *priv;
+
+       /*
+        * fasync_insert_entry() returns the old entry if any. If there was no
+        * old entry, then it used "priv" and inserted it into the fasync list.
+        * Clear the pointer to indicate that it shouldn't be freed.
+        */
+       if (!fasync_insert_entry(fa->fa_fd, filp, &fl->fl_fasync, fa))
+               *priv = NULL;
+
+       __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
 }
 
 static const struct lock_manager_operations lease_manager_ops = {
        .lm_break = lease_break_callback,
        .lm_change = lease_modify,
+       .lm_setup = lease_setup,
 };
 
 /*
@@ -444,7 +465,7 @@ static int lease_init(struct file *filp, long type, struct file_lock *fl)
        if (assign_type(fl, type) != 0)
                return -EINVAL;
 
-       fl->fl_owner = current->files;
+       fl->fl_owner = filp;
        fl->fl_pid = current->tgid;
 
        fl->fl_file = filp;
@@ -735,7 +756,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
                        break;
        }
        if (cfl) {
-               __locks_copy_lock(fl, cfl);
+               locks_copy_conflock(fl, cfl);
                if (cfl->fl_nspid)
                        fl->fl_pid = pid_vnr(cfl->fl_nspid);
        } else
@@ -941,7 +962,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
                        if (!posix_locks_conflict(request, fl))
                                continue;
                        if (conflock)
-                               __locks_copy_lock(conflock, fl);
+                               locks_copy_conflock(conflock, fl);
                        error = -EAGAIN;
                        if (!(request->fl_flags & FL_SLEEP))
                                goto out;
@@ -1273,7 +1294,7 @@ static void lease_clear_pending(struct file_lock *fl, int arg)
 }
 
 /* We already had a lease on this file; just change its type */
-int lease_modify(struct file_lock **before, int arg)
+int lease_modify(struct file_lock **before, int arg, struct list_head *dispose)
 {
        struct file_lock *fl = *before;
        int error = assign_type(fl, arg);
@@ -1292,11 +1313,10 @@ int lease_modify(struct file_lock **before, int arg)
                        printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
                        fl->fl_fasync = NULL;
                }
-               locks_delete_lock(before, NULL);
+               locks_delete_lock(before, dispose);
        }
        return 0;
 }
-
 EXPORT_SYMBOL(lease_modify);
 
 static bool past_time(unsigned long then)
@@ -1307,18 +1327,20 @@ static bool past_time(unsigned long then)
        return time_after(jiffies, then);
 }
 
-static void time_out_leases(struct inode *inode)
+static void time_out_leases(struct inode *inode, struct list_head *dispose)
 {
        struct file_lock **before;
        struct file_lock *fl;
 
+       lockdep_assert_held(&inode->i_lock);
+
        before = &inode->i_flock;
        while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) {
                trace_time_out_leases(inode, fl);
                if (past_time(fl->fl_downgrade_time))
-                       lease_modify(before, F_RDLCK);
+                       lease_modify(before, F_RDLCK, dispose);
                if (past_time(fl->fl_break_time))
-                       lease_modify(before, F_UNLCK);
+                       lease_modify(before, F_UNLCK, dispose);
                if (fl == *before)      /* lease_modify may have freed fl */
                        before = &fl->fl_next;
        }
@@ -1331,6 +1353,20 @@ static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
        return locks_conflict(breaker, lease);
 }
 
+static bool
+any_leases_conflict(struct inode *inode, struct file_lock *breaker)
+{
+       struct file_lock *fl;
+
+       lockdep_assert_held(&inode->i_lock);
+
+       for (fl = inode->i_flock ; fl && IS_LEASE(fl); fl = fl->fl_next) {
+               if (leases_conflict(fl, breaker))
+                       return true;
+       }
+       return false;
+}
+
 /**
  *     __break_lease   -       revoke all outstanding leases on file
  *     @inode: the inode of the file to return
@@ -1347,12 +1383,11 @@ static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
 int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
 {
        int error = 0;
-       struct file_lock *new_fl, *flock;
-       struct file_lock *fl;
+       struct file_lock *new_fl;
+       struct file_lock *fl, **before;
        unsigned long break_time;
-       int i_have_this_lease = 0;
-       bool lease_conflict = false;
        int want_write = (mode & O_ACCMODE) != O_RDONLY;
+       LIST_HEAD(dispose);
 
        new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK);
        if (IS_ERR(new_fl))
@@ -1361,20 +1396,9 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
 
        spin_lock(&inode->i_lock);
 
-       time_out_leases(inode);
-
-       flock = inode->i_flock;
-       if ((flock == NULL) || !IS_LEASE(flock))
-               goto out;
+       time_out_leases(inode, &dispose);
 
-       for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
-               if (leases_conflict(fl, new_fl)) {
-                       lease_conflict = true;
-                       if (fl->fl_owner == current->files)
-                               i_have_this_lease = 1;
-               }
-       }
-       if (!lease_conflict)
+       if (!any_leases_conflict(inode, new_fl))
                goto out;
 
        break_time = 0;
@@ -1384,7 +1408,9 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
                        break_time++;   /* so that 0 means no break time */
        }
 
-       for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
+       for (before = &inode->i_flock;
+                       ((fl = *before) != NULL) && IS_LEASE(fl);
+                       before = &fl->fl_next) {
                if (!leases_conflict(fl, new_fl))
                        continue;
                if (want_write) {
@@ -1393,51 +1419,56 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
                        fl->fl_flags |= FL_UNLOCK_PENDING;
                        fl->fl_break_time = break_time;
                } else {
-                       if (lease_breaking(flock))
+                       if (lease_breaking(inode->i_flock))
                                continue;
                        fl->fl_flags |= FL_DOWNGRADE_PENDING;
                        fl->fl_downgrade_time = break_time;
                }
-               fl->fl_lmops->lm_break(fl);
+               if (fl->fl_lmops->lm_break(fl))
+                       locks_delete_lock(before, &dispose);
        }
 
-       if (i_have_this_lease || (mode & O_NONBLOCK)) {
+       fl = inode->i_flock;
+       if (!fl || !IS_LEASE(fl))
+               goto out;
+
+       if (mode & O_NONBLOCK) {
                trace_break_lease_noblock(inode, new_fl);
                error = -EWOULDBLOCK;
                goto out;
        }
 
 restart:
-       break_time = flock->fl_break_time;
+       break_time = inode->i_flock->fl_break_time;
        if (break_time != 0)
                break_time -= jiffies;
        if (break_time == 0)
                break_time++;
-       locks_insert_block(flock, new_fl);
+       locks_insert_block(inode->i_flock, new_fl);
        trace_break_lease_block(inode, new_fl);
        spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        error = wait_event_interruptible_timeout(new_fl->fl_wait,
                                                !new_fl->fl_next, break_time);
        spin_lock(&inode->i_lock);
        trace_break_lease_unblock(inode, new_fl);
        locks_delete_block(new_fl);
        if (error >= 0) {
-               if (error == 0)
-                       time_out_leases(inode);
                /*
                 * Wait for the next conflicting lease that has not been
                 * broken yet
                 */
-               for (flock = inode->i_flock; flock && IS_LEASE(flock);
-                               flock = flock->fl_next) {
-                       if (leases_conflict(new_fl, flock))
-                               goto restart;
-               }
+               if (error == 0)
+                       time_out_leases(inode, &dispose);
+               if (any_leases_conflict(inode, new_fl))
+                       goto restart;
+
                error = 0;
        }
 
 out:
        spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        locks_free_lock(new_fl);
        return error;
 }
@@ -1455,8 +1486,18 @@ EXPORT_SYMBOL(__break_lease);
  */
 void lease_get_mtime(struct inode *inode, struct timespec *time)
 {
-       struct file_lock *flock = inode->i_flock;
-       if (flock && IS_LEASE(flock) && (flock->fl_type == F_WRLCK))
+       bool has_lease = false;
+       struct file_lock *flock;
+
+       if (inode->i_flock) {
+               spin_lock(&inode->i_lock);
+               flock = inode->i_flock;
+               if (flock && IS_LEASE(flock) && (flock->fl_type == F_WRLCK))
+                       has_lease = true;
+               spin_unlock(&inode->i_lock);
+       }
+
+       if (has_lease)
                *time = current_fs_time(inode->i_sb);
        else
                *time = inode->i_mtime;
@@ -1492,9 +1533,10 @@ int fcntl_getlease(struct file *filp)
        struct file_lock *fl;
        struct inode *inode = file_inode(filp);
        int type = F_UNLCK;
+       LIST_HEAD(dispose);
 
        spin_lock(&inode->i_lock);
-       time_out_leases(file_inode(filp));
+       time_out_leases(file_inode(filp), &dispose);
        for (fl = file_inode(filp)->i_flock; fl && IS_LEASE(fl);
                        fl = fl->fl_next) {
                if (fl->fl_file == filp) {
@@ -1503,6 +1545,7 @@ int fcntl_getlease(struct file *filp)
                }
        }
        spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        return type;
 }
 
@@ -1532,13 +1575,15 @@ check_conflicting_open(const struct dentry *dentry, const long arg)
        return ret;
 }
 
-static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
+static int
+generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **priv)
 {
        struct file_lock *fl, **before, **my_before = NULL, *lease;
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
        bool is_deleg = (*flp)->fl_flags & FL_DELEG;
        int error;
+       LIST_HEAD(dispose);
 
        lease = *flp;
        trace_generic_add_lease(inode, lease);
@@ -1561,6 +1606,8 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
                return -EINVAL;
        }
 
+       spin_lock(&inode->i_lock);
+       time_out_leases(inode, &dispose);
        error = check_conflicting_open(dentry, arg);
        if (error)
                goto out;
@@ -1596,10 +1643,11 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
        }
 
        if (my_before != NULL) {
-               error = lease->fl_lmops->lm_change(my_before, arg);
-               if (!error)
-                       *flp = *my_before;
-               goto out;
+               lease = *my_before;
+               error = lease->fl_lmops->lm_change(my_before, arg, &dispose);
+               if (error)
+                       goto out;
+               goto out_setup;
        }
 
        error = -EINVAL;
@@ -1619,43 +1667,61 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
        smp_mb();
        error = check_conflicting_open(dentry, arg);
        if (error)
-               locks_unlink_lock(before);
+               goto out_unlink;
+
+out_setup:
+       if (lease->fl_lmops->lm_setup)
+               lease->fl_lmops->lm_setup(lease, priv);
 out:
+       spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        if (is_deleg)
                mutex_unlock(&inode->i_mutex);
+       if (!error && !my_before)
+               *flp = NULL;
        return error;
+out_unlink:
+       locks_unlink_lock(before);
+       goto out;
 }
 
-static int generic_delete_lease(struct file *filp, struct file_lock **flp)
+static int generic_delete_lease(struct file *filp)
 {
+       int error = -EAGAIN;
        struct file_lock *fl, **before;
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
+       LIST_HEAD(dispose);
 
-       trace_generic_delete_lease(inode, *flp);
-
+       spin_lock(&inode->i_lock);
+       time_out_leases(inode, &dispose);
        for (before = &inode->i_flock;
                        ((fl = *before) != NULL) && IS_LEASE(fl);
                        before = &fl->fl_next) {
-               if (fl->fl_file != filp)
-                       continue;
-               return (*flp)->fl_lmops->lm_change(before, F_UNLCK);
+               if (fl->fl_file == filp)
+                       break;
        }
-       return -EAGAIN;
+       trace_generic_delete_lease(inode, fl);
+       if (fl)
+               error = fl->fl_lmops->lm_change(before, F_UNLCK, &dispose);
+       spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
+       return error;
 }
 
 /**
  *     generic_setlease        -       sets a lease on an open file
- *     @filp: file pointer
- *     @arg: type of lease to obtain
- *     @flp: input - file_lock to use, output - file_lock inserted
+ *     @filp:  file pointer
+ *     @arg:   type of lease to obtain
+ *     @flp:   input - file_lock to use, output - file_lock inserted
+ *     @priv:  private data for lm_setup (may be NULL if lm_setup
+ *             doesn't require it)
  *
  *     The (input) flp->fl_lmops->lm_break function is required
  *     by break_lease().
- *
- *     Called with inode->i_lock held.
  */
-int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
+int generic_setlease(struct file *filp, long arg, struct file_lock **flp,
+                       void **priv)
 {
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
@@ -1669,83 +1735,52 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
        if (error)
                return error;
 
-       time_out_leases(inode);
-
-       BUG_ON(!(*flp)->fl_lmops->lm_break);
-
        switch (arg) {
        case F_UNLCK:
-               return generic_delete_lease(filp, flp);
+               return generic_delete_lease(filp);
        case F_RDLCK:
        case F_WRLCK:
-               return generic_add_lease(filp, arg, flp);
+               if (!(*flp)->fl_lmops->lm_break) {
+                       WARN_ON_ONCE(1);
+                       return -ENOLCK;
+               }
+               return generic_add_lease(filp, arg, flp, priv);
        default:
                return -EINVAL;
        }
 }
 EXPORT_SYMBOL(generic_setlease);
 
-static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease)
-{
-       if (filp->f_op->setlease)
-               return filp->f_op->setlease(filp, arg, lease);
-       else
-               return generic_setlease(filp, arg, lease);
-}
-
 /**
- *     vfs_setlease        -       sets a lease on an open file
- *     @filp: file pointer
- *     @arg: type of lease to obtain
- *     @lease: file_lock to use
- *
- *     Call this to establish a lease on the file.
- *     The (*lease)->fl_lmops->lm_break operation must be set; if not,
- *     break_lease will oops!
- *
- *     This will call the filesystem's setlease file method, if
- *     defined.  Note that there is no getlease method; instead, the
- *     filesystem setlease method should call back to setlease() to
- *     add a lease to the inode's lease list, where fcntl_getlease() can
- *     find it.  Since fcntl_getlease() only reports whether the current
- *     task holds a lease, a cluster filesystem need only do this for
- *     leases held by processes on this node.
- *
- *     There is also no break_lease method; filesystems that
- *     handle their own leases should break leases themselves from the
- *     filesystem's open, create, and (on truncate) setattr methods.
- *
- *     Warning: the only current setlease methods exist only to disable
- *     leases in certain cases.  More vfs changes may be required to
- *     allow a full filesystem lease implementation.
+ * vfs_setlease        -       sets a lease on an open file
+ * @filp:      file pointer
+ * @arg:       type of lease to obtain
+ * @lease:     file_lock to use when adding a lease
+ * @priv:      private info for lm_setup when adding a lease (may be
+ *             NULL if lm_setup doesn't require it)
+ *
+ * Call this to establish a lease on the file. The "lease" argument is not
+ * used for F_UNLCK requests and may be NULL. For commands that set or alter
+ * an existing lease, the (*lease)->fl_lmops->lm_break operation must be set;
+ * if not, this function will return -ENOLCK (and generate a scary-looking
+ * stack trace).
+ *
+ * The "priv" pointer is passed directly to the lm_setup function as-is. It
+ * may be NULL if the lm_setup operation doesn't require it.
  */
-
-int vfs_setlease(struct file *filp, long arg, struct file_lock **lease)
+int
+vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **priv)
 {
-       struct inode *inode = file_inode(filp);
-       int error;
-
-       spin_lock(&inode->i_lock);
-       error = __vfs_setlease(filp, arg, lease);
-       spin_unlock(&inode->i_lock);
-
-       return error;
+       if (filp->f_op->setlease)
+               return filp->f_op->setlease(filp, arg, lease, priv);
+       else
+               return generic_setlease(filp, arg, lease, priv);
 }
 EXPORT_SYMBOL_GPL(vfs_setlease);
 
-static int do_fcntl_delete_lease(struct file *filp)
-{
-       struct file_lock fl, *flp = &fl;
-
-       lease_init(filp, F_UNLCK, flp);
-
-       return vfs_setlease(filp, F_UNLCK, &flp);
-}
-
 static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
 {
-       struct file_lock *fl, *ret;
-       struct inode *inode = file_inode(filp);
+       struct file_lock *fl;
        struct fasync_struct *new;
        int error;
 
@@ -1758,26 +1793,9 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
                locks_free_lock(fl);
                return -ENOMEM;
        }
-       ret = fl;
-       spin_lock(&inode->i_lock);
-       error = __vfs_setlease(filp, arg, &ret);
-       if (error)
-               goto out_unlock;
-       if (ret == fl)
-               fl = NULL;
-
-       /*
-        * fasync_insert_entry() returns the old entry if any.
-        * If there was no old entry, then it used 'new' and
-        * inserted it into the fasync list. Clear new so that
-        * we don't release it here.
-        */
-       if (!fasync_insert_entry(fd, filp, &ret->fl_fasync, new))
-               new = NULL;
+       new->fa_fd = fd;
 
-       error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
-out_unlock:
-       spin_unlock(&inode->i_lock);
+       error = vfs_setlease(filp, arg, &fl, (void **)&new);
        if (fl)
                locks_free_lock(fl);
        if (new)
@@ -1798,7 +1816,7 @@ out_unlock:
 int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
 {
        if (arg == F_UNLCK)
-               return do_fcntl_delete_lease(filp);
+               return vfs_setlease(filp, F_UNLCK, NULL, NULL);
        return do_fcntl_add_lease(fd, filp, arg);
 }
 
@@ -1867,9 +1885,12 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
            !(f.file->f_mode & (FMODE_READ|FMODE_WRITE)))
                goto out_putf;
 
-       error = flock_make_lock(f.file, &lock, cmd);
-       if (error)
+       lock = flock_make_lock(f.file, cmd);
+       if (IS_ERR(lock)) {
+               error = PTR_ERR(lock);
                goto out_putf;
+       }
+
        if (can_sleep)
                lock->fl_flags |= FL_SLEEP;
 
@@ -1981,11 +2002,13 @@ int fcntl_getlk(struct file *filp, unsigned int cmd, struct flock __user *l)
        if (file_lock.fl_type != F_UNLCK) {
                error = posix_lock_to_flock(&flock, &file_lock);
                if (error)
-                       goto out;
+                       goto rel_priv;
        }
        error = -EFAULT;
        if (!copy_to_user(l, &flock, sizeof(flock)))
                error = 0;
+rel_priv:
+       locks_release_private(&file_lock);
 out:
        return error;
 }
@@ -2206,7 +2229,8 @@ int fcntl_getlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
        error = -EFAULT;
        if (!copy_to_user(l, &flock, sizeof(flock)))
                error = 0;
-  
+
+       locks_release_private(&file_lock);
 out:
        return error;
 }
@@ -2369,7 +2393,7 @@ void locks_remove_file(struct file *filp)
        while ((fl = *before) != NULL) {
                if (fl->fl_file == filp) {
                        if (IS_LEASE(fl)) {
-                               lease_modify(before, F_UNLCK);
+                               lease_modify(before, F_UNLCK, &dispose);
                                continue;
                        }
 
@@ -2593,86 +2617,6 @@ static int __init proc_locks_init(void)
 module_init(proc_locks_init);
 #endif
 
-/**
- *     lock_may_read - checks that the region is free of locks
- *     @inode: the inode that is being read
- *     @start: the first byte to read
- *     @len: the number of bytes to read
- *
- *     Emulates Windows locking requirements.  Whole-file
- *     mandatory locks (share modes) can prohibit a read and
- *     byte-range POSIX locks can prohibit a read if they overlap.
- *
- *     N.B. this function is only ever called
- *     from knfsd and ownership of locks is never checked.
- */
-int lock_may_read(struct inode *inode, loff_t start, unsigned long len)
-{
-       struct file_lock *fl;
-       int result = 1;
-
-       spin_lock(&inode->i_lock);
-       for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-               if (IS_POSIX(fl)) {
-                       if (fl->fl_type == F_RDLCK)
-                               continue;
-                       if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
-                               continue;
-               } else if (IS_FLOCK(fl)) {
-                       if (!(fl->fl_type & LOCK_MAND))
-                               continue;
-                       if (fl->fl_type & LOCK_READ)
-                               continue;
-               } else
-                       continue;
-               result = 0;
-               break;
-       }
-       spin_unlock(&inode->i_lock);
-       return result;
-}
-
-EXPORT_SYMBOL(lock_may_read);
-
-/**
- *     lock_may_write - checks that the region is free of locks
- *     @inode: the inode that is being written
- *     @start: the first byte to write
- *     @len: the number of bytes to write
- *
- *     Emulates Windows locking requirements.  Whole-file
- *     mandatory locks (share modes) can prohibit a write and
- *     byte-range POSIX locks can prohibit a write if they overlap.
- *
- *     N.B. this function is only ever called
- *     from knfsd and ownership of locks is never checked.
- */
-int lock_may_write(struct inode *inode, loff_t start, unsigned long len)
-{
-       struct file_lock *fl;
-       int result = 1;
-
-       spin_lock(&inode->i_lock);
-       for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-               if (IS_POSIX(fl)) {
-                       if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
-                               continue;
-               } else if (IS_FLOCK(fl)) {
-                       if (!(fl->fl_type & LOCK_MAND))
-                               continue;
-                       if (fl->fl_type & LOCK_WRITE)
-                               continue;
-               } else
-                       continue;
-               result = 0;
-               break;
-       }
-       spin_unlock(&inode->i_lock);
-       return result;
-}
-
-EXPORT_SYMBOL(lock_may_write);
-
 static int __init filelock_init(void)
 {
        int i;
index 5f9ed622274f411128752d7efd472f954f669a6e..3e79220babac28383c5c68cdb3580378d3324bcc 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/backing-dev.h>
 #include <linux/pagevec.h>
 #include <linux/cleancache.h>
+#include "internal.h"
 
 /*
  * I/O completion handler for multipage BIOs.
@@ -57,6 +58,7 @@ static void mpage_end_io(struct bio *bio, int err)
 static struct bio *mpage_bio_submit(int rw, struct bio *bio)
 {
        bio->bi_end_io = mpage_end_io;
+       guard_bio_eod(rw, bio);
        submit_bio(rw, bio);
        return NULL;
 }
index 6920127c5eb7eebce51958422dfbfaf38cbd4b29..4ea92ce0537fdc9710ab4385200ed5e7f724ce76 100644 (file)
@@ -919,17 +919,6 @@ int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
 }
 EXPORT_SYMBOL_GPL(nfs_flock);
 
-/*
- * There is no protocol support for leases, so we have no way to implement
- * them correctly in the face of opens by other clients.
- */
-int nfs_setlease(struct file *file, long arg, struct file_lock **fl)
-{
-       dprintk("NFS: setlease(%pD2, arg=%ld)\n", file, arg);
-       return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(nfs_setlease);
-
 const struct file_operations nfs_file_operations = {
        .llseek         = nfs_file_llseek,
        .read           = new_sync_read,
@@ -946,6 +935,6 @@ const struct file_operations nfs_file_operations = {
        .splice_read    = nfs_file_splice_read,
        .splice_write   = iter_file_splice_write,
        .check_flags    = nfs_check_flags,
-       .setlease       = nfs_setlease,
+       .setlease       = simple_nosetlease,
 };
 EXPORT_SYMBOL_GPL(nfs_file_operations);
index 14ae6f20a1724aa4d81aed3fee294d643a6aafe1..efaa31c70fbe1c43d265c51bc542a8270348c339 100644 (file)
@@ -339,7 +339,6 @@ int nfs_file_release(struct inode *, struct file *);
 int nfs_lock(struct file *, int, struct file_lock *);
 int nfs_flock(struct file *, int, struct file_lock *);
 int nfs_check_flags(int);
-int nfs_setlease(struct file *, long, struct file_lock **);
 
 /* inode.c */
 extern struct workqueue_struct *nfsiod_workqueue;
index a816f0627a6ce03cda2502c42c780c5ab6a2742c..3e987ad9ae2547488f6a24d9ac0a411c122028ac 100644 (file)
@@ -131,5 +131,5 @@ const struct file_operations nfs4_file_operations = {
        .splice_read    = nfs_file_splice_read,
        .splice_write   = iter_file_splice_write,
        .check_flags    = nfs_check_flags,
-       .setlease       = nfs_setlease,
+       .setlease       = simple_nosetlease,
 };
index 5c0cac1730682178679f7fe7ae00c95b87da9642..e9c3afe4b5d3c306a5dd61692ed34805ca661f7a 100644 (file)
@@ -218,6 +218,13 @@ static void nfsd4_put_session(struct nfsd4_session *ses)
        spin_unlock(&nn->client_lock);
 }
 
+static inline struct nfs4_stateowner *
+nfs4_get_stateowner(struct nfs4_stateowner *sop)
+{
+       atomic_inc(&sop->so_count);
+       return sop;
+}
+
 static int
 same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner)
 {
@@ -237,10 +244,8 @@ find_openstateowner_str_locked(unsigned int hashval, struct nfsd4_open *open,
                            so_strhash) {
                if (!so->so_is_open_owner)
                        continue;
-               if (same_owner_str(so, &open->op_owner)) {
-                       atomic_inc(&so->so_count);
-                       return openowner(so);
-               }
+               if (same_owner_str(so, &open->op_owner))
+                       return openowner(nfs4_get_stateowner(so));
        }
        return NULL;
 }
@@ -678,18 +683,14 @@ nfs4_put_stid(struct nfs4_stid *s)
 static void nfs4_put_deleg_lease(struct nfs4_file *fp)
 {
        struct file *filp = NULL;
-       struct file_lock *fl;
 
        spin_lock(&fp->fi_lock);
-       if (fp->fi_lease && atomic_dec_and_test(&fp->fi_delegees)) {
+       if (fp->fi_deleg_file && atomic_dec_and_test(&fp->fi_delegees))
                swap(filp, fp->fi_deleg_file);
-               fl = fp->fi_lease;
-               fp->fi_lease = NULL;
-       }
        spin_unlock(&fp->fi_lock);
 
        if (filp) {
-               vfs_setlease(filp, F_UNLCK, &fl);
+               vfs_setlease(filp, F_UNLCK, NULL, NULL);
                fput(filp);
        }
 }
@@ -1655,7 +1656,7 @@ __destroy_client(struct nfs4_client *clp)
        }
        while (!list_empty(&clp->cl_openowners)) {
                oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient);
-               atomic_inc(&oo->oo_owner.so_count);
+               nfs4_get_stateowner(&oo->oo_owner);
                release_openowner(oo);
        }
        nfsd4_shutdown_callback(clp);
@@ -3067,8 +3068,8 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct knfsd_fh *fh)
        INIT_LIST_HEAD(&fp->fi_stateids);
        INIT_LIST_HEAD(&fp->fi_delegations);
        fh_copy_shallow(&fp->fi_fhandle, fh);
+       fp->fi_deleg_file = NULL;
        fp->fi_had_conflict = false;
-       fp->fi_lease = NULL;
        fp->fi_share_deny = 0;
        memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
        memset(fp->fi_access, 0, sizeof(fp->fi_access));
@@ -3136,8 +3137,7 @@ static void nfsd4_cstate_assign_replay(struct nfsd4_compound_state *cstate,
 {
        if (!nfsd4_has_session(cstate)) {
                mutex_lock(&so->so_replay.rp_mutex);
-               cstate->replay_owner = so;
-               atomic_inc(&so->so_count);
+               cstate->replay_owner = nfs4_get_stateowner(so);
        }
 }
 
@@ -3236,8 +3236,7 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
        atomic_inc(&stp->st_stid.sc_count);
        stp->st_stid.sc_type = NFS4_OPEN_STID;
        INIT_LIST_HEAD(&stp->st_locks);
-       stp->st_stateowner = &oo->oo_owner;
-       atomic_inc(&stp->st_stateowner->so_count);
+       stp->st_stateowner = nfs4_get_stateowner(&oo->oo_owner);
        get_nfs4_file(fp);
        stp->st_stid.sc_file = fp;
        stp->st_access_bmap = 0;
@@ -3434,18 +3433,20 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
 }
 
 /* Called from break_lease() with i_lock held. */
-static void nfsd_break_deleg_cb(struct file_lock *fl)
+static bool
+nfsd_break_deleg_cb(struct file_lock *fl)
 {
+       bool ret = false;
        struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner;
        struct nfs4_delegation *dp;
 
        if (!fp) {
                WARN(1, "(%p)->fl_owner NULL\n", fl);
-               return;
+               return ret;
        }
        if (fp->fi_had_conflict) {
                WARN(1, "duplicate break on %p\n", fp);
-               return;
+               return ret;
        }
        /*
         * We don't want the locks code to timeout the lease for us;
@@ -3457,24 +3458,23 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
        spin_lock(&fp->fi_lock);
        fp->fi_had_conflict = true;
        /*
-        * If there are no delegations on the list, then we can't count on this
-        * lease ever being cleaned up. Set the fl_break_time to jiffies so that
-        * time_out_leases will do it ASAP. The fact that fi_had_conflict is now
-        * true should keep any new delegations from being hashed.
+        * If there are no delegations on the list, then return true
+        * so that the lease code will go ahead and delete it.
         */
        if (list_empty(&fp->fi_delegations))
-               fl->fl_break_time = jiffies;
+               ret = true;
        else
                list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
                        nfsd_break_one_deleg(dp);
        spin_unlock(&fp->fi_lock);
+       return ret;
 }
 
-static
-int nfsd_change_deleg_cb(struct file_lock **onlist, int arg)
+static int
+nfsd_change_deleg_cb(struct file_lock **onlist, int arg, struct list_head *dispose)
 {
        if (arg & F_UNLCK)
-               return lease_modify(onlist, arg);
+               return lease_modify(onlist, arg, dispose);
        else
                return -EAGAIN;
 }
@@ -3820,7 +3820,7 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_file *fp, int flag)
 static int nfs4_setlease(struct nfs4_delegation *dp)
 {
        struct nfs4_file *fp = dp->dl_stid.sc_file;
-       struct file_lock *fl;
+       struct file_lock *fl, *ret;
        struct file *filp;
        int status = 0;
 
@@ -3834,11 +3834,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
                return -EBADF;
        }
        fl->fl_file = filp;
-       status = vfs_setlease(filp, fl->fl_type, &fl);
-       if (status) {
+       ret = fl;
+       status = vfs_setlease(filp, fl->fl_type, &fl, NULL);
+       if (fl)
                locks_free_lock(fl);
+       if (status)
                goto out_fput;
-       }
        spin_lock(&state_lock);
        spin_lock(&fp->fi_lock);
        /* Did the lease get broken before we took the lock? */
@@ -3846,13 +3847,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
        if (fp->fi_had_conflict)
                goto out_unlock;
        /* Race breaker */
-       if (fp->fi_lease) {
+       if (fp->fi_deleg_file) {
                status = 0;
                atomic_inc(&fp->fi_delegees);
                hash_delegation_locked(dp, fp);
                goto out_unlock;
        }
-       fp->fi_lease = fl;
        fp->fi_deleg_file = filp;
        atomic_set(&fp->fi_delegees, 1);
        hash_delegation_locked(dp, fp);
@@ -3885,7 +3885,7 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
        spin_lock(&state_lock);
        spin_lock(&fp->fi_lock);
        dp->dl_stid.sc_file = fp;
-       if (!fp->fi_lease) {
+       if (!fp->fi_deleg_file) {
                spin_unlock(&fp->fi_lock);
                spin_unlock(&state_lock);
                status = nfs4_setlease(dp);
@@ -4929,9 +4929,25 @@ nfs4_transform_lock_offset(struct file_lock *lock)
                lock->fl_end = OFFSET_MAX;
 }
 
-/* Hack!: For now, we're defining this just so we can use a pointer to it
- * as a unique cookie to identify our (NFSv4's) posix locks. */
+static void nfsd4_fl_get_owner(struct file_lock *dst, struct file_lock *src)
+{
+       struct nfs4_lockowner *lo = (struct nfs4_lockowner *)src->fl_owner;
+       dst->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lo->lo_owner));
+}
+
+static void nfsd4_fl_put_owner(struct file_lock *fl)
+{
+       struct nfs4_lockowner *lo = (struct nfs4_lockowner *)fl->fl_owner;
+
+       if (lo) {
+               nfs4_put_stateowner(&lo->lo_owner);
+               fl->fl_owner = NULL;
+       }
+}
+
 static const struct lock_manager_operations nfsd_posix_mng_ops  = {
+       .lm_get_owner = nfsd4_fl_get_owner,
+       .lm_put_owner = nfsd4_fl_put_owner,
 };
 
 static inline void
@@ -4977,10 +4993,8 @@ find_lockowner_str_locked(clientid_t *clid, struct xdr_netobj *owner,
                            so_strhash) {
                if (so->so_is_open_owner)
                        continue;
-               if (!same_owner_str(so, owner))
-                       continue;
-               atomic_inc(&so->so_count);
-               return lockowner(so);
+               if (same_owner_str(so, owner))
+                       return lockowner(nfs4_get_stateowner(so));
        }
        return NULL;
 }
@@ -5059,8 +5073,7 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
 
        atomic_inc(&stp->st_stid.sc_count);
        stp->st_stid.sc_type = NFS4_LOCK_STID;
-       stp->st_stateowner = &lo->lo_owner;
-       atomic_inc(&lo->lo_owner.so_count);
+       stp->st_stateowner = nfs4_get_stateowner(&lo->lo_owner);
        get_nfs4_file(fp);
        stp->st_stid.sc_file = fp;
        stp->st_stid.sc_free = nfs4_free_lock_stateid;
@@ -5299,7 +5312,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                status = nfserr_openmode;
                goto out;
        }
-       file_lock->fl_owner = (fl_owner_t)lock_sop;
+
+       file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lock_sop->lo_owner));
        file_lock->fl_pid = current->tgid;
        file_lock->fl_file = filp;
        file_lock->fl_flags = FL_POSIX;
@@ -5495,7 +5509,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        }
 
        file_lock->fl_type = F_UNLCK;
-       file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
+       file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(stp->st_stateowner));
        file_lock->fl_pid = current->tgid;
        file_lock->fl_file = filp;
        file_lock->fl_flags = FL_POSIX;
@@ -5602,7 +5616,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
                        }
                }
 
-               atomic_inc(&sop->so_count);
+               nfs4_get_stateowner(sop);
                break;
        }
        spin_unlock(&clp->cl_lock);
index 0a47c6a6b3015f7e4253fda96cef67c870ba9860..2712042a66b197937936811057bac63fa7a3f382 100644 (file)
@@ -486,7 +486,6 @@ struct nfs4_file {
        atomic_t                fi_access[2];
        u32                     fi_share_deny;
        struct file             *fi_deleg_file;
-       struct file_lock        *fi_lease;
        atomic_t                fi_delegees;
        struct knfsd_fh         fi_fhandle;
        bool                    fi_had_conflict;
index abc8cbcfe90e0fca9f67471740c0b41c9055b7c6..caaaf9dfe3534be9e29826e46058151b5536c48e 100644 (file)
@@ -346,13 +346,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
                goto out;
        }
 
-       error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
-       if (error) {
-               /* if we added, we must shoot */
-               if (dn_mark == new_dn_mark)
-                       destroy = 1;
-               goto out;
-       }
+       __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
 
        error = attach_dn(dn, dn_mark, id, fd, filp, mask);
        /* !error means that we attached the dn to the dn_mark, so don't free it */
index b13992a41bd94312eeecd59b4e0841a58d1d85c0..c991616acca9ef86e99d1e33dfd9d427adfb8b5f 100644 (file)
@@ -78,7 +78,7 @@ static int create_fd(struct fsnotify_group *group,
 
        pr_debug("%s: group=%p event=%p\n", __func__, group, event);
 
-       client_fd = get_unused_fd();
+       client_fd = get_unused_fd_flags(group->fanotify_data.f_flags);
        if (client_fd < 0)
                return client_fd;
 
index 85e7d2b431d9014b56fb7e3b00c337f11ea7a57b..9c0898c4cfe1ce771a8a0832adda51c80225ce59 100644 (file)
@@ -23,9 +23,6 @@ extern int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
                                      struct fsnotify_group *group, struct vfsmount *mnt,
                                      int allow_dups);
 
-/* final kfree of a group */
-extern void fsnotify_final_destroy_group(struct fsnotify_group *group);
-
 /* vfsmount specific destruction of a mark */
 extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark);
 /* inode specific destruction of a mark */
index ad199598045655105e7ba4db0ed639357bf3fc6f..d16b62cb28544a147183c3780e3a61ffa246c8bb 100644 (file)
@@ -31,7 +31,7 @@
 /*
  * Final freeing of a group
  */
-void fsnotify_final_destroy_group(struct fsnotify_group *group)
+static void fsnotify_final_destroy_group(struct fsnotify_group *group)
 {
        if (group->ops->free_group_priv)
                group->ops->free_group_priv(group);
index 0f88bc0b4e6cfd31fbcd030c5256384fa844b265..7d888d77d59afc834d74dd8db55dfe20962502e0 100644 (file)
@@ -165,8 +165,10 @@ static void inotify_free_group_priv(struct fsnotify_group *group)
        /* ideally the idr is empty and we won't hit the BUG in the callback */
        idr_for_each(&group->inotify_data.idr, idr_callback, group);
        idr_destroy(&group->inotify_data.idr);
-       atomic_dec(&group->inotify_data.user->inotify_devs);
-       free_uid(group->inotify_data.user);
+       if (group->inotify_data.user) {
+               atomic_dec(&group->inotify_data.user->inotify_devs);
+               free_uid(group->inotify_data.user);
+       }
 }
 
 static void inotify_free_event(struct fsnotify_event *fsn_event)
index dd6103cc93c1b4cd50c14400372ecdfae33bfe91..825a54e8f49072be78f5b56dab3fcac90d211994 100644 (file)
@@ -112,7 +112,7 @@ void __ntfs_error(const char *function, const struct super_block *sb,
 /* If 1, output debug messages, and if 0, don't. */
 int debug_msgs = 0;
 
-void __ntfs_debug (const char *file, int line, const char *function,
+void __ntfs_debug(const char *file, int line, const char *function,
                const char *fmt, ...)
 {
        struct va_format vaf;
index f5ec1ce7a53284969d600b4fb4027fc9e387d40d..643faa44f22b0b0a97b493cbbce0db6f745670d2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * file.c - NTFS kernel file operations.  Part of the Linux-NTFS project.
  *
- * Copyright (c) 2001-2011 Anton Altaparmakov and Tuxera Inc.
+ * Copyright (c) 2001-2014 Anton Altaparmakov and Tuxera Inc.
  *
  * This program/include file is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as published
@@ -410,7 +410,8 @@ static inline int __ntfs_grab_cache_pages(struct address_space *mapping,
        BUG_ON(!nr_pages);
        err = nr = 0;
        do {
-               pages[nr] = find_lock_page(mapping, index);
+               pages[nr] = find_get_page_flags(mapping, index, FGP_LOCK |
+                               FGP_ACCESSED);
                if (!pages[nr]) {
                        if (!*cached_page) {
                                *cached_page = page_cache_alloc(mapping);
index 6c3296e546c31a0c39fe420db338fb12cd18423c..9e1e112074fb7cd8c7d5de10b43ac60af686017b 100644 (file)
@@ -3208,7 +3208,7 @@ static void __exit exit_ntfs_fs(void)
 }
 
 MODULE_AUTHOR("Anton Altaparmakov <anton@tuxera.com>");
-MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2011 Anton Altaparmakov and Tuxera Inc.");
+MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2014 Anton Altaparmakov and Tuxera Inc.");
 MODULE_VERSION(NTFS_VERSION);
 MODULE_LICENSE("GPL");
 #ifdef DEBUG
index 4a231a166cf88d6f76495ab9420e7406ba10307a..1ef547e493731cdd55b0b919a8a45f3dc4faa596 100644 (file)
@@ -1481,8 +1481,16 @@ static int ocfs2_write_begin_inline(struct address_space *mapping,
        handle_t *handle;
        struct ocfs2_dinode *di = (struct ocfs2_dinode *)wc->w_di_bh->b_data;
 
+       handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               goto out;
+       }
+
        page = find_or_create_page(mapping, 0, GFP_NOFS);
        if (!page) {
+               ocfs2_commit_trans(osb, handle);
                ret = -ENOMEM;
                mlog_errno(ret);
                goto out;
@@ -1494,13 +1502,6 @@ static int ocfs2_write_begin_inline(struct address_space *mapping,
        wc->w_pages[0] = wc->w_target_page = page;
        wc->w_num_pages = 1;
 
-       handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
-       if (IS_ERR(handle)) {
-               ret = PTR_ERR(handle);
-               mlog_errno(ret);
-               goto out;
-       }
-
        ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), wc->w_di_bh,
                                      OCFS2_JOURNAL_ACCESS_WRITE);
        if (ret) {
index 73039295d0d1f35205e060220521934afd33ff27..d1338544816896dc773fc930b43dc44bcc157266 100644 (file)
@@ -2572,6 +2572,25 @@ int o2hb_check_node_heartbeating(u8 node_num)
 }
 EXPORT_SYMBOL_GPL(o2hb_check_node_heartbeating);
 
+int o2hb_check_node_heartbeating_no_sem(u8 node_num)
+{
+       unsigned long testing_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
+       unsigned long flags;
+
+       spin_lock_irqsave(&o2hb_live_lock, flags);
+       o2hb_fill_node_map_from_callback(testing_map, sizeof(testing_map));
+       spin_unlock_irqrestore(&o2hb_live_lock, flags);
+       if (!test_bit(node_num, testing_map)) {
+               mlog(ML_HEARTBEAT,
+                    "node (%u) does not have heartbeating enabled.\n",
+                    node_num);
+               return 0;
+       }
+
+       return 1;
+}
+EXPORT_SYMBOL_GPL(o2hb_check_node_heartbeating_no_sem);
+
 int o2hb_check_node_heartbeating_from_callback(u8 node_num)
 {
        unsigned long testing_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
index 00ad8e8fea510ede2cf33d48eecf9178d65a9605..3ef5137dc362200701a6fbd4c93f9724a4594ec2 100644 (file)
@@ -80,6 +80,7 @@ void o2hb_fill_node_map(unsigned long *map,
 void o2hb_exit(void);
 int o2hb_init(void);
 int o2hb_check_node_heartbeating(u8 node_num);
+int o2hb_check_node_heartbeating_no_sem(u8 node_num);
 int o2hb_check_node_heartbeating_from_callback(u8 node_num);
 int o2hb_check_local_node_heartbeating(void);
 void o2hb_stop_all_regions(void);
index 73ba81928bce73444b6e0dec62433c75f7602d66..27d1242c8383ba6a6962749cc4443d2feef77c93 100644 (file)
@@ -185,29 +185,13 @@ static const struct seq_operations nst_seq_ops = {
 static int nst_fop_open(struct inode *inode, struct file *file)
 {
        struct o2net_send_tracking *dummy_nst;
-       struct seq_file *seq;
-       int ret;
 
-       dummy_nst = kmalloc(sizeof(struct o2net_send_tracking), GFP_KERNEL);
-       if (dummy_nst == NULL) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       dummy_nst->st_task = NULL;
-
-       ret = seq_open(file, &nst_seq_ops);
-       if (ret)
-               goto out;
-
-       seq = file->private_data;
-       seq->private = dummy_nst;
+       dummy_nst = __seq_open_private(file, &nst_seq_ops, sizeof(*dummy_nst));
+       if (!dummy_nst)
+               return -ENOMEM;
        o2net_debug_add_nst(dummy_nst);
 
-       dummy_nst = NULL;
-
-out:
-       kfree(dummy_nst);
-       return ret;
+       return 0;
 }
 
 static int nst_fop_release(struct inode *inode, struct file *file)
@@ -412,33 +396,27 @@ static const struct seq_operations sc_seq_ops = {
        .show = sc_seq_show,
 };
 
-static int sc_common_open(struct file *file, struct o2net_sock_debug *sd)
+static int sc_common_open(struct file *file, int ctxt)
 {
+       struct o2net_sock_debug *sd;
        struct o2net_sock_container *dummy_sc;
-       struct seq_file *seq;
-       int ret;
 
-       dummy_sc = kmalloc(sizeof(struct o2net_sock_container), GFP_KERNEL);
-       if (dummy_sc == NULL) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       dummy_sc->sc_page = NULL;
+       dummy_sc = kzalloc(sizeof(*dummy_sc), GFP_KERNEL);
+       if (!dummy_sc)
+               return -ENOMEM;
 
-       ret = seq_open(file, &sc_seq_ops);
-       if (ret)
-               goto out;
+       sd = __seq_open_private(file, &sc_seq_ops, sizeof(*sd));
+       if (!sd) {
+               kfree(dummy_sc);
+               return -ENOMEM;
+       }
 
-       seq = file->private_data;
-       seq->private = sd;
+       sd->dbg_ctxt = ctxt;
        sd->dbg_sock = dummy_sc;
-       o2net_debug_add_sc(dummy_sc);
 
-       dummy_sc = NULL;
+       o2net_debug_add_sc(dummy_sc);
 
-out:
-       kfree(dummy_sc);
-       return ret;
+       return 0;
 }
 
 static int sc_fop_release(struct inode *inode, struct file *file)
@@ -453,16 +431,7 @@ static int sc_fop_release(struct inode *inode, struct file *file)
 
 static int stats_fop_open(struct inode *inode, struct file *file)
 {
-       struct o2net_sock_debug *sd;
-
-       sd = kmalloc(sizeof(struct o2net_sock_debug), GFP_KERNEL);
-       if (sd == NULL)
-               return -ENOMEM;
-
-       sd->dbg_ctxt = SHOW_SOCK_STATS;
-       sd->dbg_sock = NULL;
-
-       return sc_common_open(file, sd);
+       return sc_common_open(file, SHOW_SOCK_STATS);
 }
 
 static const struct file_operations stats_seq_fops = {
@@ -474,16 +443,7 @@ static const struct file_operations stats_seq_fops = {
 
 static int sc_fop_open(struct inode *inode, struct file *file)
 {
-       struct o2net_sock_debug *sd;
-
-       sd = kmalloc(sizeof(struct o2net_sock_debug), GFP_KERNEL);
-       if (sd == NULL)
-               return -ENOMEM;
-
-       sd->dbg_ctxt = SHOW_SOCK_CONTAINERS;
-       sd->dbg_sock = NULL;
-
-       return sc_common_open(file, sd);
+       return sc_common_open(file, SHOW_SOCK_CONTAINERS);
 }
 
 static const struct file_operations sc_seq_fops = {
index ea34952f9496a6bfaa698165d6b818bd601ebf17..97de0fbd9f784196312d2330b34e43873b6c6ecc 100644 (file)
@@ -536,7 +536,7 @@ static void o2net_set_nn_state(struct o2net_node *nn,
        if (nn->nn_persistent_error || nn->nn_sc_valid)
                wake_up(&nn->nn_sc_wq);
 
-       if (!was_err && nn->nn_persistent_error) {
+       if (was_valid && !was_err && nn->nn_persistent_error) {
                o2quo_conn_err(o2net_num_from_nn(nn));
                queue_delayed_work(o2net_wq, &nn->nn_still_up,
                                   msecs_to_jiffies(O2NET_QUORUM_DELAY_MS));
@@ -1601,7 +1601,15 @@ static void o2net_start_connect(struct work_struct *work)
        struct sockaddr_in myaddr = {0, }, remoteaddr = {0, };
        int ret = 0, stop;
        unsigned int timeout;
+       unsigned int noio_flag;
 
+       /*
+        * sock_create allocates the sock with GFP_KERNEL. We must set
+        * per-process flag PF_MEMALLOC_NOIO so that all allocations done
+        * by this process are done as if GFP_NOIO was specified. So we
+        * are not reentering filesystem while doing memory reclaim.
+        */
+       noio_flag = memalloc_noio_save();
        /* if we're greater we initiate tx, otherwise we accept */
        if (o2nm_this_node() <= o2net_num_from_nn(nn))
                goto out;
@@ -1710,6 +1718,7 @@ out:
        if (mynode)
                o2nm_node_put(mynode);
 
+       memalloc_noio_restore(noio_flag);
        return;
 }
 
@@ -1721,7 +1730,8 @@ static void o2net_connect_expired(struct work_struct *work)
        spin_lock(&nn->nn_lock);
        if (!nn->nn_sc_valid) {
                printk(KERN_NOTICE "o2net: No connection established with "
-                      "node %u after %u.%u seconds, giving up.\n",
+                      "node %u after %u.%u seconds, check network and"
+                      " cluster configuration.\n",
                     o2net_num_from_nn(nn),
                     o2net_idle_timeout() / 1000,
                     o2net_idle_timeout() % 1000);
@@ -1835,6 +1845,15 @@ static int o2net_accept_one(struct socket *sock, int *more)
        struct o2nm_node *local_node = NULL;
        struct o2net_sock_container *sc = NULL;
        struct o2net_node *nn;
+       unsigned int noio_flag;
+
+       /*
+        * sock_create_lite allocates the sock with GFP_KERNEL. We must set
+        * per-process flag PF_MEMALLOC_NOIO so that all allocations done
+        * by this process are done as if GFP_NOIO was specified. So we
+        * are not reentering filesystem while doing memory reclaim.
+        */
+       noio_flag = memalloc_noio_save();
 
        BUG_ON(sock == NULL);
        *more = 0;
@@ -1951,6 +1970,8 @@ out:
                o2nm_node_put(local_node);
        if (sc)
                sc_put(sc);
+
+       memalloc_noio_restore(noio_flag);
        return ret;
 }
 
@@ -2146,17 +2167,13 @@ int o2net_init(void)
        o2quo_init();
 
        if (o2net_debugfs_init())
-               return -ENOMEM;
+               goto out;
 
        o2net_hand = kzalloc(sizeof(struct o2net_handshake), GFP_KERNEL);
        o2net_keep_req = kzalloc(sizeof(struct o2net_msg), GFP_KERNEL);
        o2net_keep_resp = kzalloc(sizeof(struct o2net_msg), GFP_KERNEL);
-       if (!o2net_hand || !o2net_keep_req || !o2net_keep_resp) {
-               kfree(o2net_hand);
-               kfree(o2net_keep_req);
-               kfree(o2net_keep_resp);
-               return -ENOMEM;
-       }
+       if (!o2net_hand || !o2net_keep_req || !o2net_keep_resp)
+               goto out;
 
        o2net_hand->protocol_version = cpu_to_be64(O2NET_PROTOCOL_VERSION);
        o2net_hand->connector_id = cpu_to_be64(1);
@@ -2181,6 +2198,14 @@ int o2net_init(void)
        }
 
        return 0;
+
+out:
+       kfree(o2net_hand);
+       kfree(o2net_keep_req);
+       kfree(o2net_keep_resp);
+
+       o2quo_exit();
+       return -ENOMEM;
 }
 
 void o2net_exit(void)
index 18f13c2e4a108880ce95eef38874df54fb6797bb..149eb556b8c6300aabc136033c93953494d0eaf1 100644 (file)
@@ -647,41 +647,30 @@ static const struct seq_operations debug_lockres_ops = {
 static int debug_lockres_open(struct inode *inode, struct file *file)
 {
        struct dlm_ctxt *dlm = inode->i_private;
-       int ret = -ENOMEM;
-       struct seq_file *seq;
-       struct debug_lockres *dl = NULL;
+       struct debug_lockres *dl;
+       void *buf;
 
-       dl = kzalloc(sizeof(struct debug_lockres), GFP_KERNEL);
-       if (!dl) {
-               mlog_errno(ret);
+       buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
                goto bail;
-       }
 
-       dl->dl_len = PAGE_SIZE;
-       dl->dl_buf = kmalloc(dl->dl_len, GFP_KERNEL);
-       if (!dl->dl_buf) {
-               mlog_errno(ret);
-               goto bail;
-       }
+       dl = __seq_open_private(file, &debug_lockres_ops, sizeof(*dl));
+       if (!dl)
+               goto bailfree;
 
-       ret = seq_open(file, &debug_lockres_ops);
-       if (ret) {
-               mlog_errno(ret);
-               goto bail;
-       }
-
-       seq = file->private_data;
-       seq->private = dl;
+       dl->dl_len = PAGE_SIZE;
+       dl->dl_buf = buf;
 
        dlm_grab(dlm);
        dl->dl_ctxt = dlm;
 
        return 0;
+
+bailfree:
+       kfree(buf);
 bail:
-       if (dl)
-               kfree(dl->dl_buf);
-       kfree(dl);
-       return ret;
+       mlog_errno(-ENOMEM);
+       return -ENOMEM;
 }
 
 static int debug_lockres_release(struct inode *inode, struct file *file)
index 3fcf205ee900acb87eaa5f74f7c28949ee95bec4..02d315fef432a73c5f9af130c37dd2b0c60b3852 100644 (file)
@@ -839,7 +839,7 @@ static int dlm_query_join_handler(struct o2net_msg *msg, u32 len, void *data,
         * to back off and try again.  This gives heartbeat a chance
         * to catch up.
         */
-       if (!o2hb_check_node_heartbeating(query->node_idx)) {
+       if (!o2hb_check_node_heartbeating_no_sem(query->node_idx)) {
                mlog(0, "node %u is not in our live map yet\n",
                     query->node_idx);
 
@@ -1975,24 +1975,22 @@ static struct dlm_ctxt *dlm_alloc_ctxt(const char *domain,
 
        dlm = kzalloc(sizeof(*dlm), GFP_KERNEL);
        if (!dlm) {
-               mlog_errno(-ENOMEM);
+               ret = -ENOMEM;
+               mlog_errno(ret);
                goto leave;
        }
 
        dlm->name = kstrdup(domain, GFP_KERNEL);
        if (dlm->name == NULL) {
-               mlog_errno(-ENOMEM);
-               kfree(dlm);
-               dlm = NULL;
+               ret = -ENOMEM;
+               mlog_errno(ret);
                goto leave;
        }
 
        dlm->lockres_hash = (struct hlist_head **)dlm_alloc_pagevec(DLM_HASH_PAGES);
        if (!dlm->lockres_hash) {
-               mlog_errno(-ENOMEM);
-               kfree(dlm->name);
-               kfree(dlm);
-               dlm = NULL;
+               ret = -ENOMEM;
+               mlog_errno(ret);
                goto leave;
        }
 
@@ -2002,11 +2000,8 @@ static struct dlm_ctxt *dlm_alloc_ctxt(const char *domain,
        dlm->master_hash = (struct hlist_head **)
                                dlm_alloc_pagevec(DLM_HASH_PAGES);
        if (!dlm->master_hash) {
-               mlog_errno(-ENOMEM);
-               dlm_free_pagevec((void **)dlm->lockres_hash, DLM_HASH_PAGES);
-               kfree(dlm->name);
-               kfree(dlm);
-               dlm = NULL;
+               ret = -ENOMEM;
+               mlog_errno(ret);
                goto leave;
        }
 
@@ -2017,14 +2012,8 @@ static struct dlm_ctxt *dlm_alloc_ctxt(const char *domain,
        dlm->node_num = o2nm_this_node();
 
        ret = dlm_create_debugfs_subroot(dlm);
-       if (ret < 0) {
-               dlm_free_pagevec((void **)dlm->master_hash, DLM_HASH_PAGES);
-               dlm_free_pagevec((void **)dlm->lockres_hash, DLM_HASH_PAGES);
-               kfree(dlm->name);
-               kfree(dlm);
-               dlm = NULL;
+       if (ret < 0)
                goto leave;
-       }
 
        spin_lock_init(&dlm->spinlock);
        spin_lock_init(&dlm->master_lock);
@@ -2085,6 +2074,19 @@ static struct dlm_ctxt *dlm_alloc_ctxt(const char *domain,
                  atomic_read(&dlm->dlm_refs.refcount));
 
 leave:
+       if (ret < 0 && dlm) {
+               if (dlm->master_hash)
+                       dlm_free_pagevec((void **)dlm->master_hash,
+                                       DLM_HASH_PAGES);
+
+               if (dlm->lockres_hash)
+                       dlm_free_pagevec((void **)dlm->lockres_hash,
+                                       DLM_HASH_PAGES);
+
+               kfree(dlm->name);
+               kfree(dlm);
+               dlm = NULL;
+       }
        return dlm;
 }
 
index 12ba682fc53c303964ee4eb2c0035c88765573d0..215e41abf101a1e70afbe2b151980ed237a632a0 100644 (file)
@@ -625,9 +625,6 @@ struct dlm_lock_resource *dlm_new_lockres(struct dlm_ctxt *dlm,
        return res;
 
 error:
-       if (res && res->lockname.name)
-               kmem_cache_free(dlm_lockname_cache, (void *)res->lockname.name);
-
        if (res)
                kmem_cache_free(dlm_lockres_cache, res);
        return NULL;
index 45067faf5695d54d74af0933c8ea403ec4f1a5b3..3365839d29716f101759c6dc86355393ee7c3ab4 100644 (file)
@@ -1710,9 +1710,12 @@ int dlm_master_requery_handler(struct o2net_msg *msg, u32 len, void *data,
                                BUG();
                        } else
                                __dlm_lockres_grab_inflight_worker(dlm, res);
-               } else /* put.. incase we are not the master */
+                       spin_unlock(&res->spinlock);
+               } else {
+                       /* put.. incase we are not the master */
+                       spin_unlock(&res->spinlock);
                        dlm_lockres_put(res);
-               spin_unlock(&res->spinlock);
+               }
        }
        spin_unlock(&dlm->spinlock);
 
index 52cfe99ae0565f20295139f543cf2a3d490eeeb2..21262f2b16545df84d4da4b901bd9d5bf9b568cc 100644 (file)
@@ -2892,37 +2892,24 @@ static int ocfs2_dlm_debug_release(struct inode *inode, struct file *file)
 
 static int ocfs2_dlm_debug_open(struct inode *inode, struct file *file)
 {
-       int ret;
        struct ocfs2_dlm_seq_priv *priv;
-       struct seq_file *seq;
        struct ocfs2_super *osb;
 
-       priv = kzalloc(sizeof(struct ocfs2_dlm_seq_priv), GFP_KERNEL);
+       priv = __seq_open_private(file, &ocfs2_dlm_seq_ops, sizeof(*priv));
        if (!priv) {
-               ret = -ENOMEM;
-               mlog_errno(ret);
-               goto out;
+               mlog_errno(-ENOMEM);
+               return -ENOMEM;
        }
+
        osb = inode->i_private;
        ocfs2_get_dlm_debug(osb->osb_dlm_debug);
        priv->p_dlm_debug = osb->osb_dlm_debug;
        INIT_LIST_HEAD(&priv->p_iter_res.l_debug_list);
 
-       ret = seq_open(file, &ocfs2_dlm_seq_ops);
-       if (ret) {
-               kfree(priv);
-               mlog_errno(ret);
-               goto out;
-       }
-
-       seq = file->private_data;
-       seq->private = priv;
-
        ocfs2_add_lockres_tracking(&priv->p_iter_res,
                                   priv->p_dlm_debug);
 
-out:
-       return ret;
+       return 0;
 }
 
 static const struct file_operations ocfs2_dlm_debug_fops = {
index 2930e231f3f9fbda2807190726d6ceffffa2a6b5..324dc93ac896c073886846e0d883d1d8f5bd30ca 100644 (file)
@@ -760,7 +760,7 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from,
        struct address_space *mapping = inode->i_mapping;
        struct page *page;
        unsigned long index = abs_from >> PAGE_CACHE_SHIFT;
-       handle_t *handle = NULL;
+       handle_t *handle;
        int ret = 0;
        unsigned zero_from, zero_to, block_start, block_end;
        struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
@@ -769,11 +769,17 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from,
        BUG_ON(abs_to > (((u64)index + 1) << PAGE_CACHE_SHIFT));
        BUG_ON(abs_from & (inode->i_blkbits - 1));
 
+       handle = ocfs2_zero_start_ordered_transaction(inode, di_bh);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               goto out;
+       }
+
        page = find_or_create_page(mapping, index, GFP_NOFS);
        if (!page) {
                ret = -ENOMEM;
                mlog_errno(ret);
-               goto out;
+               goto out_commit_trans;
        }
 
        /* Get the offsets within the page that we want to zero */
@@ -805,15 +811,6 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from,
                        goto out_unlock;
                }
 
-               if (!handle) {
-                       handle = ocfs2_zero_start_ordered_transaction(inode,
-                                                                     di_bh);
-                       if (IS_ERR(handle)) {
-                               ret = PTR_ERR(handle);
-                               handle = NULL;
-                               break;
-                       }
-               }
 
                /* must not update i_size! */
                ret = block_commit_write(page, block_start + 1,
@@ -824,27 +821,29 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from,
                        ret = 0;
        }
 
+       /*
+        * fs-writeback will release the dirty pages without page lock
+        * whose offset are over inode size, the release happens at
+        * block_write_full_page().
+        */
+       i_size_write(inode, abs_to);
+       inode->i_blocks = ocfs2_inode_sector_count(inode);
+       di->i_size = cpu_to_le64((u64)i_size_read(inode));
+       inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+       di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec);
+       di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
+       di->i_mtime_nsec = di->i_ctime_nsec;
        if (handle) {
-               /*
-                * fs-writeback will release the dirty pages without page lock
-                * whose offset are over inode size, the release happens at
-                * block_write_full_page().
-                */
-               i_size_write(inode, abs_to);
-               inode->i_blocks = ocfs2_inode_sector_count(inode);
-               di->i_size = cpu_to_le64((u64)i_size_read(inode));
-               inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-               di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec);
-               di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
-               di->i_mtime_nsec = di->i_ctime_nsec;
                ocfs2_journal_dirty(handle, di_bh);
                ocfs2_update_inode_fsync_trans(handle, inode, 1);
-               ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
        }
 
 out_unlock:
        unlock_page(page);
        page_cache_release(page);
+out_commit_trans:
+       if (handle)
+               ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
 out:
        return ret;
 }
@@ -1253,7 +1252,7 @@ bail:
        brelse(bh);
 
        /* Release quota pointers in case we acquired them */
-       for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+       for (qtype = 0; qtype < OCFS2_MAXQUOTAS; qtype++)
                dqput(transfer_to[qtype]);
 
        if (!status && attr->ia_valid & ATTR_MODE) {
index a6c991c0fc98cf413a846ff75c1edf62c1348fcd..a9b76de46047d02f754aa8d9fbdcd96ff74b853c 100644 (file)
@@ -162,7 +162,7 @@ static inline blkcnt_t ocfs2_inode_sector_count(struct inode *inode)
 {
        int c_to_s_bits = OCFS2_SB(inode->i_sb)->s_clustersize_bits - 9;
 
-       return (blkcnt_t)(OCFS2_I(inode)->ip_clusters << c_to_s_bits);
+       return (blkcnt_t)OCFS2_I(inode)->ip_clusters << c_to_s_bits;
 }
 
 /* Validate that a bh contains a valid inode */
index 6219aaadeb08dadeb44ebdacb2b2559edfc52d7c..74caffeeee1d5453a2ec7126b11dd9a9676d9b08 100644 (file)
@@ -404,7 +404,7 @@ static int ocfs2_find_victim_alloc_group(struct inode *inode,
         * 'vict_blkno' was out of the valid range.
         */
        if ((vict_blkno < le64_to_cpu(rec->c_blkno)) ||
-           (vict_blkno >= (le32_to_cpu(ac_dinode->id1.bitmap1.i_total) <<
+           (vict_blkno >= ((u64)le32_to_cpu(ac_dinode->id1.bitmap1.i_total) <<
                                bits_per_unit))) {
                ret = -EINVAL;
                goto out;
index f266d67df3c6e07427942b5c2c61780e2e81220b..1eae330193a63f7a212c475f8df4a42701d28dc3 100644 (file)
@@ -17,6 +17,9 @@
 
 #include "ocfs2.h"
 
+/* Number of quota types we support */
+#define OCFS2_MAXQUOTAS 2
+
 /*
  * In-memory structures
  */
@@ -39,7 +42,7 @@ struct ocfs2_recovery_chunk {
 };
 
 struct ocfs2_quota_recovery {
-       struct list_head r_list[MAXQUOTAS];     /* List of chunks to recover */
+       struct list_head r_list[OCFS2_MAXQUOTAS];       /* List of chunks to recover */
 };
 
 /* In-memory structure with quota header information */
index b990a62cff50c4b6d3bd18f6a5ecede3fe14d440..c93d6722088753901c258151de82dc707e6eb71e 100644 (file)
@@ -336,8 +336,8 @@ void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex)
 int ocfs2_global_read_info(struct super_block *sb, int type)
 {
        struct inode *gqinode = NULL;
-       unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
-                                       GROUP_QUOTA_SYSTEM_INODE };
+       unsigned int ino[OCFS2_MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
+                                             GROUP_QUOTA_SYSTEM_INODE };
        struct ocfs2_global_disk_dqinfo dinfo;
        struct mem_dqinfo *info = sb_dqinfo(sb, type);
        struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
index 2001862bf2b1cfbb20ed78bd9febeacbc96b42a4..10b653930ee26aa97320e827d01499e19ffe2b16 100644 (file)
@@ -166,12 +166,12 @@ static int ocfs2_read_quota_block(struct inode *inode, u64 v_block,
 /* Check whether we understand format of quota files */
 static int ocfs2_local_check_quota_file(struct super_block *sb, int type)
 {
-       unsigned int lmagics[MAXQUOTAS] = OCFS2_LOCAL_QMAGICS;
-       unsigned int lversions[MAXQUOTAS] = OCFS2_LOCAL_QVERSIONS;
-       unsigned int gmagics[MAXQUOTAS] = OCFS2_GLOBAL_QMAGICS;
-       unsigned int gversions[MAXQUOTAS] = OCFS2_GLOBAL_QVERSIONS;
-       unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
-                                       GROUP_QUOTA_SYSTEM_INODE };
+       unsigned int lmagics[OCFS2_MAXQUOTAS] = OCFS2_LOCAL_QMAGICS;
+       unsigned int lversions[OCFS2_MAXQUOTAS] = OCFS2_LOCAL_QVERSIONS;
+       unsigned int gmagics[OCFS2_MAXQUOTAS] = OCFS2_GLOBAL_QMAGICS;
+       unsigned int gversions[OCFS2_MAXQUOTAS] = OCFS2_GLOBAL_QVERSIONS;
+       unsigned int ino[OCFS2_MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
+                                             GROUP_QUOTA_SYSTEM_INODE };
        struct buffer_head *bh = NULL;
        struct inode *linode = sb_dqopt(sb)->files[type];
        struct inode *ginode = NULL;
@@ -336,7 +336,7 @@ void ocfs2_free_quota_recovery(struct ocfs2_quota_recovery *rec)
 {
        int type;
 
-       for (type = 0; type < MAXQUOTAS; type++)
+       for (type = 0; type < OCFS2_MAXQUOTAS; type++)
                free_recovery_list(&(rec->r_list[type]));
        kfree(rec);
 }
@@ -382,7 +382,7 @@ static struct ocfs2_quota_recovery *ocfs2_alloc_quota_recovery(void)
        rec = kmalloc(sizeof(struct ocfs2_quota_recovery), GFP_NOFS);
        if (!rec)
                return NULL;
-       for (type = 0; type < MAXQUOTAS; type++)
+       for (type = 0; type < OCFS2_MAXQUOTAS; type++)
                INIT_LIST_HEAD(&(rec->r_list[type]));
        return rec;
 }
@@ -392,10 +392,11 @@ struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
                                                struct ocfs2_super *osb,
                                                int slot_num)
 {
-       unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
-                                           OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
-       unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
-                                       LOCAL_GROUP_QUOTA_SYSTEM_INODE };
+       unsigned int feature[OCFS2_MAXQUOTAS] = {
+                                       OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+                                       OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+       unsigned int ino[OCFS2_MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
+                                             LOCAL_GROUP_QUOTA_SYSTEM_INODE };
        struct super_block *sb = osb->sb;
        struct ocfs2_local_disk_dqinfo *ldinfo;
        struct inode *lqinode;
@@ -412,7 +413,7 @@ struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
                return ERR_PTR(-ENOMEM);
        /* First init... */
 
-       for (type = 0; type < MAXQUOTAS; type++) {
+       for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
                if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
                        continue;
                /* At this point, journal of the slot is already replayed so
@@ -589,8 +590,8 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
                                struct ocfs2_quota_recovery *rec,
                                int slot_num)
 {
-       unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
-                                       LOCAL_GROUP_QUOTA_SYSTEM_INODE };
+       unsigned int ino[OCFS2_MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
+                                             LOCAL_GROUP_QUOTA_SYSTEM_INODE };
        struct super_block *sb = osb->sb;
        struct ocfs2_local_disk_dqinfo *ldinfo;
        struct buffer_head *bh;
@@ -604,7 +605,7 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
               "slot %u\n", osb->dev_str, slot_num);
 
        mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
-       for (type = 0; type < MAXQUOTAS; type++) {
+       for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
                if (list_empty(&(rec->r_list[type])))
                        continue;
                trace_ocfs2_finish_quota_recovery(slot_num);
index 13a8537d8e8b0b5732fa730dcff2870255a26e07..720aa389e0eae60c2b0d01a7038b73c268932d5a 100644 (file)
@@ -591,7 +591,7 @@ static int ocfs2_control_release(struct inode *inode, struct file *file)
                 */
                ocfs2_control_this_node = -1;
                running_proto.pv_major = 0;
-               running_proto.pv_major = 0;
+               running_proto.pv_minor = 0;
        }
 
 out:
index 4142546aedae95e668487b9ba8069be1ec929227..93c85bc745e199983550b2e181b757e151cec8d5 100644 (file)
@@ -899,11 +899,12 @@ static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend)
 {
        int type;
        struct super_block *sb = osb->sb;
-       unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
-                                            OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+       unsigned int feature[OCFS2_MAXQUOTAS] = {
+                                       OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+                                       OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
        int status = 0;
 
-       for (type = 0; type < MAXQUOTAS; type++) {
+       for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
                if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
                        continue;
                if (unsuspend)
@@ -927,17 +928,19 @@ static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend)
 
 static int ocfs2_enable_quotas(struct ocfs2_super *osb)
 {
-       struct inode *inode[MAXQUOTAS] = { NULL, NULL };
+       struct inode *inode[OCFS2_MAXQUOTAS] = { NULL, NULL };
        struct super_block *sb = osb->sb;
-       unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
-                                            OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
-       unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
+       unsigned int feature[OCFS2_MAXQUOTAS] = {
+                                       OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+                                       OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+       unsigned int ino[OCFS2_MAXQUOTAS] = {
+                                       LOCAL_USER_QUOTA_SYSTEM_INODE,
                                        LOCAL_GROUP_QUOTA_SYSTEM_INODE };
        int status;
        int type;
 
        sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NEGATIVE_USAGE;
-       for (type = 0; type < MAXQUOTAS; type++) {
+       for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
                if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
                        continue;
                inode[type] = ocfs2_get_system_file_inode(osb, ino[type],
@@ -952,12 +955,12 @@ static int ocfs2_enable_quotas(struct ocfs2_super *osb)
                        goto out_quota_off;
        }
 
-       for (type = 0; type < MAXQUOTAS; type++)
+       for (type = 0; type < OCFS2_MAXQUOTAS; type++)
                iput(inode[type]);
        return 0;
 out_quota_off:
        ocfs2_disable_quotas(osb);
-       for (type = 0; type < MAXQUOTAS; type++)
+       for (type = 0; type < OCFS2_MAXQUOTAS; type++)
                iput(inode[type]);
        mlog_errno(status);
        return status;
@@ -972,7 +975,7 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb)
 
        /* We mostly ignore errors in this function because there's not much
         * we can do when we see them */
-       for (type = 0; type < MAXQUOTAS; type++) {
+       for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
                if (!sb_has_quota_loaded(sb, type))
                        continue;
                /* Cancel periodic syncing before we grab dqonoff_mutex */
@@ -993,8 +996,9 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb)
 /* Handle quota on quotactl */
 static int ocfs2_quota_on(struct super_block *sb, int type, int format_id)
 {
-       unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
-                                            OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+       unsigned int feature[OCFS2_MAXQUOTAS] = {
+                                       OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+                                       OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
 
        if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
                return -EINVAL;
index baf852b648ad7d2b4512d7279d5e97a0fb473cb6..950100e326a10d2d4819e6e031ac0f7cea4c0258 100644 (file)
@@ -376,37 +376,6 @@ static const struct file_operations proc_lstats_operations = {
 
 #endif
 
-#ifdef CONFIG_CGROUPS
-static int cgroup_open(struct inode *inode, struct file *file)
-{
-       struct pid *pid = PROC_I(inode)->pid;
-       return single_open(file, proc_cgroup_show, pid);
-}
-
-static const struct file_operations proc_cgroup_operations = {
-       .open           = cgroup_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-#endif
-
-#ifdef CONFIG_PROC_PID_CPUSET
-
-static int cpuset_open(struct inode *inode, struct file *file)
-{
-       struct pid *pid = PROC_I(inode)->pid;
-       return single_open(file, proc_cpuset_show, pid);
-}
-
-static const struct file_operations proc_cpuset_operations = {
-       .open           = cpuset_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-#endif
-
 static int proc_oom_score(struct seq_file *m, struct pid_namespace *ns,
                          struct pid *pid, struct task_struct *task)
 {
@@ -632,29 +601,35 @@ static const struct file_operations proc_single_file_operations = {
        .release        = single_release,
 };
 
-static int __mem_open(struct inode *inode, struct file *file, unsigned int mode)
+
+struct mm_struct *proc_mem_open(struct inode *inode, unsigned int mode)
 {
-       struct task_struct *task = get_proc_task(file_inode(file));
-       struct mm_struct *mm;
+       struct task_struct *task = get_proc_task(inode);
+       struct mm_struct *mm = ERR_PTR(-ESRCH);
 
-       if (!task)
-               return -ESRCH;
+       if (task) {
+               mm = mm_access(task, mode);
+               put_task_struct(task);
 
-       mm = mm_access(task, mode);
-       put_task_struct(task);
+               if (!IS_ERR_OR_NULL(mm)) {
+                       /* ensure this mm_struct can't be freed */
+                       atomic_inc(&mm->mm_count);
+                       /* but do not pin its memory */
+                       mmput(mm);
+               }
+       }
+
+       return mm;
+}
+
+static int __mem_open(struct inode *inode, struct file *file, unsigned int mode)
+{
+       struct mm_struct *mm = proc_mem_open(inode, mode);
 
        if (IS_ERR(mm))
                return PTR_ERR(mm);
 
-       if (mm) {
-               /* ensure this mm_struct can't be freed */
-               atomic_inc(&mm->mm_count);
-               /* but do not pin its memory */
-               mmput(mm);
-       }
-
        file->private_data = mm;
-
        return 0;
 }
 
@@ -2573,10 +2548,10 @@ static const struct pid_entry tgid_base_stuff[] = {
        REG("latency",  S_IRUGO, proc_lstats_operations),
 #endif
 #ifdef CONFIG_PROC_PID_CPUSET
-       REG("cpuset",     S_IRUGO, proc_cpuset_operations),
+       ONE("cpuset",     S_IRUGO, proc_cpuset_show),
 #endif
 #ifdef CONFIG_CGROUPS
-       REG("cgroup",  S_IRUGO, proc_cgroup_operations),
+       ONE("cgroup",  S_IRUGO, proc_cgroup_show),
 #endif
        ONE("oom_score",  S_IRUGO, proc_oom_score),
        REG("oom_adj",    S_IRUGO|S_IWUSR, proc_oom_adj_operations),
@@ -2919,10 +2894,10 @@ static const struct pid_entry tid_base_stuff[] = {
        REG("latency",  S_IRUGO, proc_lstats_operations),
 #endif
 #ifdef CONFIG_PROC_PID_CPUSET
-       REG("cpuset",    S_IRUGO, proc_cpuset_operations),
+       ONE("cpuset",    S_IRUGO, proc_cpuset_show),
 #endif
 #ifdef CONFIG_CGROUPS
-       REG("cgroup",  S_IRUGO, proc_cgroup_operations),
+       ONE("cgroup",  S_IRUGO, proc_cgroup_show),
 #endif
        ONE("oom_score", S_IRUGO, proc_oom_score),
        REG("oom_adj",   S_IRUGO|S_IWUSR, proc_oom_adj_operations),
index 7da13e49128ab2450bd4def6125430e95c50c0a9..aa7a0ee182e19329636b21f079fac074145aaa84 100644 (file)
@@ -268,8 +268,9 @@ extern int proc_remount(struct super_block *, int *, char *);
  * task_[no]mmu.c
  */
 struct proc_maps_private {
-       struct pid *pid;
+       struct inode *inode;
        struct task_struct *task;
+       struct mm_struct *mm;
 #ifdef CONFIG_MMU
        struct vm_area_struct *tail_vma;
 #endif
@@ -278,6 +279,8 @@ struct proc_maps_private {
 #endif
 };
 
+struct mm_struct *proc_mem_open(struct inode *inode, unsigned int mode);
+
 extern const struct file_operations proc_pid_maps_operations;
 extern const struct file_operations proc_tid_maps_operations;
 extern const struct file_operations proc_pid_numa_maps_operations;
index 6df8d0722c970ec57d19dda9dbd2806795c03863..91a4e6426321885eaa226081be2c39ad35a95f74 100644 (file)
@@ -610,8 +610,10 @@ static void __init proc_kcore_text_init(void)
 struct kcore_list kcore_modules;
 static void __init add_modules_range(void)
 {
-       kclist_add(&kcore_modules, (void *)MODULES_VADDR,
+       if (MODULES_VADDR != VMALLOC_START && MODULES_END != VMALLOC_END) {
+               kclist_add(&kcore_modules, (void *)MODULES_VADDR,
                        MODULES_END - MODULES_VADDR, KCORE_VMALLOC);
+       }
 }
 #else
 static void __init add_modules_range(void)
index e647c55275d9ff9f96b92ac859e65c10fc03d318..1e3187da1fedbb76fedd9252a8969c159d7c15a2 100644 (file)
@@ -133,6 +133,9 @@ u64 stable_page_flags(struct page *page)
        if (PageBuddy(page))
                u |= 1 << KPF_BUDDY;
 
+       if (PageBalloon(page))
+               u |= 1 << KPF_BALLOON;
+
        u |= kpf_copy_bit(k, KPF_LOCKED,        PG_locked);
 
        u |= kpf_copy_bit(k, KPF_SLAB,          PG_slab);
index c34156888d706d444a45fd171299ff9548bab93c..b7a7dc963a359229ee7607a8d32b9c28221810bf 100644 (file)
@@ -87,32 +87,14 @@ unsigned long task_statm(struct mm_struct *mm,
 
 #ifdef CONFIG_NUMA
 /*
- * These functions are for numa_maps but called in generic **maps seq_file
- * ->start(), ->stop() ops.
- *
- * numa_maps scans all vmas under mmap_sem and checks their mempolicy.
- * Each mempolicy object is controlled by reference counting. The problem here
- * is how to avoid accessing dead mempolicy object.
- *
- * Because we're holding mmap_sem while reading seq_file, it's safe to access
- * each vma's mempolicy, no vma objects will never drop refs to mempolicy.
- *
- * A task's mempolicy (task->mempolicy) has different behavior. task->mempolicy
- * is set and replaced under mmap_sem but unrefed and cleared under task_lock().
- * So, without task_lock(), we cannot trust get_vma_policy() because we cannot
- * gurantee the task never exits under us. But taking task_lock() around
- * get_vma_plicy() causes lock order problem.
- *
- * To access task->mempolicy without lock, we hold a reference count of an
- * object pointed by task->mempolicy and remember it. This will guarantee
- * that task->mempolicy points to an alive object or NULL in numa_maps accesses.
+ * Save get_task_policy() for show_numa_map().
  */
 static void hold_task_mempolicy(struct proc_maps_private *priv)
 {
        struct task_struct *task = priv->task;
 
        task_lock(task);
-       priv->task_mempolicy = task->mempolicy;
+       priv->task_mempolicy = get_task_policy(task);
        mpol_get(priv->task_mempolicy);
        task_unlock(task);
 }
@@ -129,124 +111,154 @@ static void release_task_mempolicy(struct proc_maps_private *priv)
 }
 #endif
 
-static void vma_stop(struct proc_maps_private *priv, struct vm_area_struct *vma)
+static void vma_stop(struct proc_maps_private *priv)
 {
-       if (vma && vma != priv->tail_vma) {
-               struct mm_struct *mm = vma->vm_mm;
-               release_task_mempolicy(priv);
-               up_read(&mm->mmap_sem);
-               mmput(mm);
-       }
+       struct mm_struct *mm = priv->mm;
+
+       release_task_mempolicy(priv);
+       up_read(&mm->mmap_sem);
+       mmput(mm);
+}
+
+static struct vm_area_struct *
+m_next_vma(struct proc_maps_private *priv, struct vm_area_struct *vma)
+{
+       if (vma == priv->tail_vma)
+               return NULL;
+       return vma->vm_next ?: priv->tail_vma;
+}
+
+static void m_cache_vma(struct seq_file *m, struct vm_area_struct *vma)
+{
+       if (m->count < m->size) /* vma is copied successfully */
+               m->version = m_next_vma(m->private, vma) ? vma->vm_start : -1UL;
 }
 
-static void *m_start(struct seq_file *m, loff_t *pos)
+static void *m_start(struct seq_file *m, loff_t *ppos)
 {
        struct proc_maps_private *priv = m->private;
        unsigned long last_addr = m->version;
        struct mm_struct *mm;
-       struct vm_area_struct *vma, *tail_vma = NULL;
-       loff_t l = *pos;
-
-       /* Clear the per syscall fields in priv */
-       priv->task = NULL;
-       priv->tail_vma = NULL;
-
-       /*
-        * We remember last_addr rather than next_addr to hit with
-        * vmacache most of the time. We have zero last_addr at
-        * the beginning and also after lseek. We will have -1 last_addr
-        * after the end of the vmas.
-        */
+       struct vm_area_struct *vma;
+       unsigned int pos = *ppos;
 
+       /* See m_cache_vma(). Zero at the start or after lseek. */
        if (last_addr == -1UL)
                return NULL;
 
-       priv->task = get_pid_task(priv->pid, PIDTYPE_PID);
+       priv->task = get_proc_task(priv->inode);
        if (!priv->task)
                return ERR_PTR(-ESRCH);
 
-       mm = mm_access(priv->task, PTRACE_MODE_READ);
-       if (!mm || IS_ERR(mm))
-               return mm;
-       down_read(&mm->mmap_sem);
+       mm = priv->mm;
+       if (!mm || !atomic_inc_not_zero(&mm->mm_users))
+               return NULL;
 
-       tail_vma = get_gate_vma(priv->task->mm);
-       priv->tail_vma = tail_vma;
+       down_read(&mm->mmap_sem);
        hold_task_mempolicy(priv);
-       /* Start with last addr hint */
-       vma = find_vma(mm, last_addr);
-       if (last_addr && vma) {
-               vma = vma->vm_next;
-               goto out;
+       priv->tail_vma = get_gate_vma(mm);
+
+       if (last_addr) {
+               vma = find_vma(mm, last_addr);
+               if (vma && (vma = m_next_vma(priv, vma)))
+                       return vma;
        }
 
-       /*
-        * Check the vma index is within the range and do
-        * sequential scan until m_index.
-        */
-       vma = NULL;
-       if ((unsigned long)l < mm->map_count) {
-               vma = mm->mmap;
-               while (l-- && vma)
+       m->version = 0;
+       if (pos < mm->map_count) {
+               for (vma = mm->mmap; pos; pos--) {
+                       m->version = vma->vm_start;
                        vma = vma->vm_next;
-               goto out;
+               }
+               return vma;
        }
 
-       if (l != mm->map_count)
-               tail_vma = NULL; /* After gate vma */
-
-out:
-       if (vma)
-               return vma;
+       /* we do not bother to update m->version in this case */
+       if (pos == mm->map_count && priv->tail_vma)
+               return priv->tail_vma;
 
-       release_task_mempolicy(priv);
-       /* End of vmas has been reached */
-       m->version = (tail_vma != NULL)? 0: -1UL;
-       up_read(&mm->mmap_sem);
-       mmput(mm);
-       return tail_vma;
+       vma_stop(priv);
+       return NULL;
 }
 
 static void *m_next(struct seq_file *m, void *v, loff_t *pos)
 {
        struct proc_maps_private *priv = m->private;
-       struct vm_area_struct *vma = v;
-       struct vm_area_struct *tail_vma = priv->tail_vma;
+       struct vm_area_struct *next;
 
        (*pos)++;
-       if (vma && (vma != tail_vma) && vma->vm_next)
-               return vma->vm_next;
-       vma_stop(priv, vma);
-       return (vma != tail_vma)? tail_vma: NULL;
+       next = m_next_vma(priv, v);
+       if (!next)
+               vma_stop(priv);
+       return next;
 }
 
 static void m_stop(struct seq_file *m, void *v)
 {
        struct proc_maps_private *priv = m->private;
-       struct vm_area_struct *vma = v;
 
-       if (!IS_ERR(vma))
-               vma_stop(priv, vma);
-       if (priv->task)
+       if (!IS_ERR_OR_NULL(v))
+               vma_stop(priv);
+       if (priv->task) {
                put_task_struct(priv->task);
+               priv->task = NULL;
+       }
+}
+
+static int proc_maps_open(struct inode *inode, struct file *file,
+                       const struct seq_operations *ops, int psize)
+{
+       struct proc_maps_private *priv = __seq_open_private(file, ops, psize);
+
+       if (!priv)
+               return -ENOMEM;
+
+       priv->inode = inode;
+       priv->mm = proc_mem_open(inode, PTRACE_MODE_READ);
+       if (IS_ERR(priv->mm)) {
+               int err = PTR_ERR(priv->mm);
+
+               seq_release_private(inode, file);
+               return err;
+       }
+
+       return 0;
+}
+
+static int proc_map_release(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq = file->private_data;
+       struct proc_maps_private *priv = seq->private;
+
+       if (priv->mm)
+               mmdrop(priv->mm);
+
+       return seq_release_private(inode, file);
 }
 
 static int do_maps_open(struct inode *inode, struct file *file,
                        const struct seq_operations *ops)
 {
-       struct proc_maps_private *priv;
-       int ret = -ENOMEM;
-       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-       if (priv) {
-               priv->pid = proc_pid(inode);
-               ret = seq_open(file, ops);
-               if (!ret) {
-                       struct seq_file *m = file->private_data;
-                       m->private = priv;
-               } else {
-                       kfree(priv);
-               }
+       return proc_maps_open(inode, file, ops,
+                               sizeof(struct proc_maps_private));
+}
+
+static pid_t pid_of_stack(struct proc_maps_private *priv,
+                               struct vm_area_struct *vma, bool is_pid)
+{
+       struct inode *inode = priv->inode;
+       struct task_struct *task;
+       pid_t ret = 0;
+
+       rcu_read_lock();
+       task = pid_task(proc_pid(inode), PIDTYPE_PID);
+       if (task) {
+               task = task_of_stack(task, vma, is_pid);
+               if (task)
+                       ret = task_pid_nr_ns(task, inode->i_sb->s_fs_info);
        }
+       rcu_read_unlock();
+
        return ret;
 }
 
@@ -256,7 +268,6 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
        struct mm_struct *mm = vma->vm_mm;
        struct file *file = vma->vm_file;
        struct proc_maps_private *priv = m->private;
-       struct task_struct *task = priv->task;
        vm_flags_t flags = vma->vm_flags;
        unsigned long ino = 0;
        unsigned long long pgoff = 0;
@@ -321,8 +332,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
                        goto done;
                }
 
-               tid = vm_is_stack(task, vma, is_pid);
-
+               tid = pid_of_stack(priv, vma, is_pid);
                if (tid != 0) {
                        /*
                         * Thread stack in /proc/PID/task/TID/maps or
@@ -349,15 +359,8 @@ done:
 
 static int show_map(struct seq_file *m, void *v, int is_pid)
 {
-       struct vm_area_struct *vma = v;
-       struct proc_maps_private *priv = m->private;
-       struct task_struct *task = priv->task;
-
-       show_map_vma(m, vma, is_pid);
-
-       if (m->count < m->size)  /* vma is copied successfully */
-               m->version = (vma != get_gate_vma(task->mm))
-                       ? vma->vm_start : 0;
+       show_map_vma(m, v, is_pid);
+       m_cache_vma(m, v);
        return 0;
 }
 
@@ -399,14 +402,14 @@ const struct file_operations proc_pid_maps_operations = {
        .open           = pid_maps_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = seq_release_private,
+       .release        = proc_map_release,
 };
 
 const struct file_operations proc_tid_maps_operations = {
        .open           = tid_maps_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = seq_release_private,
+       .release        = proc_map_release,
 };
 
 /*
@@ -583,8 +586,6 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma)
 
 static int show_smap(struct seq_file *m, void *v, int is_pid)
 {
-       struct proc_maps_private *priv = m->private;
-       struct task_struct *task = priv->task;
        struct vm_area_struct *vma = v;
        struct mem_size_stats mss;
        struct mm_walk smaps_walk = {
@@ -637,10 +638,7 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
                                mss.nonlinear >> 10);
 
        show_smap_vma_flags(m, vma);
-
-       if (m->count < m->size)  /* vma is copied successfully */
-               m->version = (vma != get_gate_vma(task->mm))
-                       ? vma->vm_start : 0;
+       m_cache_vma(m, vma);
        return 0;
 }
 
@@ -682,14 +680,14 @@ const struct file_operations proc_pid_smaps_operations = {
        .open           = pid_smaps_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = seq_release_private,
+       .release        = proc_map_release,
 };
 
 const struct file_operations proc_tid_smaps_operations = {
        .open           = tid_smaps_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = seq_release_private,
+       .release        = proc_map_release,
 };
 
 /*
@@ -1029,7 +1027,6 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
        spinlock_t *ptl;
        pte_t *pte;
        int err = 0;
-       pagemap_entry_t pme = make_pme(PM_NOT_PRESENT(pm->v2));
 
        /* find the first VMA at or above 'addr' */
        vma = find_vma(walk->mm, addr);
@@ -1043,6 +1040,7 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 
                for (; addr != end; addr += PAGE_SIZE) {
                        unsigned long offset;
+                       pagemap_entry_t pme;
 
                        offset = (addr & ~PAGEMAP_WALK_MASK) >>
                                        PAGE_SHIFT;
@@ -1057,32 +1055,51 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 
        if (pmd_trans_unstable(pmd))
                return 0;
-       for (; addr != end; addr += PAGE_SIZE) {
-               int flags2;
-
-               /* check to see if we've left 'vma' behind
-                * and need a new, higher one */
-               if (vma && (addr >= vma->vm_end)) {
-                       vma = find_vma(walk->mm, addr);
-                       if (vma && (vma->vm_flags & VM_SOFTDIRTY))
-                               flags2 = __PM_SOFT_DIRTY;
-                       else
-                               flags2 = 0;
-                       pme = make_pme(PM_NOT_PRESENT(pm->v2) | PM_STATUS2(pm->v2, flags2));
+
+       while (1) {
+               /* End of address space hole, which we mark as non-present. */
+               unsigned long hole_end;
+
+               if (vma)
+                       hole_end = min(end, vma->vm_start);
+               else
+                       hole_end = end;
+
+               for (; addr < hole_end; addr += PAGE_SIZE) {
+                       pagemap_entry_t pme = make_pme(PM_NOT_PRESENT(pm->v2));
+
+                       err = add_to_pagemap(addr, &pme, pm);
+                       if (err)
+                               return err;
                }
 
-               /* check that 'vma' actually covers this address,
-                * and that it isn't a huge page vma */
-               if (vma && (vma->vm_start <= addr) &&
-                   !is_vm_hugetlb_page(vma)) {
+               if (!vma || vma->vm_start >= end)
+                       break;
+               /*
+                * We can't possibly be in a hugetlb VMA. In general,
+                * for a mm_walk with a pmd_entry and a hugetlb_entry,
+                * the pmd_entry can only be called on addresses in a
+                * hugetlb if the walk starts in a non-hugetlb VMA and
+                * spans a hugepage VMA. Since pagemap_read walks are
+                * PMD-sized and PMD-aligned, this will never be true.
+                */
+               BUG_ON(is_vm_hugetlb_page(vma));
+
+               /* Addresses in the VMA. */
+               for (; addr < min(end, vma->vm_end); addr += PAGE_SIZE) {
+                       pagemap_entry_t pme;
                        pte = pte_offset_map(pmd, addr);
                        pte_to_pagemap_entry(&pme, pm, vma, addr, *pte);
-                       /* unmap before userspace copy */
                        pte_unmap(pte);
+                       err = add_to_pagemap(addr, &pme, pm);
+                       if (err)
+                               return err;
                }
-               err = add_to_pagemap(addr, &pme, pm);
-               if (err)
-                       return err;
+
+               if (addr == end)
+                       break;
+
+               vma = find_vma(walk->mm, addr);
        }
 
        cond_resched();
@@ -1415,7 +1432,6 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
        struct vm_area_struct *vma = v;
        struct numa_maps *md = &numa_priv->md;
        struct file *file = vma->vm_file;
-       struct task_struct *task = proc_priv->task;
        struct mm_struct *mm = vma->vm_mm;
        struct mm_walk walk = {};
        struct mempolicy *pol;
@@ -1435,9 +1451,13 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
        walk.private = md;
        walk.mm = mm;
 
-       pol = get_vma_policy(task, vma, vma->vm_start);
-       mpol_to_str(buffer, sizeof(buffer), pol);
-       mpol_cond_put(pol);
+       pol = __get_vma_policy(vma, vma->vm_start);
+       if (pol) {
+               mpol_to_str(buffer, sizeof(buffer), pol);
+               mpol_cond_put(pol);
+       } else {
+               mpol_to_str(buffer, sizeof(buffer), proc_priv->task_mempolicy);
+       }
 
        seq_printf(m, "%08lx %s", vma->vm_start, buffer);
 
@@ -1447,7 +1467,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
        } else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) {
                seq_puts(m, " heap");
        } else {
-               pid_t tid = vm_is_stack(task, vma, is_pid);
+               pid_t tid = pid_of_stack(proc_priv, vma, is_pid);
                if (tid != 0) {
                        /*
                         * Thread stack in /proc/PID/task/TID/maps or
@@ -1495,9 +1515,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
                        seq_printf(m, " N%d=%lu", nid, md->node[nid]);
 out:
        seq_putc(m, '\n');
-
-       if (m->count < m->size)
-               m->version = (vma != proc_priv->tail_vma) ? vma->vm_start : 0;
+       m_cache_vma(m, vma);
        return 0;
 }
 
@@ -1528,20 +1546,8 @@ static const struct seq_operations proc_tid_numa_maps_op = {
 static int numa_maps_open(struct inode *inode, struct file *file,
                          const struct seq_operations *ops)
 {
-       struct numa_maps_private *priv;
-       int ret = -ENOMEM;
-       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-       if (priv) {
-               priv->proc_maps.pid = proc_pid(inode);
-               ret = seq_open(file, ops);
-               if (!ret) {
-                       struct seq_file *m = file->private_data;
-                       m->private = priv;
-               } else {
-                       kfree(priv);
-               }
-       }
-       return ret;
+       return proc_maps_open(inode, file, ops,
+                               sizeof(struct numa_maps_private));
 }
 
 static int pid_numa_maps_open(struct inode *inode, struct file *file)
@@ -1558,13 +1564,13 @@ const struct file_operations proc_pid_numa_maps_operations = {
        .open           = pid_numa_maps_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = seq_release_private,
+       .release        = proc_map_release,
 };
 
 const struct file_operations proc_tid_numa_maps_operations = {
        .open           = tid_numa_maps_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = seq_release_private,
+       .release        = proc_map_release,
 };
 #endif /* CONFIG_NUMA */
index 678455d2d6839e9ab0df0909905d877aa5debb6a..599ec2e201043ea8f210a7c15a13c4e7eab22440 100644 (file)
@@ -123,6 +123,25 @@ unsigned long task_statm(struct mm_struct *mm,
        return size;
 }
 
+static pid_t pid_of_stack(struct proc_maps_private *priv,
+                               struct vm_area_struct *vma, bool is_pid)
+{
+       struct inode *inode = priv->inode;
+       struct task_struct *task;
+       pid_t ret = 0;
+
+       rcu_read_lock();
+       task = pid_task(proc_pid(inode), PIDTYPE_PID);
+       if (task) {
+               task = task_of_stack(task, vma, is_pid);
+               if (task)
+                       ret = task_pid_nr_ns(task, inode->i_sb->s_fs_info);
+       }
+       rcu_read_unlock();
+
+       return ret;
+}
+
 /*
  * display a single VMA to a sequenced file
  */
@@ -163,7 +182,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
                seq_pad(m, ' ');
                seq_path(m, &file->f_path, "");
        } else if (mm) {
-               pid_t tid = vm_is_stack(priv->task, vma, is_pid);
+               pid_t tid = pid_of_stack(priv, vma, is_pid);
 
                if (tid != 0) {
                        seq_pad(m, ' ');
@@ -212,22 +231,22 @@ static void *m_start(struct seq_file *m, loff_t *pos)
        loff_t n = *pos;
 
        /* pin the task and mm whilst we play with them */
-       priv->task = get_pid_task(priv->pid, PIDTYPE_PID);
+       priv->task = get_proc_task(priv->inode);
        if (!priv->task)
                return ERR_PTR(-ESRCH);
 
-       mm = mm_access(priv->task, PTRACE_MODE_READ);
-       if (!mm || IS_ERR(mm)) {
-               put_task_struct(priv->task);
-               priv->task = NULL;
-               return mm;
-       }
-       down_read(&mm->mmap_sem);
+       mm = priv->mm;
+       if (!mm || !atomic_inc_not_zero(&mm->mm_users))
+               return NULL;
 
+       down_read(&mm->mmap_sem);
        /* start from the Nth VMA */
        for (p = rb_first(&mm->mm_rb); p; p = rb_next(p))
                if (n-- == 0)
                        return p;
+
+       up_read(&mm->mmap_sem);
+       mmput(mm);
        return NULL;
 }
 
@@ -235,11 +254,13 @@ static void m_stop(struct seq_file *m, void *_vml)
 {
        struct proc_maps_private *priv = m->private;
 
+       if (!IS_ERR_OR_NULL(_vml)) {
+               up_read(&priv->mm->mmap_sem);
+               mmput(priv->mm);
+       }
        if (priv->task) {
-               struct mm_struct *mm = priv->task->mm;
-               up_read(&mm->mmap_sem);
-               mmput(mm);
                put_task_struct(priv->task);
+               priv->task = NULL;
        }
 }
 
@@ -269,20 +290,33 @@ static int maps_open(struct inode *inode, struct file *file,
                     const struct seq_operations *ops)
 {
        struct proc_maps_private *priv;
-       int ret = -ENOMEM;
-
-       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-       if (priv) {
-               priv->pid = proc_pid(inode);
-               ret = seq_open(file, ops);
-               if (!ret) {
-                       struct seq_file *m = file->private_data;
-                       m->private = priv;
-               } else {
-                       kfree(priv);
-               }
+
+       priv = __seq_open_private(file, ops, sizeof(*priv));
+       if (!priv)
+               return -ENOMEM;
+
+       priv->inode = inode;
+       priv->mm = proc_mem_open(inode, PTRACE_MODE_READ);
+       if (IS_ERR(priv->mm)) {
+               int err = PTR_ERR(priv->mm);
+
+               seq_release_private(inode, file);
+               return err;
        }
-       return ret;
+
+       return 0;
+}
+
+
+static int map_release(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq = file->private_data;
+       struct proc_maps_private *priv = seq->private;
+
+       if (priv->mm)
+               mmdrop(priv->mm);
+
+       return seq_release_private(inode, file);
 }
 
 static int pid_maps_open(struct inode *inode, struct file *file)
@@ -299,13 +333,13 @@ const struct file_operations proc_pid_maps_operations = {
        .open           = pid_maps_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = seq_release_private,
+       .release        = map_release,
 };
 
 const struct file_operations proc_tid_maps_operations = {
        .open           = tid_maps_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = seq_release_private,
+       .release        = map_release,
 };
 
index f2d0eee9d1f1061399c30a5af36c204c2e3bf491..8b663b2d95622f98b704ed693b1177d13cfa79dd 100644 (file)
@@ -2725,7 +2725,7 @@ static int __init dquot_init(void)
                panic("Cannot create dquot hash table");
 
        for (i = 0; i < _DQST_DQSTAT_LAST; i++) {
-               ret = percpu_counter_init(&dqstats.counter[i], 0);
+               ret = percpu_counter_init(&dqstats.counter[i], 0, GFP_KERNEL);
                if (ret)
                        panic("Cannot create dquot stat counters");
        }
index 735c2c2b4536b0546e728b38cf663e310b3ac45c..1894d96ccb7c936b4cbe753564c01d3589a125a7 100644 (file)
@@ -506,6 +506,9 @@ typedef struct reiserfs_proc_info_data {
 } reiserfs_proc_info_data_t;
 #endif
 
+/* Number of quota types we support */
+#define REISERFS_MAXQUOTAS 2
+
 /* reiserfs union of in-core super block data */
 struct reiserfs_sb_info {
        /* Buffer containing the super block */
@@ -615,7 +618,7 @@ struct reiserfs_sb_info {
        spinlock_t old_work_lock;     /* protects old_work and work_queued */
 
 #ifdef CONFIG_QUOTA
-       char *s_qf_names[MAXQUOTAS];
+       char *s_qf_names[REISERFS_MAXQUOTAS];
        int s_jquota_fmt;
 #endif
        char *s_jdev;           /* Stored jdev for mount option showing */
index d46e88a33b02451d2cffc955a64c6752fab7c2cd..f1376c92cf74c422aeb9331e2290e5fec88635fb 100644 (file)
@@ -206,7 +206,7 @@ static int finish_unfinished(struct super_block *s)
 #ifdef CONFIG_QUOTA
        int i;
        int ms_active_set;
-       int quota_enabled[MAXQUOTAS];
+       int quota_enabled[REISERFS_MAXQUOTAS];
 #endif
 
        /* compose key to look for "save" links */
@@ -227,7 +227,7 @@ static int finish_unfinished(struct super_block *s)
                s->s_flags |= MS_ACTIVE;
        }
        /* Turn on quotas so that they are updated correctly */
-       for (i = 0; i < MAXQUOTAS; i++) {
+       for (i = 0; i < REISERFS_MAXQUOTAS; i++) {
                quota_enabled[i] = 1;
                if (REISERFS_SB(s)->s_qf_names[i]) {
                        int ret;
@@ -370,7 +370,7 @@ static int finish_unfinished(struct super_block *s)
 #ifdef CONFIG_QUOTA
        /* Turn quotas off */
        reiserfs_write_unlock(s);
-       for (i = 0; i < MAXQUOTAS; i++) {
+       for (i = 0; i < REISERFS_MAXQUOTAS; i++) {
                if (sb_dqopt(s)->files[i] && quota_enabled[i])
                        dquot_quota_off(s, i);
        }
@@ -1360,7 +1360,7 @@ static void handle_quota_files(struct super_block *s, char **qf_names,
 {
        int i;
 
-       for (i = 0; i < MAXQUOTAS; i++) {
+       for (i = 0; i < REISERFS_MAXQUOTAS; i++) {
                if (qf_names[i] != REISERFS_SB(s)->s_qf_names[i])
                        kfree(REISERFS_SB(s)->s_qf_names[i]);
                REISERFS_SB(s)->s_qf_names[i] = qf_names[i];
@@ -1381,7 +1381,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
        struct reiserfs_journal *journal = SB_JOURNAL(s);
        char *new_opts = kstrdup(arg, GFP_KERNEL);
        int err;
-       char *qf_names[MAXQUOTAS];
+       char *qf_names[REISERFS_MAXQUOTAS];
        unsigned int qfmt = 0;
 #ifdef CONFIG_QUOTA
        int i;
@@ -1400,7 +1400,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
            (s, arg, &mount_options, &blocks, NULL, &commit_max_age,
            qf_names, &qfmt)) {
 #ifdef CONFIG_QUOTA
-               for (i = 0; i < MAXQUOTAS; i++)
+               for (i = 0; i < REISERFS_MAXQUOTAS; i++)
                        if (qf_names[i] != REISERFS_SB(s)->s_qf_names[i])
                                kfree(qf_names[i]);
 #endif
@@ -1844,7 +1844,7 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent)
        char *jdev_name;
        struct reiserfs_sb_info *sbi;
        int errval = -EINVAL;
-       char *qf_names[MAXQUOTAS] = {};
+       char *qf_names[REISERFS_MAXQUOTAS] = {};
        unsigned int qfmt = 0;
 
        save_mount_options(s, data);
@@ -2169,7 +2169,7 @@ error_unlocked:
 #ifdef CONFIG_QUOTA
        {
                int j;
-               for (j = 0; j < MAXQUOTAS; j++)
+               for (j = 0; j < REISERFS_MAXQUOTAS; j++)
                        kfree(qf_names[j]);
        }
 #endif
index b9a214d2fe98b8b37a7560ebf1a7d1dc7c5d83e0..1b836107aceee1f01303bb27ba381d8c38c06163 100644 (file)
@@ -175,7 +175,8 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
                goto fail;
 
        for (i = 0; i < SB_FREEZE_LEVELS; i++) {
-               if (percpu_counter_init(&s->s_writers.counter[i], 0) < 0)
+               if (percpu_counter_init(&s->s_writers.counter[i], 0,
+                                       GFP_KERNEL) < 0)
                        goto fail;
                lockdep_init_map(&s->s_writers.lock_map[i], sb_writers_name[i],
                                 &type->s_writers_key[i], 0);
index 80c350216ea8e159f2a22837efeb33c38b87a260..b46ffa94372a58b600660a106a2a92545252f5f1 100644 (file)
@@ -333,8 +333,7 @@ static long timerfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg
                spin_lock_irq(&ctx->wqh.lock);
                if (!timerfd_canceled(ctx)) {
                        ctx->ticks = ticks;
-                       if (ticks)
-                               wake_up_locked(&ctx->wqh);
+                       wake_up_locked(&ctx->wqh);
                } else
                        ret = -ECANCELED;
                spin_unlock_irq(&ctx->wqh.lock);
index 86c6743ec1feb7eb99b17d6557c2ed1a1a90c1b8..bb15771b92ae32ae02390179492647a1d87c9dc2 100644 (file)
@@ -223,11 +223,18 @@ out:
 
 static int udf_release_file(struct inode *inode, struct file *filp)
 {
-       if (filp->f_mode & FMODE_WRITE) {
+       if (filp->f_mode & FMODE_WRITE &&
+           atomic_read(&inode->i_writecount) > 1) {
+               /*
+                * Grab i_mutex to avoid races with writes changing i_size
+                * while we are running.
+                */
+               mutex_lock(&inode->i_mutex);
                down_write(&UDF_I(inode)->i_data_sem);
                udf_discard_prealloc(inode);
                udf_truncate_tail_extent(inode);
                up_write(&UDF_I(inode)->i_data_sem);
+               mutex_unlock(&inode->i_mutex);
        }
        return 0;
 }
index 08598843288fe0c2dd206940d6437c606ef41c1f..c9b4df5810d52560b084b9557150faf8cfbe6e29 100644 (file)
@@ -1277,7 +1277,7 @@ update_time:
  */
 #define UDF_MAX_ICB_NESTING 1024
 
-static int udf_read_inode(struct inode *inode)
+static int udf_read_inode(struct inode *inode, bool hidden_inode)
 {
        struct buffer_head *bh = NULL;
        struct fileEntry *fe;
@@ -1436,8 +1436,11 @@ reread:
 
        link_count = le16_to_cpu(fe->fileLinkCount);
        if (!link_count) {
-               ret = -ESTALE;
-               goto out;
+               if (!hidden_inode) {
+                       ret = -ESTALE;
+                       goto out;
+               }
+               link_count = 1;
        }
        set_nlink(inode, link_count);
 
@@ -1826,7 +1829,8 @@ out:
        return err;
 }
 
-struct inode *udf_iget(struct super_block *sb, struct kernel_lb_addr *ino)
+struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,
+                        bool hidden_inode)
 {
        unsigned long block = udf_get_lb_pblock(sb, ino, 0);
        struct inode *inode = iget_locked(sb, block);
@@ -1839,7 +1843,7 @@ struct inode *udf_iget(struct super_block *sb, struct kernel_lb_addr *ino)
                return inode;
 
        memcpy(&UDF_I(inode)->i_location, ino, sizeof(struct kernel_lb_addr));
-       err = udf_read_inode(inode);
+       err = udf_read_inode(inode, hidden_inode);
        if (err < 0) {
                iget_failed(inode);
                return ERR_PTR(err);
index 5401fc33f5cc906b76b846e62f74751cabd50a68..e229315bbf7adab01933827a68b59ccb8831dec7 100644 (file)
@@ -959,7 +959,7 @@ struct inode *udf_find_metadata_inode_efe(struct super_block *sb,
        addr.logicalBlockNum = meta_file_loc;
        addr.partitionReferenceNum = partition_num;
 
-       metadata_fe = udf_iget(sb, &addr);
+       metadata_fe = udf_iget_special(sb, &addr);
 
        if (IS_ERR(metadata_fe)) {
                udf_warn(sb, "metadata inode efe not found\n");
@@ -1020,7 +1020,7 @@ static int udf_load_metadata_files(struct super_block *sb, int partition)
                udf_debug("Bitmap file location: block = %d part = %d\n",
                          addr.logicalBlockNum, addr.partitionReferenceNum);
 
-               fe = udf_iget(sb, &addr);
+               fe = udf_iget_special(sb, &addr);
                if (IS_ERR(fe)) {
                        if (sb->s_flags & MS_RDONLY)
                                udf_warn(sb, "bitmap inode efe not found but it's ok since the disc is mounted read-only\n");
@@ -1119,7 +1119,7 @@ static int udf_fill_partdesc_info(struct super_block *sb,
                };
                struct inode *inode;
 
-               inode = udf_iget(sb, &loc);
+               inode = udf_iget_special(sb, &loc);
                if (IS_ERR(inode)) {
                        udf_debug("cannot load unallocSpaceTable (part %d)\n",
                                  p_index);
@@ -1154,7 +1154,7 @@ static int udf_fill_partdesc_info(struct super_block *sb,
                };
                struct inode *inode;
 
-               inode = udf_iget(sb, &loc);
+               inode = udf_iget_special(sb, &loc);
                if (IS_ERR(inode)) {
                        udf_debug("cannot load freedSpaceTable (part %d)\n",
                                  p_index);
@@ -1198,7 +1198,7 @@ static void udf_find_vat_block(struct super_block *sb, int p_index,
             vat_block >= map->s_partition_root &&
             vat_block >= start_block - 3; vat_block--) {
                ino.logicalBlockNum = vat_block - map->s_partition_root;
-               inode = udf_iget(sb, &ino);
+               inode = udf_iget_special(sb, &ino);
                if (!IS_ERR(inode)) {
                        sbi->s_vat_inode = inode;
                        break;
index 742557be9936399fba764f645c04b969d2a3cfd5..1cc3c993ebd04f4adb7b425f500e40d1185aae9f 100644 (file)
@@ -138,7 +138,18 @@ extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *,
 /* file.c */
 extern long udf_ioctl(struct file *, unsigned int, unsigned long);
 /* inode.c */
-extern struct inode *udf_iget(struct super_block *, struct kernel_lb_addr *);
+extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,
+                               bool hidden_inode);
+static inline struct inode *udf_iget_special(struct super_block *sb,
+                                            struct kernel_lb_addr *ino)
+{
+       return __udf_iget(sb, ino, true);
+}
+static inline struct inode *udf_iget(struct super_block *sb,
+                                    struct kernel_lb_addr *ino)
+{
+       return __udf_iget(sb, ino, false);
+}
 extern int udf_expand_file_adinicb(struct inode *);
 extern struct buffer_head *udf_expand_dir_adinicb(struct inode *, int *, int *);
 extern struct buffer_head *udf_bread(struct inode *, int, int, int *);
index 1f11483eba6a7b9f62d611e6f0110ec80a0ce643..77c331f1a77048ef43fddab49f098ac7ca9d4f9c 100644 (file)
@@ -81,8 +81,6 @@ static time_t year_seconds[MAX_YEAR_SECONDS] = {
 /*2038*/ SPY(68, 17, 0)
 };
 
-extern struct timezone sys_tz;
-
 #define SECS_PER_HOUR  (60 * 60)
 #define SECS_PER_DAY   (SECS_PER_HOUR * 24)
 
index c728113374f55b1f02607a2a84c0062198f415a3..f97804bdf1ff93a8d1923bc4fe75b6abc36e097d 100644 (file)
 #define METHOD_NAME__PRS        "_PRS"
 #define METHOD_NAME__PRT        "_PRT"
 #define METHOD_NAME__PRW        "_PRW"
+#define METHOD_NAME__PS0        "_PS0"
+#define METHOD_NAME__PS1        "_PS1"
+#define METHOD_NAME__PS2        "_PS2"
+#define METHOD_NAME__PS3        "_PS3"
 #define METHOD_NAME__REG        "_REG"
 #define METHOD_NAME__SB_        "_SB_"
 #define METHOD_NAME__SEG        "_SEG"
index b7c89d47efbefc18b55502bf89e6480e4d11d4b4..9fc1d71c82bc13faec7d409ebdc280f947544774 100644 (file)
@@ -46,7 +46,7 @@
 
 /* Current ACPICA subsystem version in YYYYMMDD format */
 
-#define ACPI_CA_VERSION                 0x20140724
+#define ACPI_CA_VERSION                 0x20140828
 
 #include <acpi/acconfig.h>
 #include <acpi/actypes.h>
@@ -692,6 +692,7 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                                     *event_status))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void))
 
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                acpi_get_gpe_device(u32 gpe_index,
index 7626bfeac2cb88a8c09ee06f5a81758d3357ace8..29e79370641da878dbf0875ad87950f6a0f4c83f 100644 (file)
@@ -952,7 +952,8 @@ enum acpi_srat_type {
        ACPI_SRAT_TYPE_CPU_AFFINITY = 0,
        ACPI_SRAT_TYPE_MEMORY_AFFINITY = 1,
        ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY = 2,
-       ACPI_SRAT_TYPE_RESERVED = 3     /* 3 and greater are reserved */
+       ACPI_SRAT_TYPE_GICC_AFFINITY = 3,
+       ACPI_SRAT_TYPE_RESERVED = 4     /* 4 and greater are reserved */
 };
 
 /*
@@ -968,7 +969,7 @@ struct acpi_srat_cpu_affinity {
        u32 flags;
        u8 local_sapic_eid;
        u8 proximity_domain_hi[3];
-       u32 reserved;           /* Reserved, must be zero */
+       u32 clock_domain;
 };
 
 /* Flags */
@@ -1010,6 +1011,20 @@ struct acpi_srat_x2apic_cpu_affinity {
 
 #define ACPI_SRAT_CPU_ENABLED       (1)        /* 00: Use affinity structure */
 
+/* 3: GICC Affinity (ACPI 5.1) */
+
+struct acpi_srat_gicc_affinity {
+       struct acpi_subtable_header header;
+       u32 proximity_domain;
+       u32 acpi_processor_uid;
+       u32 flags;
+       u32 clock_domain;
+};
+
+/* Flags for struct acpi_srat_gicc_affinity */
+
+#define ACPI_SRAT_GICC_ENABLED     (1) /* 00: Use affinity structure */
+
 /* Reset to default packing */
 
 #pragma pack()
index 787bcc81446381245279063545c59c08b9b9e46d..5480cb2236bf33356cb3b0c8811de2de280897af 100644 (file)
@@ -310,10 +310,15 @@ struct acpi_gtdt_timer_entry {
        u32 common_flags;
 };
 
+/* Flag Definitions: timer_flags and virtual_timer_flags above */
+
+#define ACPI_GTDT_GT_IRQ_MODE               (1)
+#define ACPI_GTDT_GT_IRQ_POLARITY           (1<<1)
+
 /* Flag Definitions: common_flags above */
 
-#define ACPI_GTDT_GT_IS_SECURE_TIMER    (1)
-#define ACPI_GTDT_GT_ALWAYS_ON          (1<<1)
+#define ACPI_GTDT_GT_IS_SECURE_TIMER        (1)
+#define ACPI_GTDT_GT_ALWAYS_ON              (1<<1)
 
 /* 1: SBSA Generic Watchdog Structure */
 
index de8bf89940f8dc3a7bc27a877067d3885cf646fa..3378dcf4c31ea85b7206fe8b5e03be828330c56d 100644 (file)
@@ -179,6 +179,15 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
 extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
                           void *cpu_addr, dma_addr_t dma_addr, size_t size);
 
+void *dma_common_contiguous_remap(struct page *page, size_t size,
+                       unsigned long vm_flags,
+                       pgprot_t prot, const void *caller);
+
+void *dma_common_pages_remap(struct page **pages, size_t size,
+                       unsigned long vm_flags, pgprot_t prot,
+                       const void *caller);
+void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags);
+
 /**
  * dma_mmap_attrs - map a coherent DMA allocation into user space
  * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
@@ -205,14 +214,6 @@ dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma, void *cpu_addr,
 
 #define dma_mmap_coherent(d, v, c, h, s) dma_mmap_attrs(d, v, c, h, s, NULL)
 
-static inline int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma,
-                     void *cpu_addr, dma_addr_t dma_addr, size_t size)
-{
-       DEFINE_DMA_ATTRS(attrs);
-       dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
-       return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs);
-}
-
 int
 dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
                       void *cpu_addr, dma_addr_t dma_addr, size_t size);
index c1d4105e1c1d57de7e916d532cf044b641348b92..383ade1a211bfcbec546899d6451171a63e5a13f 100644 (file)
@@ -27,7 +27,7 @@
  */
 
 #ifndef ARCH_NR_GPIOS
-#define ARCH_NR_GPIOS          256
+#define ARCH_NR_GPIOS          512
 #endif
 
 /*
index 975e1cc75edb55f9dcd19530eb08912b220e2641..b8fdc57a733515b8b0b031da68ac5a9f8eda74ae 100644 (file)
@@ -331,7 +331,7 @@ static inline void iounmap(void __iomem *addr)
 #ifndef CONFIG_GENERIC_IOMAP
 static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
 {
-       return (void __iomem *) port;
+       return PCI_IOBASE + (port & IO_SPACE_LIMIT);
 }
 
 static inline void ioport_unmap(void __iomem *p)
diff --git a/include/asm-generic/irq_work.h b/include/asm-generic/irq_work.h
new file mode 100644 (file)
index 0000000..a44f452
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __ASM_IRQ_WORK_H
+#define __ASM_IRQ_WORK_H
+
+static inline bool arch_irq_work_has_interrupt(void)
+{
+       return false;
+}
+
+#endif /* __ASM_IRQ_WORK_H */
+
index 53b2acc38213298ff75d8c32a3f1dbfa196562a9..081ff8826bf6855630f1a7d5a3c51302d72b87c7 100644 (file)
@@ -249,6 +249,10 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b)
 #define pgprot_writecombine pgprot_noncached
 #endif
 
+#ifndef pgprot_device
+#define pgprot_device pgprot_noncached
+#endif
+
 /*
  * When walking page tables, get the address of the next boundary,
  * or the end address of the range if that comes earlier.  Although no
@@ -660,11 +664,12 @@ static inline int pmd_trans_unstable(pmd_t *pmd)
 }
 
 #ifdef CONFIG_NUMA_BALANCING
-#ifdef CONFIG_ARCH_USES_NUMA_PROT_NONE
 /*
- * _PAGE_NUMA works identical to _PAGE_PROTNONE (it's actually the
- * same bit too). It's set only when _PAGE_PRESET is not set and it's
- * never set if _PAGE_PRESENT is set.
+ * _PAGE_NUMA distinguishes between an unmapped page table entry, an entry that
+ * is protected for PROT_NONE and a NUMA hinting fault entry. If the
+ * architecture defines __PAGE_PROTNONE then it should take that into account
+ * but those that do not can rely on the fact that the NUMA hinting scanner
+ * skips inaccessible VMAs.
  *
  * pte/pmd_present() returns true if pte/pmd_numa returns true. Page
  * fault triggers on those regions if pte/pmd_numa returns true
@@ -673,16 +678,14 @@ static inline int pmd_trans_unstable(pmd_t *pmd)
 #ifndef pte_numa
 static inline int pte_numa(pte_t pte)
 {
-       return (pte_flags(pte) &
-               (_PAGE_NUMA|_PAGE_PROTNONE|_PAGE_PRESENT)) == _PAGE_NUMA;
+       return ptenuma_flags(pte) == _PAGE_NUMA;
 }
 #endif
 
 #ifndef pmd_numa
 static inline int pmd_numa(pmd_t pmd)
 {
-       return (pmd_flags(pmd) &
-               (_PAGE_NUMA|_PAGE_PROTNONE|_PAGE_PRESENT)) == _PAGE_NUMA;
+       return pmdnuma_flags(pmd) == _PAGE_NUMA;
 }
 #endif
 
@@ -722,6 +725,8 @@ static inline pte_t pte_mknuma(pte_t pte)
 {
        pteval_t val = pte_val(pte);
 
+       VM_BUG_ON(!(val & _PAGE_PRESENT));
+
        val &= ~_PAGE_PRESENT;
        val |= _PAGE_NUMA;
 
@@ -765,16 +770,6 @@ static inline void pmdp_set_numa(struct mm_struct *mm, unsigned long addr,
 }
 #endif
 #else
-extern int pte_numa(pte_t pte);
-extern int pmd_numa(pmd_t pmd);
-extern pte_t pte_mknonnuma(pte_t pte);
-extern pmd_t pmd_mknonnuma(pmd_t pmd);
-extern pte_t pte_mknuma(pte_t pte);
-extern pmd_t pmd_mknuma(pmd_t pmd);
-extern void ptep_set_numa(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
-extern void pmdp_set_numa(struct mm_struct *mm, unsigned long addr, pmd_t *pmdp);
-#endif /* CONFIG_ARCH_USES_NUMA_PROT_NONE */
-#else
 static inline int pmd_numa(pmd_t pmd)
 {
        return 0;
index f1a24b5c3b906ef91804bca4f6f56b4158d93aca..b58fd667f87bc7c7b093c3cac1d293d78d89ec4d 100644 (file)
@@ -3,6 +3,8 @@
 
 /* References to section boundaries */
 
+#include <linux/compiler.h>
+
 /*
  * Usage guidelines:
  * _text, _data: architecture specific, don't use them in arch-independent code
@@ -37,6 +39,8 @@ extern char __start_rodata[], __end_rodata[];
 /* Start and end of .ctors section - used for constructor calls. */
 extern char __ctors_start[], __ctors_end[];
 
+extern __visible const void __nosave_begin, __nosave_end;
+
 /* function descriptor handling (if any).  Override
  * in asm/sections.h */
 #ifndef dereference_function_descriptor
diff --git a/include/dt-bindings/sound/cs35l32.h b/include/dt-bindings/sound/cs35l32.h
new file mode 100644 (file)
index 0000000..0c6d6a3
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __DT_CS35L32_H
+#define __DT_CS35L32_H
+
+#define CS35L32_BOOST_MGR_AUTO         0
+#define CS35L32_BOOST_MGR_AUTO_AUDIO   1
+#define CS35L32_BOOST_MGR_BYPASS       2
+#define CS35L32_BOOST_MGR_FIXED                3
+
+#define CS35L32_DATA_CFG_LR_VP         0
+#define CS35L32_DATA_CFG_LR_STAT       1
+#define CS35L32_DATA_CFG_LR            2
+#define CS35L32_DATA_CFG_LR_VPSTAT     3
+
+#define CS35L32_BATT_THRESH_3_1V       0
+#define CS35L32_BATT_THRESH_3_2V       1
+#define CS35L32_BATT_THRESH_3_3V       2
+#define CS35L32_BATT_THRESH_3_4V       3
+
+#define CS35L32_BATT_RECOV_3_1V                0
+#define CS35L32_BATT_RECOV_3_2V                1
+#define CS35L32_BATT_RECOV_3_3V                2
+#define CS35L32_BATT_RECOV_3_4V                3
+#define CS35L32_BATT_RECOV_3_5V                4
+#define CS35L32_BATT_RECOV_3_6V                5
+
+#endif /* __DT_CS35L32_H */
index 807cbc46d73e0eda897c3e1a3aa2df7ae9519c81..b7926bb9b4442f90d2d7f32d8c11a07370bfe8c9 100644 (file)
@@ -587,7 +587,6 @@ static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
 #if defined(CONFIG_ACPI) && defined(CONFIG_PM)
 struct acpi_device *acpi_dev_pm_get_node(struct device *dev);
 int acpi_dev_pm_attach(struct device *dev, bool power_on);
-void acpi_dev_pm_detach(struct device *dev, bool power_off);
 #else
 static inline struct acpi_device *acpi_dev_pm_get_node(struct device *dev)
 {
@@ -597,7 +596,6 @@ static inline int acpi_dev_pm_attach(struct device *dev, bool power_on)
 {
        return -ENODEV;
 }
-static inline void acpi_dev_pm_detach(struct device *dev, bool power_off) {}
 #endif
 
 #ifdef CONFIG_ACPI
index c826d1c28f9c866d79fb0460c0b0ce1e87c2b420..4fef65e5702396bab72e501d158b2c514d279127 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef _AER_H_
 #define _AER_H_
 
+#include <linux/types.h>
+
 #define AER_NONFATAL                   0
 #define AER_FATAL                      1
 #define AER_CORRECTABLE                        2
index 09a947e8bc87db6f040fb81640899aab9176e17b..642d6ae4030c289d4272d2f60285f3e1f4f55834 100644 (file)
@@ -22,19 +22,6 @@ struct ata_port_info;
 struct ahci_host_priv;
 struct platform_device;
 
-/*
- * Note ahci_platform_data is deprecated, it is only kept around for use
- * by the old da850 and spear13xx ahci code.
- * New drivers should instead declare their own platform_driver struct, and
- * use ahci_platform* functions in their own probe, suspend and resume methods.
- */
-struct ahci_platform_data {
-       int (*init)(struct device *dev, void __iomem *addr);
-       void (*exit)(struct device *dev);
-       int (*suspend)(struct device *dev);
-       int (*resume)(struct device *dev);
-};
-
 int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
 void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
 int ahci_platform_enable_resources(struct ahci_host_priv *hpriv);
index b9fde17f767cf654479759b66e22ec9828206ed3..5c618a084225ebda350e969931434b5a26846448 100644 (file)
@@ -8,11 +8,6 @@ struct pata_platform_info {
         * spacing used by ata_std_ports().
         */
        unsigned int ioport_shift;
-       /* 
-        * Indicate platform specific irq types and initial
-        * IRQ flags when call request_irq()
-        */
-       unsigned int irq_flags;
 };
 
 extern int __pata_platform_probe(struct device *dev,
index 4c7a4b2104bfc6b105f5258763fda8f0fa6685df..91b77f8d495da646da8975014e9ce118f405880e 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __LINUX_ATMEL_MCI_H
 #define __LINUX_ATMEL_MCI_H
 
+#include <linux/types.h>
+
 #define ATMCI_MAX_NR_SLOTS     2
 
 /**
index 089743ade734fc564e18bfa7be35608da68d6fde..9b0a15d06a4fd3447de1d1345560c093b2c4e4a8 100644 (file)
  *      counter raised only while it is under our special handling;
  *
  * iii. after the lockless scan step have selected a potential balloon page for
- *      isolation, re-test the page->mapping flags and the page ref counter
+ *      isolation, re-test the PageBalloon mark and the PagePrivate flag
  *      under the proper page lock, to ensure isolating a valid balloon page
  *      (not yet isolated, nor under release procedure)
  *
+ *  iv. isolation or dequeueing procedure must clear PagePrivate flag under
+ *      page lock together with removing page from balloon device page list.
+ *
  * The functions provided by this interface are placed to help on coping with
  * the aforementioned balloon page corner case, as well as to ensure the simple
  * set of exposed rules are satisfied while we are dealing with balloon pages
  * balloon driver as a page book-keeper for its registered balloon devices.
  */
 struct balloon_dev_info {
-       void *balloon_device;           /* balloon device descriptor */
-       struct address_space *mapping;  /* balloon special page->mapping */
        unsigned long isolated_pages;   /* # of isolated pages for migration */
        spinlock_t pages_lock;          /* Protection to pages list */
        struct list_head pages;         /* Pages enqueued & handled to Host */
+       int (*migratepage)(struct balloon_dev_info *, struct page *newpage,
+                       struct page *page, enum migrate_mode mode);
 };
 
 extern struct page *balloon_page_enqueue(struct balloon_dev_info *b_dev_info);
 extern struct page *balloon_page_dequeue(struct balloon_dev_info *b_dev_info);
-extern struct balloon_dev_info *balloon_devinfo_alloc(
-                                               void *balloon_dev_descriptor);
 
-static inline void balloon_devinfo_free(struct balloon_dev_info *b_dev_info)
-{
-       kfree(b_dev_info);
-}
-
-/*
- * balloon_page_free - release a balloon page back to the page free lists
- * @page: ballooned page to be set free
- *
- * This function must be used to properly set free an isolated/dequeued balloon
- * page at the end of a sucessful page migration, or at the balloon driver's
- * page release procedure.
- */
-static inline void balloon_page_free(struct page *page)
+static inline void balloon_devinfo_init(struct balloon_dev_info *balloon)
 {
-       /*
-        * Balloon pages always get an extra refcount before being isolated
-        * and before being dequeued to help on sorting out fortuite colisions
-        * between a thread attempting to isolate and another thread attempting
-        * to release the very same balloon page.
-        *
-        * Before we handle the page back to Buddy, lets drop its extra refcnt.
-        */
-       put_page(page);
-       __free_page(page);
+       balloon->isolated_pages = 0;
+       spin_lock_init(&balloon->pages_lock);
+       INIT_LIST_HEAD(&balloon->pages);
+       balloon->migratepage = NULL;
 }
 
 #ifdef CONFIG_BALLOON_COMPACTION
@@ -98,107 +80,58 @@ extern bool balloon_page_isolate(struct page *page);
 extern void balloon_page_putback(struct page *page);
 extern int balloon_page_migrate(struct page *newpage,
                                struct page *page, enum migrate_mode mode);
-extern struct address_space
-*balloon_mapping_alloc(struct balloon_dev_info *b_dev_info,
-                       const struct address_space_operations *a_ops);
-
-static inline void balloon_mapping_free(struct address_space *balloon_mapping)
-{
-       kfree(balloon_mapping);
-}
 
 /*
- * page_flags_cleared - helper to perform balloon @page ->flags tests.
- *
- * As balloon pages are obtained from buddy and we do not play with page->flags
- * at driver level (exception made when we get the page lock for compaction),
- * we can safely identify a ballooned page by checking if the
- * PAGE_FLAGS_CHECK_AT_PREP page->flags are all cleared.  This approach also
- * helps us skip ballooned pages that are locked for compaction or release, thus
- * mitigating their racy check at balloon_page_movable()
- */
-static inline bool page_flags_cleared(struct page *page)
-{
-       return !(page->flags & PAGE_FLAGS_CHECK_AT_PREP);
-}
-
-/*
- * __is_movable_balloon_page - helper to perform @page mapping->flags tests
+ * __is_movable_balloon_page - helper to perform @page PageBalloon tests
  */
 static inline bool __is_movable_balloon_page(struct page *page)
 {
-       struct address_space *mapping = page->mapping;
-       return mapping_balloon(mapping);
+       return PageBalloon(page);
 }
 
 /*
- * balloon_page_movable - test page->mapping->flags to identify balloon pages
- *                       that can be moved by compaction/migration.
- *
- * This function is used at core compaction's page isolation scheme, therefore
- * most pages exposed to it are not enlisted as balloon pages and so, to avoid
- * undesired side effects like racing against __free_pages(), we cannot afford
- * holding the page locked while testing page->mapping->flags here.
+ * balloon_page_movable - test PageBalloon to identify balloon pages
+ *                       and PagePrivate to check that the page is not
+ *                       isolated and can be moved by compaction/migration.
  *
  * As we might return false positives in the case of a balloon page being just
- * released under us, the page->mapping->flags need to be re-tested later,
- * under the proper page lock, at the functions that will be coping with the
- * balloon page case.
+ * released under us, this need to be re-tested later, under the page lock.
  */
 static inline bool balloon_page_movable(struct page *page)
 {
-       /*
-        * Before dereferencing and testing mapping->flags, let's make sure
-        * this is not a page that uses ->mapping in a different way
-        */
-       if (page_flags_cleared(page) && !page_mapped(page) &&
-           page_count(page) == 1)
-               return __is_movable_balloon_page(page);
-
-       return false;
+       return PageBalloon(page) && PagePrivate(page);
 }
 
 /*
  * isolated_balloon_page - identify an isolated balloon page on private
  *                        compaction/migration page lists.
- *
- * After a compaction thread isolates a balloon page for migration, it raises
- * the page refcount to prevent concurrent compaction threads from re-isolating
- * the same page. For that reason putback_movable_pages(), or other routines
- * that need to identify isolated balloon pages on private pagelists, cannot
- * rely on balloon_page_movable() to accomplish the task.
  */
 static inline bool isolated_balloon_page(struct page *page)
 {
-       /* Already isolated balloon pages, by default, have a raised refcount */
-       if (page_flags_cleared(page) && !page_mapped(page) &&
-           page_count(page) >= 2)
-               return __is_movable_balloon_page(page);
-
-       return false;
+       return PageBalloon(page);
 }
 
 /*
  * balloon_page_insert - insert a page into the balloon's page list and make
- *                      the page->mapping assignment accordingly.
+ *                      the page->private assignment accordingly.
+ * @balloon : pointer to balloon device
  * @page    : page to be assigned as a 'balloon page'
- * @mapping : allocated special 'balloon_mapping'
- * @head    : balloon's device page list head
  *
  * Caller must ensure the page is locked and the spin_lock protecting balloon
  * pages list is held before inserting a page into the balloon device.
  */
-static inline void balloon_page_insert(struct page *page,
-                                      struct address_space *mapping,
-                                      struct list_head *head)
+static inline void balloon_page_insert(struct balloon_dev_info *balloon,
+                                      struct page *page)
 {
-       page->mapping = mapping;
-       list_add(&page->lru, head);
+       __SetPageBalloon(page);
+       SetPagePrivate(page);
+       set_page_private(page, (unsigned long)balloon);
+       list_add(&page->lru, &balloon->pages);
 }
 
 /*
  * balloon_page_delete - delete a page from balloon's page list and clear
- *                      the page->mapping assignement accordingly.
+ *                      the page->private assignement accordingly.
  * @page    : page to be released from balloon's page list
  *
  * Caller must ensure the page is locked and the spin_lock protecting balloon
@@ -206,8 +139,12 @@ static inline void balloon_page_insert(struct page *page,
  */
 static inline void balloon_page_delete(struct page *page)
 {
-       page->mapping = NULL;
-       list_del(&page->lru);
+       __ClearPageBalloon(page);
+       set_page_private(page, 0);
+       if (PagePrivate(page)) {
+               ClearPagePrivate(page);
+               list_del(&page->lru);
+       }
 }
 
 /*
@@ -216,11 +153,7 @@ static inline void balloon_page_delete(struct page *page)
  */
 static inline struct balloon_dev_info *balloon_page_device(struct page *page)
 {
-       struct address_space *mapping = page->mapping;
-       if (likely(mapping))
-               return mapping->private_data;
-
-       return NULL;
+       return (struct balloon_dev_info *)page_private(page);
 }
 
 static inline gfp_t balloon_mapping_gfp_mask(void)
@@ -228,34 +161,24 @@ static inline gfp_t balloon_mapping_gfp_mask(void)
        return GFP_HIGHUSER_MOVABLE;
 }
 
-static inline bool balloon_compaction_check(void)
-{
-       return true;
-}
-
 #else /* !CONFIG_BALLOON_COMPACTION */
 
-static inline void *balloon_mapping_alloc(void *balloon_device,
-                               const struct address_space_operations *a_ops)
-{
-       return ERR_PTR(-EOPNOTSUPP);
-}
-
-static inline void balloon_mapping_free(struct address_space *balloon_mapping)
+static inline void balloon_page_insert(struct balloon_dev_info *balloon,
+                                      struct page *page)
 {
-       return;
+       __SetPageBalloon(page);
+       list_add(&page->lru, &balloon->pages);
 }
 
-static inline void balloon_page_insert(struct page *page,
-                                      struct address_space *mapping,
-                                      struct list_head *head)
+static inline void balloon_page_delete(struct page *page)
 {
-       list_add(&page->lru, head);
+       __ClearPageBalloon(page);
+       list_del(&page->lru);
 }
 
-static inline void balloon_page_delete(struct page *page)
+static inline bool __is_movable_balloon_page(struct page *page)
 {
-       list_del(&page->lru);
+       return false;
 }
 
 static inline bool balloon_page_movable(struct page *page)
@@ -289,9 +212,5 @@ static inline gfp_t balloon_mapping_gfp_mask(void)
        return GFP_HIGHUSER;
 }
 
-static inline bool balloon_compaction_check(void)
-{
-       return false;
-}
 #endif /* CONFIG_BALLOON_COMPACTION */
 #endif /* _LINUX_BALLOON_COMPACTION_H */
index a1e31f274fcd55f7cfff245c8516d8e263206fb0..c13a0c09faea8be17286135e7c8cf4d542bc9c41 100644 (file)
@@ -140,6 +140,7 @@ enum {
 };
 
 struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *);
+void blk_mq_finish_init(struct request_queue *q);
 int blk_mq_register_disk(struct gendisk *);
 void blk_mq_unregister_disk(struct gendisk *);
 
index 518b46555b80968c3d29df956f677763fe292c51..87be398166d3ed99f2b6fc5cfdbc33c18da8701c 100644 (file)
@@ -1564,7 +1564,7 @@ static inline int blk_rq_map_integrity_sg(struct request_queue *q,
 }
 static inline struct blk_integrity *bdev_get_integrity(struct block_device *b)
 {
-       return 0;
+       return NULL;
 }
 static inline struct blk_integrity *blk_get_integrity(struct gendisk *disk)
 {
index b5223c570eba54e4dafb4e31b1e7d89c0a65b9f8..1d51968890480bdbca29d576769a6565ba7544a2 100644 (file)
@@ -27,7 +27,6 @@
 
 struct cgroup_root;
 struct cgroup_subsys;
-struct inode;
 struct cgroup;
 
 extern int cgroup_init_early(void);
@@ -38,7 +37,8 @@ extern void cgroup_exit(struct task_struct *p);
 extern int cgroupstats_build(struct cgroupstats *stats,
                                struct dentry *dentry);
 
-extern int proc_cgroup_show(struct seq_file *, void *);
+extern int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
+                           struct pid *pid, struct task_struct *tsk);
 
 /* define the enumeration of all cgroup subsystems */
 #define SUBSYS(_x) _x ## _cgrp_id,
@@ -161,11 +161,6 @@ static inline void css_put(struct cgroup_subsys_state *css)
 
 /* bits in struct cgroup flags field */
 enum {
-       /*
-        * Control Group has previously had a child cgroup or a task,
-        * but no longer (only if CGRP_NOTIFY_ON_RELEASE is set)
-        */
-       CGRP_RELEASABLE,
        /* Control Group requires release notifications to userspace */
        CGRP_NOTIFY_ON_RELEASE,
        /*
@@ -234,13 +229,6 @@ struct cgroup {
         */
        struct list_head e_csets[CGROUP_SUBSYS_COUNT];
 
-       /*
-        * Linked list running through all cgroups that can
-        * potentially be reaped by the release agent. Protected by
-        * release_list_lock
-        */
-       struct list_head release_list;
-
        /*
         * list of pidlists, up to two for each namespace (one for procs, one
         * for tasks); created on demand.
@@ -250,6 +238,9 @@ struct cgroup {
 
        /* used to wait for offlining of csses */
        wait_queue_head_t offline_waitq;
+
+       /* used to schedule release agent */
+       struct work_struct release_agent_work;
 };
 
 #define MAX_CGROUP_ROOT_NAMELEN 64
@@ -536,13 +527,10 @@ static inline bool cgroup_has_tasks(struct cgroup *cgrp)
        return !list_empty(&cgrp->cset_links);
 }
 
-/* returns ino associated with a cgroup, 0 indicates unmounted root */
+/* returns ino associated with a cgroup */
 static inline ino_t cgroup_ino(struct cgroup *cgrp)
 {
-       if (cgrp->kn)
-               return cgrp->kn->ino;
-       else
-               return 0;
+       return cgrp->kn->ino;
 }
 
 /* cft/css accessors for cftype->write() operation */
index 01e3132820da5bc86fa3db31f526436e73ef7a5d..60bdf8dc02a3dcb9ea4b27cf5414b4a408e95a51 100644 (file)
@@ -2,14 +2,24 @@
 #define _LINUX_COMPACTION_H
 
 /* Return values for compact_zone() and try_to_compact_pages() */
+/* compaction didn't start as it was deferred due to past failures */
+#define COMPACT_DEFERRED       0
 /* compaction didn't start as it was not possible or direct reclaim was more suitable */
-#define COMPACT_SKIPPED                0
+#define COMPACT_SKIPPED                1
 /* compaction should continue to another pageblock */
-#define COMPACT_CONTINUE       1
+#define COMPACT_CONTINUE       2
 /* direct compaction partially compacted a zone and there are suitable pages */
-#define COMPACT_PARTIAL                2
+#define COMPACT_PARTIAL                3
 /* The full zone was compacted */
-#define COMPACT_COMPLETE       3
+#define COMPACT_COMPLETE       4
+
+/* Used to signal whether compaction detected need_sched() or lock contention */
+/* No contention detected */
+#define COMPACT_CONTENDED_NONE 0
+/* Either need_sched() was true or fatal signal pending */
+#define COMPACT_CONTENDED_SCHED        1
+/* Zone lock or lru_lock was contended in async compaction */
+#define COMPACT_CONTENDED_LOCK 2
 
 #ifdef CONFIG_COMPACTION
 extern int sysctl_compact_memory;
@@ -22,7 +32,8 @@ extern int sysctl_extfrag_handler(struct ctl_table *table, int write,
 extern int fragmentation_index(struct zone *zone, unsigned int order);
 extern unsigned long try_to_compact_pages(struct zonelist *zonelist,
                        int order, gfp_t gfp_mask, nodemask_t *mask,
-                       enum migrate_mode mode, bool *contended);
+                       enum migrate_mode mode, int *contended,
+                       struct zone **candidate_zone);
 extern void compact_pgdat(pg_data_t *pgdat, int order);
 extern void reset_isolation_suitable(pg_data_t *pgdat);
 extern unsigned long compaction_suitable(struct zone *zone, int order);
@@ -91,7 +102,8 @@ static inline bool compaction_restarting(struct zone *zone, int order)
 #else
 static inline unsigned long try_to_compact_pages(struct zonelist *zonelist,
                        int order, gfp_t gfp_mask, nodemask_t *nodemask,
-                       enum migrate_mode mode, bool *contended)
+                       enum migrate_mode mode, int *contended,
+                       struct zone **candidate_zone)
 {
        return COMPACT_CONTINUE;
 }
index 7d1955afa62c7c319b4467bd98139f270ed004e4..138336b6bb0437f4cc0972f7c4ce34a59bf9b2ec 100644 (file)
@@ -112,6 +112,9 @@ struct cpufreq_policy {
        spinlock_t              transition_lock;
        wait_queue_head_t       transition_wait;
        struct task_struct      *transition_task; /* Task which is doing the transition */
+
+       /* For cpufreq driver's internal use */
+       void                    *driver_data;
 };
 
 /* Only for ACPI */
index 6e39c9bb0dae2b7c2f6738139aba7fed96b74549..2f073db7392e06cf98455f9a83716be2cfaba69e 100644 (file)
@@ -86,7 +86,8 @@ extern void __cpuset_memory_pressure_bump(void);
 
 extern void cpuset_task_status_allowed(struct seq_file *m,
                                        struct task_struct *task);
-extern int proc_cpuset_show(struct seq_file *, void *);
+extern int proc_cpuset_show(struct seq_file *m, struct pid_namespace *ns,
+                           struct pid *pid, struct task_struct *tsk);
 
 extern int cpuset_mem_spread_node(void);
 extern int cpuset_slab_spread_node(void);
index 931b709862728d46f161a8bc72790fc5a8d8c2dc..d5d388160f420e86a325e1b44d8ff9f7d28b43c9 100644 (file)
@@ -263,6 +263,32 @@ struct dma_attrs;
 #define dma_unmap_sg_attrs(dev, sgl, nents, dir, attrs) \
        dma_unmap_sg(dev, sgl, nents, dir)
 
+#else
+static inline void *dma_alloc_writecombine(struct device *dev, size_t size,
+                                          dma_addr_t *dma_addr, gfp_t gfp)
+{
+       DEFINE_DMA_ATTRS(attrs);
+       dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+       return dma_alloc_attrs(dev, size, dma_addr, gfp, &attrs);
+}
+
+static inline void dma_free_writecombine(struct device *dev, size_t size,
+                                        void *cpu_addr, dma_addr_t dma_addr)
+{
+       DEFINE_DMA_ATTRS(attrs);
+       dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+       return dma_free_attrs(dev, size, cpu_addr, dma_addr, &attrs);
+}
+
+static inline int dma_mmap_writecombine(struct device *dev,
+                                       struct vm_area_struct *vma,
+                                       void *cpu_addr, dma_addr_t dma_addr,
+                                       size_t size)
+{
+       DEFINE_DMA_ATTRS(attrs);
+       dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+       return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs);
+}
 #endif /* CONFIG_HAVE_DMA_ATTRS */
 
 #ifdef CONFIG_NEED_DMA_MAP_STATE
index 4ebc49fae391ab1c8a9c1b8c769755d04db52b86..0d348e011a6e9002825989d37972cc5d0820b1da 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/percpu_counter.h>
 #include <linux/spinlock.h>
 #include <linux/seqlock.h>
+#include <linux/gfp.h>
 
 /*
  * When maximum proportion of some event type is specified, this is the
@@ -32,7 +33,7 @@ struct fprop_global {
        seqcount_t sequence;
 };
 
-int fprop_global_init(struct fprop_global *p);
+int fprop_global_init(struct fprop_global *p, gfp_t gfp);
 void fprop_global_destroy(struct fprop_global *p);
 bool fprop_new_period(struct fprop_global *p, int periods);
 
@@ -79,7 +80,7 @@ struct fprop_local_percpu {
        raw_spinlock_t lock;    /* Protect period and numerator */
 };
 
-int fprop_local_init_percpu(struct fprop_local_percpu *pl);
+int fprop_local_init_percpu(struct fprop_local_percpu *pl, gfp_t gfp);
 void fprop_local_destroy_percpu(struct fprop_local_percpu *pl);
 void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl);
 void __fprop_inc_percpu_max(struct fprop_global *p, struct fprop_local_percpu *pl,
index 94187721ad412c6c8da4b44e946d24ecd240beb0..2023306c620e69c99b7ad00d04349419668573d7 100644 (file)
@@ -851,13 +851,7 @@ static inline struct file *get_file(struct file *f)
  */
 #define FILE_LOCK_DEFERRED 1
 
-/*
- * The POSIX file lock owner is determined by
- * the "struct files_struct" in the thread group
- * (or NULL for no owner - BSD locks).
- *
- * Lockd stuffs a "host" pointer into this.
- */
+/* legacy typedef, should eventually be removed */
 typedef void *fl_owner_t;
 
 struct file_lock_operations {
@@ -868,10 +862,13 @@ struct file_lock_operations {
 struct lock_manager_operations {
        int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
        unsigned long (*lm_owner_key)(struct file_lock *);
+       void (*lm_get_owner)(struct file_lock *, struct file_lock *);
+       void (*lm_put_owner)(struct file_lock *);
        void (*lm_notify)(struct file_lock *);  /* unblock callback */
-       int (*lm_grant)(struct file_lock *, struct file_lock *, int);
-       void (*lm_break)(struct file_lock *);
-       int (*lm_change)(struct file_lock **, int);
+       int (*lm_grant)(struct file_lock *, int);
+       bool (*lm_break)(struct file_lock *);
+       int (*lm_change)(struct file_lock **, int, struct list_head *);
+       void (*lm_setup)(struct file_lock *, void **);
 };
 
 struct lock_manager {
@@ -966,7 +963,7 @@ void locks_free_lock(struct file_lock *fl);
 extern void locks_init_lock(struct file_lock *);
 extern struct file_lock * locks_alloc_lock(void);
 extern void locks_copy_lock(struct file_lock *, struct file_lock *);
-extern void __locks_copy_lock(struct file_lock *, const struct file_lock *);
+extern void locks_copy_conflock(struct file_lock *, struct file_lock *);
 extern void locks_remove_posix(struct file *, fl_owner_t);
 extern void locks_remove_file(struct file *);
 extern void locks_release_private(struct file_lock *);
@@ -980,11 +977,9 @@ extern int vfs_cancel_lock(struct file *filp, struct file_lock *fl);
 extern int flock_lock_file_wait(struct file *filp, struct file_lock *fl);
 extern int __break_lease(struct inode *inode, unsigned int flags, unsigned int type);
 extern void lease_get_mtime(struct inode *, struct timespec *time);
-extern int generic_setlease(struct file *, long, struct file_lock **);
-extern int vfs_setlease(struct file *, long, struct file_lock **);
-extern int lease_modify(struct file_lock **, int);
-extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
-extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
+extern int generic_setlease(struct file *, long, struct file_lock **, void **priv);
+extern int vfs_setlease(struct file *, long, struct file_lock **, void **);
+extern int lease_modify(struct file_lock **, int, struct list_head *);
 #else /* !CONFIG_FILE_LOCKING */
 static inline int fcntl_getlk(struct file *file, unsigned int cmd,
                              struct flock __user *user)
@@ -1013,12 +1008,12 @@ static inline int fcntl_setlk64(unsigned int fd, struct file *file,
 #endif
 static inline int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
 {
-       return 0;
+       return -EINVAL;
 }
 
 static inline int fcntl_getlease(struct file *filp)
 {
-       return 0;
+       return F_UNLCK;
 }
 
 static inline void locks_init_lock(struct file_lock *fl)
@@ -1026,7 +1021,7 @@ static inline void locks_init_lock(struct file_lock *fl)
        return;
 }
 
-static inline void __locks_copy_lock(struct file_lock *new, struct file_lock *fl)
+static inline void locks_copy_conflock(struct file_lock *new, struct file_lock *fl)
 {
        return;
 }
@@ -1100,33 +1095,22 @@ static inline void lease_get_mtime(struct inode *inode, struct timespec *time)
 }
 
 static inline int generic_setlease(struct file *filp, long arg,
-                                   struct file_lock **flp)
+                                   struct file_lock **flp, void **priv)
 {
        return -EINVAL;
 }
 
 static inline int vfs_setlease(struct file *filp, long arg,
-                              struct file_lock **lease)
+                              struct file_lock **lease, void **priv)
 {
        return -EINVAL;
 }
 
-static inline int lease_modify(struct file_lock **before, int arg)
+static inline int lease_modify(struct file_lock **before, int arg,
+                              struct list_head *dispose)
 {
        return -EINVAL;
 }
-
-static inline int lock_may_read(struct inode *inode, loff_t start,
-                               unsigned long len)
-{
-       return 1;
-}
-
-static inline int lock_may_write(struct inode *inode, loff_t start,
-                                unsigned long len)
-{
-       return 1;
-}
 #endif /* !CONFIG_FILE_LOCKING */
 
 
@@ -1151,8 +1135,8 @@ extern void fasync_free(struct fasync_struct *);
 /* can be called from interrupts */
 extern void kill_fasync(struct fasync_struct **, int, int);
 
-extern int __f_setown(struct file *filp, struct pid *, enum pid_type, int force);
-extern int f_setown(struct file *filp, unsigned long arg, int force);
+extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force);
+extern void f_setown(struct file *filp, unsigned long arg, int force);
 extern void f_delown(struct file *filp);
 extern pid_t f_getown(struct file *filp);
 extern int send_sigurg(struct fown_struct *fown);
@@ -1506,7 +1490,7 @@ struct file_operations {
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
-       int (*setlease)(struct file *, long, struct file_lock **);
+       int (*setlease)(struct file *, long, struct file_lock **, void **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                          loff_t len);
        int (*show_fdinfo)(struct seq_file *m, struct file *f);
@@ -2611,6 +2595,7 @@ extern int simple_write_end(struct file *file, struct address_space *mapping,
                        struct page *page, void *fsdata);
 extern int always_delete_dentry(const struct dentry *);
 extern struct inode *alloc_anon_inode(struct super_block *);
+extern int simple_nosetlease(struct file *, long, struct file_lock **, void **);
 extern const struct dentry_operations simple_dentry_operations;
 
 extern struct dentry *simple_lookup(struct inode *, struct dentry *, unsigned int flags);
index f49ddb1b2273b0d46e1cbf07ca434287a42e3cca..84d60cb841b100eaa71b39612b3d397b37948ddf 100644 (file)
@@ -781,13 +781,13 @@ struct fsl_ifc_regs {
                __be32 amask;
                u32 res4[0x2];
        } amask_cs[FSL_IFC_BANK_COUNT];
-       u32 res5[0x17];
+       u32 res5[0x18];
        struct {
-               __be32 csor_ext;
                __be32 csor;
+               __be32 csor_ext;
                u32 res6;
        } csor_cs[FSL_IFC_BANK_COUNT];
-       u32 res7[0x19];
+       u32 res7[0x18];
        struct {
                __be32 ftim[4];
                u32 res8[0x8];
index 1c2fdaa2ffc3ef68fed69ffc7be5ef5d357a0dcb..1ccaab44abcc80541a1aebc024b62e650cb3982c 100644 (file)
@@ -110,6 +110,10 @@ extern void gen_pool_set_algo(struct gen_pool *pool, genpool_algo_t algo,
 extern unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size,
                unsigned long start, unsigned int nr, void *data);
 
+extern unsigned long gen_pool_first_fit_order_align(unsigned long *map,
+               unsigned long size, unsigned long start, unsigned int nr,
+               void *data);
+
 extern unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size,
                unsigned long start, unsigned int nr, void *data);
 
@@ -117,6 +121,9 @@ extern struct gen_pool *devm_gen_pool_create(struct device *dev,
                int min_alloc_order, int nid);
 extern struct gen_pool *dev_get_gen_pool(struct device *dev);
 
+bool addr_in_gen_pool(struct gen_pool *pool, unsigned long start,
+                       size_t size);
+
 #ifdef CONFIG_OF
 extern struct gen_pool *of_get_named_gen_pool(struct device_node *np,
        const char *propname, int index);
index 5e7219dc0fae44968c452d9e8e6b642435cf74b6..41b30fd4d0419875ffdc5742e73a346a544d0e01 100644 (file)
@@ -156,7 +156,7 @@ struct vm_area_struct;
 #define GFP_DMA32      __GFP_DMA32
 
 /* Convert GFP flags to their corresponding migrate type */
-static inline int allocflags_to_migratetype(gfp_t gfp_flags)
+static inline int gfpflags_to_migratetype(const gfp_t gfp_flags)
 {
        WARN_ON((gfp_flags & GFP_MOVABLE_MASK) == GFP_MOVABLE_MASK);
 
index c5e41da20112e3a244d2cd9672b2bc5b7f703184..249db3057e4dc0454f8947f305e6e3b94e138719 100644 (file)
@@ -56,6 +56,8 @@ struct seq_file;
  *     as the chip access may sleep when e.g. reading out the IRQ status
  *     registers.
  * @exported: flags if the gpiochip is exported for use from sysfs. Private.
+ * @irq_not_threaded: flag must be set if @can_sleep is set but the
+ *     IRQs don't need to be threaded
  *
  * A gpio_chip can help platforms abstract various sources of GPIOs so
  * they can all be accessed through a common programing interface.
@@ -101,6 +103,7 @@ struct gpio_chip {
        struct gpio_desc        *desc;
        const char              *const *names;
        bool                    can_sleep;
+       bool                    irq_not_threaded;
        bool                    exported;
 
 #ifdef CONFIG_GPIOLIB_IRQCHIP
@@ -141,7 +144,7 @@ extern const char *gpiochip_is_requested(struct gpio_chip *chip,
 
 /* add/remove chips */
 extern int gpiochip_add(struct gpio_chip *chip);
-extern int gpiochip_remove(struct gpio_chip *chip);
+extern void gpiochip_remove(struct gpio_chip *chip);
 extern struct gpio_chip *gpiochip_find(void *data,
                              int (*match)(struct gpio_chip *chip, void *data));
 
@@ -166,7 +169,8 @@ int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
 
 #endif /* CONFIG_GPIOLIB_IRQCHIP */
 
-int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label);
+struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
+                                           const char *label);
 void gpiochip_free_own_desc(struct gpio_desc *desc);
 
 #else /* CONFIG_GPIOLIB */
index 63579cb8d3dcfb5b7d36b80cc19d784e1522e3c9..ad9051bab267dc775df152f5a6aedf5bf2a18856 100644 (file)
@@ -132,7 +132,7 @@ extern int __pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma,
 static inline int pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma,
                spinlock_t **ptl)
 {
-       VM_BUG_ON(!rwsem_is_locked(&vma->vm_mm->mmap_sem));
+       VM_BUG_ON_VMA(!rwsem_is_locked(&vma->vm_mm->mmap_sem), vma);
        if (pmd_trans_huge(*pmd))
                return __pmd_trans_huge_lock(pmd, vma, ptl);
        else
index 698ad053d064aef74793449f4b4b55018994a908..69517a24bc50678e4f2d69d931b9586cb76a9b69 100644 (file)
@@ -193,11 +193,6 @@ extern void irq_wake_thread(unsigned int irq, void *dev_id);
 /* The following three functions are for the core kernel use only. */
 extern void suspend_device_irqs(void);
 extern void resume_device_irqs(void);
-#ifdef CONFIG_PM_SLEEP
-extern int check_wakeup_irqs(void);
-#else
-static inline int check_wakeup_irqs(void) { return 0; }
-#endif
 
 /**
  * struct irq_affinity_notify - context for notification of IRQ affinity changes
index 20f9a527922a6a04f84670e7538e7abbc1ece7d1..7b02bcc85b9ead2ced4308c6ff8a120006c1c793 100644 (file)
@@ -80,6 +80,7 @@ enum iommu_attr {
        DOMAIN_ATTR_FSL_PAMU_STASH,
        DOMAIN_ATTR_FSL_PAMU_ENABLE,
        DOMAIN_ATTR_FSL_PAMUV1,
+       DOMAIN_ATTR_NESTING,    /* two stages of translation */
        DOMAIN_ATTR_MAX,
 };
 
index 142ec544167cc91fae73b4e4f1d1c8f347b179f2..2c5250222278069dabb31cf4dfbb8ce41fea95ca 100644 (file)
@@ -215,6 +215,11 @@ static inline int __deprecated check_region(resource_size_t s,
 
 /* Wrappers for managed devices */
 struct device;
+
+extern int devm_request_resource(struct device *dev, struct resource *root,
+                                struct resource *new);
+extern void devm_release_resource(struct device *dev, struct resource *new);
+
 #define devm_request_region(dev,start,n,name) \
        __devm_request_region(dev, &ioport_resource, (start), (n), (name))
 #define devm_request_mem_region(dev,start,n,name) \
index 62af59242ddc33faf11b0b872cb09499af3b42b5..03f48d936f6690a17f196f4e8707f6aece369a56 100644 (file)
@@ -173,6 +173,7 @@ struct irq_data {
  * IRQD_IRQ_DISABLED           - Disabled state of the interrupt
  * IRQD_IRQ_MASKED             - Masked state of the interrupt
  * IRQD_IRQ_INPROGRESS         - In progress state of the interrupt
+ * IRQD_WAKEUP_ARMED           - Wakeup mode armed
  */
 enum {
        IRQD_TRIGGER_MASK               = 0xf,
@@ -186,6 +187,7 @@ enum {
        IRQD_IRQ_DISABLED               = (1 << 16),
        IRQD_IRQ_MASKED                 = (1 << 17),
        IRQD_IRQ_INPROGRESS             = (1 << 18),
+       IRQD_WAKEUP_ARMED               = (1 << 19),
 };
 
 static inline bool irqd_is_setaffinity_pending(struct irq_data *d)
@@ -257,6 +259,12 @@ static inline bool irqd_irq_inprogress(struct irq_data *d)
        return d->state_use_accessors & IRQD_IRQ_INPROGRESS;
 }
 
+static inline bool irqd_is_wakeup_armed(struct irq_data *d)
+{
+       return d->state_use_accessors & IRQD_WAKEUP_ARMED;
+}
+
+
 /*
  * Functions for chained handlers which can be enabled/disabled by the
  * standard disable_irq/enable_irq calls. Must be called with
index bf9422c3aefe22ddd7efa63b3f88502c0659b0b5..bf3fe719c7ce9d3c0efa3c2449cc0d2a7e5b43ad 100644 (file)
@@ -39,9 +39,12 @@ bool irq_work_queue_on(struct irq_work *work, int cpu);
 #endif
 
 void irq_work_run(void);
+void irq_work_tick(void);
 void irq_work_sync(struct irq_work *work);
 
 #ifdef CONFIG_IRQ_WORK
+#include <asm/irq_work.h>
+
 bool irq_work_needs_cpu(void);
 #else
 static inline bool irq_work_needs_cpu(void) { return false; }
index 45e2d8c15bd211d0fa15473efc4e1f3f8c6f17e3..13eed92c7d24735d1b341085e01c4aa32be3eb1a 100644 (file)
 #define GIC_CPU_ACTIVEPRIO             0xd0
 #define GIC_CPU_IDENT                  0xfc
 
+#define GICC_ENABLE                    0x1
+#define GICC_INT_PRI_THRESHOLD         0xf0
 #define GICC_IAR_INT_ID_MASK           0x3ff
+#define GICC_INT_SPURIOUS              1023
+#define GICC_DIS_BYPASS_MASK           0x1e0
 
 #define GIC_DIST_CTRL                  0x000
 #define GIC_DIST_CTR                   0x004
 #define GIC_DIST_SGI_PENDING_CLEAR     0xf10
 #define GIC_DIST_SGI_PENDING_SET       0xf20
 
+#define GICD_ENABLE                    0x1
+#define GICD_DISABLE                   0x0
+#define GICD_INT_ACTLOW_LVLTRIG                0x0
+#define GICD_INT_EN_CLR_X32            0xffffffff
+#define GICD_INT_EN_SET_SGI            0x0000ffff
+#define GICD_INT_EN_CLR_PPI            0xffff0000
+#define GICD_INT_DEF_PRI               0xa0
+#define GICD_INT_DEF_PRI_X4            ((GICD_INT_DEF_PRI << 24) |\
+                                       (GICD_INT_DEF_PRI << 16) |\
+                                       (GICD_INT_DEF_PRI << 8) |\
+                                       GICD_INT_DEF_PRI)
+
 #define GICH_HCR                       0x0
 #define GICH_VTR                       0x4
 #define GICH_VMCR                      0x8
index 472c021a2d4f09923cd3913647be44c016ff3003..faf433af425e41e2da532939af63ec258f8fd619 100644 (file)
@@ -12,6 +12,8 @@ struct irq_affinity_notify;
 struct proc_dir_entry;
 struct module;
 struct irq_desc;
+struct irq_domain;
+struct pt_regs;
 
 /**
  * struct irq_desc - interrupt descriptor
@@ -36,6 +38,11 @@ struct irq_desc;
  * @threads_oneshot:   bitfield to handle shared oneshot threads
  * @threads_active:    number of irqaction threads currently running
  * @wait_for_threads:  wait queue for sync_irq to wait for threaded handlers
+ * @nr_actions:                number of installed actions on this descriptor
+ * @no_suspend_depth:  number of irqactions on a irq descriptor with
+ *                     IRQF_NO_SUSPEND set
+ * @force_resume_depth:        number of irqactions on a irq descriptor with
+ *                     IRQF_FORCE_RESUME set
  * @dir:               /proc/irq/ procfs entry
  * @name:              flow handler name for /proc/interrupts output
  */
@@ -68,6 +75,11 @@ struct irq_desc {
        unsigned long           threads_oneshot;
        atomic_t                threads_active;
        wait_queue_head_t       wait_for_threads;
+#ifdef CONFIG_PM_SLEEP
+       unsigned int            nr_actions;
+       unsigned int            no_suspend_depth;
+       unsigned int            force_resume_depth;
+#endif
 #ifdef CONFIG_PROC_FS
        struct proc_dir_entry   *dir;
 #endif
@@ -118,6 +130,23 @@ static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *de
 
 int generic_handle_irq(unsigned int irq);
 
+#ifdef CONFIG_HANDLE_DOMAIN_IRQ
+/*
+ * Convert a HW interrupt number to a logical one using a IRQ domain,
+ * and handle the result interrupt number. Return -EINVAL if
+ * conversion failed. Providing a NULL domain indicates that the
+ * conversion has already been done.
+ */
+int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
+                       bool lookup, struct pt_regs *regs);
+
+static inline int handle_domain_irq(struct irq_domain *domain,
+                                   unsigned int hwirq, struct pt_regs *regs)
+{
+       return __handle_domain_irq(domain, hwirq, true, regs);
+}
+#endif
+
 /* Test to see if a driver has successfully requested an irq */
 static inline int irq_has_action(unsigned int irq)
 {
index 95624bed87ef6f8c389e78621b628240bf408208..e9e420b6d9316fc191babb91ae7a7e64b932b0ea 100644 (file)
@@ -715,23 +715,8 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
        (void) (&_max1 == &_max2);              \
        _max1 > _max2 ? _max1 : _max2; })
 
-#define min3(x, y, z) ({                       \
-       typeof(x) _min1 = (x);                  \
-       typeof(y) _min2 = (y);                  \
-       typeof(z) _min3 = (z);                  \
-       (void) (&_min1 == &_min2);              \
-       (void) (&_min1 == &_min3);              \
-       _min1 < _min2 ? (_min1 < _min3 ? _min1 : _min3) : \
-               (_min2 < _min3 ? _min2 : _min3); })
-
-#define max3(x, y, z) ({                       \
-       typeof(x) _max1 = (x);                  \
-       typeof(y) _max2 = (y);                  \
-       typeof(z) _max3 = (z);                  \
-       (void) (&_max1 == &_max2);              \
-       (void) (&_max1 == &_max3);              \
-       _max1 > _max2 ? (_max1 > _max3 ? _max1 : _max3) : \
-               (_max2 > _max3 ? _max2 : _max3); })
+#define min3(x, y, z) min((typeof(x))min(x, y), z)
+#define max3(x, y, z) max((typeof(x))max(x, y), z)
 
 /**
  * min_not_zero - return the minimum that is _not_ zero, unless both are zero
@@ -746,20 +731,13 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
 /**
  * clamp - return a value clamped to a given range with strict typechecking
  * @val: current value
- * @min: minimum allowable value
- * @max: maximum allowable value
+ * @lo: lowest allowable value
+ * @hi: highest allowable value
  *
- * This macro does strict typechecking of min/max to make sure they are of the
+ * This macro does strict typechecking of lo/hi to make sure they are of the
  * same type as val.  See the unnecessary pointer comparisons.
  */
-#define clamp(val, min, max) ({                        \
-       typeof(val) __val = (val);              \
-       typeof(min) __min = (min);              \
-       typeof(max) __max = (max);              \
-       (void) (&__val == &__min);              \
-       (void) (&__val == &__max);              \
-       __val = __val < __min ? __min: __val;   \
-       __val > __max ? __max: __val; })
+#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi)
 
 /*
  * ..and if you can't take the strict
@@ -781,36 +759,26 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
  * clamp_t - return a value clamped to a given range using a given type
  * @type: the type of variable to use
  * @val: current value
- * @min: minimum allowable value
- * @max: maximum allowable value
+ * @lo: minimum allowable value
+ * @hi: maximum allowable value
  *
  * This macro does no typechecking and uses temporary variables of type
  * 'type' to make all the comparisons.
  */
-#define clamp_t(type, val, min, max) ({                \
-       type __val = (val);                     \
-       type __min = (min);                     \
-       type __max = (max);                     \
-       __val = __val < __min ? __min: __val;   \
-       __val > __max ? __max: __val; })
+#define clamp_t(type, val, lo, hi) min_t(type, max_t(type, val, lo), hi)
 
 /**
  * clamp_val - return a value clamped to a given range using val's type
  * @val: current value
- * @min: minimum allowable value
- * @max: maximum allowable value
+ * @lo: minimum allowable value
+ * @hi: maximum allowable value
  *
  * This macro does no typechecking and uses temporary variables of whatever
  * type the input argument 'val' is.  This is useful when val is an unsigned
  * type and min and max are literals that will otherwise be assigned a signed
  * integer type.
  */
-#define clamp_val(val, min, max) ({            \
-       typeof(val) __val = (val);              \
-       typeof(val) __min = (min);              \
-       typeof(val) __max = (max);              \
-       __val = __val < __min ? __min: __val;   \
-       __val > __max ? __max: __val; })
+#define clamp_val(val, lo, hi) clamp_t(typeof(val), val, lo, hi)
 
 
 /*
index 92abb497ab14bb50142652ead5dcf3f630556e3f..bd5fefeaf548670d706af4b850121ed28b7091df 100644 (file)
@@ -1404,14 +1404,14 @@ static inline int sata_srst_pmp(struct ata_link *link)
  * printk helpers
  */
 __printf(3, 4)
-int ata_port_printk(const struct ata_port *ap, const char *level,
-                   const char *fmt, ...);
+void ata_port_printk(const struct ata_port *ap, const char *level,
+                    const char *fmt, ...);
 __printf(3, 4)
-int ata_link_printk(const struct ata_link *link, const char *level,
-                   const char *fmt, ...);
+void ata_link_printk(const struct ata_link *link, const char *level,
+                    const char *fmt, ...);
 __printf(3, 4)
-int ata_dev_printk(const struct ata_device *dev, const char *level,
-                  const char *fmt, ...);
+void ata_dev_printk(const struct ata_device *dev, const char *level,
+                   const char *fmt, ...);
 
 #define ata_port_err(ap, fmt, ...)                             \
        ata_port_printk(ap, KERN_ERR, fmt, ##__VA_ARGS__)
index 219d79627c05afc2b0766085b73697a4b41bc621..ff82a32871b566401eb0a177a4b0bf45450b8f4d 100644 (file)
@@ -178,7 +178,6 @@ struct nlm_block {
        unsigned char           b_granted;      /* VFS granted lock */
        struct nlm_file *       b_file;         /* file in question */
        struct cache_req *      b_cache_req;    /* deferred request handling */
-       struct file_lock *      b_fl;           /* set for GETLK */
        struct cache_deferred_req * b_deferred_req;
        unsigned int            b_flags;        /* block flags */
 #define B_QUEUED               1       /* lock queued */
index e0752d204d9e8b8cb9ea658668c7d35f5806a754..19df5d857411a7cc567fa7571985bfdcd65c28b0 100644 (file)
@@ -440,11 +440,6 @@ void __memcg_kmem_uncharge_pages(struct page *page, int order);
 
 int memcg_cache_id(struct mem_cgroup *memcg);
 
-int memcg_alloc_cache_params(struct mem_cgroup *memcg, struct kmem_cache *s,
-                            struct kmem_cache *root_cache);
-void memcg_free_cache_params(struct kmem_cache *s);
-
-int memcg_update_cache_size(struct kmem_cache *s, int num_groups);
 void memcg_update_array_size(int num_groups);
 
 struct kmem_cache *
@@ -574,16 +569,6 @@ static inline int memcg_cache_id(struct mem_cgroup *memcg)
        return -1;
 }
 
-static inline int memcg_alloc_cache_params(struct mem_cgroup *memcg,
-               struct kmem_cache *s, struct kmem_cache *root_cache)
-{
-       return 0;
-}
-
-static inline void memcg_free_cache_params(struct kmem_cache *s)
-{
-}
-
 static inline struct kmem_cache *
 memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp)
 {
index d9524c49d767b21a44c5513c2a31a3e47312d729..8f1a41951df9ca96c24f3cdfc540425b7f0cb697 100644 (file)
@@ -84,6 +84,7 @@ extern int zone_grow_waitqueues(struct zone *zone, unsigned long nr_pages);
 extern int add_one_highpage(struct page *page, int pfn, int bad_ppro);
 /* VM interface that may be used by firmware interface */
 extern int online_pages(unsigned long, unsigned long, int);
+extern int test_pages_in_a_zone(unsigned long, unsigned long);
 extern void __offline_isolated_pages(unsigned long, unsigned long);
 
 typedef void (*online_page_callback_t)(struct page *page);
index f230a978e6baf138e8608f547d49058ae271abe4..3d385c81c153497e9970b4f6227aeded05fd6f22 100644 (file)
@@ -134,9 +134,10 @@ void mpol_free_shared_policy(struct shared_policy *p);
 struct mempolicy *mpol_shared_policy_lookup(struct shared_policy *sp,
                                            unsigned long idx);
 
-struct mempolicy *get_vma_policy(struct task_struct *tsk,
-               struct vm_area_struct *vma, unsigned long addr);
-bool vma_policy_mof(struct task_struct *task, struct vm_area_struct *vma);
+struct mempolicy *get_task_policy(struct task_struct *p);
+struct mempolicy *__get_vma_policy(struct vm_area_struct *vma,
+               unsigned long addr);
+bool vma_policy_mof(struct vm_area_struct *vma);
 
 extern void numa_default_policy(void);
 extern void numa_policy_init(void);
index 8f6f2e91e7ae639db87613a5426c64effd683996..57388171610d1a088bcaa79f5efaab5a0537a214 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/fb.h>
 #include <linux/io.h>
 #include <linux/jiffies.h>
+#include <linux/mmc/card.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 
  */
 #define TMIO_MMC_HAVE_HIGH_REG         (1 << 6)
 
+/*
+ * Some controllers have CMD12 automatically
+ * issue/non-issue register
+ */
+#define TMIO_MMC_HAVE_CMD12_CTRL       (1 << 7)
+
+/*
+ * Some controllers needs to set 1 on SDIO status reserved bits
+ */
+#define TMIO_MMC_SDIO_STATUS_QUIRK     (1 << 8)
+
+/*
+ * Some controllers have DMA enable/disable register
+ */
+#define TMIO_MMC_HAVE_CTL_DMA_REG      (1 << 9)
+
+/*
+ * Some controllers allows to set SDx actual clock
+ */
+#define TMIO_MMC_CLK_ACTUAL            (1 << 10)
+
 int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
 int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
 void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
@@ -96,6 +118,7 @@ struct tmio_mmc_dma {
        int slave_id_tx;
        int slave_id_rx;
        int alignment_shift;
+       dma_addr_t dma_rx_offset;
        bool (*filter)(struct dma_chan *chan, void *arg);
 };
 
@@ -120,6 +143,8 @@ struct tmio_mmc_data {
        /* clock management callbacks */
        int (*clk_enable)(struct platform_device *pdev, unsigned int *f);
        void (*clk_disable)(struct platform_device *pdev);
+       int (*multi_io_quirk)(struct mmc_card *card,
+                             unsigned int direction, int blk_size);
 };
 
 /*
index a2901c41466433072aa68ee269ae54ab4adc0f2d..01aad3ed89ecd10d1ad2c6415451871b86524528 100644 (file)
@@ -13,18 +13,9 @@ typedef void free_page_t(struct page *page, unsigned long private);
  * Return values from addresss_space_operations.migratepage():
  * - negative errno on page migration failure;
  * - zero on page migration success;
- *
- * The balloon page migration introduces this special case where a 'distinct'
- * return code is used to flag a successful page migration to unmap_and_move().
- * This approach is necessary because page migration can race against balloon
- * deflation procedure, and for such case we could introduce a nasty page leak
- * if a successfully migrated balloon page gets released concurrently with
- * migration's unmap_and_move() wrap-up steps.
  */
 #define MIGRATEPAGE_SUCCESS            0
-#define MIGRATEPAGE_BALLOON_SUCCESS    1 /* special ret code for balloon page
-                                          * sucessful migration case.
-                                          */
+
 enum migrate_reason {
        MR_COMPACTION,
        MR_MEMORY_FAILURE,
@@ -82,9 +73,6 @@ static inline int migrate_huge_page_move_mapping(struct address_space *mapping,
        return -ENOSYS;
 }
 
-/* Possible settings for the migrate_page() method in address_operations */
-#define migrate_page NULL
-
 #endif /* CONFIG_MIGRATION */
 
 #ifdef CONFIG_NUMA_BALANCING
index 0f4196a0bc20c8267d8f5eade59f0d14abaaa284..fa0d74e0642812dc21e9c32b8e4fc57514238117 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/pfn.h>
 #include <linux/bit_spinlock.h>
 #include <linux/shrinker.h>
+#include <linux/resource.h>
 
 struct mempolicy;
 struct anon_vma;
@@ -553,6 +554,25 @@ static inline void __ClearPageBuddy(struct page *page)
        atomic_set(&page->_mapcount, -1);
 }
 
+#define PAGE_BALLOON_MAPCOUNT_VALUE (-256)
+
+static inline int PageBalloon(struct page *page)
+{
+       return atomic_read(&page->_mapcount) == PAGE_BALLOON_MAPCOUNT_VALUE;
+}
+
+static inline void __SetPageBalloon(struct page *page)
+{
+       VM_BUG_ON_PAGE(atomic_read(&page->_mapcount) != -1, page);
+       atomic_set(&page->_mapcount, PAGE_BALLOON_MAPCOUNT_VALUE);
+}
+
+static inline void __ClearPageBalloon(struct page *page)
+{
+       VM_BUG_ON_PAGE(!PageBalloon(page), page);
+       atomic_set(&page->_mapcount, -1);
+}
+
 void put_page(struct page *page);
 void put_pages_list(struct list_head *pages);
 
@@ -1247,8 +1267,8 @@ static inline int stack_guard_page_end(struct vm_area_struct *vma,
                !vma_growsup(vma->vm_next, addr);
 }
 
-extern pid_t
-vm_is_stack(struct task_struct *task, struct vm_area_struct *vma, int in_group);
+extern struct task_struct *task_of_stack(struct task_struct *task,
+                               struct vm_area_struct *vma, bool in_group);
 
 extern unsigned long move_page_tables(struct vm_area_struct *vma,
                unsigned long old_addr, struct vm_area_struct *new_vma,
@@ -1780,6 +1800,20 @@ extern struct vm_area_struct *copy_vma(struct vm_area_struct **,
        bool *need_rmap_locks);
 extern void exit_mmap(struct mm_struct *);
 
+static inline int check_data_rlimit(unsigned long rlim,
+                                   unsigned long new,
+                                   unsigned long start,
+                                   unsigned long end_data,
+                                   unsigned long start_data)
+{
+       if (rlim < RLIM_INFINITY) {
+               if (((new - start) + (end_data - start_data)) > rlim)
+                       return -ENOSPC;
+       }
+
+       return 0;
+}
+
 extern int mm_take_all_locks(struct mm_struct *mm);
 extern void mm_drop_all_locks(struct mm_struct *mm);
 
index d424b9de3affbbee5476b9973070fc03b1b38422..b0692d28f8e649ec64d5f38db0a18967b547ff29 100644 (file)
@@ -42,7 +42,8 @@ struct mmc_csd {
        unsigned int            read_partial:1,
                                read_misalign:1,
                                write_partial:1,
-                               write_misalign:1;
+                               write_misalign:1,
+                               dsr_imp:1;
 };
 
 struct mmc_ext_csd {
@@ -74,7 +75,7 @@ struct mmc_ext_csd {
        unsigned int            sec_trim_mult;  /* Secure trim multiplier  */
        unsigned int            sec_erase_mult; /* Secure erase multiplier */
        unsigned int            trim_timeout;           /* In milliseconds */
-       bool                    enhanced_area_en;       /* enable bit */
+       bool                    partition_setting_completed;    /* enable bit */
        unsigned long long      enhanced_area_offset;   /* Units: Byte */
        unsigned int            enhanced_area_size;     /* Units: KB */
        unsigned int            cache_size;             /* Units: KB */
@@ -214,11 +215,12 @@ enum mmc_blk_status {
 };
 
 /* The number of MMC physical partitions.  These consist of:
- * boot partitions (2), general purpose partitions (4) in MMC v4.4.
+ * boot partitions (2), general purpose partitions (4) and
+ * RPMB partition (1) in MMC v4.4.
  */
 #define MMC_NUM_BOOT_PARTITION 2
 #define MMC_NUM_GP_PARTITION   4
-#define MMC_NUM_PHY_PARTITION  6
+#define MMC_NUM_PHY_PARTITION  7
 #define MAX_MMC_PART_NAME_LEN  20
 
 /*
index 29ce014ab42139cef63da0c6bc78b3d1ca9dd060..001366927cf443aa4ca46275cf8d0c5c3a601b86 100644 (file)
@@ -26,6 +26,8 @@ enum dw_mci_state {
        STATE_DATA_BUSY,
        STATE_SENDING_STOP,
        STATE_DATA_ERROR,
+       STATE_SENDING_CMD11,
+       STATE_WAITING_CMD11_DONE,
 };
 
 enum {
@@ -188,7 +190,7 @@ struct dw_mci {
        /* Workaround flags */
        u32                     quirks;
 
-       struct regulator        *vmmc;  /* Power regulator */
+       bool                    vqmmc_enabled;
        unsigned long           irq_flags; /* IRQ flags */
        int                     irq;
 };
index 7960424d0bc0cf4f1ad65e93f9df79fa83bf9403..df0c15396bbfcce6c01eabe49778c4584d4b2d69 100644 (file)
@@ -42,6 +42,7 @@ struct mmc_ios {
 #define MMC_POWER_OFF          0
 #define MMC_POWER_UP           1
 #define MMC_POWER_ON           2
+#define MMC_POWER_UNDEFINED    3
 
        unsigned char   bus_width;              /* data bus width */
 
@@ -139,6 +140,13 @@ struct mmc_host_ops {
        int     (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
        void    (*hw_reset)(struct mmc_host *host);
        void    (*card_event)(struct mmc_host *host);
+
+       /*
+        * Optional callback to support controllers with HW issues for multiple
+        * I/O. Returns the number of supported blocks for the request.
+        */
+       int     (*multi_io_quirk)(struct mmc_card *card,
+                                 unsigned int direction, int blk_size);
 };
 
 struct mmc_card;
@@ -265,7 +273,6 @@ struct mmc_host {
 
 #define MMC_CAP2_BOOTPART_NOACC        (1 << 0)        /* Boot partition no access */
 #define MMC_CAP2_FULL_PWR_CYCLE        (1 << 2)        /* Can do full power cycle */
-#define MMC_CAP2_NO_MULTI_READ (1 << 3)        /* Multiblock reads don't work */
 #define MMC_CAP2_HS200_1_8V_SDR        (1 << 5)        /* can support */
 #define MMC_CAP2_HS200_1_2V_SDR        (1 << 6)        /* can support */
 #define MMC_CAP2_HS200         (MMC_CAP2_HS200_1_8V_SDR | \
@@ -365,6 +372,9 @@ struct mmc_host {
 
        unsigned int            slotno; /* used for sdio acpi binding */
 
+       int                     dsr_req;        /* DSR value is valid */
+       u32                     dsr;    /* optional driver stage (DSR) value */
+
        unsigned long           private[0] ____cacheline_aligned;
 };
 
index 64ec963ed3478b88babe7c4292c2b2e310ca51c2..1cd00b3a75b9e33ee0d2353f92231440802ae920 100644 (file)
 #define MMC_SEND_TUNING_BLOCK    19   /* adtc                    R1  */
 #define MMC_SEND_TUNING_BLOCK_HS200    21      /* adtc R1  */
 
+#define MMC_TUNING_BLK_PATTERN_4BIT_SIZE        64
+#define MMC_TUNING_BLK_PATTERN_8BIT_SIZE       128
+extern const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE];
+extern const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE];
+
   /* class 3 */
 #define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1  */
 
@@ -281,6 +286,7 @@ struct _mmc_csd {
 #define EXT_CSD_EXP_EVENTS_CTRL                56      /* R/W, 2 bytes */
 #define EXT_CSD_DATA_SECTOR_SIZE       61      /* R */
 #define EXT_CSD_GP_SIZE_MULT           143     /* R/W */
+#define EXT_CSD_PARTITION_SETTING_COMPLETED 155        /* R/W */
 #define EXT_CSD_PARTITION_ATTRIBUTE    156     /* R/W */
 #define EXT_CSD_PARTITION_SUPPORT      160     /* RO */
 #define EXT_CSD_HPI_MGMT               161     /* R/W */
@@ -349,6 +355,7 @@ struct _mmc_csd {
 #define EXT_CSD_PART_CONFIG_ACC_RPMB   (0x3)
 #define EXT_CSD_PART_CONFIG_ACC_GP0    (0x4)
 
+#define EXT_CSD_PART_SETTING_COMPLETED (0x1)
 #define EXT_CSD_PART_SUPPORT_PART_EN   (0x1)
 
 #define EXT_CSD_CMD_SET_NORMAL         (1<<0)
index 09ebe57d5ce9b4a810d82b7900dea772b99abf02..dba793e3a331f1d3039b7c5e96eb2a5ccba188b3 100644 (file)
@@ -98,6 +98,8 @@ struct sdhci_host {
 #define SDHCI_QUIRK2_BROKEN_HS200                      (1<<6)
 /* Controller does not support DDR50 */
 #define SDHCI_QUIRK2_BROKEN_DDR50                      (1<<7)
+/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
+#define SDHCI_QUIRK2_STOP_WITH_TC                      (1<<8)
 
        int irq;                /* Device IRQ */
        void __iomem *ioaddr;   /* Mapped address */
@@ -146,6 +148,7 @@ struct sdhci_host {
        struct mmc_command *cmd;        /* Current command */
        struct mmc_data *data;  /* Current data request */
        unsigned int data_early:1;      /* Data finished before cmd */
+       unsigned int busy_handle:1;     /* Handling the order of Busy-end */
 
        struct sg_mapping_iter sg_miter;        /* SG state for PIO */
        unsigned int blocks;    /* remaining PIO blocks */
index d2433381e8286573cda47c579660562196644c83..e56fa24c9322db23bd541fdb0d15e7e012cfdddb 100644 (file)
@@ -24,7 +24,10 @@ void mmc_gpio_free_cd(struct mmc_host *host);
 
 int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
                         unsigned int idx, bool override_active_level,
-                        unsigned int debounce);
+                        unsigned int debounce, bool *gpio_invert);
+int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
+                        unsigned int idx, bool override_active_level,
+                        unsigned int debounce, bool *gpio_invert);
 void mmc_gpiod_free_cd(struct mmc_host *host);
 void mmc_gpiod_request_cd_irq(struct mmc_host *host);
 
index 2f348d02f640fb049ed9c7e17b2742ba7e10f403..877ef226f90fb3b945831c7e8f9edbabba1e9775 100644 (file)
@@ -4,10 +4,14 @@
 #include <linux/stringify.h>
 
 struct page;
+struct vm_area_struct;
+struct mm_struct;
 
 extern void dump_page(struct page *page, const char *reason);
 extern void dump_page_badflags(struct page *page, const char *reason,
                               unsigned long badflags);
+void dump_vma(const struct vm_area_struct *vma);
+void dump_mm(const struct mm_struct *mm);
 
 #ifdef CONFIG_DEBUG_VM
 #define VM_BUG_ON(cond) BUG_ON(cond)
@@ -18,12 +22,28 @@ extern void dump_page_badflags(struct page *page, const char *reason,
                        BUG();                                          \
                }                                                       \
        } while (0)
+#define VM_BUG_ON_VMA(cond, vma)                                       \
+       do {                                                            \
+               if (unlikely(cond)) {                                   \
+                       dump_vma(vma);                                  \
+                       BUG();                                          \
+               }                                                       \
+       } while (0)
+#define VM_BUG_ON_MM(cond, mm)                                         \
+       do {                                                            \
+               if (unlikely(cond)) {                                   \
+                       dump_mm(mm);                                    \
+                       BUG();                                          \
+               }                                                       \
+       } while (0)
 #define VM_WARN_ON(cond) WARN_ON(cond)
 #define VM_WARN_ON_ONCE(cond) WARN_ON_ONCE(cond)
 #define VM_WARN_ONCE(cond, format...) WARN_ONCE(cond, format)
 #else
 #define VM_BUG_ON(cond) BUILD_BUG_ON_INVALID(cond)
 #define VM_BUG_ON_PAGE(cond, page) VM_BUG_ON(cond)
+#define VM_BUG_ON_VMA(cond, vma) VM_BUG_ON(cond)
+#define VM_BUG_ON_MM(cond, mm) VM_BUG_ON(cond)
 #define VM_WARN_ON(cond) BUILD_BUG_ON_INVALID(cond)
 #define VM_WARN_ON_ONCE(cond) BUILD_BUG_ON_INVALID(cond)
 #define VM_WARN_ONCE(cond, format...) BUILD_BUG_ON_INVALID(cond)
index 318df70518509249bb67a8e9811bf20fdf5fc519..48bf12ef6620ccc863c27afc615a2e2e460a6c99 100644 (file)
@@ -521,13 +521,13 @@ struct zone {
        atomic_long_t           vm_stat[NR_VM_ZONE_STAT_ITEMS];
 } ____cacheline_internodealigned_in_smp;
 
-typedef enum {
+enum zone_flags {
        ZONE_RECLAIM_LOCKED,            /* prevents concurrent reclaim */
        ZONE_OOM_LOCKED,                /* zone is in OOM killer zonelist */
        ZONE_CONGESTED,                 /* zone has many dirty pages backed by
                                         * a congested BDI
                                         */
-       ZONE_TAIL_LRU_DIRTY,            /* reclaim scanning has recently found
+       ZONE_DIRTY,                     /* reclaim scanning has recently found
                                         * many dirty file pages at the tail
                                         * of the LRU.
                                         */
@@ -535,52 +535,7 @@ typedef enum {
                                         * many pages under writeback
                                         */
        ZONE_FAIR_DEPLETED,             /* fair zone policy batch depleted */
-} zone_flags_t;
-
-static inline void zone_set_flag(struct zone *zone, zone_flags_t flag)
-{
-       set_bit(flag, &zone->flags);
-}
-
-static inline int zone_test_and_set_flag(struct zone *zone, zone_flags_t flag)
-{
-       return test_and_set_bit(flag, &zone->flags);
-}
-
-static inline void zone_clear_flag(struct zone *zone, zone_flags_t flag)
-{
-       clear_bit(flag, &zone->flags);
-}
-
-static inline int zone_is_reclaim_congested(const struct zone *zone)
-{
-       return test_bit(ZONE_CONGESTED, &zone->flags);
-}
-
-static inline int zone_is_reclaim_dirty(const struct zone *zone)
-{
-       return test_bit(ZONE_TAIL_LRU_DIRTY, &zone->flags);
-}
-
-static inline int zone_is_reclaim_writeback(const struct zone *zone)
-{
-       return test_bit(ZONE_WRITEBACK, &zone->flags);
-}
-
-static inline int zone_is_reclaim_locked(const struct zone *zone)
-{
-       return test_bit(ZONE_RECLAIM_LOCKED, &zone->flags);
-}
-
-static inline int zone_is_fair_depleted(const struct zone *zone)
-{
-       return test_bit(ZONE_FAIR_DEPLETED, &zone->flags);
-}
-
-static inline int zone_is_oom_locked(const struct zone *zone)
-{
-       return test_bit(ZONE_OOM_LOCKED, &zone->flags);
-}
+};
 
 static inline unsigned long zone_end_pfn(const struct zone *zone)
 {
index 8103f32f6d87f39af2e494ee4948b98188150936..44f4746d033b9afc0db8f81ddefa72e778a51efb 100644 (file)
@@ -29,7 +29,6 @@ struct msi_desc {
                __u8    multi_cap : 3;  /* log2 num of messages supported */
                __u8    maskbit : 1;    /* mask-pending bit supported ? */
                __u8    is_64   : 1;    /* Address size: 0=32bit 1=64bit */
-               __u8    pos;            /* Location of the msi capability */
                __u16   entry_nr;       /* specific enabled entry */
                unsigned default_irq;   /* default pre-assigned irq */
        } msi_attrib;
@@ -47,8 +46,6 @@ struct msi_desc {
 
        /* Last set MSI message */
        struct msi_msg msg;
-
-       struct kobject kobj;
 };
 
 /*
@@ -60,7 +57,6 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc);
 void arch_teardown_msi_irq(unsigned int irq);
 int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type);
 void arch_teardown_msi_irqs(struct pci_dev *dev);
-int arch_msi_check_device(struct pci_dev* dev, int nvec, int type);
 void arch_restore_msi_irqs(struct pci_dev *dev);
 
 void default_teardown_msi_irqs(struct pci_dev *dev);
@@ -77,8 +73,6 @@ struct msi_chip {
        int (*setup_irq)(struct msi_chip *chip, struct pci_dev *dev,
                         struct msi_desc *desc);
        void (*teardown_irq)(struct msi_chip *chip, unsigned int irq);
-       int (*check_device)(struct msi_chip *chip, struct pci_dev *dev,
-                           int nvec, int type);
 };
 
 #endif /* LINUX_MSI_H */
index 6c4363b8ddc3ddba1a75d9f2396f453ae92ab5e9..6545e7aec7bb95dc41ad6225050cb19e7038287a 100644 (file)
@@ -863,4 +863,7 @@ static inline int of_changeset_update_property(struct of_changeset *ocs,
 }
 #endif
 
+/* CONFIG_OF_RESOLVE api */
+extern int of_resolve_phandles(struct device_node *tree);
+
 #endif /* _LINUX_OF_H */
index fb7b7221e06309b8cbde7f4b2f996aeff4f77f8e..8cb14eb393d6d178968cc2af6bbbffbc0bd0a1ff 100644 (file)
@@ -23,17 +23,6 @@ struct of_pci_range {
 #define for_each_of_pci_range(parser, range) \
        for (; of_pci_range_parser_one(parser, range);)
 
-static inline void of_pci_range_to_resource(struct of_pci_range *range,
-                                           struct device_node *np,
-                                           struct resource *res)
-{
-       res->flags = range->flags;
-       res->start = range->cpu_addr;
-       res->end = range->cpu_addr + range->size - 1;
-       res->parent = res->child = res->sibling = NULL;
-       res->name = np->full_name;
-}
-
 /* Translate a DMA address from device space to CPU space */
 extern u64 of_translate_dma_address(struct device_node *dev,
                                    const __be32 *in_addr);
@@ -55,7 +44,9 @@ extern void __iomem *of_iomap(struct device_node *device, int index);
 extern const __be32 *of_get_address(struct device_node *dev, int index,
                           u64 *size, unsigned int *flags);
 
+extern int pci_register_io_range(phys_addr_t addr, resource_size_t size);
 extern unsigned long pci_address_to_pio(phys_addr_t addr);
+extern phys_addr_t pci_pio_to_address(unsigned long pio);
 
 extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
                        struct device_node *node);
@@ -80,6 +71,11 @@ static inline const __be32 *of_get_address(struct device_node *dev, int index,
        return NULL;
 }
 
+static inline phys_addr_t pci_pio_to_address(unsigned long pio)
+{
+       return 0;
+}
+
 static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser,
                        struct device_node *node)
 {
@@ -138,6 +134,9 @@ extern const __be32 *of_get_pci_address(struct device_node *dev, int bar_no,
                               u64 *size, unsigned int *flags);
 extern int of_pci_address_to_resource(struct device_node *dev, int bar,
                                      struct resource *r);
+extern int of_pci_range_to_resource(struct of_pci_range *range,
+                                   struct device_node *np,
+                                   struct resource *res);
 #else /* CONFIG_OF_ADDRESS && CONFIG_PCI */
 static inline int of_pci_address_to_resource(struct device_node *dev, int bar,
                                             struct resource *r)
@@ -150,6 +149,12 @@ static inline const __be32 *of_get_pci_address(struct device_node *dev,
 {
        return NULL;
 }
+static inline int of_pci_range_to_resource(struct of_pci_range *range,
+                                          struct device_node *np,
+                                          struct resource *res)
+{
+       return -ENOSYS;
+}
 #endif /* CONFIG_OF_ADDRESS && CONFIG_PCI */
 
 #endif /* __OF_ADDRESS_H */
index dde3a4a0fa5d2e29388263a8c0ab8ea87a753c29..1fd207e7a8475b11f7b7223fa1d33664f00c30b5 100644 (file)
@@ -15,6 +15,7 @@ struct device_node *of_pci_find_child_device(struct device_node *parent,
 int of_pci_get_devfn(struct device_node *np);
 int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
 int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
+int of_get_pci_domain_nr(struct device_node *node);
 #else
 static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
 {
@@ -43,6 +44,18 @@ of_pci_parse_bus_range(struct device_node *node, struct resource *res)
 {
        return -EINVAL;
 }
+
+static inline int
+of_get_pci_domain_nr(struct device_node *node)
+{
+       return -1;
+}
+#endif
+
+#if defined(CONFIG_OF_ADDRESS)
+int of_pci_get_host_bridge_resources(struct device_node *dev,
+                       unsigned char busno, unsigned char bus_max,
+                       struct list_head *resources, resource_size_t *io_base);
 #endif
 
 #if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI)
index 19191d39c4f3d80fdf287096315602046c7acd27..7ea069cd32579caacc5953802356a62237ac0413 100644 (file)
@@ -24,8 +24,7 @@ enum mapping_flags {
        AS_ENOSPC       = __GFP_BITS_SHIFT + 1, /* ENOSPC on async write */
        AS_MM_ALL_LOCKS = __GFP_BITS_SHIFT + 2, /* under mm_take_all_locks() */
        AS_UNEVICTABLE  = __GFP_BITS_SHIFT + 3, /* e.g., ramdisk, SHM_LOCK */
-       AS_BALLOON_MAP  = __GFP_BITS_SHIFT + 4, /* balloon page special map */
-       AS_EXITING      = __GFP_BITS_SHIFT + 5, /* final truncate in progress */
+       AS_EXITING      = __GFP_BITS_SHIFT + 4, /* final truncate in progress */
 };
 
 static inline void mapping_set_error(struct address_space *mapping, int error)
@@ -55,21 +54,6 @@ static inline int mapping_unevictable(struct address_space *mapping)
        return !!mapping;
 }
 
-static inline void mapping_set_balloon(struct address_space *mapping)
-{
-       set_bit(AS_BALLOON_MAP, &mapping->flags);
-}
-
-static inline void mapping_clear_balloon(struct address_space *mapping)
-{
-       clear_bit(AS_BALLOON_MAP, &mapping->flags);
-}
-
-static inline int mapping_balloon(struct address_space *mapping)
-{
-       return mapping && test_bit(AS_BALLOON_MAP, &mapping->flags);
-}
-
 static inline void mapping_set_exiting(struct address_space *mapping)
 {
        set_bit(AS_EXITING, &mapping->flags);
index 96453f9bc8bafa947d87a64b1102f8115ad43ea9..5be8db45e368b23eb4c0d7b16b92f9db49290998 100644 (file)
@@ -45,7 +45,7 @@
  * In the interest of not exposing interfaces to user-space unnecessarily,
  * the following kernel-only defines are being added here.
  */
-#define PCI_DEVID(bus, devfn)  ((((u16)bus) << 8) | devfn)
+#define PCI_DEVID(bus, devfn)  ((((u16)(bus)) << 8) | (devfn))
 /* return bus from PCI devid = ((u16)bus_number) << 8) | devfn */
 #define PCI_BUS_NUM(x) (((x) >> 8) & 0xff)
 
@@ -457,6 +457,9 @@ struct pci_bus {
        unsigned char   primary;        /* number of primary bridge */
        unsigned char   max_bus_speed;  /* enum pci_bus_speed */
        unsigned char   cur_bus_speed;  /* enum pci_bus_speed */
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+       int             domain_nr;
+#endif
 
        char            name[48];
 
@@ -1103,6 +1106,9 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
                                                  resource_size_t),
                        void *alignf_data);
 
+
+int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
+
 static inline dma_addr_t pci_bus_address(struct pci_dev *pdev, int bar)
 {
        struct pci_bus_region region;
@@ -1288,12 +1294,32 @@ void pci_cfg_access_unlock(struct pci_dev *dev);
  */
 #ifdef CONFIG_PCI_DOMAINS
 extern int pci_domains_supported;
+int pci_get_new_domain_nr(void);
 #else
 enum { pci_domains_supported = 0 };
 static inline int pci_domain_nr(struct pci_bus *bus) { return 0; }
 static inline int pci_proc_domain(struct pci_bus *bus) { return 0; }
+static inline int pci_get_new_domain_nr(void) { return -ENOSYS; }
 #endif /* CONFIG_PCI_DOMAINS */
 
+/*
+ * Generic implementation for PCI domain support. If your
+ * architecture does not need custom management of PCI
+ * domains then this implementation will be used
+ */
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+static inline int pci_domain_nr(struct pci_bus *bus)
+{
+       return bus->domain_nr;
+}
+void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent);
+#else
+static inline void pci_bus_assign_domain_nr(struct pci_bus *bus,
+                                       struct device *parent)
+{
+}
+#endif
+
 /* some architectures require additional setup to direct VGA traffic */
 typedef int (*arch_set_vga_state_t)(struct pci_dev *pdev, bool decode,
                      unsigned int command_bits, u32 flags);
@@ -1402,6 +1428,7 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus,
 
 static inline int pci_domain_nr(struct pci_bus *bus) { return 0; }
 static inline struct pci_dev *pci_dev_get(struct pci_dev *dev) { return NULL; }
+static inline int pci_get_new_domain_nr(void) { return -ENOSYS; }
 
 #define dev_is_pci(d) (false)
 #define dev_is_pf(d) (false)
@@ -1563,16 +1590,11 @@ enum pci_fixup_pass {
 
 #ifdef CONFIG_PCI_QUIRKS
 void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
-struct pci_dev *pci_get_dma_source(struct pci_dev *dev);
 int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
 void pci_dev_specific_enable_acs(struct pci_dev *dev);
 #else
 static inline void pci_fixup_device(enum pci_fixup_pass pass,
                                    struct pci_dev *dev) { }
-static inline struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
-{
-       return pci_dev_get(dev);
-}
 static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
                                               u16 acs_flags)
 {
@@ -1707,7 +1729,7 @@ bool pci_acs_path_enabled(struct pci_dev *start,
                          struct pci_dev *end, u16 acs_flags);
 
 #define PCI_VPD_LRDT                   0x80    /* Large Resource Data Type */
-#define PCI_VPD_LRDT_ID(x)             (x | PCI_VPD_LRDT)
+#define PCI_VPD_LRDT_ID(x)             ((x) | PCI_VPD_LRDT)
 
 /* Large Resource Data Type Tag Item Names */
 #define PCI_VPD_LTIN_ID_STRING         0x02    /* Identifier String */
@@ -1834,15 +1856,17 @@ int pci_for_each_dma_alias(struct pci_dev *pdev,
                           int (*fn)(struct pci_dev *pdev,
                                     u16 alias, void *data), void *data);
 
-/**
- * pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device
- * @pdev: the PCI device
- *
- * if the device is PCIE, return NULL
- * if the device isn't connected to a PCIe bridge (that is its parent is a
- * legacy PCI bridge and the bridge is directly connected to bus 0), return its
- * parent
- */
-struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev);
-
+/* helper functions for operation of device flag */
+static inline void pci_set_dev_assigned(struct pci_dev *pdev)
+{
+       pdev->dev_flags |= PCI_DEV_FLAGS_ASSIGNED;
+}
+static inline void pci_clear_dev_assigned(struct pci_dev *pdev)
+{
+       pdev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
+}
+static inline bool pci_is_dev_assigned(struct pci_dev *pdev)
+{
+       return (pdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED) == PCI_DEV_FLAGS_ASSIGNED;
+}
 #endif /* LINUX_PCI_H */
index 5f2e559af6b0a7a6ea07247ab405ea48407d4e72..2706ee9a4327f8b307bbe3fe9d116cd69ce79179 100644 (file)
@@ -187,6 +187,4 @@ static inline int pci_get_hp_params(struct pci_dev *dev,
        return -ENODEV;
 }
 #endif
-
-void pci_configure_slot(struct pci_dev *dev);
 #endif
index 6ed0bb73a8645d941be32a368060ad7f11abea57..2338e68398cb5d70fb7aa4d04e86119e2d6da609 100644 (file)
 #define PCI_VENDOR_ID_MORETON          0x15aa
 #define PCI_DEVICE_ID_RASTEL_2PORT     0x2000
 
+#define PCI_VENDOR_ID_VMWARE           0x15ad
+
 #define PCI_VENDOR_ID_ZOLTRIX          0x15b0
 #define PCI_DEVICE_ID_ZOLTRIX_2BD0     0x2bd0
 
 #define PCI_DEVICE_ID_INTEL_UNC_R2PCIE 0x3c43
 #define PCI_DEVICE_ID_INTEL_UNC_R3QPI0 0x3c44
 #define PCI_DEVICE_ID_INTEL_UNC_R3QPI1 0x3c45
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS    0x3c71  /* 15.1 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR0   0x3c72  /* 16.2 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR1   0x3c73  /* 16.3 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR2   0x3c76  /* 16.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR3   0x3c77  /* 16.7 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0    0x3ca0  /* 14.0 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA     0x3ca8  /* 15.0 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0   0x3caa  /* 15.2 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1   0x3cab  /* 15.3 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2   0x3cac  /* 15.4 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3   0x3cad  /* 15.5 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO  0x3cb8  /* 17.0 */
 #define PCI_DEVICE_ID_INTEL_JAKETOWN_UBOX      0x3ce0
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0       0x3cf4  /* 12.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_BR         0x3cf5  /* 13.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1       0x3cf6  /* 12.7 */
 #define PCI_DEVICE_ID_INTEL_IOAT_SNB   0x402f
 #define PCI_DEVICE_ID_INTEL_5100_16    0x65f0
 #define PCI_DEVICE_ID_INTEL_5100_19    0x65f3
index 68a64f11ce0215e34f7e1e0fbeefd42c5e49b40c..d5c89e0dd0e6725c614b491c78b5bfafe9cc46f4 100644 (file)
@@ -13,7 +13,7 @@
  *
  * The refcount will have a range of 0 to ((1U << 31) - 1), i.e. one bit less
  * than an atomic_t - this is because of the way shutdown works, see
- * percpu_ref_kill()/PCPU_COUNT_BIAS.
+ * percpu_ref_kill()/PERCPU_COUNT_BIAS.
  *
  * Before you call percpu_ref_kill(), percpu_ref_put() does not check for the
  * refcount hitting 0 - it can't, if it was in percpu mode. percpu_ref_kill()
 #include <linux/kernel.h>
 #include <linux/percpu.h>
 #include <linux/rcupdate.h>
+#include <linux/gfp.h>
 
 struct percpu_ref;
 typedef void (percpu_ref_func_t)(struct percpu_ref *);
 
+/* flags set in the lower bits of percpu_ref->percpu_count_ptr */
+enum {
+       __PERCPU_REF_ATOMIC     = 1LU << 0,     /* operating in atomic mode */
+       __PERCPU_REF_DEAD       = 1LU << 1,     /* (being) killed */
+       __PERCPU_REF_ATOMIC_DEAD = __PERCPU_REF_ATOMIC | __PERCPU_REF_DEAD,
+
+       __PERCPU_REF_FLAG_BITS  = 2,
+};
+
+/* @flags for percpu_ref_init() */
+enum {
+       /*
+        * Start w/ ref == 1 in atomic mode.  Can be switched to percpu
+        * operation using percpu_ref_switch_to_percpu().  If initialized
+        * with this flag, the ref will stay in atomic mode until
+        * percpu_ref_switch_to_percpu() is invoked on it.
+        */
+       PERCPU_REF_INIT_ATOMIC  = 1 << 0,
+
+       /*
+        * Start dead w/ ref == 0 in atomic mode.  Must be revived with
+        * percpu_ref_reinit() before used.  Implies INIT_ATOMIC.
+        */
+       PERCPU_REF_INIT_DEAD    = 1 << 1,
+};
+
 struct percpu_ref {
-       atomic_t                count;
+       atomic_long_t           count;
        /*
         * The low bit of the pointer indicates whether the ref is in percpu
         * mode; if set, then get/put will manipulate the atomic_t.
         */
-       unsigned long           pcpu_count_ptr;
+       unsigned long           percpu_count_ptr;
        percpu_ref_func_t       *release;
-       percpu_ref_func_t       *confirm_kill;
+       percpu_ref_func_t       *confirm_switch;
+       bool                    force_atomic:1;
        struct rcu_head         rcu;
 };
 
 int __must_check percpu_ref_init(struct percpu_ref *ref,
-                                percpu_ref_func_t *release);
-void percpu_ref_reinit(struct percpu_ref *ref);
+                                percpu_ref_func_t *release, unsigned int flags,
+                                gfp_t gfp);
 void percpu_ref_exit(struct percpu_ref *ref);
+void percpu_ref_switch_to_atomic(struct percpu_ref *ref,
+                                percpu_ref_func_t *confirm_switch);
+void percpu_ref_switch_to_percpu(struct percpu_ref *ref);
 void percpu_ref_kill_and_confirm(struct percpu_ref *ref,
                                 percpu_ref_func_t *confirm_kill);
-void __percpu_ref_kill_expedited(struct percpu_ref *ref);
+void percpu_ref_reinit(struct percpu_ref *ref);
 
 /**
  * percpu_ref_kill - drop the initial ref
@@ -88,26 +119,24 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)
        return percpu_ref_kill_and_confirm(ref, NULL);
 }
 
-#define PCPU_REF_DEAD          1
-
 /*
  * Internal helper.  Don't use outside percpu-refcount proper.  The
  * function doesn't return the pointer and let the caller test it for NULL
  * because doing so forces the compiler to generate two conditional
- * branches as it can't assume that @ref->pcpu_count is not NULL.
+ * branches as it can't assume that @ref->percpu_count is not NULL.
  */
-static inline bool __pcpu_ref_alive(struct percpu_ref *ref,
-                                   unsigned __percpu **pcpu_countp)
+static inline bool __ref_is_percpu(struct percpu_ref *ref,
+                                         unsigned long __percpu **percpu_countp)
 {
-       unsigned long pcpu_ptr = ACCESS_ONCE(ref->pcpu_count_ptr);
+       unsigned long percpu_ptr = ACCESS_ONCE(ref->percpu_count_ptr);
 
        /* paired with smp_store_release() in percpu_ref_reinit() */
        smp_read_barrier_depends();
 
-       if (unlikely(pcpu_ptr & PCPU_REF_DEAD))
+       if (unlikely(percpu_ptr & __PERCPU_REF_ATOMIC))
                return false;
 
-       *pcpu_countp = (unsigned __percpu *)pcpu_ptr;
+       *percpu_countp = (unsigned long __percpu *)percpu_ptr;
        return true;
 }
 
@@ -115,18 +144,20 @@ static inline bool __pcpu_ref_alive(struct percpu_ref *ref,
  * percpu_ref_get - increment a percpu refcount
  * @ref: percpu_ref to get
  *
- * Analagous to atomic_inc().
-  */
+ * Analagous to atomic_long_inc().
+ *
+ * This function is safe to call as long as @ref is between init and exit.
+ */
 static inline void percpu_ref_get(struct percpu_ref *ref)
 {
-       unsigned __percpu *pcpu_count;
+       unsigned long __percpu *percpu_count;
 
        rcu_read_lock_sched();
 
-       if (__pcpu_ref_alive(ref, &pcpu_count))
-               this_cpu_inc(*pcpu_count);
+       if (__ref_is_percpu(ref, &percpu_count))
+               this_cpu_inc(*percpu_count);
        else
-               atomic_inc(&ref->count);
+               atomic_long_inc(&ref->count);
 
        rcu_read_unlock_sched();
 }
@@ -138,20 +169,20 @@ static inline void percpu_ref_get(struct percpu_ref *ref)
  * Increment a percpu refcount unless its count already reached zero.
  * Returns %true on success; %false on failure.
  *
- * The caller is responsible for ensuring that @ref stays accessible.
+ * This function is safe to call as long as @ref is between init and exit.
  */
 static inline bool percpu_ref_tryget(struct percpu_ref *ref)
 {
-       unsigned __percpu *pcpu_count;
-       int ret = false;
+       unsigned long __percpu *percpu_count;
+       int ret;
 
        rcu_read_lock_sched();
 
-       if (__pcpu_ref_alive(ref, &pcpu_count)) {
-               this_cpu_inc(*pcpu_count);
+       if (__ref_is_percpu(ref, &percpu_count)) {
+               this_cpu_inc(*percpu_count);
                ret = true;
        } else {
-               ret = atomic_inc_not_zero(&ref->count);
+               ret = atomic_long_inc_not_zero(&ref->count);
        }
 
        rcu_read_unlock_sched();
@@ -166,23 +197,26 @@ static inline bool percpu_ref_tryget(struct percpu_ref *ref)
  * Increment a percpu refcount unless it has already been killed.  Returns
  * %true on success; %false on failure.
  *
- * Completion of percpu_ref_kill() in itself doesn't guarantee that tryget
- * will fail.  For such guarantee, percpu_ref_kill_and_confirm() should be
- * used.  After the confirm_kill callback is invoked, it's guaranteed that
- * no new reference will be given out by percpu_ref_tryget().
+ * Completion of percpu_ref_kill() in itself doesn't guarantee that this
+ * function will fail.  For such guarantee, percpu_ref_kill_and_confirm()
+ * should be used.  After the confirm_kill callback is invoked, it's
+ * guaranteed that no new reference will be given out by
+ * percpu_ref_tryget_live().
  *
- * The caller is responsible for ensuring that @ref stays accessible.
+ * This function is safe to call as long as @ref is between init and exit.
  */
 static inline bool percpu_ref_tryget_live(struct percpu_ref *ref)
 {
-       unsigned __percpu *pcpu_count;
+       unsigned long __percpu *percpu_count;
        int ret = false;
 
        rcu_read_lock_sched();
 
-       if (__pcpu_ref_alive(ref, &pcpu_count)) {
-               this_cpu_inc(*pcpu_count);
+       if (__ref_is_percpu(ref, &percpu_count)) {
+               this_cpu_inc(*percpu_count);
                ret = true;
+       } else if (!(ACCESS_ONCE(ref->percpu_count_ptr) & __PERCPU_REF_DEAD)) {
+               ret = atomic_long_inc_not_zero(&ref->count);
        }
 
        rcu_read_unlock_sched();
@@ -196,16 +230,18 @@ static inline bool percpu_ref_tryget_live(struct percpu_ref *ref)
  *
  * Decrement the refcount, and if 0, call the release function (which was passed
  * to percpu_ref_init())
+ *
+ * This function is safe to call as long as @ref is between init and exit.
  */
 static inline void percpu_ref_put(struct percpu_ref *ref)
 {
-       unsigned __percpu *pcpu_count;
+       unsigned long __percpu *percpu_count;
 
        rcu_read_lock_sched();
 
-       if (__pcpu_ref_alive(ref, &pcpu_count))
-               this_cpu_dec(*pcpu_count);
-       else if (unlikely(atomic_dec_and_test(&ref->count)))
+       if (__ref_is_percpu(ref, &percpu_count))
+               this_cpu_dec(*percpu_count);
+       else if (unlikely(atomic_long_dec_and_test(&ref->count)))
                ref->release(ref);
 
        rcu_read_unlock_sched();
@@ -216,14 +252,16 @@ static inline void percpu_ref_put(struct percpu_ref *ref)
  * @ref: percpu_ref to test
  *
  * Returns %true if @ref reached zero.
+ *
+ * This function is safe to call as long as @ref is between init and exit.
  */
 static inline bool percpu_ref_is_zero(struct percpu_ref *ref)
 {
-       unsigned __percpu *pcpu_count;
+       unsigned long __percpu *percpu_count;
 
-       if (__pcpu_ref_alive(ref, &pcpu_count))
+       if (__ref_is_percpu(ref, &percpu_count))
                return false;
-       return !atomic_read(&ref->count);
+       return !atomic_long_read(&ref->count);
 }
 
 #endif
index 6f61b61b7996a8f248af9eb64139a5f9bfc52c7c..a3aa63e47637c9dc9bfa77a1166aadf67df3b158 100644 (file)
@@ -48,9 +48,9 @@
  * intelligent way to determine this would be nice.
  */
 #if BITS_PER_LONG > 32
-#define PERCPU_DYNAMIC_RESERVE         (20 << 10)
+#define PERCPU_DYNAMIC_RESERVE         (28 << 10)
 #else
-#define PERCPU_DYNAMIC_RESERVE         (12 << 10)
+#define PERCPU_DYNAMIC_RESERVE         (20 << 10)
 #endif
 
 extern void *pcpu_base_addr;
@@ -122,11 +122,16 @@ extern void __init setup_per_cpu_areas(void);
 #endif
 extern void __init percpu_init_late(void);
 
+extern void __percpu *__alloc_percpu_gfp(size_t size, size_t align, gfp_t gfp);
 extern void __percpu *__alloc_percpu(size_t size, size_t align);
 extern void free_percpu(void __percpu *__pdata);
 extern phys_addr_t per_cpu_ptr_to_phys(void *addr);
 
-#define alloc_percpu(type)     \
-       (typeof(type) __percpu *)__alloc_percpu(sizeof(type), __alignof__(type))
+#define alloc_percpu_gfp(type, gfp)                                    \
+       (typeof(type) __percpu *)__alloc_percpu_gfp(sizeof(type),       \
+                                               __alignof__(type), gfp)
+#define alloc_percpu(type)                                             \
+       (typeof(type) __percpu *)__alloc_percpu(sizeof(type),           \
+                                               __alignof__(type))
 
 #endif /* __LINUX_PERCPU_H */
index d5dd4657c8d64133eaa2e31ca745770b7b81af4b..50e50095c8d172777c4ea2857435444385b81ece 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/threads.h>
 #include <linux/percpu.h>
 #include <linux/types.h>
+#include <linux/gfp.h>
 
 #ifdef CONFIG_SMP
 
@@ -26,14 +27,14 @@ struct percpu_counter {
 
 extern int percpu_counter_batch;
 
-int __percpu_counter_init(struct percpu_counter *fbc, s64 amount,
+int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp,
                          struct lock_class_key *key);
 
-#define percpu_counter_init(fbc, value)                                        \
+#define percpu_counter_init(fbc, value, gfp)                           \
        ({                                                              \
                static struct lock_class_key __key;                     \
                                                                        \
-               __percpu_counter_init(fbc, value, &__key);              \
+               __percpu_counter_init(fbc, value, gfp, &__key);         \
        })
 
 void percpu_counter_destroy(struct percpu_counter *fbc);
@@ -89,7 +90,8 @@ struct percpu_counter {
        s64 count;
 };
 
-static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount)
+static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount,
+                                     gfp_t gfp)
 {
        fbc->count = amount;
        return 0;
diff --git a/include/linux/platform_data/gpio-dwapb.h b/include/linux/platform_data/gpio-dwapb.h
new file mode 100644 (file)
index 0000000..28702c8
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright(c) 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#ifndef GPIO_DW_APB_H
+#define GPIO_DW_APB_H
+
+struct dwapb_port_property {
+       struct device_node *node;
+       const char      *name;
+       unsigned int    idx;
+       unsigned int    ngpio;
+       unsigned int    gpio_base;
+       unsigned int    irq;
+       bool            irq_shared;
+};
+
+struct dwapb_platform_data {
+       struct dwapb_port_property *properties;
+       unsigned int nports;
+};
+
+#endif
index 72c0fe098a27871aa1ea44ea0076d45ab696ea2e..383fd68aaee15a9e345b43d0260276e0526144ce 100644 (file)
@@ -619,6 +619,7 @@ extern int dev_pm_put_subsys_data(struct device *dev);
  */
 struct dev_pm_domain {
        struct dev_pm_ops       ops;
+       void (*detach)(struct device *dev, bool power_off);
 };
 
 /*
@@ -679,12 +680,16 @@ struct dev_pm_domain {
 extern void device_pm_lock(void);
 extern void dpm_resume_start(pm_message_t state);
 extern void dpm_resume_end(pm_message_t state);
+extern void dpm_resume_noirq(pm_message_t state);
+extern void dpm_resume_early(pm_message_t state);
 extern void dpm_resume(pm_message_t state);
 extern void dpm_complete(pm_message_t state);
 
 extern void device_pm_unlock(void);
 extern int dpm_suspend_end(pm_message_t state);
 extern int dpm_suspend_start(pm_message_t state);
+extern int dpm_suspend_noirq(pm_message_t state);
+extern int dpm_suspend_late(pm_message_t state);
 extern int dpm_suspend(pm_message_t state);
 extern int dpm_prepare(pm_message_t state);
 
index ebc4c76ffb737bae3d9be1a6325752745254eaed..73e938b7e9374c68ac00fd99c65247eac9241fd4 100644 (file)
@@ -35,18 +35,10 @@ struct gpd_dev_ops {
        int (*stop)(struct device *dev);
        int (*save_state)(struct device *dev);
        int (*restore_state)(struct device *dev);
-       int (*suspend)(struct device *dev);
-       int (*suspend_late)(struct device *dev);
-       int (*resume_early)(struct device *dev);
-       int (*resume)(struct device *dev);
-       int (*freeze)(struct device *dev);
-       int (*freeze_late)(struct device *dev);
-       int (*thaw_early)(struct device *dev);
-       int (*thaw)(struct device *dev);
        bool (*active_wakeup)(struct device *dev);
 };
 
-struct gpd_cpu_data {
+struct gpd_cpuidle_data {
        unsigned int saved_exit_latency;
        struct cpuidle_state *idle_state;
 };
@@ -71,7 +63,6 @@ struct generic_pm_domain {
        unsigned int suspended_count;   /* System suspend device counter */
        unsigned int prepared_count;    /* Suspend counter of prepared devices */
        bool suspend_power_off; /* Power status before system suspend */
-       bool dev_irq_safe;      /* Device callbacks are IRQ-safe */
        int (*power_off)(struct generic_pm_domain *domain);
        s64 power_off_latency_ns;
        int (*power_on)(struct generic_pm_domain *domain);
@@ -80,8 +71,9 @@ struct generic_pm_domain {
        s64 max_off_time_ns;    /* Maximum allowed "suspended" time. */
        bool max_off_time_changed;
        bool cached_power_down_ok;
-       struct device_node *of_node; /* Node in device tree */
-       struct gpd_cpu_data *cpu_data;
+       struct gpd_cpuidle_data *cpuidle_data;
+       void (*attach_dev)(struct device *dev);
+       void (*detach_dev)(struct device *dev);
 };
 
 static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
@@ -108,7 +100,6 @@ struct gpd_timing_data {
 
 struct generic_pm_domain_data {
        struct pm_domain_data base;
-       struct gpd_dev_ops ops;
        struct gpd_timing_data td;
        struct notifier_block nb;
        struct mutex lock;
@@ -127,17 +118,11 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
        return to_gpd_data(dev->power.subsys_data->domain_data);
 }
 
-extern struct dev_power_governor simple_qos_governor;
-
 extern struct generic_pm_domain *dev_to_genpd(struct device *dev);
 extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
                                 struct device *dev,
                                 struct gpd_timing_data *td);
 
-extern int __pm_genpd_of_add_device(struct device_node *genpd_node,
-                                   struct device *dev,
-                                   struct gpd_timing_data *td);
-
 extern int __pm_genpd_name_add_device(const char *domain_name,
                                      struct device *dev,
                                      struct gpd_timing_data *td);
@@ -151,10 +136,6 @@ extern int pm_genpd_add_subdomain_names(const char *master_name,
                                        const char *subdomain_name);
 extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
                                     struct generic_pm_domain *target);
-extern int pm_genpd_add_callbacks(struct device *dev,
-                                 struct gpd_dev_ops *ops,
-                                 struct gpd_timing_data *td);
-extern int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td);
 extern int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state);
 extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
 extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
@@ -165,8 +146,7 @@ extern void pm_genpd_init(struct generic_pm_domain *genpd,
 extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
 extern int pm_genpd_name_poweron(const char *domain_name);
 
-extern bool default_stop_ok(struct device *dev);
-
+extern struct dev_power_governor simple_qos_governor;
 extern struct dev_power_governor pm_domain_always_on_gov;
 #else
 
@@ -184,12 +164,6 @@ static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd,
 {
        return -ENOSYS;
 }
-static inline int __pm_genpd_of_add_device(struct device_node *genpd_node,
-                                          struct device *dev,
-                                          struct gpd_timing_data *td)
-{
-       return -ENOSYS;
-}
 static inline int __pm_genpd_name_add_device(const char *domain_name,
                                             struct device *dev,
                                             struct gpd_timing_data *td)
@@ -217,16 +191,6 @@ static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
 {
        return -ENOSYS;
 }
-static inline int pm_genpd_add_callbacks(struct device *dev,
-                                        struct gpd_dev_ops *ops,
-                                        struct gpd_timing_data *td)
-{
-       return -ENOSYS;
-}
-static inline int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
-{
-       return -ENOSYS;
-}
 static inline int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int st)
 {
        return -ENOSYS;
@@ -255,10 +219,6 @@ static inline int pm_genpd_name_poweron(const char *domain_name)
 {
        return -ENOSYS;
 }
-static inline bool default_stop_ok(struct device *dev)
-{
-       return false;
-}
 #define simple_qos_governor NULL
 #define pm_domain_always_on_gov NULL
 #endif
@@ -269,45 +229,87 @@ static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
        return __pm_genpd_add_device(genpd, dev, NULL);
 }
 
-static inline int pm_genpd_of_add_device(struct device_node *genpd_node,
-                                        struct device *dev)
-{
-       return __pm_genpd_of_add_device(genpd_node, dev, NULL);
-}
-
 static inline int pm_genpd_name_add_device(const char *domain_name,
                                           struct device *dev)
 {
        return __pm_genpd_name_add_device(domain_name, dev, NULL);
 }
 
-static inline int pm_genpd_remove_callbacks(struct device *dev)
-{
-       return __pm_genpd_remove_callbacks(dev, true);
-}
-
 #ifdef CONFIG_PM_GENERIC_DOMAINS_RUNTIME
-extern void genpd_queue_power_off_work(struct generic_pm_domain *genpd);
 extern void pm_genpd_poweroff_unused(void);
 #else
-static inline void genpd_queue_power_off_work(struct generic_pm_domain *gpd) {}
 static inline void pm_genpd_poweroff_unused(void) {}
 #endif
 
 #ifdef CONFIG_PM_GENERIC_DOMAINS_SLEEP
-extern void pm_genpd_syscore_switch(struct device *dev, bool suspend);
+extern void pm_genpd_syscore_poweroff(struct device *dev);
+extern void pm_genpd_syscore_poweron(struct device *dev);
 #else
-static inline void pm_genpd_syscore_switch(struct device *dev, bool suspend) {}
+static inline void pm_genpd_syscore_poweroff(struct device *dev) {}
+static inline void pm_genpd_syscore_poweron(struct device *dev) {}
 #endif
 
-static inline void pm_genpd_syscore_poweroff(struct device *dev)
+/* OF PM domain providers */
+struct of_device_id;
+
+struct genpd_onecell_data {
+       struct generic_pm_domain **domains;
+       unsigned int num_domains;
+};
+
+typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
+                                               void *data);
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
+int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
+                       void *data);
+void of_genpd_del_provider(struct device_node *np);
+
+struct generic_pm_domain *__of_genpd_xlate_simple(
+                                       struct of_phandle_args *genpdspec,
+                                       void *data);
+struct generic_pm_domain *__of_genpd_xlate_onecell(
+                                       struct of_phandle_args *genpdspec,
+                                       void *data);
+
+int genpd_dev_pm_attach(struct device *dev);
+#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
+static inline int __of_genpd_add_provider(struct device_node *np,
+                                       genpd_xlate_t xlate, void *data)
+{
+       return 0;
+}
+static inline void of_genpd_del_provider(struct device_node *np) {}
+
+#define __of_genpd_xlate_simple                NULL
+#define __of_genpd_xlate_onecell       NULL
+
+static inline int genpd_dev_pm_attach(struct device *dev)
+{
+       return -ENODEV;
+}
+#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
+
+static inline int of_genpd_add_provider_simple(struct device_node *np,
+                                       struct generic_pm_domain *genpd)
+{
+       return __of_genpd_add_provider(np, __of_genpd_xlate_simple, genpd);
+}
+static inline int of_genpd_add_provider_onecell(struct device_node *np,
+                                       struct genpd_onecell_data *data)
 {
-       pm_genpd_syscore_switch(dev, true);
+       return __of_genpd_add_provider(np, __of_genpd_xlate_onecell, data);
 }
 
-static inline void pm_genpd_syscore_poweron(struct device *dev)
+#ifdef CONFIG_PM
+extern int dev_pm_domain_attach(struct device *dev, bool power_on);
+extern void dev_pm_domain_detach(struct device *dev, bool power_off);
+#else
+static inline int dev_pm_domain_attach(struct device *dev, bool power_on)
 {
-       pm_genpd_syscore_switch(dev, false);
+       return -ENODEV;
 }
+static inline void dev_pm_domain_detach(struct device *dev, bool power_off) {}
+#endif
 
 #endif /* _LINUX_PM_DOMAIN_H */
index 26a8a4ed9b07bbf6c779545d547f3aab3f5103d4..00e8e8fa73584beac318b7f1a689e1397107090f 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/percpu_counter.h>
 #include <linux/spinlock.h>
 #include <linux/mutex.h>
+#include <linux/gfp.h>
 
 struct prop_global {
        /*
@@ -40,7 +41,7 @@ struct prop_descriptor {
        struct mutex mutex;             /* serialize the prop_global switch */
 };
 
-int prop_descriptor_init(struct prop_descriptor *pd, int shift);
+int prop_descriptor_init(struct prop_descriptor *pd, int shift, gfp_t gfp);
 void prop_change_shift(struct prop_descriptor *pd, int new_shift);
 
 /*
@@ -61,7 +62,7 @@ struct prop_local_percpu {
        raw_spinlock_t lock;            /* protect the snapshot state */
 };
 
-int prop_local_init_percpu(struct prop_local_percpu *pl);
+int prop_local_init_percpu(struct prop_local_percpu *pl, gfp_t gfp);
 void prop_local_destroy_percpu(struct prop_local_percpu *pl);
 void __prop_inc_percpu(struct prop_descriptor *pd, struct prop_local_percpu *pl);
 void prop_fraction_percpu(struct prop_descriptor *pd, struct prop_local_percpu *pl,
index 48bf152761c7a3f27647609c6f83072989888c4b..67fc8fcdc4b0fdcd080ddef1c907b1c2aed3d445 100644 (file)
@@ -38,6 +38,9 @@ extern int reboot_force;
 extern int register_reboot_notifier(struct notifier_block *);
 extern int unregister_reboot_notifier(struct notifier_block *);
 
+extern int register_restart_handler(struct notifier_block *);
+extern int unregister_restart_handler(struct notifier_block *);
+extern void do_kernel_restart(char *cmd);
 
 /*
  * Architecture-specific implementations of sys_reboot commands.
index be574506e6a93074983f73b9ba62c07a50b4deeb..c0c2bce6b0b7bab50f5c7bb5a1353d8991f8e08b 100644 (file)
@@ -150,7 +150,7 @@ int anon_vma_fork(struct vm_area_struct *, struct vm_area_struct *);
 static inline void anon_vma_merge(struct vm_area_struct *vma,
                                  struct vm_area_struct *next)
 {
-       VM_BUG_ON(vma->anon_vma != next->anon_vma);
+       VM_BUG_ON_VMA(vma->anon_vma != next->anon_vma, vma);
        unlink_anon_vmas(next);
 }
 
index 9c6353d9e63ad024489daaac74c5ce04dbe39322..5e63ba59258c5909588f073c546743ef1f35c6b7 100644 (file)
@@ -1935,11 +1935,13 @@ extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut,
 #define tsk_used_math(p) ((p)->flags & PF_USED_MATH)
 #define used_math() tsk_used_math(current)
 
-/* __GFP_IO isn't allowed if PF_MEMALLOC_NOIO is set in current->flags */
+/* __GFP_IO isn't allowed if PF_MEMALLOC_NOIO is set in current->flags
+ * __GFP_FS is also cleared as it implies __GFP_IO.
+ */
 static inline gfp_t memalloc_noio_flags(gfp_t flags)
 {
        if (unlikely(current->flags & PF_MEMALLOC_NOIO))
-               flags &= ~__GFP_IO;
+               flags &= ~(__GFP_IO | __GFP_FS);
        return flags;
 }
 
index 005bf3e38db555a879f9bd0c0f9b4a8ba0f4616d..f0f8bad54be9e509f9824bb74621b6e9706b778d 100644 (file)
@@ -5,12 +5,4 @@
 
 extern struct screen_info screen_info;
 
-#define ORIG_X                 (screen_info.orig_x)
-#define ORIG_Y                 (screen_info.orig_y)
-#define ORIG_VIDEO_MODE                (screen_info.orig_video_mode)
-#define ORIG_VIDEO_COLS        (screen_info.orig_video_cols)
-#define ORIG_VIDEO_EGA_BX      (screen_info.orig_video_ega_bx)
-#define ORIG_VIDEO_LINES       (screen_info.orig_video_lines)
-#define ORIG_VIDEO_ISVGA       (screen_info.orig_video_isVGA)
-#define ORIG_VIDEO_POINTS       (screen_info.orig_video_points)
 #endif /* _SCREEN_INFO_H */
index 623f90e5f38de3f2fcc0a751a25eb10a97f21cd5..b10e7af95d3b7e4ea1a8e5b2167dee2216075a81 100644 (file)
@@ -1559,7 +1559,7 @@ struct security_operations {
        int (*file_lock) (struct file *file, unsigned int cmd);
        int (*file_fcntl) (struct file *file, unsigned int cmd,
                           unsigned long arg);
-       int (*file_set_fowner) (struct file *file);
+       void (*file_set_fowner) (struct file *file);
        int (*file_send_sigiotask) (struct task_struct *tsk,
                                    struct fown_struct *fown, int sig);
        int (*file_receive) (struct file *file);
@@ -1834,7 +1834,7 @@ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
                           unsigned long prot);
 int security_file_lock(struct file *file, unsigned int cmd);
 int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg);
-int security_file_set_fowner(struct file *file);
+void security_file_set_fowner(struct file *file);
 int security_file_send_sigiotask(struct task_struct *tsk,
                                 struct fown_struct *fown, int sig);
 int security_file_receive(struct file *file);
@@ -2312,9 +2312,9 @@ static inline int security_file_fcntl(struct file *file, unsigned int cmd,
        return 0;
 }
 
-static inline int security_file_set_fowner(struct file *file)
+static inline void security_file_set_fowner(struct file *file)
 {
-       return 0;
+       return;
 }
 
 static inline int security_file_send_sigiotask(struct task_struct *tsk,
index 1d9abb7d22a0f78e68179d68c427dbbb35f86af0..c265bec6a57db93c4de014291f4c6b2ae55d7818 100644 (file)
@@ -158,31 +158,6 @@ size_t ksize(const void *);
 #define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long)
 #endif
 
-#ifdef CONFIG_SLOB
-/*
- * Common fields provided in kmem_cache by all slab allocators
- * This struct is either used directly by the allocator (SLOB)
- * or the allocator must include definitions for all fields
- * provided in kmem_cache_common in their definition of kmem_cache.
- *
- * Once we can do anonymous structs (C11 standard) we could put a
- * anonymous struct definition in these allocators so that the
- * separate allocations in the kmem_cache structure of SLAB and
- * SLUB is no longer needed.
- */
-struct kmem_cache {
-       unsigned int object_size;/* The original size of the object */
-       unsigned int size;      /* The aligned/padded/added on size  */
-       unsigned int align;     /* Alignment as calculated */
-       unsigned long flags;    /* Active flags on the slab */
-       const char *name;       /* Slab name for sysfs */
-       int refcount;           /* Use counter */
-       void (*ctor)(void *);   /* Called on object slot creation */
-       struct list_head list;  /* List of all slab caches on the system */
-};
-
-#endif /* CONFIG_SLOB */
-
 /*
  * Kmalloc array related definitions
  */
@@ -363,14 +338,6 @@ kmem_cache_alloc_node_trace(struct kmem_cache *s,
 }
 #endif /* CONFIG_TRACING */
 
-#ifdef CONFIG_SLAB
-#include <linux/slab_def.h>
-#endif
-
-#ifdef CONFIG_SLUB
-#include <linux/slub_def.h>
-#endif
-
 extern void *kmalloc_order(size_t size, gfp_t flags, unsigned int order);
 
 #ifdef CONFIG_TRACING
@@ -582,37 +549,15 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags)
  * allocator where we care about the real place the memory allocation
  * request comes from.
  */
-#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_SLUB) || \
-       (defined(CONFIG_SLAB) && defined(CONFIG_TRACING)) || \
-       (defined(CONFIG_SLOB) && defined(CONFIG_TRACING))
 extern void *__kmalloc_track_caller(size_t, gfp_t, unsigned long);
 #define kmalloc_track_caller(size, flags) \
        __kmalloc_track_caller(size, flags, _RET_IP_)
-#else
-#define kmalloc_track_caller(size, flags) \
-       __kmalloc(size, flags)
-#endif /* DEBUG_SLAB */
 
 #ifdef CONFIG_NUMA
-/*
- * kmalloc_node_track_caller is a special version of kmalloc_node that
- * records the calling function of the routine calling it for slab leak
- * tracking instead of just the calling function (confusing, eh?).
- * It's useful when the call to kmalloc_node comes from a widely-used
- * standard allocator where we care about the real place the memory
- * allocation request comes from.
- */
-#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_SLUB) || \
-       (defined(CONFIG_SLAB) && defined(CONFIG_TRACING)) || \
-       (defined(CONFIG_SLOB) && defined(CONFIG_TRACING))
 extern void *__kmalloc_node_track_caller(size_t, gfp_t, int, unsigned long);
 #define kmalloc_node_track_caller(size, flags, node) \
        __kmalloc_node_track_caller(size, flags, node, \
                        _RET_IP_)
-#else
-#define kmalloc_node_track_caller(size, flags, node) \
-       __kmalloc_node(size, flags, node)
-#endif
 
 #else /* CONFIG_NUMA */
 
@@ -650,14 +595,7 @@ static inline void *kzalloc_node(size_t size, gfp_t flags, int node)
        return kmalloc_node(size, flags | __GFP_ZERO, node);
 }
 
-/*
- * Determine the size of a slab object
- */
-static inline unsigned int kmem_cache_size(struct kmem_cache *s)
-{
-       return s->object_size;
-}
-
+unsigned int kmem_cache_size(struct kmem_cache *s);
 void __init kmem_cache_init_late(void);
 
 #endif /* _LINUX_SLAB_H */
index 8235dfbb3b050e090868b35c2abc143ef5cf253b..b869d1662ba346b0ba246b814a9dc2b2f153188e 100644 (file)
@@ -8,6 +8,8 @@
  */
 
 struct kmem_cache {
+       struct array_cache __percpu *cpu_cache;
+
 /* 1) Cache tunables. Protected by slab_mutex */
        unsigned int batchcount;
        unsigned int limit;
@@ -71,23 +73,7 @@ struct kmem_cache {
        struct memcg_cache_params *memcg_params;
 #endif
 
-/* 6) per-cpu/per-node data, touched during every alloc/free */
-       /*
-        * We put array[] at the end of kmem_cache, because we want to size
-        * this array to nr_cpu_ids slots instead of NR_CPUS
-        * (see kmem_cache_init())
-        * We still use [NR_CPUS] and not [1] or [0] because cache_cache
-        * is statically defined, so we reserve the max number of cpus.
-        *
-        * We also need to guarantee that the list is able to accomodate a
-        * pointer for each node since "nodelists" uses the remainder of
-        * available pointers.
-        */
-       struct kmem_cache_node **node;
-       struct array_cache *array[NR_CPUS + MAX_NUMNODES];
-       /*
-        * Do not add fields after array[]
-        */
+       struct kmem_cache_node *node[MAX_NUMNODES];
 };
 
 #endif /* _LINUX_SLAB_DEF_H */
index 2d676d5aaa894fdbcab4efb5d6cc32c5f3f00d58..aa07d7b32568b920bd62a08d03a28d4517e00a58 100644 (file)
@@ -22,4 +22,22 @@ struct mcp23s08_platform_data {
         * base to base+15 (or base+31 for s17 variant).
         */
        unsigned        base;
+       /* Marks the device as a interrupt controller.
+        * NOTE: The interrupt functionality is only supported for i2c
+        * versions of the chips. The spi chips can also do the interrupts,
+        * but this is not supported by the linux driver yet.
+        */
+       bool            irq_controller;
+
+       /* Sets the mirror flag in the IOCON register. Devices
+        * with two interrupt outputs (these are the devices ending with 17 and
+        * those that have 16 IOs) have two IO banks: IO 0-7 form bank 1 and
+        * IO 8-15 are bank 2. These chips have two different interrupt outputs:
+        * One for bank 1 and another for bank 2. If irq-mirror is set, both
+        * interrupts are generated regardless of the bank that an input change
+        * occurred on. If it is not set, the interrupt are only generated for
+        * the bank they belong to.
+        * On devices with only one interrupt output this property is useless.
+        */
+       bool            mirror;
 };
index 519064e0c94302fd39ced27b6aab51b55f904f60..3388c1b6f7d8d3b981eee4d886fdcdf9e0f30297 100644 (file)
@@ -189,6 +189,8 @@ struct platform_suspend_ops {
 
 struct platform_freeze_ops {
        int (*begin)(void);
+       int (*prepare)(void);
+       void (*restore)(void);
        void (*end)(void);
 };
 
@@ -371,6 +373,8 @@ extern int unregister_pm_notifier(struct notifier_block *nb);
 extern bool events_check_enabled;
 
 extern bool pm_wakeup_pending(void);
+extern void pm_system_wakeup(void);
+extern void pm_wakeup_clear(void);
 extern bool pm_get_wakeup_count(unsigned int *count, bool block);
 extern bool pm_save_wakeup_count(unsigned int count);
 extern void pm_wakep_autosleep_enabled(bool set);
@@ -418,6 +422,8 @@ static inline int unregister_pm_notifier(struct notifier_block *nb)
 #define pm_notifier(fn, pri)   do { (void)(fn); } while (0)
 
 static inline bool pm_wakeup_pending(void) { return false; }
+static inline void pm_system_wakeup(void) {}
+static inline void pm_wakeup_clear(void) {}
 
 static inline void lock_system_sleep(void) {}
 static inline void unlock_system_sleep(void) {}
index 1b72060f093a6e74bb64d05cecf2d2894f987796..37a585beef5cf86e27fe54bc9a444ce7874ac1d3 100644 (file)
@@ -327,8 +327,10 @@ extern void lru_cache_add_active_or_unevictable(struct page *page,
 extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
                                        gfp_t gfp_mask, nodemask_t *mask);
 extern int __isolate_lru_page(struct page *page, isolate_mode_t mode);
-extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem,
-                                                 gfp_t gfp_mask, bool noswap);
+extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
+                                                 unsigned long nr_pages,
+                                                 gfp_t gfp_mask,
+                                                 bool may_swap);
 extern unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
                                                gfp_t gfp_mask, bool noswap,
                                                struct zone *zone,
@@ -354,22 +356,6 @@ static inline int zone_reclaim(struct zone *z, gfp_t mask, unsigned int order)
 extern int page_evictable(struct page *page);
 extern void check_move_unevictable_pages(struct page **, int nr_pages);
 
-extern unsigned long scan_unevictable_pages;
-extern int scan_unevictable_handler(struct ctl_table *, int,
-                                       void __user *, size_t *, loff_t *);
-#ifdef CONFIG_NUMA
-extern int scan_unevictable_register_node(struct node *node);
-extern void scan_unevictable_unregister_node(struct node *node);
-#else
-static inline int scan_unevictable_register_node(struct node *node)
-{
-       return 0;
-}
-static inline void scan_unevictable_unregister_node(struct node *node)
-{
-}
-#endif
-
 extern int kswapd_run(int nid);
 extern void kswapd_stop(int nid);
 #ifdef CONFIG_MEMCG
index 9a82c7dc3fdd9f1fd35d62fb2ab0734516c9ad6c..595ee86f5e0dba847e6a0d7199fac8b5739d6d0f 100644 (file)
@@ -181,14 +181,12 @@ static inline bool tick_nohz_full_cpu(int cpu)
        return cpumask_test_cpu(cpu, tick_nohz_full_mask);
 }
 
-extern void tick_nohz_init(void);
 extern void __tick_nohz_full_check(void);
 extern void tick_nohz_full_kick(void);
 extern void tick_nohz_full_kick_cpu(int cpu);
 extern void tick_nohz_full_kick_all(void);
 extern void __tick_nohz_task_switch(struct task_struct *tsk);
 #else
-static inline void tick_nohz_init(void) { }
 static inline bool tick_nohz_full_enabled(void) { return false; }
 static inline bool tick_nohz_full_cpu(int cpu) { return false; }
 static inline void __tick_nohz_full_check(void) { }
index dda6ee521e74e3fefff78e17b54624fa53ffae96..909b6e43b6942c2a7372314627e19d1bd0f72c7c 100644 (file)
@@ -119,11 +119,20 @@ static inline int numa_node_id(void)
  * Use the accessor functions set_numa_mem(), numa_mem_id() and cpu_to_mem().
  */
 DECLARE_PER_CPU(int, _numa_mem_);
+extern int _node_numa_mem_[MAX_NUMNODES];
 
 #ifndef set_numa_mem
 static inline void set_numa_mem(int node)
 {
        this_cpu_write(_numa_mem_, node);
+       _node_numa_mem_[numa_node_id()] = node;
+}
+#endif
+
+#ifndef node_to_mem_node
+static inline int node_to_mem_node(int node)
+{
+       return _node_numa_mem_[node];
 }
 #endif
 
@@ -146,6 +155,7 @@ static inline int cpu_to_mem(int cpu)
 static inline void set_cpu_numa_mem(int cpu, int node)
 {
        per_cpu(_numa_mem_, cpu) = node;
+       _node_numa_mem_[cpu_to_node(cpu)] = node;
 }
 #endif
 
@@ -159,6 +169,13 @@ static inline int numa_mem_id(void)
 }
 #endif
 
+#ifndef node_to_mem_node
+static inline int node_to_mem_node(int node)
+{
+       return node;
+}
+#endif
+
 #ifndef cpu_to_mem
 static inline int cpu_to_mem(int cpu)
 {
index ced92345c96307f559d165c56af092e520be3a06..730334cdf037c8ea14dc83377eb61073bb9678ed 100644 (file)
@@ -72,6 +72,13 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
                THP_ZERO_PAGE_ALLOC,
                THP_ZERO_PAGE_ALLOC_FAILED,
 #endif
+#ifdef CONFIG_MEMORY_BALLOON
+               BALLOON_INFLATE,
+               BALLOON_DEFLATE,
+#ifdef CONFIG_BALLOON_COMPACTION
+               BALLOON_MIGRATE,
+#endif
+#endif
 #ifdef CONFIG_DEBUG_TLBFLUSH
 #ifdef CONFIG_SMP
                NR_TLB_REMOTE_FLUSH,    /* cpu tried to flush others' tlbs */
index e44d634e7fb79bbe55da8153828be1b81e20d722..05c21476097796d30639dbf7a3521b4e81604d4b 100644 (file)
@@ -46,6 +46,6 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle,
                        enum zs_mapmode mm);
 void zs_unmap_object(struct zs_pool *pool, unsigned long handle);
 
-u64 zs_get_total_size_bytes(struct zs_pool *pool);
+unsigned long zs_get_total_pages(struct zs_pool *pool);
 
 #endif
index 852e96c4bb46b56d89ddb76d8b440bee01d20ce7..984fb79031de62274363cde081341daea3688593 100644 (file)
@@ -114,7 +114,7 @@ struct ccdc_fault_pixel {
        /* Number of fault pixel */
        unsigned short fp_num;
        /* Address of fault pixel table */
-       unsigned int fpc_table_addr;
+       unsigned long fpc_table_addr;
 };
 
 /* Structure for CCDC configuration parameters for raw capture mode passed
index c9d06d9f7e6eaa823eb4eb8b286ada0008abc176..398279dd1922b4b41959aa7966b0991d19fbb24b 100644 (file)
@@ -57,6 +57,8 @@ enum {
  *             0 - Active high, 1 - Active low
  * @vs_pol: Vertical synchronization polarity
  *             0 - Active high, 1 - Active low
+ * @fld_pol: Field signal polarity
+ *             0 - Positive, 1 - Negative
  * @data_pol: Data polarity
  *             0 - Normal, 1 - One's complement
  */
@@ -65,6 +67,7 @@ struct isp_parallel_platform_data {
        unsigned int clk_pol:1;
        unsigned int hs_pol:1;
        unsigned int vs_pol:1;
+       unsigned int fld_pol:1;
        unsigned int data_pol:1;
 };
 
index 80f951890b4cd1b8397de08e9f82442b97be5c19..e7a1514075ecfc8a3ac11d85fd5f9eb82a848e49 100644 (file)
@@ -135,6 +135,7 @@ void rc_map_init(void);
 #define RC_MAP_DM1105_NEC                "rc-dm1105-nec"
 #define RC_MAP_DNTV_LIVE_DVBT_PRO        "rc-dntv-live-dvbt-pro"
 #define RC_MAP_DNTV_LIVE_DVB_T           "rc-dntv-live-dvb-t"
+#define RC_MAP_DVBSKY                    "rc-dvbsky"
 #define RC_MAP_EMPTY                     "rc-empty"
 #define RC_MAP_EM_TERRATEC               "rc-em-terratec"
 #define RC_MAP_ENCORE_ENLTV2             "rc-encore-enltv2"
index 2fefcf491aa853f8ed868a92f39e8da594422c9b..6ef2d01197da95db59aea39eddc1d0b80592b0b6 100644 (file)
@@ -356,8 +356,8 @@ struct v4l2_fh;
  * @buf_struct_size: size of the driver-specific buffer structure;
  *             "0" indicates the driver doesn't want to use a custom buffer
  *             structure type, so sizeof(struct vb2_buffer) will is used
- * @timestamp_flags: Timestamp flags; V4L2_BUF_FLAGS_TIMESTAMP_* and
- *             V4L2_BUF_FLAGS_TSTAMP_SRC_*
+ * @timestamp_flags: Timestamp flags; V4L2_BUF_FLAG_TIMESTAMP_* and
+ *             V4L2_BUF_FLAG_TSTAMP_SRC_*
  * @gfp_flags: additional gfp flags used when allocating the buffers.
  *             Typically this is 0, but it may be e.g. GFP_DMA or __GFP_DMA32
  *             to force the buffer allocation to a specific memory zone.
@@ -366,6 +366,7 @@ struct v4l2_fh;
  *             cannot be started unless at least this number of buffers
  *             have been queued into the driver.
  *
+ * @mmap_lock: private mutex used when buffers are allocated/freed/mmapped
  * @memory:    current memory type used
  * @bufs:      videobuf buffer structures
  * @num_buffers: number of allocated/used buffers
@@ -402,6 +403,7 @@ struct vb2_queue {
        u32                             min_buffers_needed;
 
 /* private: internal use only */
+       struct mutex                    mmap_lock;
        enum v4l2_memory                memory;
        struct vb2_buffer               *bufs[VIDEO_MAX_FRAME];
        unsigned int                    num_buffers;
@@ -592,6 +594,15 @@ vb2_plane_size(struct vb2_buffer *vb, unsigned int plane_no)
        return 0;
 }
 
+/**
+ * vb2_start_streaming_called() - return streaming status of driver
+ * @q:         videobuf queue
+ */
+static inline bool vb2_start_streaming_called(struct vb2_queue *q)
+{
+       return q->start_streaming_called;
+}
+
 /*
  * The following functions are not part of the vb2 core API, but are simple
  * helper functions that you can use in your struct v4l2_file_operations,
diff --git a/include/misc/cxl.h b/include/misc/cxl.h
new file mode 100644 (file)
index 0000000..975cc78
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#ifndef _MISC_CXL_H
+#define _MISC_CXL_H
+
+#ifdef CONFIG_CXL_BASE
+
+#define CXL_IRQ_RANGES 4
+
+struct cxl_irq_ranges {
+       irq_hw_number_t offset[CXL_IRQ_RANGES];
+       irq_hw_number_t range[CXL_IRQ_RANGES];
+};
+
+extern atomic_t cxl_use_count;
+
+static inline bool cxl_ctx_in_use(void)
+{
+       return (atomic_read(&cxl_use_count) != 0);
+}
+
+static inline void cxl_ctx_get(void)
+{
+       atomic_inc(&cxl_use_count);
+}
+
+static inline void cxl_ctx_put(void)
+{
+       atomic_dec(&cxl_use_count);
+}
+
+void cxl_slbia(struct mm_struct *mm);
+
+#else /* CONFIG_CXL_BASE */
+
+static inline bool cxl_ctx_in_use(void) { return false; }
+static inline void cxl_slbia(struct mm_struct *mm) {}
+
+#endif /* CONFIG_CXL_BASE */
+
+#endif
index 2f26dfb8450e89057b97a09d1a14f152b4d3d8e7..1f99a1de0e4ff419b5a9f137298e0c0b3182f7a8 100644 (file)
@@ -63,7 +63,7 @@ static inline void dst_entries_add(struct dst_ops *dst, int val)
 
 static inline int dst_entries_init(struct dst_ops *dst)
 {
-       return percpu_counter_init(&dst->pcpuc_entries, 0);
+       return percpu_counter_init(&dst->pcpuc_entries, 0, GFP_KERNEL);
 }
 
 static inline void dst_entries_destroy(struct dst_ops *dst)
index 65a8855e99fee80a0cfa4327a020f64ed0580802..8d1765577acca21f698813ee8359a39d76680b7e 100644 (file)
@@ -151,7 +151,7 @@ static inline void add_frag_mem_limit(struct inet_frag_queue *q, int i)
 
 static inline void init_frag_mem_limit(struct netns_frags *nf)
 {
-       percpu_counter_init(&nf->mem, 0);
+       percpu_counter_init(&nf->mem, 0, GFP_KERNEL);
 }
 
 static inline unsigned int sum_frag_mem_limit(struct netns_frags *nf)
index 47da53c27ffa54fd5602964400713f44c5ffdf82..79abb9c71772efa32c41fb448fd3beaddbd3e66e 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/tracepoint.h>
 #include <linux/edac.h>
 #include <linux/ktime.h>
+#include <linux/pci.h>
 #include <linux/aer.h>
 #include <linux/cper.h>
 
@@ -173,25 +174,34 @@ TRACE_EVENT(mc_event,
  * u8 severity -       error severity 0:NONFATAL 1:FATAL 2:CORRECTED
  */
 
-#define aer_correctable_errors         \
-       {BIT(0),        "Receiver Error"},              \
-       {BIT(6),        "Bad TLP"},                     \
-       {BIT(7),        "Bad DLLP"},                    \
-       {BIT(8),        "RELAY_NUM Rollover"},          \
-       {BIT(12),       "Replay Timer Timeout"},        \
-       {BIT(13),       "Advisory Non-Fatal"}
-
-#define aer_uncorrectable_errors               \
-       {BIT(4),        "Data Link Protocol"},          \
-       {BIT(12),       "Poisoned TLP"},                \
-       {BIT(13),       "Flow Control Protocol"},       \
-       {BIT(14),       "Completion Timeout"},          \
-       {BIT(15),       "Completer Abort"},             \
-       {BIT(16),       "Unexpected Completion"},       \
-       {BIT(17),       "Receiver Overflow"},           \
-       {BIT(18),       "Malformed TLP"},               \
-       {BIT(19),       "ECRC"},                        \
-       {BIT(20),       "Unsupported Request"}
+#define aer_correctable_errors                                 \
+       {PCI_ERR_COR_RCVR,      "Receiver Error"},              \
+       {PCI_ERR_COR_BAD_TLP,   "Bad TLP"},                     \
+       {PCI_ERR_COR_BAD_DLLP,  "Bad DLLP"},                    \
+       {PCI_ERR_COR_REP_ROLL,  "RELAY_NUM Rollover"},          \
+       {PCI_ERR_COR_REP_TIMER, "Replay Timer Timeout"},        \
+       {PCI_ERR_COR_ADV_NFAT,  "Advisory Non-Fatal Error"},    \
+       {PCI_ERR_COR_INTERNAL,  "Corrected Internal Error"},    \
+       {PCI_ERR_COR_LOG_OVER,  "Header Log Overflow"}
+
+#define aer_uncorrectable_errors                               \
+       {PCI_ERR_UNC_UND,       "Undefined"},                   \
+       {PCI_ERR_UNC_DLP,       "Data Link Protocol Error"},    \
+       {PCI_ERR_UNC_SURPDN,    "Surprise Down Error"},         \
+       {PCI_ERR_UNC_POISON_TLP,"Poisoned TLP"},                \
+       {PCI_ERR_UNC_FCP,       "Flow Control Protocol Error"}, \
+       {PCI_ERR_UNC_COMP_TIME, "Completion Timeout"},          \
+       {PCI_ERR_UNC_COMP_ABORT,"Completer Abort"},             \
+       {PCI_ERR_UNC_UNX_COMP,  "Unexpected Completion"},       \
+       {PCI_ERR_UNC_RX_OVER,   "Receiver Overflow"},           \
+       {PCI_ERR_UNC_MALF_TLP,  "Malformed TLP"},               \
+       {PCI_ERR_UNC_ECRC,      "ECRC Error"},                  \
+       {PCI_ERR_UNC_UNSUP,     "Unsupported Request Error"},   \
+       {PCI_ERR_UNC_ACSV,      "ACS Violation"},               \
+       {PCI_ERR_UNC_INTN,      "Uncorrectable Internal Error"},\
+       {PCI_ERR_UNC_MCBTLP,    "MC Blocked TLP"},              \
+       {PCI_ERR_UNC_ATOMEG,    "AtomicOp Egress Blocked"},     \
+       {PCI_ERR_UNC_TLPPRE,    "TLP Prefix Blocked Error"}
 
 TRACE_EVENT(aer_event,
        TP_PROTO(const char *dev_name,
index 6f3e10ca0e323f8ca8422e1a75824bce89d61a62..e862497f75568d11cd4deb4f5f5a06712f63d6de 100644 (file)
@@ -183,6 +183,7 @@ struct snd_pcm_ops {
 #define SNDRV_PCM_FMTBIT_G723_40_1B    _SNDRV_PCM_FMTBIT(G723_40_1B)
 #define SNDRV_PCM_FMTBIT_DSD_U8                _SNDRV_PCM_FMTBIT(DSD_U8)
 #define SNDRV_PCM_FMTBIT_DSD_U16_LE    _SNDRV_PCM_FMTBIT(DSD_U16_LE)
+#define SNDRV_PCM_FMTBIT_DSD_U32_LE    _SNDRV_PCM_FMTBIT(DSD_U32_LE)
 
 #ifdef SNDRV_LITTLE_ENDIAN
 #define SNDRV_PCM_FMTBIT_S16           SNDRV_PCM_FMTBIT_S16_LE
@@ -365,6 +366,7 @@ struct snd_pcm_runtime {
 
 struct snd_pcm_group {         /* keep linked substreams */
        spinlock_t lock;
+       struct mutex mutex;
        struct list_head substreams;
        int count;
 };
@@ -460,6 +462,7 @@ struct snd_pcm {
        void (*private_free) (struct snd_pcm *pcm);
        struct device *dev; /* actual hw device this belongs to */
        bool internal; /* pcm is for internal use only */
+       bool nonatomic; /* whole PCM operations are in non-atomic context */
 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
        struct snd_pcm_oss oss;
 #endif
@@ -492,8 +495,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree);
  *  Native I/O
  */
 
-extern rwlock_t snd_pcm_link_rwlock;
-
 int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info);
 int snd_pcm_info_user(struct snd_pcm_substream *substream,
                      struct snd_pcm_info __user *info);
@@ -537,41 +538,18 @@ static inline int snd_pcm_stream_linked(struct snd_pcm_substream *substream)
        return substream->group != &substream->self_group;
 }
 
-static inline void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
-{
-       read_lock(&snd_pcm_link_rwlock);
-       spin_lock(&substream->self_group.lock);
-}
-
-static inline void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
-{
-       spin_unlock(&substream->self_group.lock);
-       read_unlock(&snd_pcm_link_rwlock);
-}
-
-static inline void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
-{
-       read_lock_irq(&snd_pcm_link_rwlock);
-       spin_lock(&substream->self_group.lock);
-}
-
-static inline void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
-{
-       spin_unlock(&substream->self_group.lock);
-       read_unlock_irq(&snd_pcm_link_rwlock);
-}
-
-#define snd_pcm_stream_lock_irqsave(substream, flags) \
-do { \
-       read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \
-       spin_lock(&substream->self_group.lock); \
-} while (0)
-
-#define snd_pcm_stream_unlock_irqrestore(substream, flags) \
-do { \
-       spin_unlock(&substream->self_group.lock); \
-       read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \
-} while (0)
+void snd_pcm_stream_lock(struct snd_pcm_substream *substream);
+void snd_pcm_stream_unlock(struct snd_pcm_substream *substream);
+void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream);
+void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream);
+unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream);
+#define snd_pcm_stream_lock_irqsave(substream, flags)           \
+       do {                                                     \
+               typecheck(unsigned long, flags);                 \
+               flags = _snd_pcm_stream_lock_irqsave(substream); \
+       } while (0)
+void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
+                                     unsigned long flags);
 
 #define snd_pcm_group_for_each_entry(s, substream) \
        list_for_each_entry(s, &substream->group->substreams, link_list)
index 1de744c242f62f4ff49f68579619a40ddcf957c8..a5352712194be276f68243237c6186495b85a854 100644 (file)
@@ -20,6 +20,9 @@ struct rt5645_platform_data {
        /* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */
        unsigned int dmic2_data_pin;
        /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
+
+       unsigned int hp_det_gpio;
+       bool gpio_hp_det_active_high;
 };
 
 #endif
index 3da14313bcfc158214c3f984c7adaa22c4a5be33..082670e3a3536cd87ed4f4268478d02d6b4b120a 100644 (file)
 #ifndef __LINUX_SND_RT5677_H
 #define __LINUX_SND_RT5677_H
 
+enum rt5677_dmic2_clk {
+       RT5677_DMIC_CLK1 = 0,
+       RT5677_DMIC_CLK2 = 1,
+};
+
+
 struct rt5677_platform_data {
-       /* IN1 IN2 can optionally be differential */
+       /* IN1/IN2/LOUT1/LOUT2/LOUT3 can optionally be differential */
        bool in1_diff;
        bool in2_diff;
+       bool lout1_diff;
+       bool lout2_diff;
+       bool lout3_diff;
+       /* DMIC2 clock source selection */
+       enum rt5677_dmic2_clk dmic2_clk_pin;
 };
 
 #endif
index aac04ff84eea5674279fd22984db6ca9b79ba8c7..3a4d7da67b8d895c9bd9b5299a70d698709fcd98 100644 (file)
@@ -432,6 +432,7 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
 int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
                                const char *pin);
 void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card);
+unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
 
 /* Mostly internal - should not normally be used */
 void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm);
@@ -587,13 +588,13 @@ struct snd_soc_dapm_context {
        enum snd_soc_bias_level suspend_bias_level;
        struct delayed_work delayed_work;
        unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
-
+       /* Go to BIAS_OFF in suspend if the DAPM context is idle */
+       unsigned int suspend_bias_off:1;
        void (*seq_notifier)(struct snd_soc_dapm_context *,
                             enum snd_soc_dapm_type, int);
 
        struct device *dev; /* from parent - for debug */
        struct snd_soc_component *component; /* parent component */
-       struct snd_soc_codec *codec; /* parent codec */
        struct snd_soc_card *card; /* parent card */
 
        /* used during DAPM updates */
index c83a334dd00fa9186404917183115fca3124670c..7ba7130037a079f1fb065e9c8fdcf55353aa3c9e 100644 (file)
@@ -690,6 +690,17 @@ struct snd_soc_compr_ops {
 struct snd_soc_component_driver {
        const char *name;
 
+       /* Default control and setup, added after probe() is run */
+       const struct snd_kcontrol_new *controls;
+       unsigned int num_controls;
+       const struct snd_soc_dapm_widget *dapm_widgets;
+       unsigned int num_dapm_widgets;
+       const struct snd_soc_dapm_route *dapm_routes;
+       unsigned int num_dapm_routes;
+
+       int (*probe)(struct snd_soc_component *);
+       void (*remove)(struct snd_soc_component *);
+
        /* DT */
        int (*of_xlate_dai_name)(struct snd_soc_component *component,
                                 struct of_phandle_args *args,
@@ -697,6 +708,10 @@ struct snd_soc_component_driver {
        void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
                int subseq);
        int (*stream_event)(struct snd_soc_component *, int event);
+
+       /* probe ordering - for components with runtime dependencies */
+       int probe_order;
+       int remove_order;
 };
 
 struct snd_soc_component {
@@ -710,6 +725,7 @@ struct snd_soc_component {
 
        unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
        unsigned int registered_as_component:1;
+       unsigned int probed:1;
 
        struct list_head list;
 
@@ -728,9 +744,35 @@ struct snd_soc_component {
 
        struct mutex io_mutex;
 
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs_root;
+#endif
+
+       /*
+       * DO NOT use any of the fields below in drivers, they are temporary and
+       * are going to be removed again soon. If you use them in driver code the
+       * driver will be marked as BROKEN when these fields are removed.
+       */
+
        /* Don't use these, use snd_soc_component_get_dapm() */
        struct snd_soc_dapm_context dapm;
        struct snd_soc_dapm_context *dapm_ptr;
+
+       const struct snd_kcontrol_new *controls;
+       unsigned int num_controls;
+       const struct snd_soc_dapm_widget *dapm_widgets;
+       unsigned int num_dapm_widgets;
+       const struct snd_soc_dapm_route *dapm_routes;
+       unsigned int num_dapm_routes;
+       struct snd_soc_codec *codec;
+
+       int (*probe)(struct snd_soc_component *);
+       void (*remove)(struct snd_soc_component *);
+
+#ifdef CONFIG_DEBUG_FS
+       void (*init_debugfs)(struct snd_soc_component *component);
+       const char *debugfs_prefix;
+#endif
 };
 
 /* SoC Audio Codec device */
@@ -746,11 +788,9 @@ struct snd_soc_codec {
        struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */
        unsigned int cache_bypass:1; /* Suppress access to the cache */
        unsigned int suspended:1; /* Codec is in suspend PM state */
-       unsigned int probed:1; /* Codec has been probed */
        unsigned int ac97_registered:1; /* Codec has been AC97 registered */
        unsigned int ac97_created:1; /* Codec has been created by SoC */
        unsigned int cache_init:1; /* codec cache has been initialized */
-       u32 cache_only;  /* Suppress writes to hardware */
        u32 cache_sync; /* Cache needs to be synced to hardware */
 
        /* codec IO */
@@ -766,7 +806,6 @@ struct snd_soc_codec {
        struct snd_soc_dapm_context dapm;
 
 #ifdef CONFIG_DEBUG_FS
-       struct dentry *debugfs_codec_root;
        struct dentry *debugfs_reg;
 #endif
 };
@@ -808,15 +847,12 @@ struct snd_soc_codec_driver {
        int (*set_bias_level)(struct snd_soc_codec *,
                              enum snd_soc_bias_level level);
        bool idle_bias_off;
+       bool suspend_bias_off;
 
        void (*seq_notifier)(struct snd_soc_dapm_context *,
                             enum snd_soc_dapm_type, int);
 
        bool ignore_pmdown_time;  /* Doesn't benefit from pmdown delay */
-
-       /* probe ordering - for components with runtime dependencies */
-       int probe_order;
-       int remove_order;
 };
 
 /* SoC platform interface */
@@ -832,14 +868,6 @@ struct snd_soc_platform_driver {
        int (*pcm_new)(struct snd_soc_pcm_runtime *);
        void (*pcm_free)(struct snd_pcm *);
 
-       /* Default control and setup, added after probe() is run */
-       const struct snd_kcontrol_new *controls;
-       int num_controls;
-       const struct snd_soc_dapm_widget *dapm_widgets;
-       int num_dapm_widgets;
-       const struct snd_soc_dapm_route *dapm_routes;
-       int num_dapm_routes;
-
        /*
         * For platform caused delay reporting.
         * Optional.
@@ -853,13 +881,6 @@ struct snd_soc_platform_driver {
        /* platform stream compress ops */
        const struct snd_compr_ops *compr_ops;
 
-       /* probe ordering - for components with runtime dependencies */
-       int probe_order;
-       int remove_order;
-
-       /* platform IO - used for platform DAPM */
-       unsigned int (*read)(struct snd_soc_platform *, unsigned int);
-       int (*write)(struct snd_soc_platform *, unsigned int, unsigned int);
        int (*bespoke_trigger)(struct snd_pcm_substream *, int);
 };
 
@@ -874,15 +895,10 @@ struct snd_soc_platform {
        const struct snd_soc_platform_driver *driver;
 
        unsigned int suspended:1; /* platform is suspended */
-       unsigned int probed:1;
 
        struct list_head list;
 
        struct snd_soc_component component;
-
-#ifdef CONFIG_DEBUG_FS
-       struct dentry *debugfs_platform_root;
-#endif
 };
 
 struct snd_soc_dai_link {
@@ -897,7 +913,7 @@ struct snd_soc_dai_link {
         * only for codec to codec links, or systems using device tree.
         */
        const char *cpu_name;
-       const struct device_node *cpu_of_node;
+       struct device_node *cpu_of_node;
        /*
         * You MAY specify the DAI name of the CPU DAI. If this information is
         * omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node
@@ -909,7 +925,7 @@ struct snd_soc_dai_link {
         * DT/OF node, but not both.
         */
        const char *codec_name;
-       const struct device_node *codec_of_node;
+       struct device_node *codec_of_node;
        /* You MUST specify the DAI name within the codec */
        const char *codec_dai_name;
 
@@ -922,7 +938,7 @@ struct snd_soc_dai_link {
         * do not need a platform.
         */
        const char *platform_name;
-       const struct device_node *platform_of_node;
+       struct device_node *platform_of_node;
        int be_id;      /* optional ID for machine driver BE identification */
 
        const struct snd_soc_pcm_stream *params;
@@ -994,7 +1010,7 @@ struct snd_soc_aux_dev {
        const struct device_node *codec_of_node;
 
        /* codec/machine specific init - e.g. add machine controls */
-       int (*init)(struct snd_soc_dapm_context *dapm);
+       int (*init)(struct snd_soc_component *component);
 };
 
 /* SoC card */
@@ -1112,6 +1128,7 @@ struct snd_soc_pcm_runtime {
        struct snd_soc_platform *platform;
        struct snd_soc_dai *codec_dai;
        struct snd_soc_dai *cpu_dai;
+       struct snd_soc_component *component; /* Only valid for AUX dev rtds */
 
        struct snd_soc_dai **codec_dais;
        unsigned int num_codecs;
@@ -1260,9 +1277,6 @@ void snd_soc_component_async_complete(struct snd_soc_component *component);
 int snd_soc_component_test_bits(struct snd_soc_component *component,
        unsigned int reg, unsigned int mask, unsigned int value);
 
-int snd_soc_component_init_io(struct snd_soc_component *component,
-       struct regmap *regmap);
-
 /* device driver data */
 
 static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
@@ -1276,26 +1290,37 @@ static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
        return card->drvdata;
 }
 
+static inline void snd_soc_component_set_drvdata(struct snd_soc_component *c,
+               void *data)
+{
+       dev_set_drvdata(c->dev, data);
+}
+
+static inline void *snd_soc_component_get_drvdata(struct snd_soc_component *c)
+{
+       return dev_get_drvdata(c->dev);
+}
+
 static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec,
                void *data)
 {
-       dev_set_drvdata(codec->dev, data);
+       snd_soc_component_set_drvdata(&codec->component, data);
 }
 
 static inline void *snd_soc_codec_get_drvdata(struct snd_soc_codec *codec)
 {
-       return dev_get_drvdata(codec->dev);
+       return snd_soc_component_get_drvdata(&codec->component);
 }
 
 static inline void snd_soc_platform_set_drvdata(struct snd_soc_platform *platform,
                void *data)
 {
-       dev_set_drvdata(platform->dev, data);
+       snd_soc_component_set_drvdata(&platform->component, data);
 }
 
 static inline void *snd_soc_platform_get_drvdata(struct snd_soc_platform *platform)
 {
-       return dev_get_drvdata(platform->dev);
+       return snd_soc_component_get_drvdata(&platform->component);
 }
 
 static inline void snd_soc_pcm_set_drvdata(struct snd_soc_pcm_runtime *rtd,
index f634f8f85db53addf02c5f9e85d94363aee23de6..cae9c9d4ef2260fcdeff0e74fe8177c176ed6b9b 100644 (file)
@@ -80,8 +80,6 @@ struct vx_pipe {
 
        unsigned int references;     /* an output pipe may be used for monitoring and/or playback */
        struct vx_pipe *monitoring_pipe;  /* pointer to the monitoring pipe (capture pipe only)*/
-
-       struct tasklet_struct start_tq;
 };
 
 struct vx_core;
@@ -165,9 +163,7 @@ struct vx_core {
        struct snd_vx_hardware *hw;
        struct snd_vx_ops *ops;
 
-       spinlock_t lock;
-       spinlock_t irq_lock;
-       struct tasklet_struct tq;
+       struct mutex lock;
 
        unsigned int chip_status;
        unsigned int pcm_running;
@@ -223,6 +219,7 @@ void snd_vx_free_firmware(struct vx_core *chip);
  * interrupt handler; exported for pcmcia
  */
 irqreturn_t snd_vx_irq_handler(int irq, void *dev);
+irqreturn_t snd_vx_threaded_irq_handler(int irq, void *dev);
 
 /*
  * lowlevel functions
index 0194a641e4e2589c71176fabee75c43ad1baec7d..b04ee7e5a466e4419c7c0601e285411c920b7ed3 100644 (file)
@@ -175,7 +175,7 @@ TRACE_EVENT(snd_soc_dapm_output_path,
                __entry->path_sink = (long)path->sink;
        ),
 
-       TP_printk("%c%s -> %s -> %s\n",
+       TP_printk("%c%s -> %s -> %s",
                (int) __entry->path_sink &&
                (int) __entry->path_connect ? '*' : ' ',
                __get_str(wname), __get_str(pname), __get_str(psname))
@@ -204,7 +204,7 @@ TRACE_EVENT(snd_soc_dapm_input_path,
                __entry->path_source = (long)path->source;
        ),
 
-       TP_printk("%c%s <- %s <- %s\n",
+       TP_printk("%c%s <- %s <- %s",
                (int) __entry->path_source &&
                (int) __entry->path_connect ? '*' : ' ',
                __get_str(wname), __get_str(pname), __get_str(psname))
@@ -226,7 +226,7 @@ TRACE_EVENT(snd_soc_dapm_connected,
                __entry->stream = stream;
        ),
 
-       TP_printk("%s: found %d paths\n",
+       TP_printk("%s: found %d paths",
                __entry->stream ? "capture" : "playback", __entry->paths)
 );
 
index 4ee4e30d26d9b3a9d5bccbb51348e0b876365f26..1faecea101f3e85a84c247ec997af8216de2523d 100644 (file)
@@ -23,6 +23,7 @@ struct map_lookup;
 struct extent_buffer;
 struct btrfs_work;
 struct __btrfs_workqueue;
+struct btrfs_qgroup_operation;
 
 #define show_ref_type(type)                                            \
        __print_symbolic(type,                                          \
@@ -157,12 +158,13 @@ DEFINE_EVENT(btrfs__inode, btrfs_inode_evict,
 
 #define show_map_flags(flag)                                           \
        __print_flags(flag, "|",                                        \
-               { EXTENT_FLAG_PINNED,           "PINNED"        },      \
-               { EXTENT_FLAG_COMPRESSED,       "COMPRESSED"    },      \
-               { EXTENT_FLAG_VACANCY,          "VACANCY"       },      \
-               { EXTENT_FLAG_PREALLOC,         "PREALLOC"      },      \
-               { EXTENT_FLAG_LOGGING,          "LOGGING"       },      \
-               { EXTENT_FLAG_FILLING,          "FILLING"       })
+               { (1 << EXTENT_FLAG_PINNED),            "PINNED"        },\
+               { (1 << EXTENT_FLAG_COMPRESSED),        "COMPRESSED"    },\
+               { (1 << EXTENT_FLAG_VACANCY),           "VACANCY"       },\
+               { (1 << EXTENT_FLAG_PREALLOC),          "PREALLOC"      },\
+               { (1 << EXTENT_FLAG_LOGGING),           "LOGGING"       },\
+               { (1 << EXTENT_FLAG_FILLING),           "FILLING"       },\
+               { (1 << EXTENT_FLAG_FS_MAPPING),        "FS_MAPPING"    })
 
 TRACE_EVENT_CONDITION(btrfs_get_extent,
 
@@ -996,6 +998,7 @@ DECLARE_EVENT_CLASS(btrfs__work,
                __field(        void *, func                    )
                __field(        void *, ordered_func            )
                __field(        void *, ordered_free            )
+               __field(        void *, normal_work             )
        ),
 
        TP_fast_assign(
@@ -1004,11 +1007,13 @@ DECLARE_EVENT_CLASS(btrfs__work,
                __entry->func           = work->func;
                __entry->ordered_func   = work->ordered_func;
                __entry->ordered_free   = work->ordered_free;
+               __entry->normal_work    = &work->normal_work;
        ),
 
-       TP_printk("work=%p, wq=%p, func=%p, ordered_func=%p, ordered_free=%p",
-                 __entry->work, __entry->wq, __entry->func,
-                 __entry->ordered_func, __entry->ordered_free)
+       TP_printk("work=%p (normal_work=%p), wq=%p, func=%pf, ordered_func=%p,"
+                 " ordered_free=%p",
+                 __entry->work, __entry->normal_work, __entry->wq,
+                  __entry->func, __entry->ordered_func, __entry->ordered_free)
 );
 
 /* For situiations that the work is freed */
@@ -1043,13 +1048,6 @@ DEFINE_EVENT(btrfs__work, btrfs_work_sched,
        TP_ARGS(work)
 );
 
-DEFINE_EVENT(btrfs__work, btrfs_normal_work_done,
-
-       TP_PROTO(struct btrfs_work *work),
-
-       TP_ARGS(work)
-);
-
 DEFINE_EVENT(btrfs__work__done, btrfs_all_work_done,
 
        TP_PROTO(struct btrfs_work *work),
@@ -1119,6 +1117,61 @@ DEFINE_EVENT(btrfs__workqueue_done, btrfs_workqueue_destroy,
        TP_ARGS(wq)
 );
 
+#define show_oper_type(type)                                           \
+       __print_symbolic(type,                                          \
+               { BTRFS_QGROUP_OPER_ADD_EXCL,   "OPER_ADD_EXCL" },      \
+               { BTRFS_QGROUP_OPER_ADD_SHARED, "OPER_ADD_SHARED" },    \
+               { BTRFS_QGROUP_OPER_SUB_EXCL,   "OPER_SUB_EXCL" },      \
+               { BTRFS_QGROUP_OPER_SUB_SHARED, "OPER_SUB_SHARED" })
+
+DECLARE_EVENT_CLASS(btrfs_qgroup_oper,
+
+       TP_PROTO(struct btrfs_qgroup_operation *oper),
+
+       TP_ARGS(oper),
+
+       TP_STRUCT__entry(
+               __field(        u64,  ref_root          )
+               __field(        u64,  bytenr            )
+               __field(        u64,  num_bytes         )
+               __field(        u64,  seq               )
+               __field(        int,  type              )
+               __field(        u64,  elem_seq          )
+       ),
+
+       TP_fast_assign(
+               __entry->ref_root       = oper->ref_root;
+               __entry->bytenr         = oper->bytenr,
+               __entry->num_bytes      = oper->num_bytes;
+               __entry->seq            = oper->seq;
+               __entry->type           = oper->type;
+               __entry->elem_seq       = oper->elem.seq;
+       ),
+
+       TP_printk("ref_root = %llu, bytenr = %llu, num_bytes = %llu, "
+                 "seq = %llu, elem.seq = %llu, type = %s",
+                 (unsigned long long)__entry->ref_root,
+                 (unsigned long long)__entry->bytenr,
+                 (unsigned long long)__entry->num_bytes,
+                 (unsigned long long)__entry->seq,
+                 (unsigned long long)__entry->elem_seq,
+                 show_oper_type(__entry->type))
+);
+
+DEFINE_EVENT(btrfs_qgroup_oper, btrfs_qgroup_account,
+
+       TP_PROTO(struct btrfs_qgroup_operation *oper),
+
+       TP_ARGS(oper)
+);
+
+DEFINE_EVENT(btrfs_qgroup_oper, btrfs_qgroup_record_ref,
+
+       TP_PROTO(struct btrfs_qgroup_operation *oper),
+
+       TP_ARGS(oper)
+);
+
 #endif /* _TRACE_BTRFS_H */
 
 /* This part must be outside protection */
index 59d11c22f07639ca586a46644ef32b6e061b4ce1..a0d008070962b6b153b979e51108e3d23b4a5935 100644 (file)
@@ -53,15 +53,15 @@ DECLARE_EVENT_CLASS(filelock_lease,
        ),
 
        TP_fast_assign(
-               __entry->fl = fl;
+               __entry->fl = fl ? fl : NULL;
                __entry->s_dev = inode->i_sb->s_dev;
                __entry->i_ino = inode->i_ino;
-               __entry->fl_next = fl->fl_next;
-               __entry->fl_owner = fl->fl_owner;
-               __entry->fl_flags = fl->fl_flags;
-               __entry->fl_type = fl->fl_type;
-               __entry->fl_break_time = fl->fl_break_time;
-               __entry->fl_downgrade_time = fl->fl_downgrade_time;
+               __entry->fl_next = fl ? fl->fl_next : NULL;
+               __entry->fl_owner = fl ? fl->fl_owner : NULL;
+               __entry->fl_flags = fl ? fl->fl_flags : 0;
+               __entry->fl_type = fl ? fl->fl_type : 0;
+               __entry->fl_break_time = fl ? fl->fl_break_time : 0;
+               __entry->fl_downgrade_time = fl ? fl->fl_downgrade_time : 0;
        ),
 
        TP_printk("fl=0x%p dev=0x%x:0x%x ino=0x%lx fl_next=0x%p fl_owner=0x%p fl_flags=%s fl_type=%s fl_break_time=%lu fl_downgrade_time=%lu",
index 81d2106287fef98433387e5a62afe628a9192950..245aa6e05e6adef3f0c0cc4ea2e3a017a2231481 100644 (file)
@@ -12,3 +12,4 @@ header-y += video/
 header-y += drm/
 header-y += xen/
 header-y += scsi/
+header-y += misc/
index 70e150ebc6c9392f4d49aa3744925a82af2561c5..3cc8e1c2b99602fdfbaf24d34612613fcc073149 100644 (file)
@@ -355,6 +355,7 @@ header-y += serio.h
 header-y += shm.h
 header-y += signal.h
 header-y += signalfd.h
+header-y += smiapp.h
 header-y += snmp.h
 header-y += sock_diag.h
 header-y += socket.h
index 5116a0e48172fd27110bdbcda32189473853cb9e..2f96d233c980bdaef504c0d6936f5e381e8cc532 100644 (file)
@@ -31,6 +31,7 @@
 
 #define KPF_KSM                        21
 #define KPF_THP                        22
+#define KPF_BALLOON            23
 
 
 #endif /* _UAPILINUX_KERNEL_PAGE_FLAGS_H */
index 30db069bce62c6533b72a65e4c89f536a448c79d..4a1d0cc38ff29302cf294a220bf4526a6a7a6caf 100644 (file)
 #define  PCI_EXP_RTCTL_PMEIE   0x0008  /* PME Interrupt Enable */
 #define  PCI_EXP_RTCTL_CRSSVE  0x0010  /* CRS Software Visibility Enable */
 #define PCI_EXP_RTCAP          30      /* Root Capabilities */
+#define  PCI_EXP_RTCAP_CRSVIS  0x0001  /* CRS Software Visibility capability */
 #define PCI_EXP_RTSTA          32      /* Root Status */
 #define PCI_EXP_RTSTA_PME      0x00010000 /* PME status */
 #define PCI_EXP_RTSTA_PENDING  0x00020000 /* PME pending */
 
 /* Advanced Error Reporting */
 #define PCI_ERR_UNCOR_STATUS   4       /* Uncorrectable Error Status */
-#define  PCI_ERR_UNC_TRAIN     0x00000001      /* Training */
+#define  PCI_ERR_UNC_UND       0x00000001      /* Undefined */
 #define  PCI_ERR_UNC_DLP       0x00000010      /* Data Link Protocol */
 #define  PCI_ERR_UNC_SURPDN    0x00000020      /* Surprise Down */
 #define  PCI_ERR_UNC_POISON_TLP        0x00001000      /* Poisoned TLP */
index 58afc04c107e40cf9f481a7f9eefefd67dbe74ce..513df75d0fc9e5ec3b822a587c40c9eec5bd5315 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _LINUX_PRCTL_H
 #define _LINUX_PRCTL_H
 
+#include <linux/types.h>
+
 /* Values to pass as first argument to prctl() */
 
 #define PR_SET_PDEATHSIG  1  /* Second arg is a signal */
 # define PR_SET_MM_ENV_END             11
 # define PR_SET_MM_AUXV                        12
 # define PR_SET_MM_EXE_FILE            13
+# define PR_SET_MM_MAP                 14
+# define PR_SET_MM_MAP_SIZE            15
+
+/*
+ * This structure provides new memory descriptor
+ * map which mostly modifies /proc/pid/stat[m]
+ * output for a task. This mostly done in a
+ * sake of checkpoint/restore functionality.
+ */
+struct prctl_mm_map {
+       __u64   start_code;             /* code section bounds */
+       __u64   end_code;
+       __u64   start_data;             /* data section bounds */
+       __u64   end_data;
+       __u64   start_brk;              /* heap for brk() syscall */
+       __u64   brk;
+       __u64   start_stack;            /* stack starts at */
+       __u64   arg_start;              /* command line arguments bounds */
+       __u64   arg_end;
+       __u64   env_start;              /* environment variables bounds */
+       __u64   env_end;
+       __u64   *auxv;                  /* auxiliary vector */
+       __u32   auxv_size;              /* vector size */
+       __u32   exe_fd;                 /* /proc/$pid/exe link file */
+};
 
 /*
  * Set specific pid that is allowed to ptrace the current task.
diff --git a/include/uapi/linux/smiapp.h b/include/uapi/linux/smiapp.h
new file mode 100644 (file)
index 0000000..53938f4
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * include/uapi/linux/smiapp.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2014 Intel Corporation
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __UAPI_LINUX_SMIAPP_H_
+#define __UAPI_LINUX_SMIAPP_H_
+
+#define V4L2_SMIAPP_TEST_PATTERN_MODE_DISABLED                 0
+#define V4L2_SMIAPP_TEST_PATTERN_MODE_SOLID_COLOUR             1
+#define V4L2_SMIAPP_TEST_PATTERN_MODE_COLOUR_BARS              2
+#define V4L2_SMIAPP_TEST_PATTERN_MODE_COLOUR_BARS_GREY         3
+#define V4L2_SMIAPP_TEST_PATTERN_MODE_PN9                      4
+
+#endif /* __UAPI_LINUX_SMIAPP_H_ */
index e946e43fb8d51a80073b25ca88bf023228e8d39c..661f119a51b858e3bc4f4e5f2029ce3fa5f3f6d4 100644 (file)
@@ -746,6 +746,8 @@ enum v4l2_auto_focus_range {
        V4L2_AUTO_FOCUS_RANGE_INFINITY          = 3,
 };
 
+#define V4L2_CID_PAN_SPEED                     (V4L2_CID_CAMERA_CLASS_BASE+32)
+#define V4L2_CID_TILT_SPEED                    (V4L2_CID_CAMERA_CLASS_BASE+33)
 
 /* FM Modulator class control IDs */
 
@@ -865,6 +867,10 @@ enum v4l2_jpeg_chroma_subsampling {
 #define V4L2_CID_VBLANK                                (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 1)
 #define V4L2_CID_HBLANK                                (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 2)
 #define V4L2_CID_ANALOGUE_GAIN                 (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 3)
+#define V4L2_CID_TEST_PATTERN_RED              (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 4)
+#define V4L2_CID_TEST_PATTERN_GREENR           (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 5)
+#define V4L2_CID_TEST_PATTERN_BLUE             (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 6)
+#define V4L2_CID_TEST_PATTERN_GREENB           (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 7)
 
 
 /* Image processing controls */
index 6c8f159e416eea1b2781b4e00139b4b3afe2ea9a..6a0764c89fcbeaf29cee9bf5dcba996ed9bc3c98 100644 (file)
 #ifndef _V4L2_DV_TIMINGS_H
 #define _V4L2_DV_TIMINGS_H
 
-#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6))
-/* Sadly gcc versions older than 4.6 have a bug in how they initialize
-   anonymous unions where they require additional curly brackets.
-   This violates the C1x standard. This workaround adds the curly brackets
-   if needed. */
 #define V4L2_INIT_BT_TIMINGS(_width, args...) \
        { .bt = { _width , ## args } }
-#else
-#define V4L2_INIT_BT_TIMINGS(_width, args...) \
-       .bt = { _width , ## args }
-#endif
 
 /* CEA-861-E timings (i.e. standard HDTV timings) */
 
index 6612974c64bf94c93deb267d904faafab2bd1a54..29715d27548f21b20303861c24f56faeab835cc9 100644 (file)
@@ -33,6 +33,9 @@
 /* Check if EEH is supported */
 #define VFIO_EEH                       5
 
+/* Two-stage IOMMU */
+#define VFIO_TYPE1_NESTING_IOMMU       6       /* Implies v2 */
+
 /*
  * The IOCTL interface is designed for extensibility by embedding the
  * structure length (argsz) and flags into structures passed between
index 778a3298fb3441d4ab001afe7e920c249109bf83..1c2f84fd4d99b08e58d6a3dcf2c25667ad03d7e8 100644 (file)
@@ -79,6 +79,7 @@
 /*  Four-character-code (FOURCC) */
 #define v4l2_fourcc(a, b, c, d)\
        ((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))
+#define v4l2_fourcc_be(a, b, c, d)     (v4l2_fourcc(a, b, c, d) | (1 << 31))
 
 /*
  *     E N U M S
@@ -307,6 +308,8 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_XRGB555 v4l2_fourcc('X', 'R', '1', '5') /* 16  XRGB-1-5-5-5  */
 #define V4L2_PIX_FMT_RGB565  v4l2_fourcc('R', 'G', 'B', 'P') /* 16  RGB-5-6-5     */
 #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16  RGB-5-5-5 BE  */
+#define V4L2_PIX_FMT_ARGB555X v4l2_fourcc_be('A', 'R', '1', '5') /* 16  ARGB-5-5-5 BE */
+#define V4L2_PIX_FMT_XRGB555X v4l2_fourcc_be('X', 'R', '1', '5') /* 16  XRGB-5-5-5 BE */
 #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16  RGB-5-6-5 BE  */
 #define V4L2_PIX_FMT_BGR666  v4l2_fourcc('B', 'G', 'R', 'H') /* 18  BGR-6-6-6    */
 #define V4L2_PIX_FMT_BGR24   v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
@@ -1285,11 +1288,11 @@ struct v4l2_ext_control {
        union {
                __s32 value;
                __s64 value64;
-               char *string;
-               __u8 *p_u8;
-               __u16 *p_u16;
-               __u32 *p_u32;
-               void *ptr;
+               char __user *string;
+               __u8 __user *p_u8;
+               __u16 __user *p_u16;
+               __u32 __user *p_u32;
+               void __user *ptr;
        };
 } __attribute__ ((packed));
 
diff --git a/include/uapi/misc/Kbuild b/include/uapi/misc/Kbuild
new file mode 100644 (file)
index 0000000..e96cae7
--- /dev/null
@@ -0,0 +1,2 @@
+# misc Header export list
+header-y += cxl.h
diff --git a/include/uapi/misc/cxl.h b/include/uapi/misc/cxl.h
new file mode 100644 (file)
index 0000000..cd6d789
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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.
+ */
+
+#ifndef _UAPI_MISC_CXL_H
+#define _UAPI_MISC_CXL_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+
+struct cxl_ioctl_start_work {
+       __u64 flags;
+       __u64 work_element_descriptor;
+       __u64 amr;
+       __s16 num_interrupts;
+       __s16 reserved1;
+       __s32 reserved2;
+       __u64 reserved3;
+       __u64 reserved4;
+       __u64 reserved5;
+       __u64 reserved6;
+};
+
+#define CXL_START_WORK_AMR             0x0000000000000001ULL
+#define CXL_START_WORK_NUM_IRQS                0x0000000000000002ULL
+#define CXL_START_WORK_ALL             (CXL_START_WORK_AMR |\
+                                        CXL_START_WORK_NUM_IRQS)
+
+/* ioctl numbers */
+#define CXL_MAGIC 0xCA
+#define CXL_IOCTL_START_WORK           _IOW(CXL_MAGIC, 0x00, struct cxl_ioctl_start_work)
+#define CXL_IOCTL_GET_PROCESS_ELEMENT  _IOR(CXL_MAGIC, 0x01, __u32)
+
+#define CXL_READ_MIN_SIZE 0x1000 /* 4K */
+
+/* Events from read() */
+enum cxl_event_type {
+       CXL_EVENT_RESERVED      = 0,
+       CXL_EVENT_AFU_INTERRUPT = 1,
+       CXL_EVENT_DATA_STORAGE  = 2,
+       CXL_EVENT_AFU_ERROR     = 3,
+};
+
+struct cxl_event_header {
+       __u16 type;
+       __u16 size;
+       __u16 process_element;
+       __u16 reserved1;
+};
+
+struct cxl_event_afu_interrupt {
+       __u16 flags;
+       __u16 irq; /* Raised AFU interrupt number */
+       __u32 reserved1;
+};
+
+struct cxl_event_data_storage {
+       __u16 flags;
+       __u16 reserved1;
+       __u32 reserved2;
+       __u64 addr;
+       __u64 dsisr;
+       __u64 reserved3;
+};
+
+struct cxl_event_afu_error {
+       __u16 flags;
+       __u16 reserved1;
+       __u32 reserved2;
+       __u64 error;
+};
+
+struct cxl_event {
+       struct cxl_event_header header;
+       union {
+               struct cxl_event_afu_interrupt irq;
+               struct cxl_event_data_storage fault;
+               struct cxl_event_afu_error afu_error;
+       };
+};
+
+#endif /* _UAPI_MISC_CXL_H */
index 32168f7ffce3ce03fe19bb8f187cfb6321710976..6ee586728df97a0fc335a3c314e0828ac970023f 100644 (file)
@@ -219,7 +219,8 @@ typedef int __bitwise snd_pcm_format_t;
 #define        SNDRV_PCM_FORMAT_G723_40_1B     ((__force snd_pcm_format_t) 47) /* 1 sample in 1 byte */
 #define        SNDRV_PCM_FORMAT_DSD_U8         ((__force snd_pcm_format_t) 48) /* DSD, 1-byte samples DSD (x8) */
 #define        SNDRV_PCM_FORMAT_DSD_U16_LE     ((__force snd_pcm_format_t) 49) /* DSD, 2-byte samples DSD (x16), little endian */
-#define        SNDRV_PCM_FORMAT_LAST           SNDRV_PCM_FORMAT_DSD_U16_LE
+#define        SNDRV_PCM_FORMAT_DSD_U32_LE     ((__force snd_pcm_format_t) 50) /* DSD, 4-byte samples DSD (x32), little endian */
+#define        SNDRV_PCM_FORMAT_LAST           SNDRV_PCM_FORMAT_DSD_U32_LE
 
 #ifdef SNDRV_LITTLE_ENDIAN
 #define        SNDRV_PCM_FORMAT_S16            SNDRV_PCM_FORMAT_S16_LE
index 8bee7a75e85055444e7e61c0cb0c4552286dc56a..5321cd9636e6a48e0e4ad0e68fe159e2d015e7c8 100644 (file)
@@ -28,6 +28,8 @@ int bind_ipi_to_irqhandler(enum ipi_vector ipi,
                           unsigned long irqflags,
                           const char *devname,
                           void *dev_id);
+int bind_interdomain_evtchn_to_irq(unsigned int remote_domain,
+                                  unsigned int remote_port);
 int bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain,
                                          unsigned int remote_port,
                                          irq_handler_t handler,
index 6f4eae328ca74be6469e1f4247290e11f0ad8d43..f90b0345465918b6a833a73a1931d18a4de86757 100644 (file)
@@ -3,6 +3,24 @@
  *
  * Definitions used for the Xen ELF notes.
  *
+ * 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.
+ *
  * Copyright (c) 2006, Ian Campbell, XenSource Ltd.
  */
 
  *
  * LEGACY indicated the fields in the legacy __xen_guest string which
  * this a note type replaces.
+ *
+ * String values (for non-legacy) are NULL terminated ASCII, also known
+ * as ASCIZ type.
  */
 
 /*
  * NAME=VALUE pair (string).
- *
- * LEGACY: FEATURES and PAE
  */
 #define XEN_ELFNOTE_INFO           0
 
 
 /*
  * Whether or not the guest supports cooperative suspend cancellation.
+ * This is a numeric value.
+ *
+ * Default is 0
  */
 #define XEN_ELFNOTE_SUSPEND_CANCEL 14
 
+/*
+ * The (non-default) location the initial phys-to-machine map should be
+ * placed at by the hypervisor (Dom0) or the tools (DomU).
+ * The kernel must be prepared for this mapping to be established using
+ * large pages, despite such otherwise not being available to guests.
+ * The kernel must also be able to handle the page table pages used for
+ * this mapping not being accessible through the initial mapping.
+ * (Only x86-64 supports this at present.)
+ */
+#define XEN_ELFNOTE_INIT_P2M      15
+
+/*
+ * Whether or not the guest can deal with being passed an initrd not
+ * mapped through its initial page tables.
+ */
+#define XEN_ELFNOTE_MOD_START_PFN 16
+
 /*
  * The features supported by this kernel (numeric).
  *
  */
 #define XEN_ELFNOTE_SUPPORTED_FEATURES 17
 
+/*
+ * The number of the highest elfnote defined.
+ */
+#define XEN_ELFNOTE_MAX XEN_ELFNOTE_SUPPORTED_FEATURES
+
 #endif /* __XEN_PUBLIC_ELFNOTE_H__ */
 
 /*
diff --git a/include/xen/interface/io/vscsiif.h b/include/xen/interface/io/vscsiif.h
new file mode 100644 (file)
index 0000000..d07d7ac
--- /dev/null
@@ -0,0 +1,229 @@
+/******************************************************************************
+ * vscsiif.h
+ *
+ * Based on the blkif.h code.
+ *
+ * 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.
+ *
+ * Copyright(c) FUJITSU Limited 2008.
+ */
+
+#ifndef __XEN__PUBLIC_IO_SCSI_H__
+#define __XEN__PUBLIC_IO_SCSI_H__
+
+#include "ring.h"
+#include "../grant_table.h"
+
+/*
+ * Feature and Parameter Negotiation
+ * =================================
+ * The two halves of a Xen pvSCSI driver utilize nodes within the XenStore to
+ * communicate capabilities and to negotiate operating parameters.  This
+ * section enumerates these nodes which reside in the respective front and
+ * backend portions of the XenStore, following the XenBus convention.
+ *
+ * Any specified default value is in effect if the corresponding XenBus node
+ * is not present in the XenStore.
+ *
+ * XenStore nodes in sections marked "PRIVATE" are solely for use by the
+ * driver side whose XenBus tree contains them.
+ *
+ *****************************************************************************
+ *                            Backend XenBus Nodes
+ *****************************************************************************
+ *
+ *------------------ Backend Device Identification (PRIVATE) ------------------
+ *
+ * p-devname
+ *      Values:         string
+ *
+ *      A free string used to identify the physical device (e.g. a disk name).
+ *
+ * p-dev
+ *      Values:         string
+ *
+ *      A string specifying the backend device: either a 4-tuple "h:c:t:l"
+ *      (host, controller, target, lun, all integers), or a WWN (e.g.
+ *      "naa.60014054ac780582").
+ *
+ * v-dev
+ *      Values:         string
+ *
+ *      A string specifying the frontend device in form of a 4-tuple "h:c:t:l"
+ *      (host, controller, target, lun, all integers).
+ *
+ *--------------------------------- Features ---------------------------------
+ *
+ * feature-sg-grant
+ *      Values:         unsigned [VSCSIIF_SG_TABLESIZE...65535]
+ *      Default Value:  0
+ *
+ *      Specifies the maximum number of scatter/gather elements in grant pages
+ *      supported. If not set, the backend supports up to VSCSIIF_SG_TABLESIZE
+ *      SG elements specified directly in the request.
+ *
+ *****************************************************************************
+ *                            Frontend XenBus Nodes
+ *****************************************************************************
+ *
+ *----------------------- Request Transport Parameters -----------------------
+ *
+ * event-channel
+ *      Values:         unsigned
+ *
+ *      The identifier of the Xen event channel used to signal activity
+ *      in the ring buffer.
+ *
+ * ring-ref
+ *      Values:         unsigned
+ *
+ *      The Xen grant reference granting permission for the backend to map
+ *      the sole page in a single page sized ring buffer.
+ *
+ * protocol
+ *      Values:         string (XEN_IO_PROTO_ABI_*)
+ *      Default Value:  XEN_IO_PROTO_ABI_NATIVE
+ *
+ *      The machine ABI rules governing the format of all ring request and
+ *      response structures.
+ */
+
+/* Requests from the frontend to the backend */
+
+/*
+ * Request a SCSI operation specified via a CDB in vscsiif_request.cmnd.
+ * The target is specified via channel, id and lun.
+ *
+ * The operation to be performed is specified via a CDB in cmnd[], the length
+ * of the CDB is in cmd_len. sc_data_direction specifies the direction of data
+ * (to the device, from the device, or none at all).
+ *
+ * If data is to be transferred to or from the device the buffer(s) in the
+ * guest memory is/are specified via one or multiple scsiif_request_segment
+ * descriptors each specifying a memory page via a grant_ref_t, a offset into
+ * the page and the length of the area in that page. All scsiif_request_segment
+ * areas concatenated form the resulting data buffer used by the operation.
+ * If the number of scsiif_request_segment areas is not too large (less than
+ * or equal VSCSIIF_SG_TABLESIZE) the areas can be specified directly in the
+ * seg[] array and the number of valid scsiif_request_segment elements is to be
+ * set in nr_segments.
+ *
+ * If "feature-sg-grant" in the Xenstore is set it is possible to specify more
+ * than VSCSIIF_SG_TABLESIZE scsiif_request_segment elements via indirection.
+ * The maximum number of allowed scsiif_request_segment elements is the value
+ * of the "feature-sg-grant" entry from Xenstore. When using indirection the
+ * seg[] array doesn't contain specifications of the data buffers, but
+ * references to scsiif_request_segment arrays, which in turn reference the
+ * data buffers. While nr_segments holds the number of populated seg[] entries
+ * (plus the set VSCSIIF_SG_GRANT bit), the number of scsiif_request_segment
+ * elements referencing the target data buffers is calculated from the lengths
+ * of the seg[] elements (the sum of all valid seg[].length divided by the
+ * size of one scsiif_request_segment structure).
+ */
+#define VSCSIIF_ACT_SCSI_CDB           1
+
+/*
+ * Request abort of a running operation for the specified target given by
+ * channel, id, lun and the operation's rqid in ref_rqid.
+ */
+#define VSCSIIF_ACT_SCSI_ABORT         2
+
+/*
+ * Request a device reset of the specified target (channel and id).
+ */
+#define VSCSIIF_ACT_SCSI_RESET         3
+
+/*
+ * Preset scatter/gather elements for a following request. Deprecated.
+ * Keeping the define only to avoid usage of the value "4" for other actions.
+ */
+#define VSCSIIF_ACT_SCSI_SG_PRESET     4
+
+/*
+ * Maximum scatter/gather segments per request.
+ *
+ * Considering balance between allocating at least 16 "vscsiif_request"
+ * structures on one page (4096 bytes) and the number of scatter/gather
+ * elements needed, we decided to use 26 as a magic number.
+ *
+ * If "feature-sg-grant" is set, more scatter/gather elements can be specified
+ * by placing them in one or more (up to VSCSIIF_SG_TABLESIZE) granted pages.
+ * In this case the vscsiif_request seg elements don't contain references to
+ * the user data, but to the SG elements referencing the user data.
+ */
+#define VSCSIIF_SG_TABLESIZE           26
+
+/*
+ * based on Linux kernel 2.6.18, still valid
+ * Changing these values requires support of multiple protocols via the rings
+ * as "old clients" will blindly use these values and the resulting structure
+ * sizes.
+ */
+#define VSCSIIF_MAX_COMMAND_SIZE       16
+#define VSCSIIF_SENSE_BUFFERSIZE       96
+
+struct scsiif_request_segment {
+       grant_ref_t gref;
+       uint16_t offset;
+       uint16_t length;
+};
+
+#define VSCSIIF_SG_PER_PAGE (PAGE_SIZE / sizeof(struct scsiif_request_segment))
+
+/* Size of one request is 252 bytes */
+struct vscsiif_request {
+       uint16_t rqid;          /* private guest value, echoed in resp  */
+       uint8_t act;            /* command between backend and frontend */
+       uint8_t cmd_len;        /* valid CDB bytes */
+
+       uint8_t cmnd[VSCSIIF_MAX_COMMAND_SIZE]; /* the CDB */
+       uint16_t timeout_per_command;   /* deprecated */
+       uint16_t channel, id, lun;      /* (virtual) device specification */
+       uint16_t ref_rqid;              /* command abort reference */
+       uint8_t sc_data_direction;      /* for DMA_TO_DEVICE(1)
+                                          DMA_FROM_DEVICE(2)
+                                          DMA_NONE(3) requests */
+       uint8_t nr_segments;            /* Number of pieces of scatter-gather */
+/*
+ * flag in nr_segments: SG elements via grant page
+ *
+ * If VSCSIIF_SG_GRANT is set, the low 7 bits of nr_segments specify the number
+ * of grant pages containing SG elements. Usable if "feature-sg-grant" set.
+ */
+#define VSCSIIF_SG_GRANT       0x80
+
+       struct scsiif_request_segment seg[VSCSIIF_SG_TABLESIZE];
+       uint32_t reserved[3];
+};
+
+/* Size of one response is 252 bytes */
+struct vscsiif_response {
+       uint16_t rqid;          /* identifies request */
+       uint8_t padding;
+       uint8_t sense_len;
+       uint8_t sense_buffer[VSCSIIF_SENSE_BUFFERSIZE];
+       int32_t rslt;
+       uint32_t residual_len;  /* request bufflen -
+                                  return the value from physical device */
+       uint32_t reserved[36];
+};
+
+DEFINE_RING_TYPES(vscsiif, struct vscsiif_request, struct vscsiif_response);
+
+#endif /*__XEN__PUBLIC_IO_SCSI_H__*/
index de082130ba4b3913b51ff1ddfdf9bbfef153ad6b..f68719f405af6b891ae45bac290650186b0b3e35 100644 (file)
@@ -3,6 +3,24 @@
  *
  * Guest OS interface to Xen.
  *
+ * 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.
+ *
  * Copyright (c) 2004, K A Fraser
  */
 
  * VIRTUAL INTERRUPTS
  *
  * Virtual interrupts that a guest OS may receive from Xen.
+ * In the side comments, 'V.' denotes a per-VCPU VIRQ while 'G.' denotes a
+ * global VIRQ. The former can be bound once per VCPU and cannot be re-bound.
+ * The latter can be allocated only once per guest: they must initially be
+ * allocated to VCPU0 but can subsequently be re-bound.
  */
-#define VIRQ_TIMER      0  /* Timebase update, and/or requested timeout.  */
-#define VIRQ_DEBUG      1  /* Request guest to dump debug info.           */
-#define VIRQ_CONSOLE    2  /* (DOM0) Bytes received on emergency console. */
-#define VIRQ_DOM_EXC    3  /* (DOM0) Exceptional event for some domain.   */
-#define VIRQ_DEBUGGER   6  /* (DOM0) A domain has paused for debugging.   */
-#define VIRQ_PCPU_STATE 9  /* (DOM0) PCPU state changed                   */
+#define VIRQ_TIMER      0  /* V. Timebase update, and/or requested timeout.  */
+#define VIRQ_DEBUG      1  /* V. Request guest to dump debug info.           */
+#define VIRQ_CONSOLE    2  /* G. (DOM0) Bytes received on emergency console. */
+#define VIRQ_DOM_EXC    3  /* G. (DOM0) Exceptional event for some domain.   */
+#define VIRQ_TBUF       4  /* G. (DOM0) Trace buffer has records available.  */
+#define VIRQ_DEBUGGER   6  /* G. (DOM0) A domain has paused for debugging.   */
+#define VIRQ_XENOPROF   7  /* V. XenOprofile interrupt: new sample available */
+#define VIRQ_CON_RING   8  /* G. (DOM0) Bytes received on console            */
+#define VIRQ_PCPU_STATE 9  /* G. (DOM0) PCPU state changed                   */
+#define VIRQ_MEM_EVENT  10 /* G. (DOM0) A memory event has occured           */
+#define VIRQ_XC_RESERVED 11 /* G. Reserved for XenClient                     */
+#define VIRQ_ENOMEM     12 /* G. (DOM0) Low on heap memory       */
 
 /* Architecture-specific VIRQ definitions. */
 #define VIRQ_ARCH_0    16
 #define VIRQ_ARCH_7    23
 
 #define NR_VIRQS       24
+
 /*
- * MMU-UPDATE REQUESTS
- *
- * HYPERVISOR_mmu_update() accepts a list of (ptr, val) pairs.
- * A foreigndom (FD) can be specified (or DOMID_SELF for none).
- * Where the FD has some effect, it is described below.
- * ptr[1:0] specifies the appropriate MMU_* command.
+ * enum neg_errnoval HYPERVISOR_mmu_update(const struct mmu_update reqs[],
+ *                                         unsigned count, unsigned *done_out,
+ *                                         unsigned foreigndom)
+ * @reqs is an array of mmu_update_t structures ((ptr, val) pairs).
+ * @count is the length of the above array.
+ * @pdone is an output parameter indicating number of completed operations
+ * @foreigndom[15:0]: FD, the expected owner of data pages referenced in this
+ *                    hypercall invocation. Can be DOMID_SELF.
+ * @foreigndom[31:16]: PFD, the expected owner of pagetable pages referenced
+ *                     in this hypercall invocation. The value of this field
+ *                     (x) encodes the PFD as follows:
+ *                     x == 0 => PFD == DOMID_SELF
+ *                     x != 0 => PFD == x - 1
  *
+ * Sub-commands: ptr[1:0] specifies the appropriate MMU_* command.
+ * -------------
  * ptr[1:0] == MMU_NORMAL_PT_UPDATE:
- * Updates an entry in a page table. If updating an L1 table, and the new
- * table entry is valid/present, the mapped frame must belong to the FD, if
- * an FD has been specified. If attempting to map an I/O page then the
- * caller assumes the privilege of the FD.
+ * Updates an entry in a page table belonging to PFD. If updating an L1 table,
+ * and the new table entry is valid/present, the mapped frame must belong to
+ * FD. If attempting to map an I/O page then the caller assumes the privilege
+ * of the FD.
  * FD == DOMID_IO: Permit /only/ I/O mappings, at the priv level of the caller.
  * FD == DOMID_XEN: Map restricted areas of Xen's heap space.
  * ptr[:2]  -- Machine address of the page-table entry to modify.
  * val      -- Value to write.
  *
+ * There also certain implicit requirements when using this hypercall. The
+ * pages that make up a pagetable must be mapped read-only in the guest.
+ * This prevents uncontrolled guest updates to the pagetable. Xen strictly
+ * enforces this, and will disallow any pagetable update which will end up
+ * mapping pagetable page RW, and will disallow using any writable page as a
+ * pagetable. In practice it means that when constructing a page table for a
+ * process, thread, etc, we MUST be very dilligient in following these rules:
+ *  1). Start with top-level page (PGD or in Xen language: L4). Fill out
+ *      the entries.
+ *  2). Keep on going, filling out the upper (PUD or L3), and middle (PMD
+ *      or L2).
+ *  3). Start filling out the PTE table (L1) with the PTE entries. Once
+ *      done, make sure to set each of those entries to RO (so writeable bit
+ *      is unset). Once that has been completed, set the PMD (L2) for this
+ *      PTE table as RO.
+ *  4). When completed with all of the PMD (L2) entries, and all of them have
+ *      been set to RO, make sure to set RO the PUD (L3). Do the same
+ *      operation on PGD (L4) pagetable entries that have a PUD (L3) entry.
+ *  5). Now before you can use those pages (so setting the cr3), you MUST also
+ *      pin them so that the hypervisor can verify the entries. This is done
+ *      via the HYPERVISOR_mmuext_op(MMUEXT_PIN_L4_TABLE, guest physical frame
+ *      number of the PGD (L4)). And this point the HYPERVISOR_mmuext_op(
+ *      MMUEXT_NEW_BASEPTR, guest physical frame number of the PGD (L4)) can be
+ *      issued.
+ * For 32-bit guests, the L4 is not used (as there is less pagetables), so
+ * instead use L3.
+ * At this point the pagetables can be modified using the MMU_NORMAL_PT_UPDATE
+ * hypercall. Also if so desired the OS can also try to write to the PTE
+ * and be trapped by the hypervisor (as the PTE entry is RO).
+ *
+ * To deallocate the pages, the operations are the reverse of the steps
+ * mentioned above. The argument is MMUEXT_UNPIN_TABLE for all levels and the
+ * pagetable MUST not be in use (meaning that the cr3 is not set to it).
+ *
  * ptr[1:0] == MMU_MACHPHYS_UPDATE:
  * Updates an entry in the machine->pseudo-physical mapping table.
  * ptr[:2]  -- Machine address within the frame whose mapping to modify.
  * ptr[1:0] == MMU_PT_UPDATE_PRESERVE_AD:
  * As MMU_NORMAL_PT_UPDATE above, but A/D bits currently in the PTE are ORed
  * with those in @val.
+ *
+ * @val is usually the machine frame number along with some attributes.
+ * The attributes by default follow the architecture defined bits. Meaning that
+ * if this is a X86_64 machine and four page table layout is used, the layout
+ * of val is:
+ *  - 63 if set means No execute (NX)
+ *  - 46-13 the machine frame number
+ *  - 12 available for guest
+ *  - 11 available for guest
+ *  - 10 available for guest
+ *  - 9 available for guest
+ *  - 8 global
+ *  - 7 PAT (PSE is disabled, must use hypercall to make 4MB or 2MB pages)
+ *  - 6 dirty
+ *  - 5 accessed
+ *  - 4 page cached disabled
+ *  - 3 page write through
+ *  - 2 userspace accessible
+ *  - 1 writeable
+ *  - 0 present
+ *
+ *  The one bits that does not fit with the default layout is the PAGE_PSE
+ *  also called PAGE_PAT). The MMUEXT_[UN]MARK_SUPER arguments to the
+ *  HYPERVISOR_mmuext_op serve as mechanism to set a pagetable to be 4MB
+ *  (or 2MB) instead of using the PAGE_PSE bit.
+ *
+ *  The reason that the PAGE_PSE (bit 7) is not being utilized is due to Xen
+ *  using it as the Page Attribute Table (PAT) bit - for details on it please
+ *  refer to Intel SDM 10.12. The PAT allows to set the caching attributes of
+ *  pages instead of using MTRRs.
+ *
+ *  The PAT MSR is as follows (it is a 64-bit value, each entry is 8 bits):
+ *                    PAT4                 PAT0
+ *  +-----+-----+----+----+----+-----+----+----+
+ *  | UC  | UC- | WC | WB | UC | UC- | WC | WB |  <= Linux
+ *  +-----+-----+----+----+----+-----+----+----+
+ *  | UC  | UC- | WT | WB | UC | UC- | WT | WB |  <= BIOS (default when machine boots)
+ *  +-----+-----+----+----+----+-----+----+----+
+ *  | rsv | rsv | WP | WC | UC | UC- | WT | WB |  <= Xen
+ *  +-----+-----+----+----+----+-----+----+----+
+ *
+ *  The lookup of this index table translates to looking up
+ *  Bit 7, Bit 4, and Bit 3 of val entry:
+ *
+ *  PAT/PSE (bit 7) ... PCD (bit 4) .. PWT (bit 3).
+ *
+ *  If all bits are off, then we are using PAT0. If bit 3 turned on,
+ *  then we are using PAT1, if bit 3 and bit 4, then PAT2..
+ *
+ *  As you can see, the Linux PAT1 translates to PAT4 under Xen. Which means
+ *  that if a guest that follows Linux's PAT setup and would like to set Write
+ *  Combined on pages it MUST use PAT4 entry. Meaning that Bit 7 (PAGE_PAT) is
+ *  set. For example, under Linux it only uses PAT0, PAT1, and PAT2 for the
+ *  caching as:
+ *
+ *   WB = none (so PAT0)
+ *   WC = PWT (bit 3 on)
+ *   UC = PWT | PCD (bit 3 and 4 are on).
+ *
+ * To make it work with Xen, it needs to translate the WC bit as so:
+ *
+ *  PWT (so bit 3 on) --> PAT (so bit 7 is on) and clear bit 3
+ *
+ * And to translate back it would:
+ *
+ * PAT (bit 7 on) --> PWT (bit 3 on) and clear bit 7.
  */
 #define MMU_NORMAL_PT_UPDATE      0 /* checked '*ptr = val'. ptr is MA.       */
 #define MMU_MACHPHYS_UPDATE       1 /* ptr = MA of frame to modify entry for  */
 /*
  * MMU EXTENDED OPERATIONS
  *
- * HYPERVISOR_mmuext_op() accepts a list of mmuext_op structures.
+ * enum neg_errnoval HYPERVISOR_mmuext_op(mmuext_op_t uops[],
+ *                                        unsigned int count,
+ *                                        unsigned int *pdone,
+ *                                        unsigned int foreigndom)
+ */
+/* HYPERVISOR_mmuext_op() accepts a list of mmuext_op structures.
  * A foreigndom (FD) can be specified (or DOMID_SELF for none).
  * Where the FD has some effect, it is described below.
  *
  * cmd: MMUEXT_FLUSH_CACHE
  * No additional arguments. Writes back and flushes cache contents.
  *
+ * cmd: MMUEXT_FLUSH_CACHE_GLOBAL
+ * No additional arguments. Writes back and flushes cache contents
+ * on all CPUs in the system.
+ *
  * cmd: MMUEXT_SET_LDT
  * linear_addr: Linear address of LDT base (NB. must be page-aligned).
  * nr_ents: Number of entries in LDT.
+ *
+ * cmd: MMUEXT_CLEAR_PAGE
+ * mfn: Machine frame number to be cleared.
+ *
+ * cmd: MMUEXT_COPY_PAGE
+ * mfn: Machine frame number of the destination page.
+ * src_mfn: Machine frame number of the source page.
+ *
+ * cmd: MMUEXT_[UN]MARK_SUPER
+ * mfn: Machine frame number of head of superpage to be [un]marked.
  */
 #define MMUEXT_PIN_L1_TABLE      0
 #define MMUEXT_PIN_L2_TABLE      1
 #define MMUEXT_FLUSH_CACHE      12
 #define MMUEXT_SET_LDT          13
 #define MMUEXT_NEW_USER_BASEPTR 15
+#define MMUEXT_CLEAR_PAGE       16
+#define MMUEXT_COPY_PAGE        17
+#define MMUEXT_FLUSH_CACHE_GLOBAL 18
+#define MMUEXT_MARK_SUPER       19
+#define MMUEXT_UNMARK_SUPER     20
 
 #ifndef __ASSEMBLY__
 struct mmuext_op {
        unsigned int cmd;
        union {
-               /* [UN]PIN_TABLE, NEW_BASEPTR, NEW_USER_BASEPTR */
+               /* [UN]PIN_TABLE, NEW_BASEPTR, NEW_USER_BASEPTR
+                * CLEAR_PAGE, COPY_PAGE, [UN]MARK_SUPER */
                xen_pfn_t mfn;
                /* INVLPG_LOCAL, INVLPG_ALL, SET_LDT */
                unsigned long linear_addr;
@@ -198,6 +361,8 @@ struct mmuext_op {
                unsigned int nr_ents;
                /* TLB_FLUSH_MULTI, INVLPG_MULTI */
                void *vcpumask;
+               /* COPY_PAGE */
+               xen_pfn_t src_mfn;
        } arg2;
 };
 DEFINE_GUEST_HANDLE_STRUCT(mmuext_op);
@@ -225,10 +390,23 @@ DEFINE_GUEST_HANDLE_STRUCT(mmuext_op);
  */
 #define VMASST_CMD_enable                0
 #define VMASST_CMD_disable               1
+
+/* x86/32 guests: simulate full 4GB segment limits. */
 #define VMASST_TYPE_4gb_segments         0
+
+/* x86/32 guests: trap (vector 15) whenever above vmassist is used. */
 #define VMASST_TYPE_4gb_segments_notify  1
+
+/*
+ * x86 guests: support writes to bottom-level PTEs.
+ * NB1. Page-directory entries cannot be written.
+ * NB2. Guest must continue to remove all writable mappings of PTEs.
+ */
 #define VMASST_TYPE_writable_pagetables  2
+
+/* x86/PAE guests: support PDPTs above 4GB. */
 #define VMASST_TYPE_pae_extended_cr3     3
+
 #define MAX_VMASST_TYPE 3
 
 #ifndef __ASSEMBLY__
@@ -260,6 +438,15 @@ typedef uint16_t domid_t;
  */
 #define DOMID_XEN  (0x7FF2U)
 
+/* DOMID_COW is used as the owner of sharable pages */
+#define DOMID_COW  (0x7FF3U)
+
+/* DOMID_INVALID is used to identify pages with unknown owner. */
+#define DOMID_INVALID (0x7FF4U)
+
+/* Idle domain. */
+#define DOMID_IDLE (0x7FFFU)
+
 /*
  * Send an array of these to HYPERVISOR_mmu_update().
  * NB. The fields are natural pointer/address size for this architecture.
@@ -272,7 +459,9 @@ DEFINE_GUEST_HANDLE_STRUCT(mmu_update);
 
 /*
  * Send an array of these to HYPERVISOR_multicall().
- * NB. The fields are natural register size for this architecture.
+ * NB. The fields are logically the natural register size for this
+ * architecture. In cases where xen_ulong_t is larger than this then
+ * any unused bits in the upper portion must be zero.
  */
 struct multicall_entry {
     xen_ulong_t op;
@@ -442,8 +631,48 @@ struct start_info {
        unsigned long mod_start;    /* VIRTUAL address of pre-loaded module.  */
        unsigned long mod_len;      /* Size (bytes) of pre-loaded module.     */
        int8_t cmd_line[MAX_GUEST_CMDLINE];
+       /* The pfn range here covers both page table and p->m table frames.   */
+       unsigned long first_p2m_pfn;/* 1st pfn forming initial P->M table.    */
+       unsigned long nr_p2m_frames;/* # of pfns forming initial P->M table.  */
 };
 
+/* These flags are passed in the 'flags' field of start_info_t. */
+#define SIF_PRIVILEGED    (1<<0)  /* Is the domain privileged? */
+#define SIF_INITDOMAIN    (1<<1)  /* Is this the initial control domain? */
+#define SIF_MULTIBOOT_MOD (1<<2)  /* Is mod_start a multiboot module? */
+#define SIF_MOD_START_PFN (1<<3)  /* Is mod_start a PFN? */
+#define SIF_PM_MASK       (0xFF<<8) /* reserve 1 byte for xen-pm options */
+
+/*
+ * A multiboot module is a package containing modules very similar to a
+ * multiboot module array. The only differences are:
+ * - the array of module descriptors is by convention simply at the beginning
+ *   of the multiboot module,
+ * - addresses in the module descriptors are based on the beginning of the
+ *   multiboot module,
+ * - the number of modules is determined by a termination descriptor that has
+ *   mod_start == 0.
+ *
+ * This permits to both build it statically and reference it in a configuration
+ * file, and let the PV guest easily rebase the addresses to virtual addresses
+ * and at the same time count the number of modules.
+ */
+struct xen_multiboot_mod_list {
+       /* Address of first byte of the module */
+       uint32_t mod_start;
+       /* Address of last byte of the module (inclusive) */
+       uint32_t mod_end;
+       /* Address of zero-terminated command line */
+       uint32_t cmdline;
+       /* Unused, must be zero */
+       uint32_t pad;
+};
+/*
+ * The console structure in start_info.console.dom0
+ *
+ * This structure includes a variety of information required to
+ * have a working VGA/VESA console.
+ */
 struct dom0_vga_console_info {
        uint8_t video_type;
 #define XEN_VGATYPE_TEXT_MODE_3 0x03
@@ -484,11 +713,6 @@ struct dom0_vga_console_info {
        } u;
 };
 
-/* These flags are passed in the 'flags' field of start_info_t. */
-#define SIF_PRIVILEGED    (1<<0)  /* Is the domain privileged? */
-#define SIF_INITDOMAIN    (1<<1)  /* Is this the initial control domain? */
-#define SIF_PM_MASK       (0xFF<<8) /* reserve 1 byte for xen-pm options */
-
 typedef uint64_t cpumap_t;
 
 typedef uint8_t xen_domain_handle_t[16];
index 0324c6d340c19df3ef67d03b3307ce8ac082730c..b78f21caf55aa074d3f883d0a0269f1c7d268339 100644 (file)
@@ -86,6 +86,7 @@ struct xenbus_device_id
 
 /* A xenbus driver. */
 struct xenbus_driver {
+       const char *name;       /* defaults to ids[0].devicetype */
        const struct xenbus_device_id *ids;
        int (*probe)(struct xenbus_device *dev,
                     const struct xenbus_device_id *id);
@@ -100,20 +101,22 @@ struct xenbus_driver {
        int (*is_ready)(struct xenbus_device *dev);
 };
 
-#define DEFINE_XENBUS_DRIVER(var, drvname, methods...)         \
-struct xenbus_driver var ## _driver = {                                \
-       .driver.name = drvname + 0 ?: var ## _ids->devicetype,  \
-       .driver.owner = THIS_MODULE,                            \
-       .ids = var ## _ids, ## methods                          \
-}
-
 static inline struct xenbus_driver *to_xenbus_driver(struct device_driver *drv)
 {
        return container_of(drv, struct xenbus_driver, driver);
 }
 
-int __must_check xenbus_register_frontend(struct xenbus_driver *);
-int __must_check xenbus_register_backend(struct xenbus_driver *);
+int __must_check __xenbus_register_frontend(struct xenbus_driver *drv,
+                                           struct module *owner,
+                                           const char *mod_name);
+int __must_check __xenbus_register_backend(struct xenbus_driver *drv,
+                                          struct module *owner,
+                                          const char *mod_name);
+
+#define xenbus_register_frontend(drv) \
+       __xenbus_register_frontend(drv, THIS_MODULE, KBUILD_MODNAME);
+#define xenbus_register_backend(drv) \
+       __xenbus_register_backend(drv, THIS_MODULE, KBUILD_MODNAME);
 
 void xenbus_unregister_driver(struct xenbus_driver *drv);
 
index e25a82a291a63febbb4d19f143cb21b00e70317f..d2355812ba48a7e1f52855ad9c431878ee32f175 100644 (file)
@@ -889,17 +889,6 @@ config ARCH_SUPPORTS_INT128
 config ARCH_WANT_NUMA_VARIABLE_LOCALITY
        bool
 
-#
-# For architectures that are willing to define _PAGE_NUMA as _PAGE_PROTNONE
-config ARCH_WANTS_PROT_NUMA_PROT_NONE
-       bool
-
-config ARCH_USES_NUMA_PROT_NONE
-       bool
-       default y
-       depends on ARCH_WANTS_PROT_NUMA_PROT_NONE
-       depends on NUMA_BALANCING
-
 config NUMA_BALANCING_DEFAULT_ENABLED
        bool "Automatically enable NUMA aware memory/task placement"
        default y
index bb1aed928f21391b63493112c6bb1a1eae39077f..8af2f1abfe38d5f36eade88169f4f9cd04d8ec5a 100644 (file)
@@ -577,7 +577,6 @@ asmlinkage __visible void __init start_kernel(void)
                local_irq_disable();
        idr_init_cache();
        rcu_init();
-       tick_nohz_init();
        context_tracking_init();
        radix_tree_init();
        /* init some links before init_ISA_irqs() */
index b4c667d22e7930968dfd65f7f6b5f6924f52f5bd..33738ef972f3b917f0820e3c751978e7956b1d4a 100644 (file)
@@ -472,7 +472,6 @@ static void do_acct_process(struct bsd_acct_struct *acct)
        acct_t ac;
        unsigned long flim;
        const struct cred *orig_cred;
-       struct pid_namespace *ns = acct->ns;
        struct file *file = acct->file;
 
        /*
@@ -500,10 +499,15 @@ static void do_acct_process(struct bsd_acct_struct *acct)
        ac.ac_gid16 = ac.ac_gid;
 #endif
 #if ACCT_VERSION == 3
-       ac.ac_pid = task_tgid_nr_ns(current, ns);
-       rcu_read_lock();
-       ac.ac_ppid = task_tgid_nr_ns(rcu_dereference(current->real_parent), ns);
-       rcu_read_unlock();
+       {
+               struct pid_namespace *ns = acct->ns;
+
+               ac.ac_pid = task_tgid_nr_ns(current, ns);
+               rcu_read_lock();
+               ac.ac_ppid = task_tgid_nr_ns(rcu_dereference(current->real_parent),
+                                            ns);
+               rcu_read_unlock();
+       }
 #endif
        /*
         * Get freeze protection. If the fs is frozen, just skip the write
index 61f023ce0228feca468a502eeee91950f656c2e6..4c3773c0bf630c1de55a6f5c24bd148c53f1186e 100644 (file)
@@ -115,7 +115,7 @@ static void async_run_entry_fn(struct work_struct *work)
 
        /* 1) run (and print duration) */
        if (initcall_debug && system_state == SYSTEM_BOOTING) {
-               printk(KERN_DEBUG "calling  %lli_%pF @ %i\n",
+               pr_debug("calling  %lli_%pF @ %i\n",
                        (long long)entry->cookie,
                        entry->func, task_pid_nr(current));
                calltime = ktime_get();
@@ -124,7 +124,7 @@ static void async_run_entry_fn(struct work_struct *work)
        if (initcall_debug && system_state == SYSTEM_BOOTING) {
                rettime = ktime_get();
                delta = ktime_sub(rettime, calltime);
-               printk(KERN_DEBUG "initcall %lli_%pF returned 0 after %lld usecs\n",
+               pr_debug("initcall %lli_%pF returned 0 after %lld usecs\n",
                        (long long)entry->cookie,
                        entry->func,
                        (long long)ktime_to_ns(delta) >> 10);
@@ -285,7 +285,7 @@ void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain
        ktime_t uninitialized_var(starttime), delta, endtime;
 
        if (initcall_debug && system_state == SYSTEM_BOOTING) {
-               printk(KERN_DEBUG "async_waiting @ %i\n", task_pid_nr(current));
+               pr_debug("async_waiting @ %i\n", task_pid_nr(current));
                starttime = ktime_get();
        }
 
@@ -295,7 +295,7 @@ void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain
                endtime = ktime_get();
                delta = ktime_sub(endtime, starttime);
 
-               printk(KERN_DEBUG "async_continuing @ %i after %lli usec\n",
+               pr_debug("async_continuing @ %i after %lli usec\n",
                        task_pid_nr(current),
                        (long long)ktime_to_ns(delta) >> 10);
        }
index 3a73f995a81e6167659a7b488f23a4feab3d2787..136eceadeed138b71fd55dfed22a9850b38dd776 100644 (file)
@@ -185,7 +185,6 @@ static int need_forkexit_callback __read_mostly;
 static struct cftype cgroup_dfl_base_files[];
 static struct cftype cgroup_legacy_base_files[];
 
-static void cgroup_put(struct cgroup *cgrp);
 static int rebind_subsystems(struct cgroup_root *dst_root,
                             unsigned int ss_mask);
 static int cgroup_destroy_locked(struct cgroup *cgrp);
@@ -195,7 +194,6 @@ static void css_release(struct percpu_ref *ref);
 static void kill_css(struct cgroup_subsys_state *css);
 static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
                              bool is_add);
-static void cgroup_pidlist_destroy_all(struct cgroup *cgrp);
 
 /* IDR wrappers which synchronize using cgroup_idr_lock */
 static int cgroup_idr_alloc(struct idr *idr, void *ptr, int start, int end,
@@ -331,14 +329,6 @@ bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor)
        return false;
 }
 
-static int cgroup_is_releasable(const struct cgroup *cgrp)
-{
-       const int bits =
-               (1 << CGRP_RELEASABLE) |
-               (1 << CGRP_NOTIFY_ON_RELEASE);
-       return (cgrp->flags & bits) == bits;
-}
-
 static int notify_on_release(const struct cgroup *cgrp)
 {
        return test_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags);
@@ -394,12 +384,7 @@ static int notify_on_release(const struct cgroup *cgrp)
                        ;                                               \
                else
 
-/* the list of cgroups eligible for automatic release. Protected by
- * release_list_lock */
-static LIST_HEAD(release_list);
-static DEFINE_RAW_SPINLOCK(release_list_lock);
 static void cgroup_release_agent(struct work_struct *work);
-static DECLARE_WORK(release_agent_work, cgroup_release_agent);
 static void check_for_release(struct cgroup *cgrp);
 
 /*
@@ -498,7 +483,7 @@ static unsigned long css_set_hash(struct cgroup_subsys_state *css[])
        return key;
 }
 
-static void put_css_set_locked(struct css_set *cset, bool taskexit)
+static void put_css_set_locked(struct css_set *cset)
 {
        struct cgrp_cset_link *link, *tmp_link;
        struct cgroup_subsys *ss;
@@ -524,11 +509,7 @@ static void put_css_set_locked(struct css_set *cset, bool taskexit)
                /* @cgrp can't go away while we're holding css_set_rwsem */
                if (list_empty(&cgrp->cset_links)) {
                        cgroup_update_populated(cgrp, false);
-                       if (notify_on_release(cgrp)) {
-                               if (taskexit)
-                                       set_bit(CGRP_RELEASABLE, &cgrp->flags);
-                               check_for_release(cgrp);
-                       }
+                       check_for_release(cgrp);
                }
 
                kfree(link);
@@ -537,7 +518,7 @@ static void put_css_set_locked(struct css_set *cset, bool taskexit)
        kfree_rcu(cset, rcu_head);
 }
 
-static void put_css_set(struct css_set *cset, bool taskexit)
+static void put_css_set(struct css_set *cset)
 {
        /*
         * Ensure that the refcount doesn't hit zero while any readers
@@ -548,7 +529,7 @@ static void put_css_set(struct css_set *cset, bool taskexit)
                return;
 
        down_write(&css_set_rwsem);
-       put_css_set_locked(cset, taskexit);
+       put_css_set_locked(cset);
        up_write(&css_set_rwsem);
 }
 
@@ -969,14 +950,6 @@ static struct cgroup *task_cgroup_from_root(struct task_struct *task,
  * knows that the cgroup won't be removed, as cgroup_rmdir()
  * needs that mutex.
  *
- * The fork and exit callbacks cgroup_fork() and cgroup_exit(), don't
- * (usually) take cgroup_mutex.  These are the two most performance
- * critical pieces of code here.  The exception occurs on cgroup_exit(),
- * when a task in a notify_on_release cgroup exits.  Then cgroup_mutex
- * is taken, and if the cgroup count is zero, a usermode call made
- * to the release agent with the name of the cgroup (path relative to
- * the root of cgroup file system) as the argument.
- *
  * A cgroup can only be deleted if both its 'count' of using tasks
  * is zero, and its list of 'children' cgroups is empty.  Since all
  * tasks in the system use _some_ cgroup, and since there is always at
@@ -1587,7 +1560,6 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
        INIT_LIST_HEAD(&cgrp->self.sibling);
        INIT_LIST_HEAD(&cgrp->self.children);
        INIT_LIST_HEAD(&cgrp->cset_links);
-       INIT_LIST_HEAD(&cgrp->release_list);
        INIT_LIST_HEAD(&cgrp->pidlists);
        mutex_init(&cgrp->pidlist_mutex);
        cgrp->self.cgroup = cgrp;
@@ -1597,6 +1569,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
                INIT_LIST_HEAD(&cgrp->e_csets[ssid]);
 
        init_waitqueue_head(&cgrp->offline_waitq);
+       INIT_WORK(&cgrp->release_agent_work, cgroup_release_agent);
 }
 
 static void init_cgroup_root(struct cgroup_root *root,
@@ -1634,7 +1607,8 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned int ss_mask)
                goto out;
        root_cgrp->id = ret;
 
-       ret = percpu_ref_init(&root_cgrp->self.refcnt, css_release);
+       ret = percpu_ref_init(&root_cgrp->self.refcnt, css_release, 0,
+                             GFP_KERNEL);
        if (ret)
                goto out;
 
@@ -2052,8 +2026,7 @@ static void cgroup_task_migrate(struct cgroup *old_cgrp,
         * task. As trading it for new_cset is protected by cgroup_mutex,
         * we're safe to drop it here; it will be freed under RCU.
         */
-       set_bit(CGRP_RELEASABLE, &old_cgrp->flags);
-       put_css_set_locked(old_cset, false);
+       put_css_set_locked(old_cset);
 }
 
 /**
@@ -2074,7 +2047,7 @@ static void cgroup_migrate_finish(struct list_head *preloaded_csets)
                cset->mg_src_cgrp = NULL;
                cset->mg_dst_cset = NULL;
                list_del_init(&cset->mg_preload_node);
-               put_css_set_locked(cset, false);
+               put_css_set_locked(cset);
        }
        up_write(&css_set_rwsem);
 }
@@ -2168,8 +2141,8 @@ static int cgroup_migrate_prepare_dst(struct cgroup *dst_cgrp,
                if (src_cset == dst_cset) {
                        src_cset->mg_src_cgrp = NULL;
                        list_del_init(&src_cset->mg_preload_node);
-                       put_css_set(src_cset, false);
-                       put_css_set(dst_cset, false);
+                       put_css_set(src_cset);
+                       put_css_set(dst_cset);
                        continue;
                }
 
@@ -2178,7 +2151,7 @@ static int cgroup_migrate_prepare_dst(struct cgroup *dst_cgrp,
                if (list_empty(&dst_cset->mg_preload_node))
                        list_add(&dst_cset->mg_preload_node, &csets);
                else
-                       put_css_set(dst_cset, false);
+                       put_css_set(dst_cset);
        }
 
        list_splice_tail(&csets, preloaded_csets);
@@ -4173,7 +4146,6 @@ static u64 cgroup_read_notify_on_release(struct cgroup_subsys_state *css,
 static int cgroup_write_notify_on_release(struct cgroup_subsys_state *css,
                                          struct cftype *cft, u64 val)
 {
-       clear_bit(CGRP_RELEASABLE, &css->cgroup->flags);
        if (val)
                set_bit(CGRP_NOTIFY_ON_RELEASE, &css->cgroup->flags);
        else
@@ -4351,6 +4323,7 @@ static void css_free_work_fn(struct work_struct *work)
                /* cgroup free path */
                atomic_dec(&cgrp->root->nr_cgrps);
                cgroup_pidlist_destroy_all(cgrp);
+               cancel_work_sync(&cgrp->release_agent_work);
 
                if (cgroup_parent(cgrp)) {
                        /*
@@ -4510,7 +4483,7 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss,
 
        init_and_link_css(css, ss, cgrp);
 
-       err = percpu_ref_init(&css->refcnt, css_release);
+       err = percpu_ref_init(&css->refcnt, css_release, 0, GFP_KERNEL);
        if (err)
                goto err_free_css;
 
@@ -4583,7 +4556,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
                goto out_unlock;
        }
 
-       ret = percpu_ref_init(&cgrp->self.refcnt, css_release);
+       ret = percpu_ref_init(&cgrp->self.refcnt, css_release, 0, GFP_KERNEL);
        if (ret)
                goto out_free_cgrp;
 
@@ -4813,19 +4786,12 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
        for_each_css(css, ssid, cgrp)
                kill_css(css);
 
-       /* CSS_ONLINE is clear, remove from ->release_list for the last time */
-       raw_spin_lock(&release_list_lock);
-       if (!list_empty(&cgrp->release_list))
-               list_del_init(&cgrp->release_list);
-       raw_spin_unlock(&release_list_lock);
-
        /*
         * Remove @cgrp directory along with the base files.  @cgrp has an
         * extra ref on its kn.
         */
        kernfs_remove(cgrp->kn);
 
-       set_bit(CGRP_RELEASABLE, &cgroup_parent(cgrp)->flags);
        check_for_release(cgroup_parent(cgrp));
 
        /* put the base reference */
@@ -4842,13 +4808,10 @@ static int cgroup_rmdir(struct kernfs_node *kn)
        cgrp = cgroup_kn_lock_live(kn);
        if (!cgrp)
                return 0;
-       cgroup_get(cgrp);       /* for @kn->priv clearing */
 
        ret = cgroup_destroy_locked(cgrp);
 
        cgroup_kn_unlock(kn);
-
-       cgroup_put(cgrp);
        return ret;
 }
 
@@ -5052,12 +5015,9 @@ core_initcall(cgroup_wq_init);
  *  - Print task's cgroup paths into seq_file, one line for each hierarchy
  *  - Used for /proc/<pid>/cgroup.
  */
-
-/* TODO: Use a proper seq_file iterator */
-int proc_cgroup_show(struct seq_file *m, void *v)
+int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
+                    struct pid *pid, struct task_struct *tsk)
 {
-       struct pid *pid;
-       struct task_struct *tsk;
        char *buf, *path;
        int retval;
        struct cgroup_root *root;
@@ -5067,14 +5027,6 @@ int proc_cgroup_show(struct seq_file *m, void *v)
        if (!buf)
                goto out;
 
-       retval = -ESRCH;
-       pid = m->private;
-       tsk = get_pid_task(pid, PIDTYPE_PID);
-       if (!tsk)
-               goto out_free;
-
-       retval = 0;
-
        mutex_lock(&cgroup_mutex);
        down_read(&css_set_rwsem);
 
@@ -5104,11 +5056,10 @@ int proc_cgroup_show(struct seq_file *m, void *v)
                seq_putc(m, '\n');
        }
 
+       retval = 0;
 out_unlock:
        up_read(&css_set_rwsem);
        mutex_unlock(&cgroup_mutex);
-       put_task_struct(tsk);
-out_free:
        kfree(buf);
 out:
        return retval;
@@ -5179,7 +5130,7 @@ void cgroup_post_fork(struct task_struct *child)
        int i;
 
        /*
-        * This may race against cgroup_enable_task_cg_links().  As that
+        * This may race against cgroup_enable_task_cg_lists().  As that
         * function sets use_task_css_set_links before grabbing
         * tasklist_lock and we just went through tasklist_lock to add
         * @child, it's guaranteed that either we see the set
@@ -5194,7 +5145,7 @@ void cgroup_post_fork(struct task_struct *child)
         * when implementing operations which need to migrate all tasks of
         * a cgroup to another.
         *
-        * Note that if we lose to cgroup_enable_task_cg_links(), @child
+        * Note that if we lose to cgroup_enable_task_cg_lists(), @child
         * will remain in init_css_set.  This is safe because all tasks are
         * in the init_css_set before cg_links is enabled and there's no
         * operation which transfers all tasks out of init_css_set.
@@ -5278,30 +5229,14 @@ void cgroup_exit(struct task_struct *tsk)
        }
 
        if (put_cset)
-               put_css_set(cset, true);
+               put_css_set(cset);
 }
 
 static void check_for_release(struct cgroup *cgrp)
 {
-       if (cgroup_is_releasable(cgrp) && list_empty(&cgrp->cset_links) &&
-           !css_has_online_children(&cgrp->self)) {
-               /*
-                * Control Group is currently removeable. If it's not
-                * already queued for a userspace notification, queue
-                * it now
-                */
-               int need_schedule_work = 0;
-
-               raw_spin_lock(&release_list_lock);
-               if (!cgroup_is_dead(cgrp) &&
-                   list_empty(&cgrp->release_list)) {
-                       list_add(&cgrp->release_list, &release_list);
-                       need_schedule_work = 1;
-               }
-               raw_spin_unlock(&release_list_lock);
-               if (need_schedule_work)
-                       schedule_work(&release_agent_work);
-       }
+       if (notify_on_release(cgrp) && !cgroup_has_tasks(cgrp) &&
+           !css_has_online_children(&cgrp->self) && !cgroup_is_dead(cgrp))
+               schedule_work(&cgrp->release_agent_work);
 }
 
 /*
@@ -5329,52 +5264,39 @@ static void check_for_release(struct cgroup *cgrp)
  */
 static void cgroup_release_agent(struct work_struct *work)
 {
-       BUG_ON(work != &release_agent_work);
+       struct cgroup *cgrp =
+               container_of(work, struct cgroup, release_agent_work);
+       char *pathbuf = NULL, *agentbuf = NULL, *path;
+       char *argv[3], *envp[3];
+
        mutex_lock(&cgroup_mutex);
-       raw_spin_lock(&release_list_lock);
-       while (!list_empty(&release_list)) {
-               char *argv[3], *envp[3];
-               int i;
-               char *pathbuf = NULL, *agentbuf = NULL, *path;
-               struct cgroup *cgrp = list_entry(release_list.next,
-                                                   struct cgroup,
-                                                   release_list);
-               list_del_init(&cgrp->release_list);
-               raw_spin_unlock(&release_list_lock);
-               pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
-               if (!pathbuf)
-                       goto continue_free;
-               path = cgroup_path(cgrp, pathbuf, PATH_MAX);
-               if (!path)
-                       goto continue_free;
-               agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL);
-               if (!agentbuf)
-                       goto continue_free;
-
-               i = 0;
-               argv[i++] = agentbuf;
-               argv[i++] = path;
-               argv[i] = NULL;
-
-               i = 0;
-               /* minimal command environment */
-               envp[i++] = "HOME=/";
-               envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
-               envp[i] = NULL;
-
-               /* Drop the lock while we invoke the usermode helper,
-                * since the exec could involve hitting disk and hence
-                * be a slow process */
-               mutex_unlock(&cgroup_mutex);
-               call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
-               mutex_lock(&cgroup_mutex);
- continue_free:
-               kfree(pathbuf);
-               kfree(agentbuf);
-               raw_spin_lock(&release_list_lock);
-       }
-       raw_spin_unlock(&release_list_lock);
+
+       pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+       agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL);
+       if (!pathbuf || !agentbuf)
+               goto out;
+
+       path = cgroup_path(cgrp, pathbuf, PATH_MAX);
+       if (!path)
+               goto out;
+
+       argv[0] = agentbuf;
+       argv[1] = path;
+       argv[2] = NULL;
+
+       /* minimal command environment */
+       envp[0] = "HOME=/";
+       envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+       envp[2] = NULL;
+
+       mutex_unlock(&cgroup_mutex);
+       call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
+       goto out_free;
+out:
        mutex_unlock(&cgroup_mutex);
+out_free:
+       kfree(agentbuf);
+       kfree(pathbuf);
 }
 
 static int __init cgroup_disable(char *str)
@@ -5562,7 +5484,8 @@ static int cgroup_css_links_read(struct seq_file *seq, void *v)
 
 static u64 releasable_read(struct cgroup_subsys_state *css, struct cftype *cft)
 {
-       return test_bit(CGRP_RELEASABLE, &css->cgroup->flags);
+       return (!cgroup_has_tasks(css->cgroup) &&
+               !css_has_online_children(&css->cgroup->self));
 }
 
 static struct cftype debug_files[] =  {
index 52cb04c993b7282fb61d68e7a5223598841d9ff4..1f107c74087bc52f690d957dbe610472688b4c1b 100644 (file)
@@ -2730,10 +2730,9 @@ void __cpuset_memory_pressure_bump(void)
  *    and we take cpuset_mutex, keeping cpuset_attach() from changing it
  *    anyway.
  */
-int proc_cpuset_show(struct seq_file *m, void *unused_v)
+int proc_cpuset_show(struct seq_file *m, struct pid_namespace *ns,
+                    struct pid *pid, struct task_struct *tsk)
 {
-       struct pid *pid;
-       struct task_struct *tsk;
        char *buf, *p;
        struct cgroup_subsys_state *css;
        int retval;
@@ -2743,24 +2742,16 @@ int proc_cpuset_show(struct seq_file *m, void *unused_v)
        if (!buf)
                goto out;
 
-       retval = -ESRCH;
-       pid = m->private;
-       tsk = get_pid_task(pid, PIDTYPE_PID);
-       if (!tsk)
-               goto out_free;
-
        retval = -ENAMETOOLONG;
        rcu_read_lock();
        css = task_css(tsk, cpuset_cgrp_id);
        p = cgroup_path(css->cgroup, buf, PATH_MAX);
        rcu_read_unlock();
        if (!p)
-               goto out_put_task;
+               goto out_free;
        seq_puts(m, p);
        seq_putc(m, '\n');
        retval = 0;
-out_put_task:
-       put_task_struct(tsk);
 out_free:
        kfree(buf);
 out:
index 963bf139e2b244fa1a4e2efb9a697e02b0c8dfa5..b1c663593f5c9f082cb3dd997f94978a723b7599 100644 (file)
@@ -392,14 +392,9 @@ perf_cgroup_match(struct perf_event *event)
                                    event->cgrp->css.cgroup);
 }
 
-static inline void perf_put_cgroup(struct perf_event *event)
-{
-       css_put(&event->cgrp->css);
-}
-
 static inline void perf_detach_cgroup(struct perf_event *event)
 {
-       perf_put_cgroup(event);
+       css_put(&event->cgrp->css);
        event->cgrp = NULL;
 }
 
index a91e47d86de214613fd4a2f04d3e7dea7509a7a7..8c162d102740a1ab806a5fe37ddf70e045c29832 100644 (file)
@@ -601,9 +601,8 @@ static void check_mm(struct mm_struct *mm)
                        printk(KERN_ALERT "BUG: Bad rss-counter state "
                                          "mm:%p idx:%d val:%ld\n", mm, i, x);
        }
-
 #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
-       VM_BUG_ON(mm->pmd_huge_pte);
+       VM_BUG_ON_MM(mm->pmd_huge_pte, mm);
 #endif
 }
 
index d269cecdfbf08a48c3a24b1adf89a037eff7485d..225086b2652e9bee236d2c1a49262b56f2ea6972 100644 (file)
@@ -55,6 +55,9 @@ config GENERIC_IRQ_CHIP
 config IRQ_DOMAIN
        bool
 
+config HANDLE_DOMAIN_IRQ
+       bool
+
 config IRQ_DOMAIN_DEBUG
        bool "Expose hardware/virtual IRQ mapping via debugfs"
        depends on IRQ_DOMAIN && DEBUG_FS
index 6223fab9a9d22b7bedd5e6b1f23ccd8a0347d6d1..8fb52e9bddc1deb1334eab08d1b2cf75233c299e 100644 (file)
@@ -342,6 +342,31 @@ static bool irq_check_poll(struct irq_desc *desc)
        return irq_wait_for_poll(desc);
 }
 
+static bool irq_may_run(struct irq_desc *desc)
+{
+       unsigned int mask = IRQD_IRQ_INPROGRESS | IRQD_WAKEUP_ARMED;
+
+       /*
+        * If the interrupt is not in progress and is not an armed
+        * wakeup interrupt, proceed.
+        */
+       if (!irqd_has_set(&desc->irq_data, mask))
+               return true;
+
+       /*
+        * If the interrupt is an armed wakeup source, mark it pending
+        * and suspended, disable it and notify the pm core about the
+        * event.
+        */
+       if (irq_pm_check_wakeup(desc))
+               return false;
+
+       /*
+        * Handle a potential concurrent poll on a different core.
+        */
+       return irq_check_poll(desc);
+}
+
 /**
  *     handle_simple_irq - Simple and software-decoded IRQs.
  *     @irq:   the interrupt number
@@ -359,9 +384,8 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc)
 {
        raw_spin_lock(&desc->lock);
 
-       if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
-               if (!irq_check_poll(desc))
-                       goto out_unlock;
+       if (!irq_may_run(desc))
+               goto out_unlock;
 
        desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
        kstat_incr_irqs_this_cpu(irq, desc);
@@ -412,9 +436,8 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
        raw_spin_lock(&desc->lock);
        mask_ack_irq(desc);
 
-       if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
-               if (!irq_check_poll(desc))
-                       goto out_unlock;
+       if (!irq_may_run(desc))
+               goto out_unlock;
 
        desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
        kstat_incr_irqs_this_cpu(irq, desc);
@@ -485,9 +508,8 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
 
        raw_spin_lock(&desc->lock);
 
-       if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
-               if (!irq_check_poll(desc))
-                       goto out;
+       if (!irq_may_run(desc))
+               goto out;
 
        desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
        kstat_incr_irqs_this_cpu(irq, desc);
@@ -541,19 +563,23 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc)
        raw_spin_lock(&desc->lock);
 
        desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
+
+       if (!irq_may_run(desc)) {
+               desc->istate |= IRQS_PENDING;
+               mask_ack_irq(desc);
+               goto out_unlock;
+       }
+
        /*
-        * If we're currently running this IRQ, or its disabled,
-        * we shouldn't process the IRQ. Mark it pending, handle
-        * the necessary masking and go out
+        * If its disabled or no action available then mask it and get
+        * out of here.
         */
-       if (unlikely(irqd_irq_disabled(&desc->irq_data) ||
-                    irqd_irq_inprogress(&desc->irq_data) || !desc->action)) {
-               if (!irq_check_poll(desc)) {
-                       desc->istate |= IRQS_PENDING;
-                       mask_ack_irq(desc);
-                       goto out_unlock;
-               }
+       if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {
+               desc->istate |= IRQS_PENDING;
+               mask_ack_irq(desc);
+               goto out_unlock;
        }
+
        kstat_incr_irqs_this_cpu(irq, desc);
 
        /* Start handling the irq */
@@ -602,18 +628,21 @@ void handle_edge_eoi_irq(unsigned int irq, struct irq_desc *desc)
        raw_spin_lock(&desc->lock);
 
        desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
+
+       if (!irq_may_run(desc)) {
+               desc->istate |= IRQS_PENDING;
+               goto out_eoi;
+       }
+
        /*
-        * If we're currently running this IRQ, or its disabled,
-        * we shouldn't process the IRQ. Mark it pending, handle
-        * the necessary masking and go out
+        * If its disabled or no action available then mask it and get
+        * out of here.
         */
-       if (unlikely(irqd_irq_disabled(&desc->irq_data) ||
-                    irqd_irq_inprogress(&desc->irq_data) || !desc->action)) {
-               if (!irq_check_poll(desc)) {
-                       desc->istate |= IRQS_PENDING;
-                       goto out_eoi;
-               }
+       if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {
+               desc->istate |= IRQS_PENDING;
+               goto out_eoi;
        }
+
        kstat_incr_irqs_this_cpu(irq, desc);
 
        do {
index 099ea2e0eb8833676b3d234f2740487873bac093..4332d766619d1c700c600ec0678bc6c3ca47a6fa 100644 (file)
@@ -63,8 +63,8 @@ enum {
 
 extern int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
                unsigned long flags);
-extern void __disable_irq(struct irq_desc *desc, unsigned int irq, bool susp);
-extern void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume);
+extern void __disable_irq(struct irq_desc *desc, unsigned int irq);
+extern void __enable_irq(struct irq_desc *desc, unsigned int irq);
 
 extern int irq_startup(struct irq_desc *desc, bool resend);
 extern void irq_shutdown(struct irq_desc *desc);
@@ -194,3 +194,15 @@ static inline void kstat_incr_irqs_this_cpu(unsigned int irq, struct irq_desc *d
        __this_cpu_inc(*desc->kstat_irqs);
        __this_cpu_inc(kstat.irqs_sum);
 }
+
+#ifdef CONFIG_PM_SLEEP
+bool irq_pm_check_wakeup(struct irq_desc *desc);
+void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action);
+void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action);
+#else
+static inline bool irq_pm_check_wakeup(struct irq_desc *desc) { return false; }
+static inline void
+irq_pm_install_action(struct irq_desc *desc, struct irqaction *action) { }
+static inline void
+irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action) { }
+#endif
index 1487a123db5c82887c9dfd3c49833e48004e99a8..a1782f88f0af3049164be1962e4cfeeb1c776b5c 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/kernel_stat.h>
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
+#include <linux/irqdomain.h>
 
 #include "internals.h"
 
@@ -336,6 +337,47 @@ int generic_handle_irq(unsigned int irq)
 }
 EXPORT_SYMBOL_GPL(generic_handle_irq);
 
+#ifdef CONFIG_HANDLE_DOMAIN_IRQ
+/**
+ * __handle_domain_irq - Invoke the handler for a HW irq belonging to a domain
+ * @domain:    The domain where to perform the lookup
+ * @hwirq:     The HW irq number to convert to a logical one
+ * @lookup:    Whether to perform the domain lookup or not
+ * @regs:      Register file coming from the low-level handling code
+ *
+ * Returns:    0 on success, or -EINVAL if conversion has failed
+ */
+int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
+                       bool lookup, struct pt_regs *regs)
+{
+       struct pt_regs *old_regs = set_irq_regs(regs);
+       unsigned int irq = hwirq;
+       int ret = 0;
+
+       irq_enter();
+
+#ifdef CONFIG_IRQ_DOMAIN
+       if (lookup)
+               irq = irq_find_mapping(domain, hwirq);
+#endif
+
+       /*
+        * Some hardware gives randomly wrong interrupts.  Rather
+        * than crashing, do something sensible.
+        */
+       if (unlikely(!irq || irq >= nr_irqs)) {
+               ack_bad_irq(irq);
+               ret = -EINVAL;
+       } else {
+               generic_handle_irq(irq);
+       }
+
+       irq_exit();
+       set_irq_regs(old_regs);
+       return ret;
+}
+#endif
+
 /* Dynamic interrupt handling */
 
 /**
index 3dc6a61bf06a447acb70f67fc3942fc2286e32d2..0a9104b4608b8dc374769a7435b594f27a0b97ae 100644 (file)
@@ -382,14 +382,8 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)
 }
 #endif
 
-void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
+void __disable_irq(struct irq_desc *desc, unsigned int irq)
 {
-       if (suspend) {
-               if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND))
-                       return;
-               desc->istate |= IRQS_SUSPENDED;
-       }
-
        if (!desc->depth++)
                irq_disable(desc);
 }
@@ -401,7 +395,7 @@ static int __disable_irq_nosync(unsigned int irq)
 
        if (!desc)
                return -EINVAL;
-       __disable_irq(desc, irq, false);
+       __disable_irq(desc, irq);
        irq_put_desc_busunlock(desc, flags);
        return 0;
 }
@@ -442,20 +436,8 @@ void disable_irq(unsigned int irq)
 }
 EXPORT_SYMBOL(disable_irq);
 
-void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
+void __enable_irq(struct irq_desc *desc, unsigned int irq)
 {
-       if (resume) {
-               if (!(desc->istate & IRQS_SUSPENDED)) {
-                       if (!desc->action)
-                               return;
-                       if (!(desc->action->flags & IRQF_FORCE_RESUME))
-                               return;
-                       /* Pretend that it got disabled ! */
-                       desc->depth++;
-               }
-               desc->istate &= ~IRQS_SUSPENDED;
-       }
-
        switch (desc->depth) {
        case 0:
  err_out:
@@ -497,7 +479,7 @@ void enable_irq(unsigned int irq)
                 KERN_ERR "enable_irq before setup/request_irq: irq %u\n", irq))
                goto out;
 
-       __enable_irq(desc, irq, false);
+       __enable_irq(desc, irq);
 out:
        irq_put_desc_busunlock(desc, flags);
 }
@@ -1218,6 +1200,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
        new->irq = irq;
        *old_ptr = new;
 
+       irq_pm_install_action(desc, new);
+
        /* Reset broken irq detection when installing new handler */
        desc->irq_count = 0;
        desc->irqs_unhandled = 0;
@@ -1228,7 +1212,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
         */
        if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {
                desc->istate &= ~IRQS_SPURIOUS_DISABLED;
-               __enable_irq(desc, irq, false);
+               __enable_irq(desc, irq);
        }
 
        raw_spin_unlock_irqrestore(&desc->lock, flags);
@@ -1336,6 +1320,8 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
        /* Found it - now remove it from the list of entries: */
        *action_ptr = action->next;
 
+       irq_pm_remove_action(desc, action);
+
        /* If this was the last handler, shut down the IRQ line: */
        if (!desc->action) {
                irq_shutdown(desc);
index abcd6ca86cb76b56e5979613a1964c0db743b5d0..3ca5325927045572edfa7d3eebd79f01b2a4c29a 100644 (file)
 #include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
+#include <linux/suspend.h>
 #include <linux/syscore_ops.h>
 
 #include "internals.h"
 
+bool irq_pm_check_wakeup(struct irq_desc *desc)
+{
+       if (irqd_is_wakeup_armed(&desc->irq_data)) {
+               irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
+               desc->istate |= IRQS_SUSPENDED | IRQS_PENDING;
+               desc->depth++;
+               irq_disable(desc);
+               pm_system_wakeup();
+               return true;
+       }
+       return false;
+}
+
+/*
+ * Called from __setup_irq() with desc->lock held after @action has
+ * been installed in the action chain.
+ */
+void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action)
+{
+       desc->nr_actions++;
+
+       if (action->flags & IRQF_FORCE_RESUME)
+               desc->force_resume_depth++;
+
+       WARN_ON_ONCE(desc->force_resume_depth &&
+                    desc->force_resume_depth != desc->nr_actions);
+
+       if (action->flags & IRQF_NO_SUSPEND)
+               desc->no_suspend_depth++;
+
+       WARN_ON_ONCE(desc->no_suspend_depth &&
+                    desc->no_suspend_depth != desc->nr_actions);
+}
+
+/*
+ * Called from __free_irq() with desc->lock held after @action has
+ * been removed from the action chain.
+ */
+void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action)
+{
+       desc->nr_actions--;
+
+       if (action->flags & IRQF_FORCE_RESUME)
+               desc->force_resume_depth--;
+
+       if (action->flags & IRQF_NO_SUSPEND)
+               desc->no_suspend_depth--;
+}
+
+static bool suspend_device_irq(struct irq_desc *desc, int irq)
+{
+       if (!desc->action || desc->no_suspend_depth)
+               return false;
+
+       if (irqd_is_wakeup_set(&desc->irq_data)) {
+               irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
+               /*
+                * We return true here to force the caller to issue
+                * synchronize_irq(). We need to make sure that the
+                * IRQD_WAKEUP_ARMED is visible before we return from
+                * suspend_device_irqs().
+                */
+               return true;
+       }
+
+       desc->istate |= IRQS_SUSPENDED;
+       __disable_irq(desc, irq);
+
+       /*
+        * Hardware which has no wakeup source configuration facility
+        * requires that the non wakeup interrupts are masked at the
+        * chip level. The chip implementation indicates that with
+        * IRQCHIP_MASK_ON_SUSPEND.
+        */
+       if (irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
+               mask_irq(desc);
+       return true;
+}
+
 /**
  * suspend_device_irqs - disable all currently enabled interrupt lines
  *
- * During system-wide suspend or hibernation device drivers need to be prevented
- * from receiving interrupts and this function is provided for this purpose.
- * It marks all interrupt lines in use, except for the timer ones, as disabled
- * and sets the IRQS_SUSPENDED flag for each of them.
+ * During system-wide suspend or hibernation device drivers need to be
+ * prevented from receiving interrupts and this function is provided
+ * for this purpose.
+ *
+ * So we disable all interrupts and mark them IRQS_SUSPENDED except
+ * for those which are unused, those which are marked as not
+ * suspendable via an interrupt request with the flag IRQF_NO_SUSPEND
+ * set and those which are marked as active wakeup sources.
+ *
+ * The active wakeup sources are handled by the flow handler entry
+ * code which checks for the IRQD_WAKEUP_ARMED flag, suspends the
+ * interrupt and notifies the pm core about the wakeup.
  */
 void suspend_device_irqs(void)
 {
@@ -28,18 +116,36 @@ void suspend_device_irqs(void)
 
        for_each_irq_desc(irq, desc) {
                unsigned long flags;
+               bool sync;
 
                raw_spin_lock_irqsave(&desc->lock, flags);
-               __disable_irq(desc, irq, true);
+               sync = suspend_device_irq(desc, irq);
                raw_spin_unlock_irqrestore(&desc->lock, flags);
-       }
 
-       for_each_irq_desc(irq, desc)
-               if (desc->istate & IRQS_SUSPENDED)
+               if (sync)
                        synchronize_irq(irq);
+       }
 }
 EXPORT_SYMBOL_GPL(suspend_device_irqs);
 
+static void resume_irq(struct irq_desc *desc, int irq)
+{
+       irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
+
+       if (desc->istate & IRQS_SUSPENDED)
+               goto resume;
+
+       /* Force resume the interrupt? */
+       if (!desc->force_resume_depth)
+               return;
+
+       /* Pretend that it got disabled ! */
+       desc->depth++;
+resume:
+       desc->istate &= ~IRQS_SUSPENDED;
+       __enable_irq(desc, irq);
+}
+
 static void resume_irqs(bool want_early)
 {
        struct irq_desc *desc;
@@ -54,7 +160,7 @@ static void resume_irqs(bool want_early)
                        continue;
 
                raw_spin_lock_irqsave(&desc->lock, flags);
-               __enable_irq(desc, irq, true);
+               resume_irq(desc, irq);
                raw_spin_unlock_irqrestore(&desc->lock, flags);
        }
 }
@@ -93,38 +199,3 @@ void resume_device_irqs(void)
        resume_irqs(false);
 }
 EXPORT_SYMBOL_GPL(resume_device_irqs);
-
-/**
- * check_wakeup_irqs - check if any wake-up interrupts are pending
- */
-int check_wakeup_irqs(void)
-{
-       struct irq_desc *desc;
-       int irq;
-
-       for_each_irq_desc(irq, desc) {
-               /*
-                * Only interrupts which are marked as wakeup source
-                * and have not been disabled before the suspend check
-                * can abort suspend.
-                */
-               if (irqd_is_wakeup_set(&desc->irq_data)) {
-                       if (desc->depth == 1 && desc->istate & IRQS_PENDING)
-                               return -EBUSY;
-                       continue;
-               }
-               /*
-                * Check the non wakeup interrupts whether they need
-                * to be masked before finally going into suspend
-                * state. That's for hardware which has no wakeup
-                * source configuration facility. The chip
-                * implementation indicates that with
-                * IRQCHIP_MASK_ON_SUSPEND.
-                */
-               if (desc->istate & IRQS_SUSPENDED &&
-                   irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
-                       mask_irq(desc);
-       }
-
-       return 0;
-}
index e6bcbe756663abd64adf9c416eddee7a91c9a2c9..385b85aded199f5f2be0ee590278b559ac53b136 100644 (file)
@@ -115,8 +115,10 @@ bool irq_work_needs_cpu(void)
 
        raised = &__get_cpu_var(raised_list);
        lazy = &__get_cpu_var(lazy_list);
-       if (llist_empty(raised) && llist_empty(lazy))
-               return false;
+
+       if (llist_empty(raised) || arch_irq_work_has_interrupt())
+               if (llist_empty(lazy))
+                       return false;
 
        /* All work should have been flushed before going offline */
        WARN_ON_ONCE(cpu_is_offline(smp_processor_id()));
@@ -171,6 +173,15 @@ void irq_work_run(void)
 }
 EXPORT_SYMBOL_GPL(irq_work_run);
 
+void irq_work_tick(void)
+{
+       struct llist_head *raised = &__get_cpu_var(raised_list);
+
+       if (!llist_empty(raised) && !arch_irq_work_has_interrupt())
+               irq_work_run_list(raised);
+       irq_work_run_list(&__get_cpu_var(lazy_list));
+}
+
 /*
  * Synchronize against the irq_work @entry, ensures the entry is not
  * currently in use.
index ef483220e85564670600c9a3951e66fe20672bed..10e489c448fe4e934e2c203ca2aa7a8d0679bb5e 100644 (file)
@@ -369,7 +369,7 @@ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
 {
        struct task_struct *p;
 
-       p = kthread_create_on_node(threadfn, data, cpu_to_mem(cpu), namefmt,
+       p = kthread_create_on_node(threadfn, data, cpu_to_node(cpu), namefmt,
                                   cpu);
        if (IS_ERR(p))
                return p;
index e4e4121fa327d72e15f121697a493561ee6611b2..bbef57f5bdfdbc1764f015e2cafe3ec38ce79f0b 100644 (file)
@@ -302,6 +302,10 @@ config PM_GENERIC_DOMAINS_RUNTIME
        def_bool y
        depends on PM_RUNTIME && PM_GENERIC_DOMAINS
 
+config PM_GENERIC_DOMAINS_OF
+       def_bool y
+       depends on PM_GENERIC_DOMAINS && OF
+
 config CPU_PM
        bool
        depends on SUSPEND || CPU_IDLE
index 4ee194eb524b3663dd39dfa7c22eb9565321853b..7b323221b9ee9ad015556cf965ab8f211a1ff8c8 100644 (file)
@@ -129,6 +129,7 @@ int freeze_processes(void)
        if (!pm_freezing)
                atomic_inc(&system_freezing_cnt);
 
+       pm_wakeup_clear();
        printk("Freezing user space processes ... ");
        pm_freezing = true;
        error = try_to_freeze_tasks(true);
index f1604d8cf489a2e0cc689ab82ce6c3adfb82f693..791a61892bb536d5ce9296a1ceae1c1c1d0e7384 100644 (file)
@@ -725,6 +725,14 @@ static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn)
        clear_bit(bit, addr);
 }
 
+static void memory_bm_clear_current(struct memory_bitmap *bm)
+{
+       int bit;
+
+       bit = max(bm->cur.node_bit - 1, 0);
+       clear_bit(bit, bm->cur.node->data);
+}
+
 static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
 {
        void *addr;
@@ -1333,23 +1341,39 @@ static struct memory_bitmap copy_bm;
 
 void swsusp_free(void)
 {
-       struct zone *zone;
-       unsigned long pfn, max_zone_pfn;
+       unsigned long fb_pfn, fr_pfn;
 
-       for_each_populated_zone(zone) {
-               max_zone_pfn = zone_end_pfn(zone);
-               for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
-                       if (pfn_valid(pfn)) {
-                               struct page *page = pfn_to_page(pfn);
-
-                               if (swsusp_page_is_forbidden(page) &&
-                                   swsusp_page_is_free(page)) {
-                                       swsusp_unset_page_forbidden(page);
-                                       swsusp_unset_page_free(page);
-                                       __free_page(page);
-                               }
-                       }
+       if (!forbidden_pages_map || !free_pages_map)
+               goto out;
+
+       memory_bm_position_reset(forbidden_pages_map);
+       memory_bm_position_reset(free_pages_map);
+
+loop:
+       fr_pfn = memory_bm_next_pfn(free_pages_map);
+       fb_pfn = memory_bm_next_pfn(forbidden_pages_map);
+
+       /*
+        * Find the next bit set in both bitmaps. This is guaranteed to
+        * terminate when fb_pfn == fr_pfn == BM_END_OF_MAP.
+        */
+       do {
+               if (fb_pfn < fr_pfn)
+                       fb_pfn = memory_bm_next_pfn(forbidden_pages_map);
+               if (fr_pfn < fb_pfn)
+                       fr_pfn = memory_bm_next_pfn(free_pages_map);
+       } while (fb_pfn != fr_pfn);
+
+       if (fr_pfn != BM_END_OF_MAP && pfn_valid(fr_pfn)) {
+               struct page *page = pfn_to_page(fr_pfn);
+
+               memory_bm_clear_current(forbidden_pages_map);
+               memory_bm_clear_current(free_pages_map);
+               __free_page(page);
+               goto loop;
        }
+
+out:
        nr_copy_pages = 0;
        nr_meta_pages = 0;
        restore_pblist = NULL;
index 18c62195660f6c6c458d74346ee7979ec4388db4..4ca9a33ff62020e63d15219ce9f097611ebf6507 100644 (file)
@@ -145,18 +145,30 @@ static int platform_suspend_prepare(suspend_state_t state)
 }
 
 static int platform_suspend_prepare_late(suspend_state_t state)
+{
+       return state == PM_SUSPEND_FREEZE && freeze_ops->prepare ?
+               freeze_ops->prepare() : 0;
+}
+
+static int platform_suspend_prepare_noirq(suspend_state_t state)
 {
        return state != PM_SUSPEND_FREEZE && suspend_ops->prepare_late ?
                suspend_ops->prepare_late() : 0;
 }
 
-static void platform_suspend_wake(suspend_state_t state)
+static void platform_resume_noirq(suspend_state_t state)
 {
        if (state != PM_SUSPEND_FREEZE && suspend_ops->wake)
                suspend_ops->wake();
 }
 
-static void platform_suspend_finish(suspend_state_t state)
+static void platform_resume_early(suspend_state_t state)
+{
+       if (state == PM_SUSPEND_FREEZE && freeze_ops->restore)
+               freeze_ops->restore();
+}
+
+static void platform_resume_finish(suspend_state_t state)
 {
        if (state != PM_SUSPEND_FREEZE && suspend_ops->finish)
                suspend_ops->finish();
@@ -172,7 +184,7 @@ static int platform_suspend_begin(suspend_state_t state)
                return 0;
 }
 
-static void platform_suspend_end(suspend_state_t state)
+static void platform_resume_end(suspend_state_t state)
 {
        if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->end)
                freeze_ops->end();
@@ -180,7 +192,7 @@ static void platform_suspend_end(suspend_state_t state)
                suspend_ops->end();
 }
 
-static void platform_suspend_recover(suspend_state_t state)
+static void platform_recover(suspend_state_t state)
 {
        if (state != PM_SUSPEND_FREEZE && suspend_ops->recover)
                suspend_ops->recover();
@@ -265,12 +277,21 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
        if (error)
                goto Platform_finish;
 
-       error = dpm_suspend_end(PMSG_SUSPEND);
+       error = dpm_suspend_late(PMSG_SUSPEND);
        if (error) {
-               printk(KERN_ERR "PM: Some devices failed to power down\n");
+               printk(KERN_ERR "PM: late suspend of devices failed\n");
                goto Platform_finish;
        }
        error = platform_suspend_prepare_late(state);
+       if (error)
+               goto Devices_early_resume;
+
+       error = dpm_suspend_noirq(PMSG_SUSPEND);
+       if (error) {
+               printk(KERN_ERR "PM: noirq suspend of devices failed\n");
+               goto Platform_early_resume;
+       }
+       error = platform_suspend_prepare_noirq(state);
        if (error)
                goto Platform_wake;
 
@@ -318,11 +339,17 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
        enable_nonboot_cpus();
 
  Platform_wake:
-       platform_suspend_wake(state);
-       dpm_resume_start(PMSG_RESUME);
+       platform_resume_noirq(state);
+       dpm_resume_noirq(PMSG_RESUME);
+
+ Platform_early_resume:
+       platform_resume_early(state);
+
+ Devices_early_resume:
+       dpm_resume_early(PMSG_RESUME);
 
  Platform_finish:
-       platform_suspend_finish(state);
+       platform_resume_finish(state);
        return error;
 }
 
@@ -361,14 +388,16 @@ int suspend_devices_and_enter(suspend_state_t state)
        suspend_test_start();
        dpm_resume_end(PMSG_RESUME);
        suspend_test_finish("resume devices");
+       trace_suspend_resume(TPS("resume_console"), state, true);
        resume_console();
+       trace_suspend_resume(TPS("resume_console"), state, false);
 
  Close:
-       platform_suspend_end(state);
+       platform_resume_end(state);
        return error;
 
  Recover_platform:
-       platform_suspend_recover(state);
+       platform_recover(state);
        goto Resume_devices;
 }
 
index bd91bc177c93a65ca757c70467cd6e235af6a67c..084452e34a125ff24da375e6dce25c1224b46310 100644 (file)
@@ -22,6 +22,8 @@
 #define TEST_SUSPEND_SECONDS   10
 
 static unsigned long suspend_test_start_time;
+static u32 test_repeat_count_max = 1;
+static u32 test_repeat_count_current;
 
 void suspend_test_start(void)
 {
@@ -74,6 +76,7 @@ static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state)
        int                     status;
 
        /* this may fail if the RTC hasn't been initialized */
+repeat:
        status = rtc_read_time(rtc, &alm.time);
        if (status < 0) {
                printk(err_readtime, dev_name(&rtc->dev), status);
@@ -100,10 +103,21 @@ static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state)
        if (state == PM_SUSPEND_STANDBY) {
                printk(info_test, pm_states[state]);
                status = pm_suspend(state);
+               if (status < 0)
+                       state = PM_SUSPEND_FREEZE;
        }
+       if (state == PM_SUSPEND_FREEZE) {
+               printk(info_test, pm_states[state]);
+               status = pm_suspend(state);
+       }
+
        if (status < 0)
                printk(err_suspend, status);
 
+       test_repeat_count_current++;
+       if (test_repeat_count_current < test_repeat_count_max)
+               goto repeat;
+
        /* Some platforms can't detect that the alarm triggered the
         * wakeup, or (accordingly) disable it after it afterwards.
         * It's supposed to give oneshot behavior; cope.
@@ -137,16 +151,28 @@ static char warn_bad_state[] __initdata =
 static int __init setup_test_suspend(char *value)
 {
        int i;
+       char *repeat;
+       char *suspend_type;
 
-       /* "=mem" ==> "mem" */
+       /* example : "=mem[,N]" ==> "mem[,N]" */
        value++;
+       suspend_type = strsep(&value, ",");
+       if (!suspend_type)
+               return 0;
+
+       repeat = strsep(&value, ",");
+       if (repeat) {
+               if (kstrtou32(repeat, 0, &test_repeat_count_max))
+                       return 0;
+       }
+
        for (i = 0; pm_labels[i]; i++)
-               if (!strcmp(pm_labels[i], value)) {
+               if (!strcmp(pm_labels[i], suspend_type)) {
                        test_state_label = pm_labels[i];
                        return 0;
                }
 
-       printk(warn_bad_state, value);
+       printk(warn_bad_state, suspend_type);
        return 0;
 }
 __setup("test_suspend", setup_test_suspend);
index a3a9e240fcdba6662b5cccfe0e1f283a70910ffd..5925f5ae8dff0a3810761ef2255627b4ccdedea1 100644 (file)
@@ -104,6 +104,87 @@ int unregister_reboot_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL(unregister_reboot_notifier);
 
+/*
+ *     Notifier list for kernel code which wants to be called
+ *     to restart the system.
+ */
+static ATOMIC_NOTIFIER_HEAD(restart_handler_list);
+
+/**
+ *     register_restart_handler - Register function to be called to reset
+ *                                the system
+ *     @nb: Info about handler function to be called
+ *     @nb->priority:  Handler priority. Handlers should follow the
+ *                     following guidelines for setting priorities.
+ *                     0:      Restart handler of last resort,
+ *                             with limited restart capabilities
+ *                     128:    Default restart handler; use if no other
+ *                             restart handler is expected to be available,
+ *                             and/or if restart functionality is
+ *                             sufficient to restart the entire system
+ *                     255:    Highest priority restart handler, will
+ *                             preempt all other restart handlers
+ *
+ *     Registers a function with code to be called to restart the
+ *     system.
+ *
+ *     Registered functions will be called from machine_restart as last
+ *     step of the restart sequence (if the architecture specific
+ *     machine_restart function calls do_kernel_restart - see below
+ *     for details).
+ *     Registered functions are expected to restart the system immediately.
+ *     If more than one function is registered, the restart handler priority
+ *     selects which function will be called first.
+ *
+ *     Restart handlers are expected to be registered from non-architecture
+ *     code, typically from drivers. A typical use case would be a system
+ *     where restart functionality is provided through a watchdog. Multiple
+ *     restart handlers may exist; for example, one restart handler might
+ *     restart the entire system, while another only restarts the CPU.
+ *     In such cases, the restart handler which only restarts part of the
+ *     hardware is expected to register with low priority to ensure that
+ *     it only runs if no other means to restart the system is available.
+ *
+ *     Currently always returns zero, as atomic_notifier_chain_register()
+ *     always returns zero.
+ */
+int register_restart_handler(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&restart_handler_list, nb);
+}
+EXPORT_SYMBOL(register_restart_handler);
+
+/**
+ *     unregister_restart_handler - Unregister previously registered
+ *                                  restart handler
+ *     @nb: Hook to be unregistered
+ *
+ *     Unregisters a previously registered restart handler function.
+ *
+ *     Returns zero on success, or %-ENOENT on failure.
+ */
+int unregister_restart_handler(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&restart_handler_list, nb);
+}
+EXPORT_SYMBOL(unregister_restart_handler);
+
+/**
+ *     do_kernel_restart - Execute kernel restart handler call chain
+ *
+ *     Calls functions registered with register_restart_handler.
+ *
+ *     Expected to be called from machine_restart as last step of the restart
+ *     sequence.
+ *
+ *     Restarts the system immediately if a restart handler function has been
+ *     registered. Otherwise does nothing.
+ */
+void do_kernel_restart(char *cmd)
+{
+       atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
+}
+
 void migrate_to_reboot_cpu(void)
 {
        /* The boot cpu is always logical cpu 0 */
index 60c5a3856ab7a5266fabd7eef844b9d1a636d0a8..46322019ab7d388ad62029b42248d2c0a5ecaf72 100644 (file)
@@ -1245,6 +1245,76 @@ int release_mem_region_adjustable(struct resource *parent,
 /*
  * Managed region resource
  */
+static void devm_resource_release(struct device *dev, void *ptr)
+{
+       struct resource **r = ptr;
+
+       release_resource(*r);
+}
+
+/**
+ * devm_request_resource() - request and reserve an I/O or memory resource
+ * @dev: device for which to request the resource
+ * @root: root of the resource tree from which to request the resource
+ * @new: descriptor of the resource to request
+ *
+ * This is a device-managed version of request_resource(). There is usually
+ * no need to release resources requested by this function explicitly since
+ * that will be taken care of when the device is unbound from its driver.
+ * If for some reason the resource needs to be released explicitly, because
+ * of ordering issues for example, drivers must call devm_release_resource()
+ * rather than the regular release_resource().
+ *
+ * When a conflict is detected between any existing resources and the newly
+ * requested resource, an error message will be printed.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int devm_request_resource(struct device *dev, struct resource *root,
+                         struct resource *new)
+{
+       struct resource *conflict, **ptr;
+
+       ptr = devres_alloc(devm_resource_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       *ptr = new;
+
+       conflict = request_resource_conflict(root, new);
+       if (conflict) {
+               dev_err(dev, "resource collision: %pR conflicts with %s %pR\n",
+                       new, conflict->name, conflict);
+               devres_free(ptr);
+               return -EBUSY;
+       }
+
+       devres_add(dev, ptr);
+       return 0;
+}
+EXPORT_SYMBOL(devm_request_resource);
+
+static int devm_resource_match(struct device *dev, void *res, void *data)
+{
+       struct resource **ptr = res;
+
+       return *ptr == data;
+}
+
+/**
+ * devm_release_resource() - release a previously requested resource
+ * @dev: device for which to release the resource
+ * @new: descriptor of the resource to release
+ *
+ * Releases a resource previously requested using devm_request_resource().
+ */
+void devm_release_resource(struct device *dev, struct resource *new)
+{
+       WARN_ON(devres_release(dev, devm_resource_release, devm_resource_match,
+                              new));
+}
+EXPORT_SYMBOL(devm_release_resource);
+
 struct region_devres {
        struct resource *parent;
        resource_size_t start;
index bfa3c86d0d6860ccfc53f003b2d5601ae6352e1a..82088b29704ef80ddee61724db4f694a5c7d2ad6 100644 (file)
@@ -1946,7 +1946,7 @@ void task_numa_work(struct callback_head *work)
                vma = mm->mmap;
        }
        for (; vma; vma = vma->vm_next) {
-               if (!vma_migratable(vma) || !vma_policy_mof(p, vma))
+               if (!vma_migratable(vma) || !vma_policy_mof(vma))
                        continue;
 
                /*
index ce8129192a26029da0d5b33b75daaeb85b1d08e5..dfce4debd138f1cedc3672c14ae0ad5bc361c6a8 100644 (file)
 #include <asm/unistd.h>
 
 #ifndef SET_UNALIGN_CTL
-# define SET_UNALIGN_CTL(a,b)  (-EINVAL)
+# define SET_UNALIGN_CTL(a, b) (-EINVAL)
 #endif
 #ifndef GET_UNALIGN_CTL
-# define GET_UNALIGN_CTL(a,b)  (-EINVAL)
+# define GET_UNALIGN_CTL(a, b) (-EINVAL)
 #endif
 #ifndef SET_FPEMU_CTL
-# define SET_FPEMU_CTL(a,b)    (-EINVAL)
+# define SET_FPEMU_CTL(a, b)   (-EINVAL)
 #endif
 #ifndef GET_FPEMU_CTL
-# define GET_FPEMU_CTL(a,b)    (-EINVAL)
+# define GET_FPEMU_CTL(a, b)   (-EINVAL)
 #endif
 #ifndef SET_FPEXC_CTL
-# define SET_FPEXC_CTL(a,b)    (-EINVAL)
+# define SET_FPEXC_CTL(a, b)   (-EINVAL)
 #endif
 #ifndef GET_FPEXC_CTL
-# define GET_FPEXC_CTL(a,b)    (-EINVAL)
+# define GET_FPEXC_CTL(a, b)   (-EINVAL)
 #endif
 #ifndef GET_ENDIAN
-# define GET_ENDIAN(a,b)       (-EINVAL)
+# define GET_ENDIAN(a, b)      (-EINVAL)
 #endif
 #ifndef SET_ENDIAN
-# define SET_ENDIAN(a,b)       (-EINVAL)
+# define SET_ENDIAN(a, b)      (-EINVAL)
 #endif
 #ifndef GET_TSC_CTL
 # define GET_TSC_CTL(a)                (-EINVAL)
@@ -182,39 +182,40 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval)
        rcu_read_lock();
        read_lock(&tasklist_lock);
        switch (which) {
-               case PRIO_PROCESS:
-                       if (who)
-                               p = find_task_by_vpid(who);
-                       else
-                               p = current;
-                       if (p)
-                               error = set_one_prio(p, niceval, error);
-                       break;
-               case PRIO_PGRP:
-                       if (who)
-                               pgrp = find_vpid(who);
-                       else
-                               pgrp = task_pgrp(current);
-                       do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
-                               error = set_one_prio(p, niceval, error);
-                       } while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
-                       break;
-               case PRIO_USER:
-                       uid = make_kuid(cred->user_ns, who);
-                       user = cred->user;
-                       if (!who)
-                               uid = cred->uid;
-                       else if (!uid_eq(uid, cred->uid) &&
-                                !(user = find_user(uid)))
+       case PRIO_PROCESS:
+               if (who)
+                       p = find_task_by_vpid(who);
+               else
+                       p = current;
+               if (p)
+                       error = set_one_prio(p, niceval, error);
+               break;
+       case PRIO_PGRP:
+               if (who)
+                       pgrp = find_vpid(who);
+               else
+                       pgrp = task_pgrp(current);
+               do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
+                       error = set_one_prio(p, niceval, error);
+               } while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
+               break;
+       case PRIO_USER:
+               uid = make_kuid(cred->user_ns, who);
+               user = cred->user;
+               if (!who)
+                       uid = cred->uid;
+               else if (!uid_eq(uid, cred->uid)) {
+                       user = find_user(uid);
+                       if (!user)
                                goto out_unlock;        /* No processes for this user */
-
-                       do_each_thread(g, p) {
-                               if (uid_eq(task_uid(p), uid))
-                                       error = set_one_prio(p, niceval, error);
-                       } while_each_thread(g, p);
-                       if (!uid_eq(uid, cred->uid))
-                               free_uid(user);         /* For find_user() */
-                       break;
+               }
+               do_each_thread(g, p) {
+                       if (uid_eq(task_uid(p), uid))
+                               error = set_one_prio(p, niceval, error);
+               } while_each_thread(g, p);
+               if (!uid_eq(uid, cred->uid))
+                       free_uid(user);         /* For find_user() */
+               break;
        }
 out_unlock:
        read_unlock(&tasklist_lock);
@@ -244,47 +245,48 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who)
        rcu_read_lock();
        read_lock(&tasklist_lock);
        switch (which) {
-               case PRIO_PROCESS:
-                       if (who)
-                               p = find_task_by_vpid(who);
-                       else
-                               p = current;
-                       if (p) {
+       case PRIO_PROCESS:
+               if (who)
+                       p = find_task_by_vpid(who);
+               else
+                       p = current;
+               if (p) {
+                       niceval = nice_to_rlimit(task_nice(p));
+                       if (niceval > retval)
+                               retval = niceval;
+               }
+               break;
+       case PRIO_PGRP:
+               if (who)
+                       pgrp = find_vpid(who);
+               else
+                       pgrp = task_pgrp(current);
+               do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
+                       niceval = nice_to_rlimit(task_nice(p));
+                       if (niceval > retval)
+                               retval = niceval;
+               } while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
+               break;
+       case PRIO_USER:
+               uid = make_kuid(cred->user_ns, who);
+               user = cred->user;
+               if (!who)
+                       uid = cred->uid;
+               else if (!uid_eq(uid, cred->uid)) {
+                       user = find_user(uid);
+                       if (!user)
+                               goto out_unlock;        /* No processes for this user */
+               }
+               do_each_thread(g, p) {
+                       if (uid_eq(task_uid(p), uid)) {
                                niceval = nice_to_rlimit(task_nice(p));
                                if (niceval > retval)
                                        retval = niceval;
                        }
-                       break;
-               case PRIO_PGRP:
-                       if (who)
-                               pgrp = find_vpid(who);
-                       else
-                               pgrp = task_pgrp(current);
-                       do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
-                               niceval = nice_to_rlimit(task_nice(p));
-                               if (niceval > retval)
-                                       retval = niceval;
-                       } while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
-                       break;
-               case PRIO_USER:
-                       uid = make_kuid(cred->user_ns, who);
-                       user = cred->user;
-                       if (!who)
-                               uid = cred->uid;
-                       else if (!uid_eq(uid, cred->uid) &&
-                                !(user = find_user(uid)))
-                               goto out_unlock;        /* No processes for this user */
-
-                       do_each_thread(g, p) {
-                               if (uid_eq(task_uid(p), uid)) {
-                                       niceval = nice_to_rlimit(task_nice(p));
-                                       if (niceval > retval)
-                                               retval = niceval;
-                               }
-                       } while_each_thread(g, p);
-                       if (!uid_eq(uid, cred->uid))
-                               free_uid(user);         /* for find_user() */
-                       break;
+               } while_each_thread(g, p);
+               if (!uid_eq(uid, cred->uid))
+                       free_uid(user);         /* for find_user() */
+               break;
        }
 out_unlock:
        read_unlock(&tasklist_lock);
@@ -306,7 +308,7 @@ out_unlock:
  *
  * The general idea is that a program which uses just setregid() will be
  * 100% compatible with BSD.  A program which uses just setgid() will be
- * 100% compatible with POSIX with saved IDs. 
+ * 100% compatible with POSIX with saved IDs.
  *
  * SMP: There are not races, the GIDs are checked only by filesystem
  *      operations (as far as semantic preservation is concerned).
@@ -364,7 +366,7 @@ error:
 }
 
 /*
- * setgid() is implemented like SysV w/ SAVED_IDS 
+ * setgid() is implemented like SysV w/ SAVED_IDS
  *
  * SMP: Same implicit races as above.
  */
@@ -442,7 +444,7 @@ static int set_user(struct cred *new)
  *
  * The general idea is that a program which uses just setreuid() will be
  * 100% compatible with BSD.  A program which uses just setuid() will be
- * 100% compatible with POSIX with saved IDs. 
+ * 100% compatible with POSIX with saved IDs.
  */
 SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
 {
@@ -503,17 +505,17 @@ error:
        abort_creds(new);
        return retval;
 }
-               
+
 /*
- * setuid() is implemented like SysV with SAVED_IDS 
- * 
+ * setuid() is implemented like SysV with SAVED_IDS
+ *
  * Note that SAVED_ID's is deficient in that a setuid root program
- * like sendmail, for example, cannot set its uid to be a normal 
+ * like sendmail, for example, cannot set its uid to be a normal
  * user and then switch back, because if you're root, setuid() sets
  * the saved uid too.  If you don't like this, blame the bright people
  * in the POSIX committee and/or USG.  Note that the BSD-style setreuid()
  * will allow a root program to temporarily drop privileges and be able to
- * regain them by swapping the real and effective uid.  
+ * regain them by swapping the real and effective uid.
  */
 SYSCALL_DEFINE1(setuid, uid_t, uid)
 {
@@ -637,10 +639,12 @@ SYSCALL_DEFINE3(getresuid, uid_t __user *, ruidp, uid_t __user *, euidp, uid_t _
        euid = from_kuid_munged(cred->user_ns, cred->euid);
        suid = from_kuid_munged(cred->user_ns, cred->suid);
 
-       if (!(retval   = put_user(ruid, ruidp)) &&
-           !(retval   = put_user(euid, euidp)))
-               retval = put_user(suid, suidp);
-
+       retval = put_user(ruid, ruidp);
+       if (!retval) {
+               retval = put_user(euid, euidp);
+               if (!retval)
+                       return put_user(suid, suidp);
+       }
        return retval;
 }
 
@@ -709,9 +713,12 @@ SYSCALL_DEFINE3(getresgid, gid_t __user *, rgidp, gid_t __user *, egidp, gid_t _
        egid = from_kgid_munged(cred->user_ns, cred->egid);
        sgid = from_kgid_munged(cred->user_ns, cred->sgid);
 
-       if (!(retval   = put_user(rgid, rgidp)) &&
-           !(retval   = put_user(egid, egidp)))
-               retval = put_user(sgid, sgidp);
+       retval = put_user(rgid, rgidp);
+       if (!retval) {
+               retval = put_user(egid, egidp);
+               if (!retval)
+                       retval = put_user(sgid, sgidp);
+       }
 
        return retval;
 }
@@ -1284,7 +1291,6 @@ SYSCALL_DEFINE2(getrlimit, unsigned int, resource, struct rlimit __user *, rlim)
 /*
  *     Back compatibility for getrlimit. Needed for some apps.
  */
 SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource,
                struct rlimit __user *, rlim)
 {
@@ -1299,7 +1305,7 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource,
                x.rlim_cur = 0x7FFFFFFF;
        if (x.rlim_max > 0x7FFFFFFF)
                x.rlim_max = 0x7FFFFFFF;
-       return copy_to_user(rlim, &x, sizeof(x))?-EFAULT:0;
+       return copy_to_user(rlim, &x, sizeof(x)) ? -EFAULT : 0;
 }
 
 #endif
@@ -1527,7 +1533,7 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
        cputime_t tgutime, tgstime, utime, stime;
        unsigned long maxrss = 0;
 
-       memset((char *) r, 0, sizeof *r);
+       memset((char *)r, 0, sizeof (*r));
        utime = stime = 0;
 
        if (who == RUSAGE_THREAD) {
@@ -1541,41 +1547,41 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
                return;
 
        switch (who) {
-               case RUSAGE_BOTH:
-               case RUSAGE_CHILDREN:
-                       utime = p->signal->cutime;
-                       stime = p->signal->cstime;
-                       r->ru_nvcsw = p->signal->cnvcsw;
-                       r->ru_nivcsw = p->signal->cnivcsw;
-                       r->ru_minflt = p->signal->cmin_flt;
-                       r->ru_majflt = p->signal->cmaj_flt;
-                       r->ru_inblock = p->signal->cinblock;
-                       r->ru_oublock = p->signal->coublock;
-                       maxrss = p->signal->cmaxrss;
-
-                       if (who == RUSAGE_CHILDREN)
-                               break;
-
-               case RUSAGE_SELF:
-                       thread_group_cputime_adjusted(p, &tgutime, &tgstime);
-                       utime += tgutime;
-                       stime += tgstime;
-                       r->ru_nvcsw += p->signal->nvcsw;
-                       r->ru_nivcsw += p->signal->nivcsw;
-                       r->ru_minflt += p->signal->min_flt;
-                       r->ru_majflt += p->signal->maj_flt;
-                       r->ru_inblock += p->signal->inblock;
-                       r->ru_oublock += p->signal->oublock;
-                       if (maxrss < p->signal->maxrss)
-                               maxrss = p->signal->maxrss;
-                       t = p;
-                       do {
-                               accumulate_thread_rusage(t, r);
-                       } while_each_thread(p, t);
+       case RUSAGE_BOTH:
+       case RUSAGE_CHILDREN:
+               utime = p->signal->cutime;
+               stime = p->signal->cstime;
+               r->ru_nvcsw = p->signal->cnvcsw;
+               r->ru_nivcsw = p->signal->cnivcsw;
+               r->ru_minflt = p->signal->cmin_flt;
+               r->ru_majflt = p->signal->cmaj_flt;
+               r->ru_inblock = p->signal->cinblock;
+               r->ru_oublock = p->signal->coublock;
+               maxrss = p->signal->cmaxrss;
+
+               if (who == RUSAGE_CHILDREN)
                        break;
 
-               default:
-                       BUG();
+       case RUSAGE_SELF:
+               thread_group_cputime_adjusted(p, &tgutime, &tgstime);
+               utime += tgutime;
+               stime += tgstime;
+               r->ru_nvcsw += p->signal->nvcsw;
+               r->ru_nivcsw += p->signal->nivcsw;
+               r->ru_minflt += p->signal->min_flt;
+               r->ru_majflt += p->signal->maj_flt;
+               r->ru_inblock += p->signal->inblock;
+               r->ru_oublock += p->signal->oublock;
+               if (maxrss < p->signal->maxrss)
+                       maxrss = p->signal->maxrss;
+               t = p;
+               do {
+                       accumulate_thread_rusage(t, r);
+               } while_each_thread(p, t);
+               break;
+
+       default:
+               BUG();
        }
        unlock_task_sighand(p, &flags);
 
@@ -1585,6 +1591,7 @@ out:
 
        if (who != RUSAGE_CHILDREN) {
                struct mm_struct *mm = get_task_mm(p);
+
                if (mm) {
                        setmax_mm_hiwater_rss(&maxrss, mm);
                        mmput(mm);
@@ -1596,6 +1603,7 @@ out:
 int getrusage(struct task_struct *p, int who, struct rusage __user *ru)
 {
        struct rusage r;
+
        k_getrusage(p, who, &r);
        return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
 }
@@ -1628,12 +1636,14 @@ SYSCALL_DEFINE1(umask, int, mask)
        return mask;
 }
 
-static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
+static int prctl_set_mm_exe_file_locked(struct mm_struct *mm, unsigned int fd)
 {
        struct fd exe;
        struct inode *inode;
        int err;
 
+       VM_BUG_ON_MM(!rwsem_is_locked(&mm->mmap_sem), mm);
+
        exe = fdget(fd);
        if (!exe.file)
                return -EBADF;
@@ -1654,8 +1664,6 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
        if (err)
                goto exit;
 
-       down_write(&mm->mmap_sem);
-
        /*
         * Forbid mm->exe_file change if old file still mapped.
         */
@@ -1667,7 +1675,7 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
                        if (vma->vm_file &&
                            path_equal(&vma->vm_file->f_path,
                                       &mm->exe_file->f_path))
-                               goto exit_unlock;
+                               goto exit;
        }
 
        /*
@@ -1678,34 +1686,222 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
         */
        err = -EPERM;
        if (test_and_set_bit(MMF_EXE_FILE_CHANGED, &mm->flags))
-               goto exit_unlock;
+               goto exit;
 
        err = 0;
        set_mm_exe_file(mm, exe.file);  /* this grabs a reference to exe.file */
-exit_unlock:
-       up_write(&mm->mmap_sem);
-
 exit:
        fdput(exe);
        return err;
 }
 
+#ifdef CONFIG_CHECKPOINT_RESTORE
+/*
+ * WARNING: we don't require any capability here so be very careful
+ * in what is allowed for modification from userspace.
+ */
+static int validate_prctl_map(struct prctl_mm_map *prctl_map)
+{
+       unsigned long mmap_max_addr = TASK_SIZE;
+       struct mm_struct *mm = current->mm;
+       int error = -EINVAL, i;
+
+       static const unsigned char offsets[] = {
+               offsetof(struct prctl_mm_map, start_code),
+               offsetof(struct prctl_mm_map, end_code),
+               offsetof(struct prctl_mm_map, start_data),
+               offsetof(struct prctl_mm_map, end_data),
+               offsetof(struct prctl_mm_map, start_brk),
+               offsetof(struct prctl_mm_map, brk),
+               offsetof(struct prctl_mm_map, start_stack),
+               offsetof(struct prctl_mm_map, arg_start),
+               offsetof(struct prctl_mm_map, arg_end),
+               offsetof(struct prctl_mm_map, env_start),
+               offsetof(struct prctl_mm_map, env_end),
+       };
+
+       /*
+        * Make sure the members are not somewhere outside
+        * of allowed address space.
+        */
+       for (i = 0; i < ARRAY_SIZE(offsets); i++) {
+               u64 val = *(u64 *)((char *)prctl_map + offsets[i]);
+
+               if ((unsigned long)val >= mmap_max_addr ||
+                   (unsigned long)val < mmap_min_addr)
+                       goto out;
+       }
+
+       /*
+        * Make sure the pairs are ordered.
+        */
+#define __prctl_check_order(__m1, __op, __m2)                          \
+       ((unsigned long)prctl_map->__m1 __op                            \
+        (unsigned long)prctl_map->__m2) ? 0 : -EINVAL
+       error  = __prctl_check_order(start_code, <, end_code);
+       error |= __prctl_check_order(start_data, <, end_data);
+       error |= __prctl_check_order(start_brk, <=, brk);
+       error |= __prctl_check_order(arg_start, <=, arg_end);
+       error |= __prctl_check_order(env_start, <=, env_end);
+       if (error)
+               goto out;
+#undef __prctl_check_order
+
+       error = -EINVAL;
+
+       /*
+        * @brk should be after @end_data in traditional maps.
+        */
+       if (prctl_map->start_brk <= prctl_map->end_data ||
+           prctl_map->brk <= prctl_map->end_data)
+               goto out;
+
+       /*
+        * Neither we should allow to override limits if they set.
+        */
+       if (check_data_rlimit(rlimit(RLIMIT_DATA), prctl_map->brk,
+                             prctl_map->start_brk, prctl_map->end_data,
+                             prctl_map->start_data))
+                       goto out;
+
+       /*
+        * Someone is trying to cheat the auxv vector.
+        */
+       if (prctl_map->auxv_size) {
+               if (!prctl_map->auxv || prctl_map->auxv_size > sizeof(mm->saved_auxv))
+                       goto out;
+       }
+
+       /*
+        * Finally, make sure the caller has the rights to
+        * change /proc/pid/exe link: only local root should
+        * be allowed to.
+        */
+       if (prctl_map->exe_fd != (u32)-1) {
+               struct user_namespace *ns = current_user_ns();
+               const struct cred *cred = current_cred();
+
+               if (!uid_eq(cred->uid, make_kuid(ns, 0)) ||
+                   !gid_eq(cred->gid, make_kgid(ns, 0)))
+                       goto out;
+       }
+
+       error = 0;
+out:
+       return error;
+}
+
+static int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data_size)
+{
+       struct prctl_mm_map prctl_map = { .exe_fd = (u32)-1, };
+       unsigned long user_auxv[AT_VECTOR_SIZE];
+       struct mm_struct *mm = current->mm;
+       int error;
+
+       BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv));
+       BUILD_BUG_ON(sizeof(struct prctl_mm_map) > 256);
+
+       if (opt == PR_SET_MM_MAP_SIZE)
+               return put_user((unsigned int)sizeof(prctl_map),
+                               (unsigned int __user *)addr);
+
+       if (data_size != sizeof(prctl_map))
+               return -EINVAL;
+
+       if (copy_from_user(&prctl_map, addr, sizeof(prctl_map)))
+               return -EFAULT;
+
+       error = validate_prctl_map(&prctl_map);
+       if (error)
+               return error;
+
+       if (prctl_map.auxv_size) {
+               memset(user_auxv, 0, sizeof(user_auxv));
+               if (copy_from_user(user_auxv,
+                                  (const void __user *)prctl_map.auxv,
+                                  prctl_map.auxv_size))
+                       return -EFAULT;
+
+               /* Last entry must be AT_NULL as specification requires */
+               user_auxv[AT_VECTOR_SIZE - 2] = AT_NULL;
+               user_auxv[AT_VECTOR_SIZE - 1] = AT_NULL;
+       }
+
+       down_write(&mm->mmap_sem);
+       if (prctl_map.exe_fd != (u32)-1)
+               error = prctl_set_mm_exe_file_locked(mm, prctl_map.exe_fd);
+       downgrade_write(&mm->mmap_sem);
+       if (error)
+               goto out;
+
+       /*
+        * We don't validate if these members are pointing to
+        * real present VMAs because application may have correspond
+        * VMAs already unmapped and kernel uses these members for statistics
+        * output in procfs mostly, except
+        *
+        *  - @start_brk/@brk which are used in do_brk but kernel lookups
+        *    for VMAs when updating these memvers so anything wrong written
+        *    here cause kernel to swear at userspace program but won't lead
+        *    to any problem in kernel itself
+        */
+
+       mm->start_code  = prctl_map.start_code;
+       mm->end_code    = prctl_map.end_code;
+       mm->start_data  = prctl_map.start_data;
+       mm->end_data    = prctl_map.end_data;
+       mm->start_brk   = prctl_map.start_brk;
+       mm->brk         = prctl_map.brk;
+       mm->start_stack = prctl_map.start_stack;
+       mm->arg_start   = prctl_map.arg_start;
+       mm->arg_end     = prctl_map.arg_end;
+       mm->env_start   = prctl_map.env_start;
+       mm->env_end     = prctl_map.env_end;
+
+       /*
+        * Note this update of @saved_auxv is lockless thus
+        * if someone reads this member in procfs while we're
+        * updating -- it may get partly updated results. It's
+        * known and acceptable trade off: we leave it as is to
+        * not introduce additional locks here making the kernel
+        * more complex.
+        */
+       if (prctl_map.auxv_size)
+               memcpy(mm->saved_auxv, user_auxv, sizeof(user_auxv));
+
+       error = 0;
+out:
+       up_read(&mm->mmap_sem);
+       return error;
+}
+#endif /* CONFIG_CHECKPOINT_RESTORE */
+
 static int prctl_set_mm(int opt, unsigned long addr,
                        unsigned long arg4, unsigned long arg5)
 {
-       unsigned long rlim = rlimit(RLIMIT_DATA);
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma;
        int error;
 
-       if (arg5 || (arg4 && opt != PR_SET_MM_AUXV))
+       if (arg5 || (arg4 && (opt != PR_SET_MM_AUXV &&
+                             opt != PR_SET_MM_MAP &&
+                             opt != PR_SET_MM_MAP_SIZE)))
                return -EINVAL;
 
+#ifdef CONFIG_CHECKPOINT_RESTORE
+       if (opt == PR_SET_MM_MAP || opt == PR_SET_MM_MAP_SIZE)
+               return prctl_set_mm_map(opt, (const void __user *)addr, arg4);
+#endif
+
        if (!capable(CAP_SYS_RESOURCE))
                return -EPERM;
 
-       if (opt == PR_SET_MM_EXE_FILE)
-               return prctl_set_mm_exe_file(mm, (unsigned int)addr);
+       if (opt == PR_SET_MM_EXE_FILE) {
+               down_write(&mm->mmap_sem);
+               error = prctl_set_mm_exe_file_locked(mm, (unsigned int)addr);
+               up_write(&mm->mmap_sem);
+               return error;
+       }
 
        if (addr >= TASK_SIZE || addr < mmap_min_addr)
                return -EINVAL;
@@ -1733,9 +1929,8 @@ static int prctl_set_mm(int opt, unsigned long addr,
                if (addr <= mm->end_data)
                        goto out;
 
-               if (rlim < RLIM_INFINITY &&
-                   (mm->brk - addr) +
-                   (mm->end_data - mm->start_data) > rlim)
+               if (check_data_rlimit(rlimit(RLIMIT_DATA), mm->brk, addr,
+                                     mm->end_data, mm->start_data))
                        goto out;
 
                mm->start_brk = addr;
@@ -1745,9 +1940,8 @@ static int prctl_set_mm(int opt, unsigned long addr,
                if (addr <= mm->end_data)
                        goto out;
 
-               if (rlim < RLIM_INFINITY &&
-                   (addr - mm->start_brk) +
-                   (mm->end_data - mm->start_data) > rlim)
+               if (check_data_rlimit(rlimit(RLIMIT_DATA), addr, mm->start_brk,
+                                     mm->end_data, mm->start_data))
                        goto out;
 
                mm->brk = addr;
@@ -2023,6 +2217,7 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
 {
        int err = 0;
        int cpu = raw_smp_processor_id();
+
        if (cpup)
                err |= put_user(cpu, cpup);
        if (nodep)
@@ -2135,7 +2330,7 @@ COMPAT_SYSCALL_DEFINE1(sysinfo, struct compat_sysinfo __user *, info)
        /* Check to see if any memory value is too large for 32-bit and scale
         *  down if needed
         */
-       if ((s.totalram >> 32) || (s.totalswap >> 32)) {
+       if (upper_32_bits(s.totalram) || upper_32_bits(s.totalswap)) {
                int bitcount = 0;
 
                while (s.mem_unit < PAGE_SIZE) {
index 75875a741b5e7f9cb26fcd837609638b1ffafda8..91180987e40e6c6acf88ad2a6e9aab4ea558b1f1 100644 (file)
@@ -1460,13 +1460,6 @@ static struct ctl_table vm_table[] = {
                .extra2         = &one,
        },
 #endif
-       {
-               .procname       = "scan_unevictable_pages",
-               .data           = &scan_unevictable_pages,
-               .maxlen         = sizeof(scan_unevictable_pages),
-               .mode           = 0644,
-               .proc_handler   = scan_unevictable_handler,
-       },
 #ifdef CONFIG_MEMORY_FAILURE
        {
                .procname       = "memory_failure_early_kill",
index 0a0608edeb2665e88d2cb1c917b4cf4f112a4e8e..052b4b53c3d6610b59bb426e785450707b6253f3 100644 (file)
@@ -400,4 +400,5 @@ void tick_resume(void)
 void __init tick_init(void)
 {
        tick_broadcast_init();
+       tick_nohz_init();
 }
index c19c1d84b6f34f66e84b32627b702756364e614a..366aeb4f2c6696ee6239e501ea4904f3812cd44c 100644 (file)
@@ -99,6 +99,13 @@ static inline int tick_broadcast_oneshot_active(void) { return 0; }
 static inline bool tick_broadcast_oneshot_available(void) { return false; }
 #endif /* !TICK_ONESHOT */
 
+/* NO_HZ_FULL internal */
+#ifdef CONFIG_NO_HZ_FULL
+extern void tick_nohz_init(void);
+# else
+static inline void tick_nohz_init(void) { }
+#endif
+
 /*
  * Broadcasting support
  */
index f654a8a298fad5cac36465bdcb23fddeb854f2ef..7c1412ea2d29836c54bff9ea841172e2f0827fec 100644 (file)
@@ -295,22 +295,12 @@ out:
 /* Parse the boot-time nohz CPU list from the kernel parameters. */
 static int __init tick_nohz_full_setup(char *str)
 {
-       int cpu;
-
        alloc_bootmem_cpumask_var(&tick_nohz_full_mask);
-       alloc_bootmem_cpumask_var(&housekeeping_mask);
        if (cpulist_parse(str, tick_nohz_full_mask) < 0) {
                pr_warning("NOHZ: Incorrect nohz_full cpumask\n");
+               free_bootmem_cpumask_var(tick_nohz_full_mask);
                return 1;
        }
-
-       cpu = smp_processor_id();
-       if (cpumask_test_cpu(cpu, tick_nohz_full_mask)) {
-               pr_warning("NO_HZ: Clearing %d from nohz_full range for timekeeping\n", cpu);
-               cpumask_clear_cpu(cpu, tick_nohz_full_mask);
-       }
-       cpumask_andnot(housekeeping_mask,
-                      cpu_possible_mask, tick_nohz_full_mask);
        tick_nohz_full_running = true;
 
        return 1;
@@ -349,18 +339,11 @@ static int tick_nohz_init_all(void)
 
 #ifdef CONFIG_NO_HZ_FULL_ALL
        if (!alloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL)) {
-               pr_err("NO_HZ: Can't allocate full dynticks cpumask\n");
-               return err;
-       }
-       if (!alloc_cpumask_var(&housekeeping_mask, GFP_KERNEL)) {
-               pr_err("NO_HZ: Can't allocate not-full dynticks cpumask\n");
+               WARN(1, "NO_HZ: Can't allocate full dynticks cpumask\n");
                return err;
        }
        err = 0;
        cpumask_setall(tick_nohz_full_mask);
-       cpumask_clear_cpu(smp_processor_id(), tick_nohz_full_mask);
-       cpumask_clear(housekeeping_mask);
-       cpumask_set_cpu(smp_processor_id(), housekeeping_mask);
        tick_nohz_full_running = true;
 #endif
        return err;
@@ -375,6 +358,37 @@ void __init tick_nohz_init(void)
                        return;
        }
 
+       if (!alloc_cpumask_var(&housekeeping_mask, GFP_KERNEL)) {
+               WARN(1, "NO_HZ: Can't allocate not-full dynticks cpumask\n");
+               cpumask_clear(tick_nohz_full_mask);
+               tick_nohz_full_running = false;
+               return;
+       }
+
+       /*
+        * Full dynticks uses irq work to drive the tick rescheduling on safe
+        * locking contexts. But then we need irq work to raise its own
+        * interrupts to avoid circular dependency on the tick
+        */
+       if (!arch_irq_work_has_interrupt()) {
+               pr_warning("NO_HZ: Can't run full dynticks because arch doesn't "
+                          "support irq work self-IPIs\n");
+               cpumask_clear(tick_nohz_full_mask);
+               cpumask_copy(housekeeping_mask, cpu_possible_mask);
+               tick_nohz_full_running = false;
+               return;
+       }
+
+       cpu = smp_processor_id();
+
+       if (cpumask_test_cpu(cpu, tick_nohz_full_mask)) {
+               pr_warning("NO_HZ: Clearing %d from nohz_full range for timekeeping\n", cpu);
+               cpumask_clear_cpu(cpu, tick_nohz_full_mask);
+       }
+
+       cpumask_andnot(housekeeping_mask,
+                      cpu_possible_mask, tick_nohz_full_mask);
+
        for_each_cpu(cpu, tick_nohz_full_mask)
                context_tracking_cpu_set(cpu);
 
@@ -982,6 +996,10 @@ static void tick_nohz_handler(struct clock_event_device *dev)
        tick_sched_do_timer(now);
        tick_sched_handle(ts, regs);
 
+       /* No need to reprogram if we are running tickless  */
+       if (unlikely(ts->tick_stopped))
+               return;
+
        while (tick_nohz_reprogram(ts, now)) {
                now = ktime_get();
                tick_do_update_jiffies64(now);
@@ -1109,6 +1127,10 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
        if (regs)
                tick_sched_handle(ts, regs);
 
+       /* No need to reprogram if we are in idle or full dynticks mode */
+       if (unlikely(ts->tick_stopped))
+               return HRTIMER_NORESTART;
+
        hrtimer_forward(timer, now, tick_period);
 
        return HRTIMER_RESTART;
index aca5dfe2fa3de5df0738b6dc495b2e186483a593..9bbb8344ed3bf675c9605383c0614be37cc4ddb5 100644 (file)
@@ -1385,7 +1385,7 @@ void update_process_times(int user_tick)
        rcu_check_callbacks(cpu, user_tick);
 #ifdef CONFIG_IRQ_WORK
        if (in_irq())
-               irq_work_run();
+               irq_work_tick();
 #endif
        scheduler_tick();
        run_posix_cpu_timers(p);
index a8d6914030fe6ec5aa9e08154ce29b480d2c0745..7b223b212683202e2e50ee83153eb8df7dbbe3de 100644 (file)
@@ -47,6 +47,7 @@ static DEFINE_PER_CPU(bool, softlockup_touch_sync);
 static DEFINE_PER_CPU(bool, soft_watchdog_warn);
 static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
 static DEFINE_PER_CPU(unsigned long, soft_lockup_hrtimer_cnt);
+static DEFINE_PER_CPU(struct task_struct *, softlockup_task_ptr_saved);
 #ifdef CONFIG_HARDLOCKUP_DETECTOR
 static DEFINE_PER_CPU(bool, hard_watchdog_warn);
 static DEFINE_PER_CPU(bool, watchdog_nmi_touch);
@@ -333,8 +334,22 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
                        return HRTIMER_RESTART;
 
                /* only warn once */
-               if (__this_cpu_read(soft_watchdog_warn) == true)
+               if (__this_cpu_read(soft_watchdog_warn) == true) {
+                       /*
+                        * When multiple processes are causing softlockups the
+                        * softlockup detector only warns on the first one
+                        * because the code relies on a full quiet cycle to
+                        * re-arm.  The second process prevents the quiet cycle
+                        * and never gets reported.  Use task pointers to detect
+                        * this.
+                        */
+                       if (__this_cpu_read(softlockup_task_ptr_saved) !=
+                           current) {
+                               __this_cpu_write(soft_watchdog_warn, false);
+                               __touch_watchdog();
+                       }
                        return HRTIMER_RESTART;
+               }
 
                if (softlockup_all_cpu_backtrace) {
                        /* Prevent multiple soft-lockup reports if one cpu is already
@@ -350,6 +365,7 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
                pr_emerg("BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n",
                        smp_processor_id(), duration,
                        current->comm, task_pid_nr(current));
+               __this_cpu_write(softlockup_task_ptr_saved, current);
                print_modules();
                print_irqtrace_events(current);
                if (regs)
index ebf3bac460b01c3638e87d23975426f448d58064..8f25652f40d4f3aafc770b87b2692b54629a6273 100644 (file)
  */
 #include <linux/flex_proportions.h>
 
-int fprop_global_init(struct fprop_global *p)
+int fprop_global_init(struct fprop_global *p, gfp_t gfp)
 {
        int err;
 
        p->period = 0;
        /* Use 1 to avoid dealing with periods with 0 events... */
-       err = percpu_counter_init(&p->events, 1);
+       err = percpu_counter_init(&p->events, 1, gfp);
        if (err)
                return err;
        seqcount_init(&p->sequence);
@@ -168,11 +168,11 @@ void fprop_fraction_single(struct fprop_global *p,
  */
 #define PROP_BATCH (8*(1+ilog2(nr_cpu_ids)))
 
-int fprop_local_init_percpu(struct fprop_local_percpu *pl)
+int fprop_local_init_percpu(struct fprop_local_percpu *pl, gfp_t gfp)
 {
        int err;
 
-       err = percpu_counter_init(&pl->events, 0);
+       err = percpu_counter_init(&pl->events, 0, gfp);
        if (err)
                return err;
        pl->period = 0;
index 38d2db82228c2ce716a68d266efe772e1bc6bc5d..cce4dd68c40da211948177f6903917d4ac81b176 100644 (file)
@@ -402,6 +402,35 @@ void gen_pool_for_each_chunk(struct gen_pool *pool,
 }
 EXPORT_SYMBOL(gen_pool_for_each_chunk);
 
+/**
+ * addr_in_gen_pool - checks if an address falls within the range of a pool
+ * @pool:      the generic memory pool
+ * @start:     start address
+ * @size:      size of the region
+ *
+ * Check if the range of addresses falls within the specified pool. Returns
+ * true if the entire range is contained in the pool and false otherwise.
+ */
+bool addr_in_gen_pool(struct gen_pool *pool, unsigned long start,
+                       size_t size)
+{
+       bool found = false;
+       unsigned long end = start + size;
+       struct gen_pool_chunk *chunk;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(chunk, &(pool)->chunks, next_chunk) {
+               if (start >= chunk->start_addr && start <= chunk->end_addr) {
+                       if (end <= chunk->end_addr) {
+                               found = true;
+                               break;
+                       }
+               }
+       }
+       rcu_read_unlock();
+       return found;
+}
+
 /**
  * gen_pool_avail - get available free space of the pool
  * @pool: pool to get available free space
@@ -480,6 +509,26 @@ unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size,
 }
 EXPORT_SYMBOL(gen_pool_first_fit);
 
+/**
+ * gen_pool_first_fit_order_align - find the first available region
+ * of memory matching the size requirement. The region will be aligned
+ * to the order of the size specified.
+ * @map: The address to base the search on
+ * @size: The bitmap size in bits
+ * @start: The bitnumber to start searching at
+ * @nr: The number of zeroed bits we're looking for
+ * @data: additional data - unused
+ */
+unsigned long gen_pool_first_fit_order_align(unsigned long *map,
+               unsigned long size, unsigned long start,
+               unsigned int nr, void *data)
+{
+       unsigned long align_mask = roundup_pow_of_two(nr) - 1;
+
+       return bitmap_find_next_zero_area(map, size, start, nr, align_mask);
+}
+EXPORT_SYMBOL(gen_pool_first_fit_order_align);
+
 /**
  * gen_pool_best_fit - find the best fitting region of memory
  * macthing the size requirement (no alignment constraint)
index a89cf09a82684d729222699afb1f911162977ba7..6111bcb28376bfe412830431ca32f3e4607cfc29 100644 (file)
@@ -1,6 +1,8 @@
 #define pr_fmt(fmt) "%s: " fmt "\n", __func__
 
 #include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
 #include <linux/percpu-refcount.h>
 
 /*
@@ -11,8 +13,8 @@
  * percpu counters will all sum to the correct value
  *
  * (More precisely: because moduler arithmatic is commutative the sum of all the
- * pcpu_count vars will be equal to what it would have been if all the gets and
- * puts were done to a single integer, even if some of the percpu integers
+ * percpu_count vars will be equal to what it would have been if all the gets
+ * and puts were done to a single integer, even if some of the percpu integers
  * overflow or underflow).
  *
  * The real trick to implementing percpu refcounts is shutdown. We can't detect
  * works.
  *
  * Converting to non percpu mode is done with some RCUish stuff in
- * percpu_ref_kill. Additionally, we need a bias value so that the atomic_t
- * can't hit 0 before we've added up all the percpu refs.
+ * percpu_ref_kill. Additionally, we need a bias value so that the
+ * atomic_long_t can't hit 0 before we've added up all the percpu refs.
  */
 
-#define PCPU_COUNT_BIAS                (1U << 31)
+#define PERCPU_COUNT_BIAS      (1LU << (BITS_PER_LONG - 1))
 
-static unsigned __percpu *pcpu_count_ptr(struct percpu_ref *ref)
+static DECLARE_WAIT_QUEUE_HEAD(percpu_ref_switch_waitq);
+
+static unsigned long __percpu *percpu_count_ptr(struct percpu_ref *ref)
 {
-       return (unsigned __percpu *)(ref->pcpu_count_ptr & ~PCPU_REF_DEAD);
+       return (unsigned long __percpu *)
+               (ref->percpu_count_ptr & ~__PERCPU_REF_ATOMIC_DEAD);
 }
 
 /**
  * percpu_ref_init - initialize a percpu refcount
  * @ref: percpu_ref to initialize
  * @release: function which will be called when refcount hits 0
+ * @flags: PERCPU_REF_INIT_* flags
+ * @gfp: allocation mask to use
  *
- * Initializes the refcount in single atomic counter mode with a refcount of 1;
- * analagous to atomic_set(ref, 1).
+ * Initializes @ref.  If @flags is zero, @ref starts in percpu mode with a
+ * refcount of 1; analagous to atomic_long_set(ref, 1).  See the
+ * definitions of PERCPU_REF_INIT_* flags for flag behaviors.
  *
  * Note that @release must not sleep - it may potentially be called from RCU
  * callback context by percpu_ref_kill().
  */
-int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release)
+int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release,
+                   unsigned int flags, gfp_t gfp)
 {
-       atomic_set(&ref->count, 1 + PCPU_COUNT_BIAS);
+       size_t align = max_t(size_t, 1 << __PERCPU_REF_FLAG_BITS,
+                            __alignof__(unsigned long));
+       unsigned long start_count = 0;
 
-       ref->pcpu_count_ptr = (unsigned long)alloc_percpu(unsigned);
-       if (!ref->pcpu_count_ptr)
+       ref->percpu_count_ptr = (unsigned long)
+               __alloc_percpu_gfp(sizeof(unsigned long), align, gfp);
+       if (!ref->percpu_count_ptr)
                return -ENOMEM;
 
-       ref->release = release;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(percpu_ref_init);
-
-/**
- * percpu_ref_reinit - re-initialize a percpu refcount
- * @ref: perpcu_ref to re-initialize
- *
- * Re-initialize @ref so that it's in the same state as when it finished
- * percpu_ref_init().  @ref must have been initialized successfully, killed
- * and reached 0 but not exited.
- *
- * Note that percpu_ref_tryget[_live]() are safe to perform on @ref while
- * this function is in progress.
- */
-void percpu_ref_reinit(struct percpu_ref *ref)
-{
-       unsigned __percpu *pcpu_count = pcpu_count_ptr(ref);
-       int cpu;
+       ref->force_atomic = flags & PERCPU_REF_INIT_ATOMIC;
 
-       BUG_ON(!pcpu_count);
-       WARN_ON(!percpu_ref_is_zero(ref));
+       if (flags & (PERCPU_REF_INIT_ATOMIC | PERCPU_REF_INIT_DEAD))
+               ref->percpu_count_ptr |= __PERCPU_REF_ATOMIC;
+       else
+               start_count += PERCPU_COUNT_BIAS;
 
-       atomic_set(&ref->count, 1 + PCPU_COUNT_BIAS);
+       if (flags & PERCPU_REF_INIT_DEAD)
+               ref->percpu_count_ptr |= __PERCPU_REF_DEAD;
+       else
+               start_count++;
 
-       /*
-        * Restore per-cpu operation.  smp_store_release() is paired with
-        * smp_read_barrier_depends() in __pcpu_ref_alive() and guarantees
-        * that the zeroing is visible to all percpu accesses which can see
-        * the following PCPU_REF_DEAD clearing.
-        */
-       for_each_possible_cpu(cpu)
-               *per_cpu_ptr(pcpu_count, cpu) = 0;
+       atomic_long_set(&ref->count, start_count);
 
-       smp_store_release(&ref->pcpu_count_ptr,
-                         ref->pcpu_count_ptr & ~PCPU_REF_DEAD);
+       ref->release = release;
+       return 0;
 }
-EXPORT_SYMBOL_GPL(percpu_ref_reinit);
+EXPORT_SYMBOL_GPL(percpu_ref_init);
 
 /**
  * percpu_ref_exit - undo percpu_ref_init()
@@ -107,26 +98,39 @@ EXPORT_SYMBOL_GPL(percpu_ref_reinit);
  */
 void percpu_ref_exit(struct percpu_ref *ref)
 {
-       unsigned __percpu *pcpu_count = pcpu_count_ptr(ref);
+       unsigned long __percpu *percpu_count = percpu_count_ptr(ref);
 
-       if (pcpu_count) {
-               free_percpu(pcpu_count);
-               ref->pcpu_count_ptr = PCPU_REF_DEAD;
+       if (percpu_count) {
+               free_percpu(percpu_count);
+               ref->percpu_count_ptr = __PERCPU_REF_ATOMIC_DEAD;
        }
 }
 EXPORT_SYMBOL_GPL(percpu_ref_exit);
 
-static void percpu_ref_kill_rcu(struct rcu_head *rcu)
+static void percpu_ref_call_confirm_rcu(struct rcu_head *rcu)
+{
+       struct percpu_ref *ref = container_of(rcu, struct percpu_ref, rcu);
+
+       ref->confirm_switch(ref);
+       ref->confirm_switch = NULL;
+       wake_up_all(&percpu_ref_switch_waitq);
+
+       /* drop ref from percpu_ref_switch_to_atomic() */
+       percpu_ref_put(ref);
+}
+
+static void percpu_ref_switch_to_atomic_rcu(struct rcu_head *rcu)
 {
        struct percpu_ref *ref = container_of(rcu, struct percpu_ref, rcu);
-       unsigned __percpu *pcpu_count = pcpu_count_ptr(ref);
-       unsigned count = 0;
+       unsigned long __percpu *percpu_count = percpu_count_ptr(ref);
+       unsigned long count = 0;
        int cpu;
 
        for_each_possible_cpu(cpu)
-               count += *per_cpu_ptr(pcpu_count, cpu);
+               count += *per_cpu_ptr(percpu_count, cpu);
 
-       pr_debug("global %i pcpu %i", atomic_read(&ref->count), (int) count);
+       pr_debug("global %ld percpu %ld",
+                atomic_long_read(&ref->count), (long)count);
 
        /*
         * It's crucial that we sum the percpu counters _before_ adding the sum
@@ -140,21 +144,137 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu)
         * reaching 0 before we add the percpu counts. But doing it at the same
         * time is equivalent and saves us atomic operations:
         */
+       atomic_long_add((long)count - PERCPU_COUNT_BIAS, &ref->count);
+
+       WARN_ONCE(atomic_long_read(&ref->count) <= 0,
+                 "percpu ref (%pf) <= 0 (%ld) after switching to atomic",
+                 ref->release, atomic_long_read(&ref->count));
+
+       /* @ref is viewed as dead on all CPUs, send out switch confirmation */
+       percpu_ref_call_confirm_rcu(rcu);
+}
+
+static void percpu_ref_noop_confirm_switch(struct percpu_ref *ref)
+{
+}
+
+static void __percpu_ref_switch_to_atomic(struct percpu_ref *ref,
+                                         percpu_ref_func_t *confirm_switch)
+{
+       if (!(ref->percpu_count_ptr & __PERCPU_REF_ATOMIC)) {
+               /* switching from percpu to atomic */
+               ref->percpu_count_ptr |= __PERCPU_REF_ATOMIC;
+
+               /*
+                * Non-NULL ->confirm_switch is used to indicate that
+                * switching is in progress.  Use noop one if unspecified.
+                */
+               WARN_ON_ONCE(ref->confirm_switch);
+               ref->confirm_switch =
+                       confirm_switch ?: percpu_ref_noop_confirm_switch;
+
+               percpu_ref_get(ref);    /* put after confirmation */
+               call_rcu_sched(&ref->rcu, percpu_ref_switch_to_atomic_rcu);
+       } else if (confirm_switch) {
+               /*
+                * Somebody already set ATOMIC.  Switching may still be in
+                * progress.  @confirm_switch must be invoked after the
+                * switching is complete and a full sched RCU grace period
+                * has passed.  Wait synchronously for the previous
+                * switching and schedule @confirm_switch invocation.
+                */
+               wait_event(percpu_ref_switch_waitq, !ref->confirm_switch);
+               ref->confirm_switch = confirm_switch;
 
-       atomic_add((int) count - PCPU_COUNT_BIAS, &ref->count);
+               percpu_ref_get(ref);    /* put after confirmation */
+               call_rcu_sched(&ref->rcu, percpu_ref_call_confirm_rcu);
+       }
+}
+
+/**
+ * percpu_ref_switch_to_atomic - switch a percpu_ref to atomic mode
+ * @ref: percpu_ref to switch to atomic mode
+ * @confirm_switch: optional confirmation callback
+ *
+ * There's no reason to use this function for the usual reference counting.
+ * Use percpu_ref_kill[_and_confirm]().
+ *
+ * Schedule switching of @ref to atomic mode.  All its percpu counts will
+ * be collected to the main atomic counter.  On completion, when all CPUs
+ * are guaraneed to be in atomic mode, @confirm_switch, which may not
+ * block, is invoked.  This function may be invoked concurrently with all
+ * the get/put operations and can safely be mixed with kill and reinit
+ * operations.  Note that @ref will stay in atomic mode across kill/reinit
+ * cycles until percpu_ref_switch_to_percpu() is called.
+ *
+ * This function normally doesn't block and can be called from any context
+ * but it may block if @confirm_kill is specified and @ref is already in
+ * the process of switching to atomic mode.  In such cases, @confirm_switch
+ * will be invoked after the switching is complete.
+ *
+ * Due to the way percpu_ref is implemented, @confirm_switch will be called
+ * after at least one full sched RCU grace period has passed but this is an
+ * implementation detail and must not be depended upon.
+ */
+void percpu_ref_switch_to_atomic(struct percpu_ref *ref,
+                                percpu_ref_func_t *confirm_switch)
+{
+       ref->force_atomic = true;
+       __percpu_ref_switch_to_atomic(ref, confirm_switch);
+}
 
-       WARN_ONCE(atomic_read(&ref->count) <= 0, "percpu ref <= 0 (%i)",
-                 atomic_read(&ref->count));
+static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
+{
+       unsigned long __percpu *percpu_count = percpu_count_ptr(ref);
+       int cpu;
+
+       BUG_ON(!percpu_count);
 
-       /* @ref is viewed as dead on all CPUs, send out kill confirmation */
-       if (ref->confirm_kill)
-               ref->confirm_kill(ref);
+       if (!(ref->percpu_count_ptr & __PERCPU_REF_ATOMIC))
+               return;
+
+       wait_event(percpu_ref_switch_waitq, !ref->confirm_switch);
+
+       atomic_long_add(PERCPU_COUNT_BIAS, &ref->count);
 
        /*
-        * Now we're in single atomic_t mode with a consistent refcount, so it's
-        * safe to drop our initial ref:
+        * Restore per-cpu operation.  smp_store_release() is paired with
+        * smp_read_barrier_depends() in __ref_is_percpu() and guarantees
+        * that the zeroing is visible to all percpu accesses which can see
+        * the following __PERCPU_REF_ATOMIC clearing.
         */
-       percpu_ref_put(ref);
+       for_each_possible_cpu(cpu)
+               *per_cpu_ptr(percpu_count, cpu) = 0;
+
+       smp_store_release(&ref->percpu_count_ptr,
+                         ref->percpu_count_ptr & ~__PERCPU_REF_ATOMIC);
+}
+
+/**
+ * percpu_ref_switch_to_percpu - switch a percpu_ref to percpu mode
+ * @ref: percpu_ref to switch to percpu mode
+ *
+ * There's no reason to use this function for the usual reference counting.
+ * To re-use an expired ref, use percpu_ref_reinit().
+ *
+ * Switch @ref to percpu mode.  This function may be invoked concurrently
+ * with all the get/put operations and can safely be mixed with kill and
+ * reinit operations.  This function reverses the sticky atomic state set
+ * by PERCPU_REF_INIT_ATOMIC or percpu_ref_switch_to_atomic().  If @ref is
+ * dying or dead, the actual switching takes place on the following
+ * percpu_ref_reinit().
+ *
+ * This function normally doesn't block and can be called from any context
+ * but it may block if @ref is in the process of switching to atomic mode
+ * by percpu_ref_switch_atomic().
+ */
+void percpu_ref_switch_to_percpu(struct percpu_ref *ref)
+{
+       ref->force_atomic = false;
+
+       /* a dying or dead ref can't be switched to percpu mode w/o reinit */
+       if (!(ref->percpu_count_ptr & __PERCPU_REF_DEAD))
+               __percpu_ref_switch_to_percpu(ref);
 }
 
 /**
@@ -164,39 +284,48 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu)
  *
  * Equivalent to percpu_ref_kill() but also schedules kill confirmation if
  * @confirm_kill is not NULL.  @confirm_kill, which may not block, will be
- * called after @ref is seen as dead from all CPUs - all further
- * invocations of percpu_ref_tryget() will fail.  See percpu_ref_tryget()
- * for more details.
+ * called after @ref is seen as dead from all CPUs at which point all
+ * further invocations of percpu_ref_tryget_live() will fail.  See
+ * percpu_ref_tryget_live() for details.
+ *
+ * This function normally doesn't block and can be called from any context
+ * but it may block if @confirm_kill is specified and @ref is in the
+ * process of switching to atomic mode by percpu_ref_switch_atomic().
  *
- * Due to the way percpu_ref is implemented, @confirm_kill will be called
- * after at least one full RCU grace period has passed but this is an
- * implementation detail and callers must not depend on it.
+ * Due to the way percpu_ref is implemented, @confirm_switch will be called
+ * after at least one full sched RCU grace period has passed but this is an
+ * implementation detail and must not be depended upon.
  */
 void percpu_ref_kill_and_confirm(struct percpu_ref *ref,
                                 percpu_ref_func_t *confirm_kill)
 {
-       WARN_ONCE(ref->pcpu_count_ptr & PCPU_REF_DEAD,
-                 "percpu_ref_kill() called more than once!\n");
+       WARN_ONCE(ref->percpu_count_ptr & __PERCPU_REF_DEAD,
+                 "%s called more than once on %pf!", __func__, ref->release);
 
-       ref->pcpu_count_ptr |= PCPU_REF_DEAD;
-       ref->confirm_kill = confirm_kill;
-
-       call_rcu_sched(&ref->rcu, percpu_ref_kill_rcu);
+       ref->percpu_count_ptr |= __PERCPU_REF_DEAD;
+       __percpu_ref_switch_to_atomic(ref, confirm_kill);
+       percpu_ref_put(ref);
 }
 EXPORT_SYMBOL_GPL(percpu_ref_kill_and_confirm);
 
-/*
- * XXX: Temporary kludge to work around SCSI blk-mq stall.  Used only by
- * block/blk-mq.c::blk_mq_freeze_queue().  Will be removed during v3.18
- * devel cycle.  Do not use anywhere else.
+/**
+ * percpu_ref_reinit - re-initialize a percpu refcount
+ * @ref: perpcu_ref to re-initialize
+ *
+ * Re-initialize @ref so that it's in the same state as when it finished
+ * percpu_ref_init() ignoring %PERCPU_REF_INIT_DEAD.  @ref must have been
+ * initialized successfully and reached 0 but not exited.
+ *
+ * Note that percpu_ref_tryget[_live]() are safe to perform on @ref while
+ * this function is in progress.
  */
-void __percpu_ref_kill_expedited(struct percpu_ref *ref)
+void percpu_ref_reinit(struct percpu_ref *ref)
 {
-       WARN_ONCE(ref->pcpu_count_ptr & PCPU_REF_DEAD,
-                 "percpu_ref_kill() called more than once on %pf!",
-                 ref->release);
+       WARN_ON_ONCE(!percpu_ref_is_zero(ref));
 
-       ref->pcpu_count_ptr |= PCPU_REF_DEAD;
-       synchronize_sched_expedited();
-       percpu_ref_kill_rcu(&ref->rcu);
+       ref->percpu_count_ptr &= ~__PERCPU_REF_DEAD;
+       percpu_ref_get(ref);
+       if (!ref->force_atomic)
+               __percpu_ref_switch_to_percpu(ref);
 }
+EXPORT_SYMBOL_GPL(percpu_ref_reinit);
index 7dd33577b9058ea1569eda068ea437079f5bc65e..48144cdae819017e8a9c0a89aae976359d87121b 100644 (file)
@@ -112,13 +112,15 @@ s64 __percpu_counter_sum(struct percpu_counter *fbc)
 }
 EXPORT_SYMBOL(__percpu_counter_sum);
 
-int __percpu_counter_init(struct percpu_counter *fbc, s64 amount,
+int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp,
                          struct lock_class_key *key)
 {
+       unsigned long flags __maybe_unused;
+
        raw_spin_lock_init(&fbc->lock);
        lockdep_set_class(&fbc->lock, key);
        fbc->count = amount;
-       fbc->counters = alloc_percpu(s32);
+       fbc->counters = alloc_percpu_gfp(s32, gfp);
        if (!fbc->counters)
                return -ENOMEM;
 
@@ -126,9 +128,9 @@ int __percpu_counter_init(struct percpu_counter *fbc, s64 amount,
 
 #ifdef CONFIG_HOTPLUG_CPU
        INIT_LIST_HEAD(&fbc->list);
-       spin_lock(&percpu_counters_lock);
+       spin_lock_irqsave(&percpu_counters_lock, flags);
        list_add(&fbc->list, &percpu_counters);
-       spin_unlock(&percpu_counters_lock);
+       spin_unlock_irqrestore(&percpu_counters_lock, flags);
 #endif
        return 0;
 }
@@ -136,15 +138,17 @@ EXPORT_SYMBOL(__percpu_counter_init);
 
 void percpu_counter_destroy(struct percpu_counter *fbc)
 {
+       unsigned long flags __maybe_unused;
+
        if (!fbc->counters)
                return;
 
        debug_percpu_counter_deactivate(fbc);
 
 #ifdef CONFIG_HOTPLUG_CPU
-       spin_lock(&percpu_counters_lock);
+       spin_lock_irqsave(&percpu_counters_lock, flags);
        list_del(&fbc->list);
-       spin_unlock(&percpu_counters_lock);
+       spin_unlock_irqrestore(&percpu_counters_lock, flags);
 #endif
        free_percpu(fbc->counters);
        fbc->counters = NULL;
@@ -173,7 +177,7 @@ static int percpu_counter_hotcpu_callback(struct notifier_block *nb,
                return NOTIFY_OK;
 
        cpu = (unsigned long)hcpu;
-       spin_lock(&percpu_counters_lock);
+       spin_lock_irq(&percpu_counters_lock);
        list_for_each_entry(fbc, &percpu_counters, list) {
                s32 *pcount;
                unsigned long flags;
@@ -184,7 +188,7 @@ static int percpu_counter_hotcpu_callback(struct notifier_block *nb,
                *pcount = 0;
                raw_spin_unlock_irqrestore(&fbc->lock, flags);
        }
-       spin_unlock(&percpu_counters_lock);
+       spin_unlock_irq(&percpu_counters_lock);
 #endif
        return NOTIFY_OK;
 }
index 05df84801b5666438dc49b22a4fce52e6a3c0131..6f724298f67a11199407870e2dbb1541ee55db6c 100644 (file)
@@ -73,7 +73,7 @@
 #include <linux/proportions.h>
 #include <linux/rcupdate.h>
 
-int prop_descriptor_init(struct prop_descriptor *pd, int shift)
+int prop_descriptor_init(struct prop_descriptor *pd, int shift, gfp_t gfp)
 {
        int err;
 
@@ -83,11 +83,11 @@ int prop_descriptor_init(struct prop_descriptor *pd, int shift)
        pd->index = 0;
        pd->pg[0].shift = shift;
        mutex_init(&pd->mutex);
-       err = percpu_counter_init(&pd->pg[0].events, 0);
+       err = percpu_counter_init(&pd->pg[0].events, 0, gfp);
        if (err)
                goto out;
 
-       err = percpu_counter_init(&pd->pg[1].events, 0);
+       err = percpu_counter_init(&pd->pg[1].events, 0, gfp);
        if (err)
                percpu_counter_destroy(&pd->pg[0].events);
 
@@ -188,12 +188,12 @@ prop_adjust_shift(int *pl_shift, unsigned long *pl_period, int new_shift)
 
 #define PROP_BATCH (8*(1+ilog2(nr_cpu_ids)))
 
-int prop_local_init_percpu(struct prop_local_percpu *pl)
+int prop_local_init_percpu(struct prop_local_percpu *pl, gfp_t gfp)
 {
        raw_spin_lock_init(&pl->lock);
        pl->shift = 0;
        pl->period = 0;
-       return percpu_counter_init(&pl->events, 0);
+       return percpu_counter_init(&pl->events, 0, gfp);
 }
 
 void prop_local_destroy_percpu(struct prop_local_percpu *pl)
index 886db2158538572ee52790b31d93b4baeb8b187f..1d1ae6b078fdd9121abbd01409f01437bb67e1e8 100644 (file)
@@ -137,6 +137,9 @@ config HAVE_MEMBLOCK_NODE_MAP
 config HAVE_MEMBLOCK_PHYS_MAP
        boolean
 
+config HAVE_GENERIC_RCU_GUP
+       boolean
+
 config ARCH_DISCARD_MEMBLOCK
        boolean
 
@@ -227,12 +230,17 @@ config SPLIT_PTLOCK_CPUS
 config ARCH_ENABLE_SPLIT_PMD_PTLOCK
        boolean
 
+#
+# support for memory balloon
+config MEMORY_BALLOON
+       boolean
+
 #
 # support for memory balloon compaction
 config BALLOON_COMPACTION
        bool "Allow for balloon memory compaction/migration"
        def_bool y
-       depends on COMPACTION && VIRTIO_BALLOON
+       depends on COMPACTION && MEMORY_BALLOON
        help
          Memory fragmentation introduced by ballooning might reduce
          significantly the number of 2MB contiguous memory blocks that can be
index fe7a053c0f45f66a1398d740419ba9ffe9c1ca3a..1f534a7f0a711396bab99d6399f2bc40898ae850 100644 (file)
@@ -16,9 +16,9 @@ obj-y                 := filemap.o mempool.o oom_kill.o \
                           readahead.o swap.o truncate.o vmscan.o shmem.o \
                           util.o mmzone.o vmstat.o backing-dev.o \
                           mm_init.o mmu_context.o percpu.o slab_common.o \
-                          compaction.o balloon_compaction.o vmacache.o \
+                          compaction.o vmacache.o \
                           interval_tree.o list_lru.o workingset.o \
-                          iov_iter.o $(mmu-y)
+                          iov_iter.o debug.o $(mmu-y)
 
 obj-y += init-mm.o
 
@@ -67,3 +67,4 @@ obj-$(CONFIG_ZBUD)    += zbud.o
 obj-$(CONFIG_ZSMALLOC) += zsmalloc.o
 obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
 obj-$(CONFIG_CMA)      += cma.o
+obj-$(CONFIG_MEMORY_BALLOON) += balloon_compaction.o
index 1706cbbdf5f0381aaf81f21f6bc47b1133e2746b..12a992b625765b6b43b34a184e7e62536ae3e321 100644 (file)
@@ -455,7 +455,7 @@ int bdi_init(struct backing_dev_info *bdi)
        bdi_wb_init(&bdi->wb, bdi);
 
        for (i = 0; i < NR_BDI_STAT_ITEMS; i++) {
-               err = percpu_counter_init(&bdi->bdi_stat[i], 0);
+               err = percpu_counter_init(&bdi->bdi_stat[i], 0, GFP_KERNEL);
                if (err)
                        goto err;
        }
@@ -470,7 +470,7 @@ int bdi_init(struct backing_dev_info *bdi)
        bdi->write_bandwidth = INIT_BW;
        bdi->avg_write_bandwidth = INIT_BW;
 
-       err = fprop_local_init_percpu(&bdi->completions);
+       err = fprop_local_init_percpu(&bdi->completions, GFP_KERNEL);
 
        if (err) {
 err:
@@ -631,7 +631,7 @@ long wait_iff_congested(struct zone *zone, int sync, long timeout)
         * of sleeping on the congestion queue
         */
        if (atomic_read(&nr_bdi_congested[sync]) == 0 ||
-                       !zone_is_reclaim_congested(zone)) {
+           !test_bit(ZONE_CONGESTED, &zone->flags)) {
                cond_resched();
 
                /* In case we scheduled, work out time remaining */
index 6e45a5074bf023b85896b1feb01198a5f1c652ff..b3cbe19f71b5fe48ca853b55b8d5c1163bfdea03 100644 (file)
 #include <linux/export.h>
 #include <linux/balloon_compaction.h>
 
-/*
- * balloon_devinfo_alloc - allocates a balloon device information descriptor.
- * @balloon_dev_descriptor: pointer to reference the balloon device which
- *                          this struct balloon_dev_info will be servicing.
- *
- * Driver must call it to properly allocate and initialize an instance of
- * struct balloon_dev_info which will be used to reference a balloon device
- * as well as to keep track of the balloon device page list.
- */
-struct balloon_dev_info *balloon_devinfo_alloc(void *balloon_dev_descriptor)
-{
-       struct balloon_dev_info *b_dev_info;
-       b_dev_info = kmalloc(sizeof(*b_dev_info), GFP_KERNEL);
-       if (!b_dev_info)
-               return ERR_PTR(-ENOMEM);
-
-       b_dev_info->balloon_device = balloon_dev_descriptor;
-       b_dev_info->mapping = NULL;
-       b_dev_info->isolated_pages = 0;
-       spin_lock_init(&b_dev_info->pages_lock);
-       INIT_LIST_HEAD(&b_dev_info->pages);
-
-       return b_dev_info;
-}
-EXPORT_SYMBOL_GPL(balloon_devinfo_alloc);
-
 /*
  * balloon_page_enqueue - allocates a new page and inserts it into the balloon
  *                       page list.
@@ -61,7 +35,8 @@ struct page *balloon_page_enqueue(struct balloon_dev_info *b_dev_info)
         */
        BUG_ON(!trylock_page(page));
        spin_lock_irqsave(&b_dev_info->pages_lock, flags);
-       balloon_page_insert(page, b_dev_info->mapping, &b_dev_info->pages);
+       balloon_page_insert(b_dev_info, page);
+       __count_vm_event(BALLOON_INFLATE);
        spin_unlock_irqrestore(&b_dev_info->pages_lock, flags);
        unlock_page(page);
        return page;
@@ -93,18 +68,14 @@ struct page *balloon_page_dequeue(struct balloon_dev_info *b_dev_info)
                 * to be released by the balloon driver.
                 */
                if (trylock_page(page)) {
+                       if (!PagePrivate(page)) {
+                               /* raced with isolation */
+                               unlock_page(page);
+                               continue;
+                       }
                        spin_lock_irqsave(&b_dev_info->pages_lock, flags);
-                       /*
-                        * Raise the page refcount here to prevent any wrong
-                        * attempt to isolate this page, in case of coliding
-                        * with balloon_page_isolate() just after we release
-                        * the page lock.
-                        *
-                        * balloon_page_free() will take care of dropping
-                        * this extra refcount later.
-                        */
-                       get_page(page);
                        balloon_page_delete(page);
+                       __count_vm_event(BALLOON_DEFLATE);
                        spin_unlock_irqrestore(&b_dev_info->pages_lock, flags);
                        unlock_page(page);
                        dequeued_page = true;
@@ -132,62 +103,14 @@ struct page *balloon_page_dequeue(struct balloon_dev_info *b_dev_info)
 EXPORT_SYMBOL_GPL(balloon_page_dequeue);
 
 #ifdef CONFIG_BALLOON_COMPACTION
-/*
- * balloon_mapping_alloc - allocates a special ->mapping for ballooned pages.
- * @b_dev_info: holds the balloon device information descriptor.
- * @a_ops: balloon_mapping address_space_operations descriptor.
- *
- * Driver must call it to properly allocate and initialize an instance of
- * struct address_space which will be used as the special page->mapping for
- * balloon device enlisted page instances.
- */
-struct address_space *balloon_mapping_alloc(struct balloon_dev_info *b_dev_info,
-                               const struct address_space_operations *a_ops)
-{
-       struct address_space *mapping;
-
-       mapping = kmalloc(sizeof(*mapping), GFP_KERNEL);
-       if (!mapping)
-               return ERR_PTR(-ENOMEM);
-
-       /*
-        * Give a clean 'zeroed' status to all elements of this special
-        * balloon page->mapping struct address_space instance.
-        */
-       address_space_init_once(mapping);
-
-       /*
-        * Set mapping->flags appropriately, to allow balloon pages
-        * ->mapping identification.
-        */
-       mapping_set_balloon(mapping);
-       mapping_set_gfp_mask(mapping, balloon_mapping_gfp_mask());
-
-       /* balloon's page->mapping->a_ops callback descriptor */
-       mapping->a_ops = a_ops;
-
-       /*
-        * Establish a pointer reference back to the balloon device descriptor
-        * this particular page->mapping will be servicing.
-        * This is used by compaction / migration procedures to identify and
-        * access the balloon device pageset while isolating / migrating pages.
-        *
-        * As some balloon drivers can register multiple balloon devices
-        * for a single guest, this also helps compaction / migration to
-        * properly deal with multiple balloon pagesets, when required.
-        */
-       mapping->private_data = b_dev_info;
-       b_dev_info->mapping = mapping;
-
-       return mapping;
-}
-EXPORT_SYMBOL_GPL(balloon_mapping_alloc);
 
 static inline void __isolate_balloon_page(struct page *page)
 {
-       struct balloon_dev_info *b_dev_info = page->mapping->private_data;
+       struct balloon_dev_info *b_dev_info = balloon_page_device(page);
        unsigned long flags;
+
        spin_lock_irqsave(&b_dev_info->pages_lock, flags);
+       ClearPagePrivate(page);
        list_del(&page->lru);
        b_dev_info->isolated_pages++;
        spin_unlock_irqrestore(&b_dev_info->pages_lock, flags);
@@ -195,20 +118,16 @@ static inline void __isolate_balloon_page(struct page *page)
 
 static inline void __putback_balloon_page(struct page *page)
 {
-       struct balloon_dev_info *b_dev_info = page->mapping->private_data;
+       struct balloon_dev_info *b_dev_info = balloon_page_device(page);
        unsigned long flags;
+
        spin_lock_irqsave(&b_dev_info->pages_lock, flags);
+       SetPagePrivate(page);
        list_add(&page->lru, &b_dev_info->pages);
        b_dev_info->isolated_pages--;
        spin_unlock_irqrestore(&b_dev_info->pages_lock, flags);
 }
 
-static inline int __migrate_balloon_page(struct address_space *mapping,
-               struct page *newpage, struct page *page, enum migrate_mode mode)
-{
-       return page->mapping->a_ops->migratepage(mapping, newpage, page, mode);
-}
-
 /* __isolate_lru_page() counterpart for a ballooned page */
 bool balloon_page_isolate(struct page *page)
 {
@@ -235,12 +154,11 @@ bool balloon_page_isolate(struct page *page)
                 */
                if (likely(trylock_page(page))) {
                        /*
-                        * A ballooned page, by default, has just one refcount.
+                        * A ballooned page, by default, has PagePrivate set.
                         * Prevent concurrent compaction threads from isolating
-                        * an already isolated balloon page by refcount check.
+                        * an already isolated balloon page by clearing it.
                         */
-                       if (__is_movable_balloon_page(page) &&
-                           page_count(page) == 2) {
+                       if (balloon_page_movable(page)) {
                                __isolate_balloon_page(page);
                                unlock_page(page);
                                return true;
@@ -276,7 +194,7 @@ void balloon_page_putback(struct page *page)
 int balloon_page_migrate(struct page *newpage,
                         struct page *page, enum migrate_mode mode)
 {
-       struct address_space *mapping;
+       struct balloon_dev_info *balloon = balloon_page_device(page);
        int rc = -EAGAIN;
 
        /*
@@ -292,9 +210,8 @@ int balloon_page_migrate(struct page *newpage,
                return rc;
        }
 
-       mapping = page->mapping;
-       if (mapping)
-               rc = __migrate_balloon_page(mapping, newpage, page, mode);
+       if (balloon && balloon->migratepage)
+               rc = balloon->migratepage(balloon, newpage, page, mode);
 
        unlock_page(newpage);
        return rc;
index 90bd3507b413b613c6eb0f51b643e2e0fcd615d2..8a000cebb0d7428d5ec48dcfa979086c57e85109 100644 (file)
@@ -16,9 +16,9 @@
 #include <linux/kmemleak.h>
 #include <linux/range.h>
 #include <linux/memblock.h>
+#include <linux/bug.h>
+#include <linux/io.h>
 
-#include <asm/bug.h>
-#include <asm/io.h>
 #include <asm/processor.h>
 
 #include "internal.h"
index c17751c0dcafb95eda9f41e9cdc9ce101edbebed..474c644a0dc6deafea451935e6d44b72d5e1903e 100644 (file)
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -32,6 +32,7 @@
 #include <linux/slab.h>
 #include <linux/log2.h>
 #include <linux/cma.h>
+#include <linux/highmem.h>
 
 struct cma {
        unsigned long   base_pfn;
@@ -163,6 +164,8 @@ int __init cma_declare_contiguous(phys_addr_t base,
                        bool fixed, struct cma **res_cma)
 {
        struct cma *cma;
+       phys_addr_t memblock_end = memblock_end_of_DRAM();
+       phys_addr_t highmem_start = __pa(high_memory);
        int ret = 0;
 
        pr_debug("%s(size %lx, base %08lx, limit %08lx alignment %08lx)\n",
@@ -196,6 +199,24 @@ int __init cma_declare_contiguous(phys_addr_t base,
        if (!IS_ALIGNED(size >> PAGE_SHIFT, 1 << order_per_bit))
                return -EINVAL;
 
+       /*
+        * adjust limit to avoid crossing low/high memory boundary for
+        * automatically allocated regions
+        */
+       if (((limit == 0 || limit > memblock_end) &&
+            (memblock_end - size < highmem_start &&
+             memblock_end > highmem_start)) ||
+           (!fixed && limit > highmem_start && limit - size < highmem_start)) {
+               limit = highmem_start;
+       }
+
+       if (fixed && base < highmem_start && base+size > highmem_start) {
+               ret = -EINVAL;
+               pr_err("Region at %08lx defined on low/high memory boundary (%08lx)\n",
+                       (unsigned long)base, (unsigned long)highmem_start);
+               goto err;
+       }
+
        /* Reserve memory */
        if (base && fixed) {
                if (memblock_is_region_reserved(base, size) ||
index 21bf292b642a6c67fcf953a631ba0bb4534a0215..edba18aed1738c752793f63ae409c22ae373342a 100644 (file)
@@ -67,6 +67,49 @@ static inline bool migrate_async_suitable(int migratetype)
        return is_migrate_cma(migratetype) || migratetype == MIGRATE_MOVABLE;
 }
 
+/*
+ * Check that the whole (or subset of) a pageblock given by the interval of
+ * [start_pfn, end_pfn) is valid and within the same zone, before scanning it
+ * with the migration of free compaction scanner. The scanners then need to
+ * use only pfn_valid_within() check for arches that allow holes within
+ * pageblocks.
+ *
+ * Return struct page pointer of start_pfn, or NULL if checks were not passed.
+ *
+ * It's possible on some configurations to have a setup like node0 node1 node0
+ * i.e. it's possible that all pages within a zones range of pages do not
+ * belong to a single zone. We assume that a border between node0 and node1
+ * can occur within a single pageblock, but not a node0 node1 node0
+ * interleaving within a single pageblock. It is therefore sufficient to check
+ * the first and last page of a pageblock and avoid checking each individual
+ * page in a pageblock.
+ */
+static struct page *pageblock_pfn_to_page(unsigned long start_pfn,
+                               unsigned long end_pfn, struct zone *zone)
+{
+       struct page *start_page;
+       struct page *end_page;
+
+       /* end_pfn is one past the range we are checking */
+       end_pfn--;
+
+       if (!pfn_valid(start_pfn) || !pfn_valid(end_pfn))
+               return NULL;
+
+       start_page = pfn_to_page(start_pfn);
+
+       if (page_zone(start_page) != zone)
+               return NULL;
+
+       end_page = pfn_to_page(end_pfn);
+
+       /* This gives a shorter code than deriving page_zone(end_page) */
+       if (page_zone_id(start_page) != page_zone_id(end_page))
+               return NULL;
+
+       return start_page;
+}
+
 #ifdef CONFIG_COMPACTION
 /* Returns true if the pageblock should be scanned for pages to isolate. */
 static inline bool isolation_suitable(struct compact_control *cc,
@@ -132,7 +175,7 @@ void reset_isolation_suitable(pg_data_t *pgdat)
  */
 static void update_pageblock_skip(struct compact_control *cc,
                        struct page *page, unsigned long nr_isolated,
-                       bool set_unsuitable, bool migrate_scanner)
+                       bool migrate_scanner)
 {
        struct zone *zone = cc->zone;
        unsigned long pfn;
@@ -146,12 +189,7 @@ static void update_pageblock_skip(struct compact_control *cc,
        if (nr_isolated)
                return;
 
-       /*
-        * Only skip pageblocks when all forms of compaction will be known to
-        * fail in the near future.
-        */
-       if (set_unsuitable)
-               set_pageblock_skip(page);
+       set_pageblock_skip(page);
 
        pfn = page_to_pfn(page);
 
@@ -180,52 +218,77 @@ static inline bool isolation_suitable(struct compact_control *cc,
 
 static void update_pageblock_skip(struct compact_control *cc,
                        struct page *page, unsigned long nr_isolated,
-                       bool set_unsuitable, bool migrate_scanner)
+                       bool migrate_scanner)
 {
 }
 #endif /* CONFIG_COMPACTION */
 
-static inline bool should_release_lock(spinlock_t *lock)
+/*
+ * Compaction requires the taking of some coarse locks that are potentially
+ * very heavily contended. For async compaction, back out if the lock cannot
+ * be taken immediately. For sync compaction, spin on the lock if needed.
+ *
+ * Returns true if the lock is held
+ * Returns false if the lock is not held and compaction should abort
+ */
+static bool compact_trylock_irqsave(spinlock_t *lock, unsigned long *flags,
+                                               struct compact_control *cc)
 {
-       return need_resched() || spin_is_contended(lock);
+       if (cc->mode == MIGRATE_ASYNC) {
+               if (!spin_trylock_irqsave(lock, *flags)) {
+                       cc->contended = COMPACT_CONTENDED_LOCK;
+                       return false;
+               }
+       } else {
+               spin_lock_irqsave(lock, *flags);
+       }
+
+       return true;
 }
 
 /*
  * Compaction requires the taking of some coarse locks that are potentially
- * very heavily contended. Check if the process needs to be scheduled or
- * if the lock is contended. For async compaction, back out in the event
- * if contention is severe. For sync compaction, schedule.
+ * very heavily contended. The lock should be periodically unlocked to avoid
+ * having disabled IRQs for a long time, even when there is nobody waiting on
+ * the lock. It might also be that allowing the IRQs will result in
+ * need_resched() becoming true. If scheduling is needed, async compaction
+ * aborts. Sync compaction schedules.
+ * Either compaction type will also abort if a fatal signal is pending.
+ * In either case if the lock was locked, it is dropped and not regained.
  *
- * Returns true if the lock is held.
- * Returns false if the lock is released and compaction should abort
+ * Returns true if compaction should abort due to fatal signal pending, or
+ *             async compaction due to need_resched()
+ * Returns false when compaction can continue (sync compaction might have
+ *             scheduled)
  */
-static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
-                                     bool locked, struct compact_control *cc)
+static bool compact_unlock_should_abort(spinlock_t *lock,
+               unsigned long flags, bool *locked, struct compact_control *cc)
 {
-       if (should_release_lock(lock)) {
-               if (locked) {
-                       spin_unlock_irqrestore(lock, *flags);
-                       locked = false;
-               }
+       if (*locked) {
+               spin_unlock_irqrestore(lock, flags);
+               *locked = false;
+       }
+
+       if (fatal_signal_pending(current)) {
+               cc->contended = COMPACT_CONTENDED_SCHED;
+               return true;
+       }
 
-               /* async aborts if taking too long or contended */
+       if (need_resched()) {
                if (cc->mode == MIGRATE_ASYNC) {
-                       cc->contended = true;
-                       return false;
+                       cc->contended = COMPACT_CONTENDED_SCHED;
+                       return true;
                }
-
                cond_resched();
        }
 
-       if (!locked)
-               spin_lock_irqsave(lock, *flags);
-       return true;
+       return false;
 }
 
 /*
  * Aside from avoiding lock contention, compaction also periodically checks
  * need_resched() and either schedules in sync compaction or aborts async
- * compaction. This is similar to what compact_checklock_irqsave() does, but
+ * compaction. This is similar to what compact_unlock_should_abort() does, but
  * is used where no lock is concerned.
  *
  * Returns false when no scheduling was needed, or sync compaction scheduled.
@@ -236,7 +299,7 @@ static inline bool compact_should_abort(struct compact_control *cc)
        /* async compaction aborts if contended */
        if (need_resched()) {
                if (cc->mode == MIGRATE_ASYNC) {
-                       cc->contended = true;
+                       cc->contended = COMPACT_CONTENDED_SCHED;
                        return true;
                }
 
@@ -250,8 +313,15 @@ static inline bool compact_should_abort(struct compact_control *cc)
 static bool suitable_migration_target(struct page *page)
 {
        /* If the page is a large free page, then disallow migration */
-       if (PageBuddy(page) && page_order(page) >= pageblock_order)
-               return false;
+       if (PageBuddy(page)) {
+               /*
+                * We are checking page_order without zone->lock taken. But
+                * the only small danger is that we skip a potentially suitable
+                * pageblock, so it's not worth to check order for valid range.
+                */
+               if (page_order_unsafe(page) >= pageblock_order)
+                       return false;
+       }
 
        /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
        if (migrate_async_suitable(get_pageblock_migratetype(page)))
@@ -267,16 +337,16 @@ static bool suitable_migration_target(struct page *page)
  * (even though it may still end up isolating some pages).
  */
 static unsigned long isolate_freepages_block(struct compact_control *cc,
-                               unsigned long blockpfn,
+                               unsigned long *start_pfn,
                                unsigned long end_pfn,
                                struct list_head *freelist,
                                bool strict)
 {
        int nr_scanned = 0, total_isolated = 0;
        struct page *cursor, *valid_page = NULL;
-       unsigned long flags;
+       unsigned long flags = 0;
        bool locked = false;
-       bool checked_pageblock = false;
+       unsigned long blockpfn = *start_pfn;
 
        cursor = pfn_to_page(blockpfn);
 
@@ -285,6 +355,16 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
                int isolated, i;
                struct page *page = cursor;
 
+               /*
+                * Periodically drop the lock (if held) regardless of its
+                * contention, to give chance to IRQs. Abort if fatal signal
+                * pending or async compaction detects need_resched()
+                */
+               if (!(blockpfn % SWAP_CLUSTER_MAX)
+                   && compact_unlock_should_abort(&cc->zone->lock, flags,
+                                                               &locked, cc))
+                       break;
+
                nr_scanned++;
                if (!pfn_valid_within(blockpfn))
                        goto isolate_fail;
@@ -295,33 +375,30 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
                        goto isolate_fail;
 
                /*
-                * The zone lock must be held to isolate freepages.
-                * Unfortunately this is a very coarse lock and can be
-                * heavily contended if there are parallel allocations
-                * or parallel compactions. For async compaction do not
-                * spin on the lock and we acquire the lock as late as
-                * possible.
+                * If we already hold the lock, we can skip some rechecking.
+                * Note that if we hold the lock now, checked_pageblock was
+                * already set in some previous iteration (or strict is true),
+                * so it is correct to skip the suitable migration target
+                * recheck as well.
                 */
-               locked = compact_checklock_irqsave(&cc->zone->lock, &flags,
-                                                               locked, cc);
-               if (!locked)
-                       break;
-
-               /* Recheck this is a suitable migration target under lock */
-               if (!strict && !checked_pageblock) {
+               if (!locked) {
                        /*
-                        * We need to check suitability of pageblock only once
-                        * and this isolate_freepages_block() is called with
-                        * pageblock range, so just check once is sufficient.
+                        * The zone lock must be held to isolate freepages.
+                        * Unfortunately this is a very coarse lock and can be
+                        * heavily contended if there are parallel allocations
+                        * or parallel compactions. For async compaction do not
+                        * spin on the lock and we acquire the lock as late as
+                        * possible.
                         */
-                       checked_pageblock = true;
-                       if (!suitable_migration_target(page))
+                       locked = compact_trylock_irqsave(&cc->zone->lock,
+                                                               &flags, cc);
+                       if (!locked)
                                break;
-               }
 
-               /* Recheck this is a buddy page under lock */
-               if (!PageBuddy(page))
-                       goto isolate_fail;
+                       /* Recheck this is a buddy page under lock */
+                       if (!PageBuddy(page))
+                               goto isolate_fail;
+               }
 
                /* Found a free page, break it into order-0 pages */
                isolated = split_free_page(page);
@@ -346,6 +423,9 @@ isolate_fail:
 
        }
 
+       /* Record how far we have got within the block */
+       *start_pfn = blockpfn;
+
        trace_mm_compaction_isolate_freepages(nr_scanned, total_isolated);
 
        /*
@@ -361,8 +441,7 @@ isolate_fail:
 
        /* Update the pageblock-skip if the whole pageblock was scanned */
        if (blockpfn == end_pfn)
-               update_pageblock_skip(cc, valid_page, total_isolated, true,
-                                     false);
+               update_pageblock_skip(cc, valid_page, total_isolated, false);
 
        count_compact_events(COMPACTFREE_SCANNED, nr_scanned);
        if (total_isolated)
@@ -390,19 +469,21 @@ isolate_freepages_range(struct compact_control *cc,
        unsigned long isolated, pfn, block_end_pfn;
        LIST_HEAD(freelist);
 
-       for (pfn = start_pfn; pfn < end_pfn; pfn += isolated) {
-               if (!pfn_valid(pfn) || cc->zone != page_zone(pfn_to_page(pfn)))
-                       break;
+       pfn = start_pfn;
+       block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
+
+       for (; pfn < end_pfn; pfn += isolated,
+                               block_end_pfn += pageblock_nr_pages) {
+               /* Protect pfn from changing by isolate_freepages_block */
+               unsigned long isolate_start_pfn = pfn;
 
-               /*
-                * On subsequent iterations ALIGN() is actually not needed,
-                * but we keep it that we not to complicate the code.
-                */
-               block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
                block_end_pfn = min(block_end_pfn, end_pfn);
 
-               isolated = isolate_freepages_block(cc, pfn, block_end_pfn,
-                                                  &freelist, true);
+               if (!pageblock_pfn_to_page(pfn, block_end_pfn, cc->zone))
+                       break;
+
+               isolated = isolate_freepages_block(cc, &isolate_start_pfn,
+                                               block_end_pfn, &freelist, true);
 
                /*
                 * In strict mode, isolate_freepages_block() returns 0 if
@@ -433,22 +514,19 @@ isolate_freepages_range(struct compact_control *cc,
 }
 
 /* Update the number of anon and file isolated pages in the zone */
-static void acct_isolated(struct zone *zone, bool locked, struct compact_control *cc)
+static void acct_isolated(struct zone *zone, struct compact_control *cc)
 {
        struct page *page;
        unsigned int count[2] = { 0, };
 
+       if (list_empty(&cc->migratepages))
+               return;
+
        list_for_each_entry(page, &cc->migratepages, lru)
                count[!!page_is_file_cache(page)]++;
 
-       /* If locked we can use the interrupt unsafe versions */
-       if (locked) {
-               __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
-               __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
-       } else {
-               mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
-               mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
-       }
+       mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
+       mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
 }
 
 /* Similar to reclaim, but different enough that they don't share logic */
@@ -467,40 +545,34 @@ static bool too_many_isolated(struct zone *zone)
 }
 
 /**
- * isolate_migratepages_range() - isolate all migrate-able pages in range.
- * @zone:      Zone pages are in.
+ * isolate_migratepages_block() - isolate all migrate-able pages within
+ *                               a single pageblock
  * @cc:                Compaction control structure.
- * @low_pfn:   The first PFN of the range.
- * @end_pfn:   The one-past-the-last PFN of the range.
- * @unevictable: true if it allows to isolate unevictable pages
+ * @low_pfn:   The first PFN to isolate
+ * @end_pfn:   The one-past-the-last PFN to isolate, within same pageblock
+ * @isolate_mode: Isolation mode to be used.
  *
  * Isolate all pages that can be migrated from the range specified by
- * [low_pfn, end_pfn).  Returns zero if there is a fatal signal
- * pending), otherwise PFN of the first page that was not scanned
- * (which may be both less, equal to or more then end_pfn).
+ * [low_pfn, end_pfn). The range is expected to be within same pageblock.
+ * Returns zero if there is a fatal signal pending, otherwise PFN of the
+ * first page that was not scanned (which may be both less, equal to or more
+ * than end_pfn).
  *
- * Assumes that cc->migratepages is empty and cc->nr_migratepages is
- * zero.
- *
- * Apart from cc->migratepages and cc->nr_migratetypes this function
- * does not modify any cc's fields, in particular it does not modify
- * (or read for that matter) cc->migrate_pfn.
+ * The pages are isolated on cc->migratepages list (not required to be empty),
+ * and cc->nr_migratepages is updated accordingly. The cc->migrate_pfn field
+ * is neither read nor updated.
  */
-unsigned long
-isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
-               unsigned long low_pfn, unsigned long end_pfn, bool unevictable)
+static unsigned long
+isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
+                       unsigned long end_pfn, isolate_mode_t isolate_mode)
 {
-       unsigned long last_pageblock_nr = 0, pageblock_nr;
+       struct zone *zone = cc->zone;
        unsigned long nr_scanned = 0, nr_isolated = 0;
        struct list_head *migratelist = &cc->migratepages;
        struct lruvec *lruvec;
-       unsigned long flags;
+       unsigned long flags = 0;
        bool locked = false;
        struct page *page = NULL, *valid_page = NULL;
-       bool set_unsuitable = true;
-       const isolate_mode_t mode = (cc->mode == MIGRATE_ASYNC ?
-                                       ISOLATE_ASYNC_MIGRATE : 0) |
-                                   (unevictable ? ISOLATE_UNEVICTABLE : 0);
 
        /*
         * Ensure that there are not too many pages isolated from the LRU
@@ -523,72 +595,43 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
 
        /* Time to isolate some pages for migration */
        for (; low_pfn < end_pfn; low_pfn++) {
-               /* give a chance to irqs before checking need_resched() */
-               if (locked && !(low_pfn % SWAP_CLUSTER_MAX)) {
-                       if (should_release_lock(&zone->lru_lock)) {
-                               spin_unlock_irqrestore(&zone->lru_lock, flags);
-                               locked = false;
-                       }
-               }
-
                /*
-                * migrate_pfn does not necessarily start aligned to a
-                * pageblock. Ensure that pfn_valid is called when moving
-                * into a new MAX_ORDER_NR_PAGES range in case of large
-                * memory holes within the zone
+                * Periodically drop the lock (if held) regardless of its
+                * contention, to give chance to IRQs. Abort async compaction
+                * if contended.
                 */
-               if ((low_pfn & (MAX_ORDER_NR_PAGES - 1)) == 0) {
-                       if (!pfn_valid(low_pfn)) {
-                               low_pfn += MAX_ORDER_NR_PAGES - 1;
-                               continue;
-                       }
-               }
+               if (!(low_pfn % SWAP_CLUSTER_MAX)
+                   && compact_unlock_should_abort(&zone->lru_lock, flags,
+                                                               &locked, cc))
+                       break;
 
                if (!pfn_valid_within(low_pfn))
                        continue;
                nr_scanned++;
 
-               /*
-                * Get the page and ensure the page is within the same zone.
-                * See the comment in isolate_freepages about overlapping
-                * nodes. It is deliberate that the new zone lock is not taken
-                * as memory compaction should not move pages between nodes.
-                */
                page = pfn_to_page(low_pfn);
-               if (page_zone(page) != zone)
-                       continue;
 
                if (!valid_page)
                        valid_page = page;
 
-               /* If isolation recently failed, do not retry */
-               pageblock_nr = low_pfn >> pageblock_order;
-               if (last_pageblock_nr != pageblock_nr) {
-                       int mt;
-
-                       last_pageblock_nr = pageblock_nr;
-                       if (!isolation_suitable(cc, page))
-                               goto next_pageblock;
+               /*
+                * Skip if free. We read page order here without zone lock
+                * which is generally unsafe, but the race window is small and
+                * the worst thing that can happen is that we skip some
+                * potential isolation targets.
+                */
+               if (PageBuddy(page)) {
+                       unsigned long freepage_order = page_order_unsafe(page);
 
                        /*
-                        * For async migration, also only scan in MOVABLE
-                        * blocks. Async migration is optimistic to see if
-                        * the minimum amount of work satisfies the allocation
+                        * Without lock, we cannot be sure that what we got is
+                        * a valid page order. Consider only values in the
+                        * valid order range to prevent low_pfn overflow.
                         */
-                       mt = get_pageblock_migratetype(page);
-                       if (cc->mode == MIGRATE_ASYNC &&
-                           !migrate_async_suitable(mt)) {
-                               set_unsuitable = false;
-                               goto next_pageblock;
-                       }
-               }
-
-               /*
-                * Skip if free. page_order cannot be used without zone->lock
-                * as nothing prevents parallel allocations or buddy merging.
-                */
-               if (PageBuddy(page))
+                       if (freepage_order > 0 && freepage_order < MAX_ORDER)
+                               low_pfn += (1UL << freepage_order) - 1;
                        continue;
+               }
 
                /*
                 * Check may be lockless but that's ok as we recheck later.
@@ -597,7 +640,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
                 */
                if (!PageLRU(page)) {
                        if (unlikely(balloon_page_movable(page))) {
-                               if (locked && balloon_page_isolate(page)) {
+                               if (balloon_page_isolate(page)) {
                                        /* Successfully isolated */
                                        goto isolate_success;
                                }
@@ -617,8 +660,11 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
                 */
                if (PageTransHuge(page)) {
                        if (!locked)
-                               goto next_pageblock;
-                       low_pfn += (1 << compound_order(page)) - 1;
+                               low_pfn = ALIGN(low_pfn + 1,
+                                               pageblock_nr_pages) - 1;
+                       else
+                               low_pfn += (1 << compound_order(page)) - 1;
+
                        continue;
                }
 
@@ -631,24 +677,26 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
                    page_count(page) > page_mapcount(page))
                        continue;
 
-               /* Check if it is ok to still hold the lock */
-               locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
-                                                               locked, cc);
-               if (!locked || fatal_signal_pending(current))
-                       break;
+               /* If we already hold the lock, we can skip some rechecking */
+               if (!locked) {
+                       locked = compact_trylock_irqsave(&zone->lru_lock,
+                                                               &flags, cc);
+                       if (!locked)
+                               break;
 
-               /* Recheck PageLRU and PageTransHuge under lock */
-               if (!PageLRU(page))
-                       continue;
-               if (PageTransHuge(page)) {
-                       low_pfn += (1 << compound_order(page)) - 1;
-                       continue;
+                       /* Recheck PageLRU and PageTransHuge under lock */
+                       if (!PageLRU(page))
+                               continue;
+                       if (PageTransHuge(page)) {
+                               low_pfn += (1 << compound_order(page)) - 1;
+                               continue;
+                       }
                }
 
                lruvec = mem_cgroup_page_lruvec(page, zone);
 
                /* Try isolate the page */
-               if (__isolate_lru_page(page, mode) != 0)
+               if (__isolate_lru_page(page, isolate_mode) != 0)
                        continue;
 
                VM_BUG_ON_PAGE(PageTransCompound(page), page);
@@ -667,14 +715,14 @@ isolate_success:
                        ++low_pfn;
                        break;
                }
-
-               continue;
-
-next_pageblock:
-               low_pfn = ALIGN(low_pfn + 1, pageblock_nr_pages) - 1;
        }
 
-       acct_isolated(zone, locked, cc);
+       /*
+        * The PageBuddy() check could have potentially brought us outside
+        * the range to be scanned.
+        */
+       if (unlikely(low_pfn > end_pfn))
+               low_pfn = end_pfn;
 
        if (locked)
                spin_unlock_irqrestore(&zone->lru_lock, flags);
@@ -684,8 +732,7 @@ next_pageblock:
         * if the whole pageblock was scanned without isolating any page.
         */
        if (low_pfn == end_pfn)
-               update_pageblock_skip(cc, valid_page, nr_isolated,
-                                     set_unsuitable, true);
+               update_pageblock_skip(cc, valid_page, nr_isolated, true);
 
        trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
 
@@ -696,17 +743,65 @@ next_pageblock:
        return low_pfn;
 }
 
+/**
+ * isolate_migratepages_range() - isolate migrate-able pages in a PFN range
+ * @cc:        Compaction control structure.
+ * @start_pfn: The first PFN to start isolating.
+ * @end_pfn:   The one-past-last PFN.
+ *
+ * Returns zero if isolation fails fatally due to e.g. pending signal.
+ * Otherwise, function returns one-past-the-last PFN of isolated page
+ * (which may be greater than end_pfn if end fell in a middle of a THP page).
+ */
+unsigned long
+isolate_migratepages_range(struct compact_control *cc, unsigned long start_pfn,
+                                                       unsigned long end_pfn)
+{
+       unsigned long pfn, block_end_pfn;
+
+       /* Scan block by block. First and last block may be incomplete */
+       pfn = start_pfn;
+       block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
+
+       for (; pfn < end_pfn; pfn = block_end_pfn,
+                               block_end_pfn += pageblock_nr_pages) {
+
+               block_end_pfn = min(block_end_pfn, end_pfn);
+
+               if (!pageblock_pfn_to_page(pfn, block_end_pfn, cc->zone))
+                       continue;
+
+               pfn = isolate_migratepages_block(cc, pfn, block_end_pfn,
+                                                       ISOLATE_UNEVICTABLE);
+
+               /*
+                * In case of fatal failure, release everything that might
+                * have been isolated in the previous iteration, and signal
+                * the failure back to caller.
+                */
+               if (!pfn) {
+                       putback_movable_pages(&cc->migratepages);
+                       cc->nr_migratepages = 0;
+                       break;
+               }
+       }
+       acct_isolated(cc->zone, cc);
+
+       return pfn;
+}
+
 #endif /* CONFIG_COMPACTION || CONFIG_CMA */
 #ifdef CONFIG_COMPACTION
 /*
  * Based on information in the current compact_control, find blocks
  * suitable for isolating free pages from and then isolate them.
  */
-static void isolate_freepages(struct zone *zone,
-                               struct compact_control *cc)
+static void isolate_freepages(struct compact_control *cc)
 {
+       struct zone *zone = cc->zone;
        struct page *page;
        unsigned long block_start_pfn;  /* start of current pageblock */
+       unsigned long isolate_start_pfn; /* exact pfn we start at */
        unsigned long block_end_pfn;    /* end of current pageblock */
        unsigned long low_pfn;       /* lowest pfn scanner is able to scan */
        int nr_freepages = cc->nr_freepages;
@@ -715,14 +810,15 @@ static void isolate_freepages(struct zone *zone,
        /*
         * Initialise the free scanner. The starting point is where we last
         * successfully isolated from, zone-cached value, or the end of the
-        * zone when isolating for the first time. We need this aligned to
-        * the pageblock boundary, because we do
+        * zone when isolating for the first time. For looping we also need
+        * this pfn aligned down to the pageblock boundary, because we do
         * block_start_pfn -= pageblock_nr_pages in the for loop.
         * For ending point, take care when isolating in last pageblock of a
         * a zone which ends in the middle of a pageblock.
         * The low boundary is the end of the pageblock the migration scanner
         * is using.
         */
+       isolate_start_pfn = cc->free_pfn;
        block_start_pfn = cc->free_pfn & ~(pageblock_nr_pages-1);
        block_end_pfn = min(block_start_pfn + pageblock_nr_pages,
                                                zone_end_pfn(zone));
@@ -735,7 +831,8 @@ static void isolate_freepages(struct zone *zone,
         */
        for (; block_start_pfn >= low_pfn && cc->nr_migratepages > nr_freepages;
                                block_end_pfn = block_start_pfn,
-                               block_start_pfn -= pageblock_nr_pages) {
+                               block_start_pfn -= pageblock_nr_pages,
+                               isolate_start_pfn = block_start_pfn) {
                unsigned long isolated;
 
                /*
@@ -747,18 +844,9 @@ static void isolate_freepages(struct zone *zone,
                                                && compact_should_abort(cc))
                        break;
 
-               if (!pfn_valid(block_start_pfn))
-                       continue;
-
-               /*
-                * Check for overlapping nodes/zones. It's possible on some
-                * configurations to have a setup like
-                * node0 node1 node0
-                * i.e. it's possible that all pages within a zones range of
-                * pages do not belong to a single zone.
-                */
-               page = pfn_to_page(block_start_pfn);
-               if (page_zone(page) != zone)
+               page = pageblock_pfn_to_page(block_start_pfn, block_end_pfn,
+                                                                       zone);
+               if (!page)
                        continue;
 
                /* Check the block is suitable for migration */
@@ -769,12 +857,24 @@ static void isolate_freepages(struct zone *zone,
                if (!isolation_suitable(cc, page))
                        continue;
 
-               /* Found a block suitable for isolating free pages from */
-               cc->free_pfn = block_start_pfn;
-               isolated = isolate_freepages_block(cc, block_start_pfn,
+               /* Found a block suitable for isolating free pages from. */
+               isolated = isolate_freepages_block(cc, &isolate_start_pfn,
                                        block_end_pfn, freelist, false);
                nr_freepages += isolated;
 
+               /*
+                * Remember where the free scanner should restart next time,
+                * which is where isolate_freepages_block() left off.
+                * But if it scanned the whole pageblock, isolate_start_pfn
+                * now points at block_end_pfn, which is the start of the next
+                * pageblock.
+                * In that case we will however want to restart at the start
+                * of the previous pageblock.
+                */
+               cc->free_pfn = (isolate_start_pfn < block_end_pfn) ?
+                               isolate_start_pfn :
+                               block_start_pfn - pageblock_nr_pages;
+
                /*
                 * Set a flag that we successfully isolated in this pageblock.
                 * In the next loop iteration, zone->compact_cached_free_pfn
@@ -822,7 +922,7 @@ static struct page *compaction_alloc(struct page *migratepage,
         */
        if (list_empty(&cc->freepages)) {
                if (!cc->contended)
-                       isolate_freepages(cc->zone, cc);
+                       isolate_freepages(cc);
 
                if (list_empty(&cc->freepages))
                        return NULL;
@@ -856,38 +956,84 @@ typedef enum {
 } isolate_migrate_t;
 
 /*
- * Isolate all pages that can be migrated from the block pointed to by
- * the migrate scanner within compact_control.
+ * Isolate all pages that can be migrated from the first suitable block,
+ * starting at the block pointed to by the migrate scanner pfn within
+ * compact_control.
  */
 static isolate_migrate_t isolate_migratepages(struct zone *zone,
                                        struct compact_control *cc)
 {
        unsigned long low_pfn, end_pfn;
+       struct page *page;
+       const isolate_mode_t isolate_mode =
+               (cc->mode == MIGRATE_ASYNC ? ISOLATE_ASYNC_MIGRATE : 0);
 
-       /* Do not scan outside zone boundaries */
-       low_pfn = max(cc->migrate_pfn, zone->zone_start_pfn);
+       /*
+        * Start at where we last stopped, or beginning of the zone as
+        * initialized by compact_zone()
+        */
+       low_pfn = cc->migrate_pfn;
 
        /* Only scan within a pageblock boundary */
        end_pfn = ALIGN(low_pfn + 1, pageblock_nr_pages);
 
-       /* Do not cross the free scanner or scan within a memory hole */
-       if (end_pfn > cc->free_pfn || !pfn_valid(low_pfn)) {
-               cc->migrate_pfn = end_pfn;
-               return ISOLATE_NONE;
-       }
+       /*
+        * Iterate over whole pageblocks until we find the first suitable.
+        * Do not cross the free scanner.
+        */
+       for (; end_pfn <= cc->free_pfn;
+                       low_pfn = end_pfn, end_pfn += pageblock_nr_pages) {
 
-       /* Perform the isolation */
-       low_pfn = isolate_migratepages_range(zone, cc, low_pfn, end_pfn, false);
-       if (!low_pfn || cc->contended)
-               return ISOLATE_ABORT;
+               /*
+                * This can potentially iterate a massively long zone with
+                * many pageblocks unsuitable, so periodically check if we
+                * need to schedule, or even abort async compaction.
+                */
+               if (!(low_pfn % (SWAP_CLUSTER_MAX * pageblock_nr_pages))
+                                               && compact_should_abort(cc))
+                       break;
+
+               page = pageblock_pfn_to_page(low_pfn, end_pfn, zone);
+               if (!page)
+                       continue;
+
+               /* If isolation recently failed, do not retry */
+               if (!isolation_suitable(cc, page))
+                       continue;
+
+               /*
+                * For async compaction, also only scan in MOVABLE blocks.
+                * Async compaction is optimistic to see if the minimum amount
+                * of work satisfies the allocation.
+                */
+               if (cc->mode == MIGRATE_ASYNC &&
+                   !migrate_async_suitable(get_pageblock_migratetype(page)))
+                       continue;
+
+               /* Perform the isolation */
+               low_pfn = isolate_migratepages_block(cc, low_pfn, end_pfn,
+                                                               isolate_mode);
 
+               if (!low_pfn || cc->contended)
+                       return ISOLATE_ABORT;
+
+               /*
+                * Either we isolated something and proceed with migration. Or
+                * we failed and compact_zone should decide if we should
+                * continue or not.
+                */
+               break;
+       }
+
+       acct_isolated(zone, cc);
+       /* Record where migration scanner will be restarted */
        cc->migrate_pfn = low_pfn;
 
-       return ISOLATE_SUCCESS;
+       return cc->nr_migratepages ? ISOLATE_SUCCESS : ISOLATE_NONE;
 }
 
-static int compact_finished(struct zone *zone,
-                           struct compact_control *cc)
+static int compact_finished(struct zone *zone, struct compact_control *cc,
+                           const int migratetype)
 {
        unsigned int order;
        unsigned long watermark;
@@ -933,7 +1079,7 @@ static int compact_finished(struct zone *zone,
                struct free_area *area = &zone->free_area[order];
 
                /* Job done if page is free of the right migratetype */
-               if (!list_empty(&area->free_list[cc->migratetype]))
+               if (!list_empty(&area->free_list[migratetype]))
                        return COMPACT_PARTIAL;
 
                /* Job done if allocation would set block type */
@@ -999,6 +1145,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
        int ret;
        unsigned long start_pfn = zone->zone_start_pfn;
        unsigned long end_pfn = zone_end_pfn(zone);
+       const int migratetype = gfpflags_to_migratetype(cc->gfp_mask);
        const bool sync = cc->mode != MIGRATE_ASYNC;
 
        ret = compaction_suitable(zone, cc->order);
@@ -1041,7 +1188,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
 
        migrate_prep_local();
 
-       while ((ret = compact_finished(zone, cc)) == COMPACT_CONTINUE) {
+       while ((ret = compact_finished(zone, cc, migratetype)) ==
+                                               COMPACT_CONTINUE) {
                int err;
 
                switch (isolate_migratepages(zone, cc)) {
@@ -1056,9 +1204,6 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
                        ;
                }
 
-               if (!cc->nr_migratepages)
-                       continue;
-
                err = migrate_pages(&cc->migratepages, compaction_alloc,
                                compaction_free, (unsigned long)cc, cc->mode,
                                MR_COMPACTION);
@@ -1092,14 +1237,14 @@ out:
 }
 
 static unsigned long compact_zone_order(struct zone *zone, int order,
-               gfp_t gfp_mask, enum migrate_mode mode, bool *contended)
+               gfp_t gfp_mask, enum migrate_mode mode, int *contended)
 {
        unsigned long ret;
        struct compact_control cc = {
                .nr_freepages = 0,
                .nr_migratepages = 0,
                .order = order,
-               .migratetype = allocflags_to_migratetype(gfp_mask),
+               .gfp_mask = gfp_mask,
                .zone = zone,
                .mode = mode,
        };
@@ -1124,48 +1269,117 @@ int sysctl_extfrag_threshold = 500;
  * @gfp_mask: The GFP mask of the current allocation
  * @nodemask: The allowed nodes to allocate from
  * @mode: The migration mode for async, sync light, or sync migration
- * @contended: Return value that is true if compaction was aborted due to lock contention
- * @page: Optionally capture a free page of the requested order during compaction
+ * @contended: Return value that determines if compaction was aborted due to
+ *            need_resched() or lock contention
+ * @candidate_zone: Return the zone where we think allocation should succeed
  *
  * This is the main entry point for direct page compaction.
  */
 unsigned long try_to_compact_pages(struct zonelist *zonelist,
                        int order, gfp_t gfp_mask, nodemask_t *nodemask,
-                       enum migrate_mode mode, bool *contended)
+                       enum migrate_mode mode, int *contended,
+                       struct zone **candidate_zone)
 {
        enum zone_type high_zoneidx = gfp_zone(gfp_mask);
        int may_enter_fs = gfp_mask & __GFP_FS;
        int may_perform_io = gfp_mask & __GFP_IO;
        struct zoneref *z;
        struct zone *zone;
-       int rc = COMPACT_SKIPPED;
+       int rc = COMPACT_DEFERRED;
        int alloc_flags = 0;
+       int all_zones_contended = COMPACT_CONTENDED_LOCK; /* init for &= op */
+
+       *contended = COMPACT_CONTENDED_NONE;
 
        /* Check if the GFP flags allow compaction */
        if (!order || !may_enter_fs || !may_perform_io)
-               return rc;
-
-       count_compact_event(COMPACTSTALL);
+               return COMPACT_SKIPPED;
 
 #ifdef CONFIG_CMA
-       if (allocflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE)
+       if (gfpflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE)
                alloc_flags |= ALLOC_CMA;
 #endif
        /* Compact each zone in the list */
        for_each_zone_zonelist_nodemask(zone, z, zonelist, high_zoneidx,
                                                                nodemask) {
                int status;
+               int zone_contended;
+
+               if (compaction_deferred(zone, order))
+                       continue;
 
                status = compact_zone_order(zone, order, gfp_mask, mode,
-                                               contended);
+                                                       &zone_contended);
                rc = max(status, rc);
+               /*
+                * It takes at least one zone that wasn't lock contended
+                * to clear all_zones_contended.
+                */
+               all_zones_contended &= zone_contended;
 
                /* If a normal allocation would succeed, stop compacting */
                if (zone_watermark_ok(zone, order, low_wmark_pages(zone), 0,
-                                     alloc_flags))
-                       break;
+                                     alloc_flags)) {
+                       *candidate_zone = zone;
+                       /*
+                        * We think the allocation will succeed in this zone,
+                        * but it is not certain, hence the false. The caller
+                        * will repeat this with true if allocation indeed
+                        * succeeds in this zone.
+                        */
+                       compaction_defer_reset(zone, order, false);
+                       /*
+                        * It is possible that async compaction aborted due to
+                        * need_resched() and the watermarks were ok thanks to
+                        * somebody else freeing memory. The allocation can
+                        * however still fail so we better signal the
+                        * need_resched() contention anyway (this will not
+                        * prevent the allocation attempt).
+                        */
+                       if (zone_contended == COMPACT_CONTENDED_SCHED)
+                               *contended = COMPACT_CONTENDED_SCHED;
+
+                       goto break_loop;
+               }
+
+               if (mode != MIGRATE_ASYNC) {
+                       /*
+                        * We think that allocation won't succeed in this zone
+                        * so we defer compaction there. If it ends up
+                        * succeeding after all, it will be reset.
+                        */
+                       defer_compaction(zone, order);
+               }
+
+               /*
+                * We might have stopped compacting due to need_resched() in
+                * async compaction, or due to a fatal signal detected. In that
+                * case do not try further zones and signal need_resched()
+                * contention.
+                */
+               if ((zone_contended == COMPACT_CONTENDED_SCHED)
+                                       || fatal_signal_pending(current)) {
+                       *contended = COMPACT_CONTENDED_SCHED;
+                       goto break_loop;
+               }
+
+               continue;
+break_loop:
+               /*
+                * We might not have tried all the zones, so  be conservative
+                * and assume they are not all lock contended.
+                */
+               all_zones_contended = 0;
+               break;
        }
 
+       /*
+        * If at least one zone wasn't deferred or skipped, we report if all
+        * zones that were tried were lock contended.
+        */
+       if (rc > COMPACT_SKIPPED && all_zones_contended)
+               *contended = COMPACT_CONTENDED_LOCK;
+
        return rc;
 }
 
diff --git a/mm/debug.c b/mm/debug.c
new file mode 100644 (file)
index 0000000..5ce45c9
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * mm/debug.c
+ *
+ * mm/ specific debug routines.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ftrace_event.h>
+#include <linux/memcontrol.h>
+
+static const struct trace_print_flags pageflag_names[] = {
+       {1UL << PG_locked,              "locked"        },
+       {1UL << PG_error,               "error"         },
+       {1UL << PG_referenced,          "referenced"    },
+       {1UL << PG_uptodate,            "uptodate"      },
+       {1UL << PG_dirty,               "dirty"         },
+       {1UL << PG_lru,                 "lru"           },
+       {1UL << PG_active,              "active"        },
+       {1UL << PG_slab,                "slab"          },
+       {1UL << PG_owner_priv_1,        "owner_priv_1"  },
+       {1UL << PG_arch_1,              "arch_1"        },
+       {1UL << PG_reserved,            "reserved"      },
+       {1UL << PG_private,             "private"       },
+       {1UL << PG_private_2,           "private_2"     },
+       {1UL << PG_writeback,           "writeback"     },
+#ifdef CONFIG_PAGEFLAGS_EXTENDED
+       {1UL << PG_head,                "head"          },
+       {1UL << PG_tail,                "tail"          },
+#else
+       {1UL << PG_compound,            "compound"      },
+#endif
+       {1UL << PG_swapcache,           "swapcache"     },
+       {1UL << PG_mappedtodisk,        "mappedtodisk"  },
+       {1UL << PG_reclaim,             "reclaim"       },
+       {1UL << PG_swapbacked,          "swapbacked"    },
+       {1UL << PG_unevictable,         "unevictable"   },
+#ifdef CONFIG_MMU
+       {1UL << PG_mlocked,             "mlocked"       },
+#endif
+#ifdef CONFIG_ARCH_USES_PG_UNCACHED
+       {1UL << PG_uncached,            "uncached"      },
+#endif
+#ifdef CONFIG_MEMORY_FAILURE
+       {1UL << PG_hwpoison,            "hwpoison"      },
+#endif
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       {1UL << PG_compound_lock,       "compound_lock" },
+#endif
+};
+
+static void dump_flags(unsigned long flags,
+                       const struct trace_print_flags *names, int count)
+{
+       const char *delim = "";
+       unsigned long mask;
+       int i;
+
+       pr_emerg("flags: %#lx(", flags);
+
+       /* remove zone id */
+       flags &= (1UL << NR_PAGEFLAGS) - 1;
+
+       for (i = 0; i < count && flags; i++) {
+
+               mask = names[i].mask;
+               if ((flags & mask) != mask)
+                       continue;
+
+               flags &= ~mask;
+               pr_cont("%s%s", delim, names[i].name);
+               delim = "|";
+       }
+
+       /* check for left over flags */
+       if (flags)
+               pr_cont("%s%#lx", delim, flags);
+
+       pr_cont(")\n");
+}
+
+void dump_page_badflags(struct page *page, const char *reason,
+               unsigned long badflags)
+{
+       pr_emerg("page:%p count:%d mapcount:%d mapping:%p index:%#lx\n",
+                 page, atomic_read(&page->_count), page_mapcount(page),
+                 page->mapping, page->index);
+       BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS);
+       dump_flags(page->flags, pageflag_names, ARRAY_SIZE(pageflag_names));
+       if (reason)
+               pr_alert("page dumped because: %s\n", reason);
+       if (page->flags & badflags) {
+               pr_alert("bad because of flags:\n");
+               dump_flags(page->flags & badflags,
+                               pageflag_names, ARRAY_SIZE(pageflag_names));
+       }
+       mem_cgroup_print_bad_page(page);
+}
+
+void dump_page(struct page *page, const char *reason)
+{
+       dump_page_badflags(page, reason, 0);
+}
+EXPORT_SYMBOL(dump_page);
+
+#ifdef CONFIG_DEBUG_VM
+
+static const struct trace_print_flags vmaflags_names[] = {
+       {VM_READ,                       "read"          },
+       {VM_WRITE,                      "write"         },
+       {VM_EXEC,                       "exec"          },
+       {VM_SHARED,                     "shared"        },
+       {VM_MAYREAD,                    "mayread"       },
+       {VM_MAYWRITE,                   "maywrite"      },
+       {VM_MAYEXEC,                    "mayexec"       },
+       {VM_MAYSHARE,                   "mayshare"      },
+       {VM_GROWSDOWN,                  "growsdown"     },
+       {VM_PFNMAP,                     "pfnmap"        },
+       {VM_DENYWRITE,                  "denywrite"     },
+       {VM_LOCKED,                     "locked"        },
+       {VM_IO,                         "io"            },
+       {VM_SEQ_READ,                   "seqread"       },
+       {VM_RAND_READ,                  "randread"      },
+       {VM_DONTCOPY,                   "dontcopy"      },
+       {VM_DONTEXPAND,                 "dontexpand"    },
+       {VM_ACCOUNT,                    "account"       },
+       {VM_NORESERVE,                  "noreserve"     },
+       {VM_HUGETLB,                    "hugetlb"       },
+       {VM_NONLINEAR,                  "nonlinear"     },
+#if defined(CONFIG_X86)
+       {VM_PAT,                        "pat"           },
+#elif defined(CONFIG_PPC)
+       {VM_SAO,                        "sao"           },
+#elif defined(CONFIG_PARISC) || defined(CONFIG_METAG) || defined(CONFIG_IA64)
+       {VM_GROWSUP,                    "growsup"       },
+#elif !defined(CONFIG_MMU)
+       {VM_MAPPED_COPY,                "mappedcopy"    },
+#else
+       {VM_ARCH_1,                     "arch_1"        },
+#endif
+       {VM_DONTDUMP,                   "dontdump"      },
+#ifdef CONFIG_MEM_SOFT_DIRTY
+       {VM_SOFTDIRTY,                  "softdirty"     },
+#endif
+       {VM_MIXEDMAP,                   "mixedmap"      },
+       {VM_HUGEPAGE,                   "hugepage"      },
+       {VM_NOHUGEPAGE,                 "nohugepage"    },
+       {VM_MERGEABLE,                  "mergeable"     },
+};
+
+void dump_vma(const struct vm_area_struct *vma)
+{
+       pr_emerg("vma %p start %p end %p\n"
+               "next %p prev %p mm %p\n"
+               "prot %lx anon_vma %p vm_ops %p\n"
+               "pgoff %lx file %p private_data %p\n",
+               vma, (void *)vma->vm_start, (void *)vma->vm_end, vma->vm_next,
+               vma->vm_prev, vma->vm_mm,
+               (unsigned long)pgprot_val(vma->vm_page_prot),
+               vma->anon_vma, vma->vm_ops, vma->vm_pgoff,
+               vma->vm_file, vma->vm_private_data);
+       dump_flags(vma->vm_flags, vmaflags_names, ARRAY_SIZE(vmaflags_names));
+}
+EXPORT_SYMBOL(dump_vma);
+
+void dump_mm(const struct mm_struct *mm)
+{
+       pr_emerg("mm %p mmap %p seqnum %d task_size %lu\n"
+#ifdef CONFIG_MMU
+               "get_unmapped_area %p\n"
+#endif
+               "mmap_base %lu mmap_legacy_base %lu highest_vm_end %lu\n"
+               "pgd %p mm_users %d mm_count %d nr_ptes %lu map_count %d\n"
+               "hiwater_rss %lx hiwater_vm %lx total_vm %lx locked_vm %lx\n"
+               "pinned_vm %lx shared_vm %lx exec_vm %lx stack_vm %lx\n"
+               "start_code %lx end_code %lx start_data %lx end_data %lx\n"
+               "start_brk %lx brk %lx start_stack %lx\n"
+               "arg_start %lx arg_end %lx env_start %lx env_end %lx\n"
+               "binfmt %p flags %lx core_state %p\n"
+#ifdef CONFIG_AIO
+               "ioctx_table %p\n"
+#endif
+#ifdef CONFIG_MEMCG
+               "owner %p "
+#endif
+               "exe_file %p\n"
+#ifdef CONFIG_MMU_NOTIFIER
+               "mmu_notifier_mm %p\n"
+#endif
+#ifdef CONFIG_NUMA_BALANCING
+               "numa_next_scan %lu numa_scan_offset %lu numa_scan_seq %d\n"
+#endif
+#if defined(CONFIG_NUMA_BALANCING) || defined(CONFIG_COMPACTION)
+               "tlb_flush_pending %d\n"
+#endif
+               "%s",   /* This is here to hold the comma */
+
+               mm, mm->mmap, mm->vmacache_seqnum, mm->task_size,
+#ifdef CONFIG_MMU
+               mm->get_unmapped_area,
+#endif
+               mm->mmap_base, mm->mmap_legacy_base, mm->highest_vm_end,
+               mm->pgd, atomic_read(&mm->mm_users),
+               atomic_read(&mm->mm_count),
+               atomic_long_read((atomic_long_t *)&mm->nr_ptes),
+               mm->map_count,
+               mm->hiwater_rss, mm->hiwater_vm, mm->total_vm, mm->locked_vm,
+               mm->pinned_vm, mm->shared_vm, mm->exec_vm, mm->stack_vm,
+               mm->start_code, mm->end_code, mm->start_data, mm->end_data,
+               mm->start_brk, mm->brk, mm->start_stack,
+               mm->arg_start, mm->arg_end, mm->env_start, mm->env_end,
+               mm->binfmt, mm->flags, mm->core_state,
+#ifdef CONFIG_AIO
+               mm->ioctx_table,
+#endif
+#ifdef CONFIG_MEMCG
+               mm->owner,
+#endif
+               mm->exe_file,
+#ifdef CONFIG_MMU_NOTIFIER
+               mm->mmu_notifier_mm,
+#endif
+#ifdef CONFIG_NUMA_BALANCING
+               mm->numa_next_scan, mm->numa_scan_offset, mm->numa_scan_seq,
+#endif
+#if defined(CONFIG_NUMA_BALANCING) || defined(CONFIG_COMPACTION)
+               mm->tlb_flush_pending,
+#endif
+               ""              /* This is here to not have a comma! */
+               );
+
+               dump_flags(mm->def_flags, vmaflags_names,
+                               ARRAY_SIZE(vmaflags_names));
+}
+
+#endif         /* CONFIG_DEBUG_VM */
index ba8019b063e18ecb14da4cf3a64dfa671586f8e0..fd5fe4342e9320de634db2e92d9757dd1677f5be 100644 (file)
@@ -62,6 +62,7 @@ struct dma_page {             /* cacheable header for 'allocation' bytes */
 };
 
 static DEFINE_MUTEX(pools_lock);
+static DEFINE_MUTEX(pools_reg_lock);
 
 static ssize_t
 show_pools(struct device *dev, struct device_attribute *attr, char *buf)
@@ -132,29 +133,27 @@ struct dma_pool *dma_pool_create(const char *name, struct device *dev,
 {
        struct dma_pool *retval;
        size_t allocation;
+       bool empty = false;
 
-       if (align == 0) {
+       if (align == 0)
                align = 1;
-       } else if (align & (align - 1)) {
+       else if (align & (align - 1))
                return NULL;
-       }
 
-       if (size == 0) {
+       if (size == 0)
                return NULL;
-       } else if (size < 4) {
+       else if (size < 4)
                size = 4;
-       }
 
        if ((size % align) != 0)
                size = ALIGN(size, align);
 
        allocation = max_t(size_t, size, PAGE_SIZE);
 
-       if (!boundary) {
+       if (!boundary)
                boundary = allocation;
-       } else if ((boundary < size) || (boundary & (boundary - 1))) {
+       else if ((boundary < size) || (boundary & (boundary - 1)))
                return NULL;
-       }
 
        retval = kmalloc_node(sizeof(*retval), GFP_KERNEL, dev_to_node(dev));
        if (!retval)
@@ -172,15 +171,34 @@ struct dma_pool *dma_pool_create(const char *name, struct device *dev,
 
        INIT_LIST_HEAD(&retval->pools);
 
+       /*
+        * pools_lock ensures that the ->dma_pools list does not get corrupted.
+        * pools_reg_lock ensures that there is not a race between
+        * dma_pool_create() and dma_pool_destroy() or within dma_pool_create()
+        * when the first invocation of dma_pool_create() failed on
+        * device_create_file() and the second assumes that it has been done (I
+        * know it is a short window).
+        */
+       mutex_lock(&pools_reg_lock);
        mutex_lock(&pools_lock);
-       if (list_empty(&dev->dma_pools) &&
-           device_create_file(dev, &dev_attr_pools)) {
-               kfree(retval);
-               retval = NULL;
-       } else
-               list_add(&retval->pools, &dev->dma_pools);
+       if (list_empty(&dev->dma_pools))
+               empty = true;
+       list_add(&retval->pools, &dev->dma_pools);
        mutex_unlock(&pools_lock);
-
+       if (empty) {
+               int err;
+
+               err = device_create_file(dev, &dev_attr_pools);
+               if (err) {
+                       mutex_lock(&pools_lock);
+                       list_del(&retval->pools);
+                       mutex_unlock(&pools_lock);
+                       mutex_unlock(&pools_reg_lock);
+                       kfree(retval);
+                       return NULL;
+               }
+       }
+       mutex_unlock(&pools_reg_lock);
        return retval;
 }
 EXPORT_SYMBOL(dma_pool_create);
@@ -251,11 +269,17 @@ static void pool_free_page(struct dma_pool *pool, struct dma_page *page)
  */
 void dma_pool_destroy(struct dma_pool *pool)
 {
+       bool empty = false;
+
+       mutex_lock(&pools_reg_lock);
        mutex_lock(&pools_lock);
        list_del(&pool->pools);
        if (pool->dev && list_empty(&pool->dev->dma_pools))
-               device_remove_file(pool->dev, &dev_attr_pools);
+               empty = true;
        mutex_unlock(&pools_lock);
+       if (empty)
+               device_remove_file(pool->dev, &dev_attr_pools);
+       mutex_unlock(&pools_reg_lock);
 
        while (!list_empty(&pool->page_list)) {
                struct dma_page *page;
index 0ab0a3ea5721a75b204bd34dc7c1bdeece07b04d..14b4642279f1260e6bbd6a6b73b0518ee377acd1 100644 (file)
@@ -1753,7 +1753,7 @@ EXPORT_SYMBOL(generic_file_read_iter);
 static int page_cache_read(struct file *file, pgoff_t offset)
 {
        struct address_space *mapping = file->f_mapping;
-       struct page *page; 
+       struct page *page;
        int ret;
 
        do {
@@ -1770,7 +1770,7 @@ static int page_cache_read(struct file *file, pgoff_t offset)
                page_cache_release(page);
 
        } while (ret == AOP_TRUNCATED_PAGE);
-               
+
        return ret;
 }
 
index af7ea3e0826bfff7c1ba0b0ebdfe2fef41cf8c1e..cd62c8c90d4a3584fb087b2f2e949e7004ba615c 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
 #include <linux/swap.h>
 #include <linux/swapops.h>
 
+#include <linux/sched.h>
+#include <linux/rwsem.h>
+#include <asm/pgtable.h>
+
 #include "internal.h"
 
 static struct page *no_page_table(struct vm_area_struct *vma,
@@ -676,3 +680,353 @@ struct page *get_dump_page(unsigned long addr)
        return page;
 }
 #endif /* CONFIG_ELF_CORE */
+
+/*
+ * Generic RCU Fast GUP
+ *
+ * get_user_pages_fast attempts to pin user pages by walking the page
+ * tables directly and avoids taking locks. Thus the walker needs to be
+ * protected from page table pages being freed from under it, and should
+ * block any THP splits.
+ *
+ * One way to achieve this is to have the walker disable interrupts, and
+ * rely on IPIs from the TLB flushing code blocking before the page table
+ * pages are freed. This is unsuitable for architectures that do not need
+ * to broadcast an IPI when invalidating TLBs.
+ *
+ * Another way to achieve this is to batch up page table containing pages
+ * belonging to more than one mm_user, then rcu_sched a callback to free those
+ * pages. Disabling interrupts will allow the fast_gup walker to both block
+ * the rcu_sched callback, and an IPI that we broadcast for splitting THPs
+ * (which is a relatively rare event). The code below adopts this strategy.
+ *
+ * Before activating this code, please be aware that the following assumptions
+ * are currently made:
+ *
+ *  *) HAVE_RCU_TABLE_FREE is enabled, and tlb_remove_table is used to free
+ *      pages containing page tables.
+ *
+ *  *) THP splits will broadcast an IPI, this can be achieved by overriding
+ *      pmdp_splitting_flush.
+ *
+ *  *) ptes can be read atomically by the architecture.
+ *
+ *  *) access_ok is sufficient to validate userspace address ranges.
+ *
+ * The last two assumptions can be relaxed by the addition of helper functions.
+ *
+ * This code is based heavily on the PowerPC implementation by Nick Piggin.
+ */
+#ifdef CONFIG_HAVE_GENERIC_RCU_GUP
+
+#ifdef __HAVE_ARCH_PTE_SPECIAL
+static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
+                        int write, struct page **pages, int *nr)
+{
+       pte_t *ptep, *ptem;
+       int ret = 0;
+
+       ptem = ptep = pte_offset_map(&pmd, addr);
+       do {
+               /*
+                * In the line below we are assuming that the pte can be read
+                * atomically. If this is not the case for your architecture,
+                * please wrap this in a helper function!
+                *
+                * for an example see gup_get_pte in arch/x86/mm/gup.c
+                */
+               pte_t pte = ACCESS_ONCE(*ptep);
+               struct page *page;
+
+               /*
+                * Similar to the PMD case below, NUMA hinting must take slow
+                * path
+                */
+               if (!pte_present(pte) || pte_special(pte) ||
+                       pte_numa(pte) || (write && !pte_write(pte)))
+                       goto pte_unmap;
+
+               VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
+               page = pte_page(pte);
+
+               if (!page_cache_get_speculative(page))
+                       goto pte_unmap;
+
+               if (unlikely(pte_val(pte) != pte_val(*ptep))) {
+                       put_page(page);
+                       goto pte_unmap;
+               }
+
+               pages[*nr] = page;
+               (*nr)++;
+
+       } while (ptep++, addr += PAGE_SIZE, addr != end);
+
+       ret = 1;
+
+pte_unmap:
+       pte_unmap(ptem);
+       return ret;
+}
+#else
+
+/*
+ * If we can't determine whether or not a pte is special, then fail immediately
+ * for ptes. Note, we can still pin HugeTLB and THP as these are guaranteed not
+ * to be special.
+ *
+ * For a futex to be placed on a THP tail page, get_futex_key requires a
+ * __get_user_pages_fast implementation that can pin pages. Thus it's still
+ * useful to have gup_huge_pmd even if we can't operate on ptes.
+ */
+static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
+                        int write, struct page **pages, int *nr)
+{
+       return 0;
+}
+#endif /* __HAVE_ARCH_PTE_SPECIAL */
+
+static int gup_huge_pmd(pmd_t orig, pmd_t *pmdp, unsigned long addr,
+               unsigned long end, int write, struct page **pages, int *nr)
+{
+       struct page *head, *page, *tail;
+       int refs;
+
+       if (write && !pmd_write(orig))
+               return 0;
+
+       refs = 0;
+       head = pmd_page(orig);
+       page = head + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
+       tail = page;
+       do {
+               VM_BUG_ON_PAGE(compound_head(page) != head, page);
+               pages[*nr] = page;
+               (*nr)++;
+               page++;
+               refs++;
+       } while (addr += PAGE_SIZE, addr != end);
+
+       if (!page_cache_add_speculative(head, refs)) {
+               *nr -= refs;
+               return 0;
+       }
+
+       if (unlikely(pmd_val(orig) != pmd_val(*pmdp))) {
+               *nr -= refs;
+               while (refs--)
+                       put_page(head);
+               return 0;
+       }
+
+       /*
+        * Any tail pages need their mapcount reference taken before we
+        * return. (This allows the THP code to bump their ref count when
+        * they are split into base pages).
+        */
+       while (refs--) {
+               if (PageTail(tail))
+                       get_huge_page_tail(tail);
+               tail++;
+       }
+
+       return 1;
+}
+
+static int gup_huge_pud(pud_t orig, pud_t *pudp, unsigned long addr,
+               unsigned long end, int write, struct page **pages, int *nr)
+{
+       struct page *head, *page, *tail;
+       int refs;
+
+       if (write && !pud_write(orig))
+               return 0;
+
+       refs = 0;
+       head = pud_page(orig);
+       page = head + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
+       tail = page;
+       do {
+               VM_BUG_ON_PAGE(compound_head(page) != head, page);
+               pages[*nr] = page;
+               (*nr)++;
+               page++;
+               refs++;
+       } while (addr += PAGE_SIZE, addr != end);
+
+       if (!page_cache_add_speculative(head, refs)) {
+               *nr -= refs;
+               return 0;
+       }
+
+       if (unlikely(pud_val(orig) != pud_val(*pudp))) {
+               *nr -= refs;
+               while (refs--)
+                       put_page(head);
+               return 0;
+       }
+
+       while (refs--) {
+               if (PageTail(tail))
+                       get_huge_page_tail(tail);
+               tail++;
+       }
+
+       return 1;
+}
+
+static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
+               int write, struct page **pages, int *nr)
+{
+       unsigned long next;
+       pmd_t *pmdp;
+
+       pmdp = pmd_offset(&pud, addr);
+       do {
+               pmd_t pmd = ACCESS_ONCE(*pmdp);
+
+               next = pmd_addr_end(addr, end);
+               if (pmd_none(pmd) || pmd_trans_splitting(pmd))
+                       return 0;
+
+               if (unlikely(pmd_trans_huge(pmd) || pmd_huge(pmd))) {
+                       /*
+                        * NUMA hinting faults need to be handled in the GUP
+                        * slowpath for accounting purposes and so that they
+                        * can be serialised against THP migration.
+                        */
+                       if (pmd_numa(pmd))
+                               return 0;
+
+                       if (!gup_huge_pmd(pmd, pmdp, addr, next, write,
+                               pages, nr))
+                               return 0;
+
+               } else if (!gup_pte_range(pmd, addr, next, write, pages, nr))
+                               return 0;
+       } while (pmdp++, addr = next, addr != end);
+
+       return 1;
+}
+
+static int gup_pud_range(pgd_t *pgdp, unsigned long addr, unsigned long end,
+               int write, struct page **pages, int *nr)
+{
+       unsigned long next;
+       pud_t *pudp;
+
+       pudp = pud_offset(pgdp, addr);
+       do {
+               pud_t pud = ACCESS_ONCE(*pudp);
+
+               next = pud_addr_end(addr, end);
+               if (pud_none(pud))
+                       return 0;
+               if (pud_huge(pud)) {
+                       if (!gup_huge_pud(pud, pudp, addr, next, write,
+                                       pages, nr))
+                               return 0;
+               } else if (!gup_pmd_range(pud, addr, next, write, pages, nr))
+                       return 0;
+       } while (pudp++, addr = next, addr != end);
+
+       return 1;
+}
+
+/*
+ * Like get_user_pages_fast() except it's IRQ-safe in that it won't fall back to
+ * the regular GUP. It will only return non-negative values.
+ */
+int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
+                         struct page **pages)
+{
+       struct mm_struct *mm = current->mm;
+       unsigned long addr, len, end;
+       unsigned long next, flags;
+       pgd_t *pgdp;
+       int nr = 0;
+
+       start &= PAGE_MASK;
+       addr = start;
+       len = (unsigned long) nr_pages << PAGE_SHIFT;
+       end = start + len;
+
+       if (unlikely(!access_ok(write ? VERIFY_WRITE : VERIFY_READ,
+                                       start, len)))
+               return 0;
+
+       /*
+        * Disable interrupts.  We use the nested form as we can already have
+        * interrupts disabled by get_futex_key.
+        *
+        * With interrupts disabled, we block page table pages from being
+        * freed from under us. See mmu_gather_tlb in asm-generic/tlb.h
+        * for more details.
+        *
+        * We do not adopt an rcu_read_lock(.) here as we also want to
+        * block IPIs that come from THPs splitting.
+        */
+
+       local_irq_save(flags);
+       pgdp = pgd_offset(mm, addr);
+       do {
+               next = pgd_addr_end(addr, end);
+               if (pgd_none(*pgdp))
+                       break;
+               else if (!gup_pud_range(pgdp, addr, next, write, pages, &nr))
+                       break;
+       } while (pgdp++, addr = next, addr != end);
+       local_irq_restore(flags);
+
+       return nr;
+}
+
+/**
+ * get_user_pages_fast() - pin user pages in memory
+ * @start:     starting user address
+ * @nr_pages:  number of pages from start to pin
+ * @write:     whether pages will be written to
+ * @pages:     array that receives pointers to the pages pinned.
+ *             Should be at least nr_pages long.
+ *
+ * Attempt to pin user pages in memory without taking mm->mmap_sem.
+ * If not successful, it will fall back to taking the lock and
+ * calling get_user_pages().
+ *
+ * Returns number of pages pinned. This may be fewer than the number
+ * requested. If nr_pages is 0 or negative, returns 0. If no pages
+ * were pinned, returns -errno.
+ */
+int get_user_pages_fast(unsigned long start, int nr_pages, int write,
+                       struct page **pages)
+{
+       struct mm_struct *mm = current->mm;
+       int nr, ret;
+
+       start &= PAGE_MASK;
+       nr = __get_user_pages_fast(start, nr_pages, write, pages);
+       ret = nr;
+
+       if (nr < nr_pages) {
+               /* Try to get the remaining pages with get_user_pages */
+               start += nr << PAGE_SHIFT;
+               pages += nr;
+
+               down_read(&mm->mmap_sem);
+               ret = get_user_pages(current, mm, start,
+                                    nr_pages - nr, write, 0, pages, NULL);
+               up_read(&mm->mmap_sem);
+
+               /* Have to be a bit careful with return values */
+               if (nr > 0) {
+                       if (ret < 0)
+                               ret = nr;
+                       else
+                               ret += nr;
+               }
+       }
+
+       return ret;
+}
+
+#endif /* CONFIG_HAVE_GENERIC_RCU_GUP */
index f8ffd9412ec502d10f2c103554ffe333306e2e3f..74c78aa8bc2fa68454928b09f34a7b97f3419e05 100644 (file)
@@ -1096,7 +1096,7 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
        unsigned long mmun_end;         /* For mmu_notifiers */
 
        ptl = pmd_lockptr(mm, pmd);
-       VM_BUG_ON(!vma->anon_vma);
+       VM_BUG_ON_VMA(!vma->anon_vma, vma);
        haddr = address & HPAGE_PMD_MASK;
        if (is_huge_zero_pmd(orig_pmd))
                goto alloc;
@@ -2048,7 +2048,7 @@ int __khugepaged_enter(struct mm_struct *mm)
                return -ENOMEM;
 
        /* __khugepaged_exit() must not run from under us */
-       VM_BUG_ON(khugepaged_test_exit(mm));
+       VM_BUG_ON_MM(khugepaged_test_exit(mm), mm);
        if (unlikely(test_and_set_bit(MMF_VM_HUGEPAGE, &mm->flags))) {
                free_mm_slot(mm_slot);
                return 0;
@@ -2083,7 +2083,7 @@ int khugepaged_enter_vma_merge(struct vm_area_struct *vma)
        if (vma->vm_ops)
                /* khugepaged not yet working on file or special mappings */
                return 0;
-       VM_BUG_ON(vma->vm_flags & VM_NO_THP);
+       VM_BUG_ON_VMA(vma->vm_flags & VM_NO_THP, vma);
        hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
        hend = vma->vm_end & HPAGE_PMD_MASK;
        if (hstart < hend)
@@ -2322,23 +2322,17 @@ static struct page
                       int node)
 {
        VM_BUG_ON_PAGE(*hpage, *hpage);
+
        /*
-        * Allocate the page while the vma is still valid and under
-        * the mmap_sem read mode so there is no memory allocation
-        * later when we take the mmap_sem in write mode. This is more
-        * friendly behavior (OTOH it may actually hide bugs) to
-        * filesystems in userland with daemons allocating memory in
-        * the userland I/O paths.  Allocating memory with the
-        * mmap_sem in read mode is good idea also to allow greater
-        * scalability.
+        * Before allocating the hugepage, release the mmap_sem read lock.
+        * The allocation can take potentially a long time if it involves
+        * sync compaction, and we do not need to hold the mmap_sem during
+        * that. We will recheck the vma after taking it again in write mode.
         */
+       up_read(&mm->mmap_sem);
+
        *hpage = alloc_pages_exact_node(node, alloc_hugepage_gfpmask(
                khugepaged_defrag(), __GFP_OTHER_NODE), HPAGE_PMD_ORDER);
-       /*
-        * After allocating the hugepage, release the mmap_sem read lock in
-        * preparation for taking it in write mode.
-        */
-       up_read(&mm->mmap_sem);
        if (unlikely(!*hpage)) {
                count_vm_event(THP_COLLAPSE_ALLOC_FAILED);
                *hpage = ERR_PTR(-ENOMEM);
@@ -2412,7 +2406,7 @@ static bool hugepage_vma_check(struct vm_area_struct *vma)
                return false;
        if (is_vma_temporary_stack(vma))
                return false;
-       VM_BUG_ON(vma->vm_flags & VM_NO_THP);
+       VM_BUG_ON_VMA(vma->vm_flags & VM_NO_THP, vma);
        return true;
 }
 
index eeceeeb0901978f378ead370134ba37c253ab04c..9fd722769927f9e5bb5e03fb6516db7a5f7c8f42 100644 (file)
@@ -434,7 +434,7 @@ static inline struct resv_map *inode_resv_map(struct inode *inode)
 
 static struct resv_map *vma_resv_map(struct vm_area_struct *vma)
 {
-       VM_BUG_ON(!is_vm_hugetlb_page(vma));
+       VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
        if (vma->vm_flags & VM_MAYSHARE) {
                struct address_space *mapping = vma->vm_file->f_mapping;
                struct inode *inode = mapping->host;
@@ -449,8 +449,8 @@ static struct resv_map *vma_resv_map(struct vm_area_struct *vma)
 
 static void set_vma_resv_map(struct vm_area_struct *vma, struct resv_map *map)
 {
-       VM_BUG_ON(!is_vm_hugetlb_page(vma));
-       VM_BUG_ON(vma->vm_flags & VM_MAYSHARE);
+       VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
+       VM_BUG_ON_VMA(vma->vm_flags & VM_MAYSHARE, vma);
 
        set_vma_private_data(vma, (get_vma_private_data(vma) &
                                HPAGE_RESV_MASK) | (unsigned long)map);
@@ -458,15 +458,15 @@ static void set_vma_resv_map(struct vm_area_struct *vma, struct resv_map *map)
 
 static void set_vma_resv_flags(struct vm_area_struct *vma, unsigned long flags)
 {
-       VM_BUG_ON(!is_vm_hugetlb_page(vma));
-       VM_BUG_ON(vma->vm_flags & VM_MAYSHARE);
+       VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
+       VM_BUG_ON_VMA(vma->vm_flags & VM_MAYSHARE, vma);
 
        set_vma_private_data(vma, get_vma_private_data(vma) | flags);
 }
 
 static int is_vma_resv_set(struct vm_area_struct *vma, unsigned long flag)
 {
-       VM_BUG_ON(!is_vm_hugetlb_page(vma));
+       VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
 
        return (get_vma_private_data(vma) & flag) != 0;
 }
@@ -474,7 +474,7 @@ static int is_vma_resv_set(struct vm_area_struct *vma, unsigned long flag)
 /* Reset counters to 0 and clear all HPAGE_RESV_* flags */
 void reset_vma_resv_huge_pages(struct vm_area_struct *vma)
 {
-       VM_BUG_ON(!is_vm_hugetlb_page(vma));
+       VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
        if (!(vma->vm_flags & VM_MAYSHARE))
                vma->vm_private_data = (void *)0;
 }
index a1b651b11c5fcba7a0322bc19c93286896d08cbf..829304090b90e8ff57ee3eaf5281987deccb7e55 100644 (file)
@@ -142,10 +142,10 @@ struct compact_control {
        bool finished_update_migrate;
 
        int order;                      /* order a direct compactor needs */
-       int migratetype;                /* MOVABLE, RECLAIMABLE etc */
+       const gfp_t gfp_mask;           /* gfp mask of a direct compactor */
        struct zone *zone;
-       bool contended;                 /* True if a lock was contended, or
-                                        * need_resched() true during async
+       int contended;                  /* Signal need_sched() or lock
+                                        * contention detected during
                                         * compaction
                                         */
 };
@@ -154,8 +154,8 @@ unsigned long
 isolate_freepages_range(struct compact_control *cc,
                        unsigned long start_pfn, unsigned long end_pfn);
 unsigned long
-isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
-       unsigned long low_pfn, unsigned long end_pfn, bool unevictable);
+isolate_migratepages_range(struct compact_control *cc,
+                          unsigned long low_pfn, unsigned long end_pfn);
 
 #endif
 
@@ -164,7 +164,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
  * general, page_zone(page)->lock must be held by the caller to prevent the
  * page from being allocated in parallel and returning garbage as the order.
  * If a caller does not hold page_zone(page)->lock, it must guarantee that the
- * page cannot be allocated or merged in parallel.
+ * page cannot be allocated or merged in parallel. Alternatively, it must
+ * handle invalid values gracefully, and use page_order_unsafe() below.
  */
 static inline unsigned long page_order(struct page *page)
 {
@@ -172,6 +173,19 @@ static inline unsigned long page_order(struct page *page)
        return page_private(page);
 }
 
+/*
+ * Like page_order(), but for callers who cannot afford to hold the zone lock.
+ * PageBuddy() should be checked first by the caller to minimize race window,
+ * and invalid values must be handled gracefully.
+ *
+ * ACCESS_ONCE is used so that if the caller assigns the result into a local
+ * variable and e.g. tests it for valid range before using, the compiler cannot
+ * decide to remove the variable and inline the page_private(page) multiple
+ * times, potentially observing different values in the tests and the actual
+ * use of the result.
+ */
+#define page_order_unsafe(page)                ACCESS_ONCE(page_private(page))
+
 static inline bool is_cow_mapping(vm_flags_t flags)
 {
        return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
index 4a5822a586e6a57c6bdb19842096011b76b94de5..8da581fa906093b8c66735b18975f5c874e30ea4 100644 (file)
@@ -34,7 +34,7 @@ void vma_interval_tree_insert_after(struct vm_area_struct *node,
        struct vm_area_struct *parent;
        unsigned long last = vma_last_pgoff(node);
 
-       VM_BUG_ON(vma_start_pgoff(node) != vma_start_pgoff(prev));
+       VM_BUG_ON_VMA(vma_start_pgoff(node) != vma_start_pgoff(prev), node);
 
        if (!prev->shared.linear.rb.rb_right) {
                parent = prev;
index fd814fd61319adcc4763efa749005fc67ede8593..cab58bb592d8fa881dccfcddf0dcc7612f05128b 100644 (file)
@@ -2,6 +2,7 @@
 #include <linux/mm_types.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
+#include "slab.h"
 #include <linux/kmemcheck.h>
 
 void kmemcheck_alloc_shadow(struct page *page, int order, gfp_t flags, int node)
index fb759022270654bb0e9625b3cf351590a75b2a09..6b2e337bc03c7d6c5fce5e33023bdb5d2f111789 100644 (file)
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -2310,7 +2310,7 @@ static int __init ksm_init(void)
 
        ksm_thread = kthread_run(ksm_scan_thread, NULL, "ksmd");
        if (IS_ERR(ksm_thread)) {
-               printk(KERN_ERR "ksm: creating kthread failed\n");
+               pr_err("ksm: creating kthread failed\n");
                err = PTR_ERR(ksm_thread);
                goto out_free;
        }
@@ -2318,7 +2318,7 @@ static int __init ksm_init(void)
 #ifdef CONFIG_SYSFS
        err = sysfs_create_group(mm_kobj, &ksm_attr_group);
        if (err) {
-               printk(KERN_ERR "ksm: register sysfs failed\n");
+               pr_err("ksm: register sysfs failed\n");
                kthread_stop(ksm_thread);
                goto out_free;
        }
index 28928ce9b07fbed2529ebdcd708d2aa3345410a4..23976fd885fd588a7687bf5631f98ea38f2235b4 100644 (file)
@@ -318,9 +318,6 @@ struct mem_cgroup {
        /* OOM-Killer disable */
        int             oom_kill_disable;
 
-       /* set when res.limit == memsw.limit */
-       bool            memsw_is_minimum;
-
        /* protect arrays of thresholds */
        struct mutex thresholds_lock;
 
@@ -483,14 +480,6 @@ enum res_type {
 /* Used for OOM nofiier */
 #define OOM_CONTROL            (0)
 
-/*
- * Reclaim flags for mem_cgroup_hierarchical_reclaim
- */
-#define MEM_CGROUP_RECLAIM_NOSWAP_BIT  0x0
-#define MEM_CGROUP_RECLAIM_NOSWAP      (1 << MEM_CGROUP_RECLAIM_NOSWAP_BIT)
-#define MEM_CGROUP_RECLAIM_SHRINK_BIT  0x1
-#define MEM_CGROUP_RECLAIM_SHRINK      (1 << MEM_CGROUP_RECLAIM_SHRINK_BIT)
-
 /*
  * The memcg_create_mutex will be held whenever a new cgroup is created.
  * As a consequence, any change that needs to protect against new child cgroups
@@ -649,11 +638,13 @@ int memcg_limited_groups_array_size;
 struct static_key memcg_kmem_enabled_key;
 EXPORT_SYMBOL(memcg_kmem_enabled_key);
 
+static void memcg_free_cache_id(int id);
+
 static void disarm_kmem_keys(struct mem_cgroup *memcg)
 {
        if (memcg_kmem_is_active(memcg)) {
                static_key_slow_dec(&memcg_kmem_enabled_key);
-               ida_simple_remove(&kmem_limited_groups, memcg->kmemcg_id);
+               memcg_free_cache_id(memcg->kmemcg_id);
        }
        /*
         * This check can't live in kmem destruction function,
@@ -1806,42 +1797,6 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
                         NULL, "Memory cgroup out of memory");
 }
 
-static unsigned long mem_cgroup_reclaim(struct mem_cgroup *memcg,
-                                       gfp_t gfp_mask,
-                                       unsigned long flags)
-{
-       unsigned long total = 0;
-       bool noswap = false;
-       int loop;
-
-       if (flags & MEM_CGROUP_RECLAIM_NOSWAP)
-               noswap = true;
-       if (!(flags & MEM_CGROUP_RECLAIM_SHRINK) && memcg->memsw_is_minimum)
-               noswap = true;
-
-       for (loop = 0; loop < MEM_CGROUP_MAX_RECLAIM_LOOPS; loop++) {
-               if (loop)
-                       drain_all_stock_async(memcg);
-               total += try_to_free_mem_cgroup_pages(memcg, gfp_mask, noswap);
-               /*
-                * Allow limit shrinkers, which are triggered directly
-                * by userspace, to catch signals and stop reclaim
-                * after minimal progress, regardless of the margin.
-                */
-               if (total && (flags & MEM_CGROUP_RECLAIM_SHRINK))
-                       break;
-               if (mem_cgroup_margin(memcg))
-                       break;
-               /*
-                * If nothing was reclaimed after two attempts, there
-                * may be no reclaimable pages in this hierarchy.
-                */
-               if (loop && !total)
-                       break;
-       }
-       return total;
-}
-
 /**
  * test_mem_cgroup_node_reclaimable
  * @memcg: the target memcg
@@ -2544,8 +2499,9 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
        struct mem_cgroup *mem_over_limit;
        struct res_counter *fail_res;
        unsigned long nr_reclaimed;
-       unsigned long flags = 0;
        unsigned long long size;
+       bool may_swap = true;
+       bool drained = false;
        int ret = 0;
 
        if (mem_cgroup_is_root(memcg))
@@ -2555,16 +2511,17 @@ retry:
                goto done;
 
        size = batch * PAGE_SIZE;
-       if (!res_counter_charge(&memcg->res, size, &fail_res)) {
-               if (!do_swap_account)
-                       goto done_restock;
-               if (!res_counter_charge(&memcg->memsw, size, &fail_res))
+       if (!do_swap_account ||
+           !res_counter_charge(&memcg->memsw, size, &fail_res)) {
+               if (!res_counter_charge(&memcg->res, size, &fail_res))
                        goto done_restock;
-               res_counter_uncharge(&memcg->res, size);
-               mem_over_limit = mem_cgroup_from_res_counter(fail_res, memsw);
-               flags |= MEM_CGROUP_RECLAIM_NOSWAP;
-       } else
+               if (do_swap_account)
+                       res_counter_uncharge(&memcg->memsw, size);
                mem_over_limit = mem_cgroup_from_res_counter(fail_res, res);
+       } else {
+               mem_over_limit = mem_cgroup_from_res_counter(fail_res, memsw);
+               may_swap = false;
+       }
 
        if (batch > nr_pages) {
                batch = nr_pages;
@@ -2588,11 +2545,18 @@ retry:
        if (!(gfp_mask & __GFP_WAIT))
                goto nomem;
 
-       nr_reclaimed = mem_cgroup_reclaim(mem_over_limit, gfp_mask, flags);
+       nr_reclaimed = try_to_free_mem_cgroup_pages(mem_over_limit, nr_pages,
+                                                   gfp_mask, may_swap);
 
        if (mem_cgroup_margin(mem_over_limit) >= nr_pages)
                goto retry;
 
+       if (!drained) {
+               drain_all_stock_async(mem_over_limit);
+               drained = true;
+               goto retry;
+       }
+
        if (gfp_mask & __GFP_NORETRY)
                goto nomem;
        /*
@@ -2798,12 +2762,6 @@ static DEFINE_MUTEX(memcg_slab_mutex);
 
 static DEFINE_MUTEX(activate_kmem_mutex);
 
-static inline bool memcg_can_account_kmem(struct mem_cgroup *memcg)
-{
-       return !mem_cgroup_disabled() && !mem_cgroup_is_root(memcg) &&
-               memcg_kmem_is_active(memcg);
-}
-
 /*
  * This is a bit cumbersome, but it is rarely used and avoids a backpointer
  * in the memcg_cache_params struct.
@@ -2823,7 +2781,7 @@ static int mem_cgroup_slabinfo_read(struct seq_file *m, void *v)
        struct mem_cgroup *memcg = mem_cgroup_from_css(seq_css(m));
        struct memcg_cache_params *params;
 
-       if (!memcg_can_account_kmem(memcg))
+       if (!memcg_kmem_is_active(memcg))
                return -EIO;
 
        print_slabinfo_header(m);
@@ -2906,19 +2864,44 @@ int memcg_cache_id(struct mem_cgroup *memcg)
        return memcg ? memcg->kmemcg_id : -1;
 }
 
-static size_t memcg_caches_array_size(int num_groups)
+static int memcg_alloc_cache_id(void)
 {
-       ssize_t size;
-       if (num_groups <= 0)
-               return 0;
+       int id, size;
+       int err;
+
+       id = ida_simple_get(&kmem_limited_groups,
+                           0, MEMCG_CACHES_MAX_SIZE, GFP_KERNEL);
+       if (id < 0)
+               return id;
+
+       if (id < memcg_limited_groups_array_size)
+               return id;
+
+       /*
+        * There's no space for the new id in memcg_caches arrays,
+        * so we have to grow them.
+        */
 
-       size = 2 * num_groups;
+       size = 2 * (id + 1);
        if (size < MEMCG_CACHES_MIN_SIZE)
                size = MEMCG_CACHES_MIN_SIZE;
        else if (size > MEMCG_CACHES_MAX_SIZE)
                size = MEMCG_CACHES_MAX_SIZE;
 
-       return size;
+       mutex_lock(&memcg_slab_mutex);
+       err = memcg_update_all_caches(size);
+       mutex_unlock(&memcg_slab_mutex);
+
+       if (err) {
+               ida_simple_remove(&kmem_limited_groups, id);
+               return err;
+       }
+       return id;
+}
+
+static void memcg_free_cache_id(int id)
+{
+       ida_simple_remove(&kmem_limited_groups, id);
 }
 
 /*
@@ -2928,97 +2911,7 @@ static size_t memcg_caches_array_size(int num_groups)
  */
 void memcg_update_array_size(int num)
 {
-       if (num > memcg_limited_groups_array_size)
-               memcg_limited_groups_array_size = memcg_caches_array_size(num);
-}
-
-int memcg_update_cache_size(struct kmem_cache *s, int num_groups)
-{
-       struct memcg_cache_params *cur_params = s->memcg_params;
-
-       VM_BUG_ON(!is_root_cache(s));
-
-       if (num_groups > memcg_limited_groups_array_size) {
-               int i;
-               struct memcg_cache_params *new_params;
-               ssize_t size = memcg_caches_array_size(num_groups);
-
-               size *= sizeof(void *);
-               size += offsetof(struct memcg_cache_params, memcg_caches);
-
-               new_params = kzalloc(size, GFP_KERNEL);
-               if (!new_params)
-                       return -ENOMEM;
-
-               new_params->is_root_cache = true;
-
-               /*
-                * There is the chance it will be bigger than
-                * memcg_limited_groups_array_size, if we failed an allocation
-                * in a cache, in which case all caches updated before it, will
-                * have a bigger array.
-                *
-                * But if that is the case, the data after
-                * memcg_limited_groups_array_size is certainly unused
-                */
-               for (i = 0; i < memcg_limited_groups_array_size; i++) {
-                       if (!cur_params->memcg_caches[i])
-                               continue;
-                       new_params->memcg_caches[i] =
-                                               cur_params->memcg_caches[i];
-               }
-
-               /*
-                * Ideally, we would wait until all caches succeed, and only
-                * then free the old one. But this is not worth the extra
-                * pointer per-cache we'd have to have for this.
-                *
-                * It is not a big deal if some caches are left with a size
-                * bigger than the others. And all updates will reset this
-                * anyway.
-                */
-               rcu_assign_pointer(s->memcg_params, new_params);
-               if (cur_params)
-                       kfree_rcu(cur_params, rcu_head);
-       }
-       return 0;
-}
-
-int memcg_alloc_cache_params(struct mem_cgroup *memcg, struct kmem_cache *s,
-                            struct kmem_cache *root_cache)
-{
-       size_t size;
-
-       if (!memcg_kmem_enabled())
-               return 0;
-
-       if (!memcg) {
-               size = offsetof(struct memcg_cache_params, memcg_caches);
-               size += memcg_limited_groups_array_size * sizeof(void *);
-       } else
-               size = sizeof(struct memcg_cache_params);
-
-       s->memcg_params = kzalloc(size, GFP_KERNEL);
-       if (!s->memcg_params)
-               return -ENOMEM;
-
-       if (memcg) {
-               s->memcg_params->memcg = memcg;
-               s->memcg_params->root_cache = root_cache;
-               css_get(&memcg->css);
-       } else
-               s->memcg_params->is_root_cache = true;
-
-       return 0;
-}
-
-void memcg_free_cache_params(struct kmem_cache *s)
-{
-       if (!s->memcg_params)
-               return;
-       if (!s->memcg_params->is_root_cache)
-               css_put(&s->memcg_params->memcg->css);
-       kfree(s->memcg_params);
+       memcg_limited_groups_array_size = num;
 }
 
 static void memcg_register_cache(struct mem_cgroup *memcg,
@@ -3051,6 +2944,7 @@ static void memcg_register_cache(struct mem_cgroup *memcg,
        if (!cachep)
                return;
 
+       css_get(&memcg->css);
        list_add(&cachep->memcg_params->list, &memcg->memcg_slab_caches);
 
        /*
@@ -3084,6 +2978,9 @@ static void memcg_unregister_cache(struct kmem_cache *cachep)
        list_del(&cachep->memcg_params->list);
 
        kmem_cache_destroy(cachep);
+
+       /* drop the reference taken in memcg_register_cache */
+       css_put(&memcg->css);
 }
 
 /*
@@ -3261,7 +3158,7 @@ struct kmem_cache *__memcg_kmem_get_cache(struct kmem_cache *cachep,
        rcu_read_lock();
        memcg = mem_cgroup_from_task(rcu_dereference(current->mm->owner));
 
-       if (!memcg_can_account_kmem(memcg))
+       if (!memcg_kmem_is_active(memcg))
                goto out;
 
        memcg_cachep = cache_from_memcg_idx(cachep, memcg_cache_id(memcg));
@@ -3346,7 +3243,7 @@ __memcg_kmem_newpage_charge(gfp_t gfp, struct mem_cgroup **_memcg, int order)
 
        memcg = get_mem_cgroup_from_mm(current->mm);
 
-       if (!memcg_can_account_kmem(memcg)) {
+       if (!memcg_kmem_is_active(memcg)) {
                css_put(&memcg->css);
                return true;
        }
@@ -3688,7 +3585,6 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
                                unsigned long long val)
 {
        int retry_count;
-       u64 memswlimit, memlimit;
        int ret = 0;
        int children = mem_cgroup_count_children(memcg);
        u64 curusage, oldusage;
@@ -3715,31 +3611,23 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
                 * We have to guarantee memcg->res.limit <= memcg->memsw.limit.
                 */
                mutex_lock(&set_limit_mutex);
-               memswlimit = res_counter_read_u64(&memcg->memsw, RES_LIMIT);
-               if (memswlimit < val) {
+               if (res_counter_read_u64(&memcg->memsw, RES_LIMIT) < val) {
                        ret = -EINVAL;
                        mutex_unlock(&set_limit_mutex);
                        break;
                }
 
-               memlimit = res_counter_read_u64(&memcg->res, RES_LIMIT);
-               if (memlimit < val)
+               if (res_counter_read_u64(&memcg->res, RES_LIMIT) < val)
                        enlarge = 1;
 
                ret = res_counter_set_limit(&memcg->res, val);
-               if (!ret) {
-                       if (memswlimit == val)
-                               memcg->memsw_is_minimum = true;
-                       else
-                               memcg->memsw_is_minimum = false;
-               }
                mutex_unlock(&set_limit_mutex);
 
                if (!ret)
                        break;
 
-               mem_cgroup_reclaim(memcg, GFP_KERNEL,
-                                  MEM_CGROUP_RECLAIM_SHRINK);
+               try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL, true);
+
                curusage = res_counter_read_u64(&memcg->res, RES_USAGE);
                /* Usage is reduced ? */
                if (curusage >= oldusage)
@@ -3757,7 +3645,7 @@ static int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
                                        unsigned long long val)
 {
        int retry_count;
-       u64 memlimit, memswlimit, oldusage, curusage;
+       u64 oldusage, curusage;
        int children = mem_cgroup_count_children(memcg);
        int ret = -EBUSY;
        int enlarge = 0;
@@ -3776,30 +3664,21 @@ static int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
                 * We have to guarantee memcg->res.limit <= memcg->memsw.limit.
                 */
                mutex_lock(&set_limit_mutex);
-               memlimit = res_counter_read_u64(&memcg->res, RES_LIMIT);
-               if (memlimit > val) {
+               if (res_counter_read_u64(&memcg->res, RES_LIMIT) > val) {
                        ret = -EINVAL;
                        mutex_unlock(&set_limit_mutex);
                        break;
                }
-               memswlimit = res_counter_read_u64(&memcg->memsw, RES_LIMIT);
-               if (memswlimit < val)
+               if (res_counter_read_u64(&memcg->memsw, RES_LIMIT) < val)
                        enlarge = 1;
                ret = res_counter_set_limit(&memcg->memsw, val);
-               if (!ret) {
-                       if (memlimit == val)
-                               memcg->memsw_is_minimum = true;
-                       else
-                               memcg->memsw_is_minimum = false;
-               }
                mutex_unlock(&set_limit_mutex);
 
                if (!ret)
                        break;
 
-               mem_cgroup_reclaim(memcg, GFP_KERNEL,
-                                  MEM_CGROUP_RECLAIM_NOSWAP |
-                                  MEM_CGROUP_RECLAIM_SHRINK);
+               try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL, false);
+
                curusage = res_counter_read_u64(&memcg->memsw, RES_USAGE);
                /* Usage is reduced ? */
                if (curusage >= oldusage)
@@ -4048,8 +3927,8 @@ static int mem_cgroup_force_empty(struct mem_cgroup *memcg)
                if (signal_pending(current))
                        return -EINTR;
 
-               progress = try_to_free_mem_cgroup_pages(memcg, GFP_KERNEL,
-                                               false);
+               progress = try_to_free_mem_cgroup_pages(memcg, 1,
+                                                       GFP_KERNEL, true);
                if (!progress) {
                        nr_retries--;
                        /* maybe some writeback is necessary */
@@ -4214,23 +4093,12 @@ static int __memcg_activate_kmem(struct mem_cgroup *memcg,
        if (err)
                goto out;
 
-       memcg_id = ida_simple_get(&kmem_limited_groups,
-                                 0, MEMCG_CACHES_MAX_SIZE, GFP_KERNEL);
+       memcg_id = memcg_alloc_cache_id();
        if (memcg_id < 0) {
                err = memcg_id;
                goto out;
        }
 
-       /*
-        * Make sure we have enough space for this cgroup in each root cache's
-        * memcg_params.
-        */
-       mutex_lock(&memcg_slab_mutex);
-       err = memcg_update_all_caches(memcg_id + 1);
-       mutex_unlock(&memcg_slab_mutex);
-       if (err)
-               goto out_rmid;
-
        memcg->kmemcg_id = memcg_id;
        INIT_LIST_HEAD(&memcg->memcg_slab_caches);
 
@@ -4251,10 +4119,6 @@ static int __memcg_activate_kmem(struct mem_cgroup *memcg,
 out:
        memcg_resume_kmem_account();
        return err;
-
-out_rmid:
-       ida_simple_remove(&kmem_limited_groups, memcg_id);
-       goto out;
 }
 
 static int memcg_activate_kmem(struct mem_cgroup *memcg,
index 44c6bd201d3a1cac7120527b45e2a86f5f77abff..8639f6b28746b313bcd12bca3693a11079e18143 100644 (file)
@@ -148,7 +148,7 @@ static int hwpoison_filter_task(struct page *p)
        ino = cgroup_ino(css->cgroup);
        css_put(css);
 
-       if (!ino || ino != hwpoison_filter_memcg)
+       if (ino != hwpoison_filter_memcg)
                return -EINVAL;
 
        return 0;
index 2ff8c2325e968b509e983077a41ec4d0f42c00f4..29d8693d0c61cf663ee45551aaecff29ff7237cf 100644 (file)
@@ -1307,7 +1307,7 @@ int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
 /*
  * Confirm all pages in a range [start, end) is belongs to the same zone.
  */
-static int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
+int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
 {
        unsigned long pfn;
        struct zone *zone = NULL;
index 8f5330d74f47bded6c1119b49fe34c2ba2573029..e58725aff7e999aeecde0e332960c498aa786128 100644 (file)
@@ -123,25 +123,23 @@ static struct mempolicy default_policy = {
 
 static struct mempolicy preferred_node_policy[MAX_NUMNODES];
 
-static struct mempolicy *get_task_policy(struct task_struct *p)
+struct mempolicy *get_task_policy(struct task_struct *p)
 {
        struct mempolicy *pol = p->mempolicy;
+       int node;
 
-       if (!pol) {
-               int node = numa_node_id();
+       if (pol)
+               return pol;
 
-               if (node != NUMA_NO_NODE) {
-                       pol = &preferred_node_policy[node];
-                       /*
-                        * preferred_node_policy is not initialised early in
-                        * boot
-                        */
-                       if (!pol->mode)
-                               pol = NULL;
-               }
+       node = numa_node_id();
+       if (node != NUMA_NO_NODE) {
+               pol = &preferred_node_policy[node];
+               /* preferred_node_policy is not initialised early in boot */
+               if (pol->mode)
+                       return pol;
        }
 
-       return pol;
+       return &default_policy;
 }
 
 static const struct mempolicy_operations {
@@ -683,7 +681,9 @@ queue_pages_range(struct mm_struct *mm, unsigned long start, unsigned long end,
                }
 
                if (flags & MPOL_MF_LAZY) {
-                       change_prot_numa(vma, start, endvma);
+                       /* Similar to task_numa_work, skip inaccessible VMAs */
+                       if (vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))
+                               change_prot_numa(vma, start, endvma);
                        goto next;
                }
 
@@ -804,7 +804,6 @@ static long do_set_mempolicy(unsigned short mode, unsigned short flags,
                             nodemask_t *nodes)
 {
        struct mempolicy *new, *old;
-       struct mm_struct *mm = current->mm;
        NODEMASK_SCRATCH(scratch);
        int ret;
 
@@ -816,20 +815,11 @@ static long do_set_mempolicy(unsigned short mode, unsigned short flags,
                ret = PTR_ERR(new);
                goto out;
        }
-       /*
-        * prevent changing our mempolicy while show_numa_maps()
-        * is using it.
-        * Note:  do_set_mempolicy() can be called at init time
-        * with no 'mm'.
-        */
-       if (mm)
-               down_write(&mm->mmap_sem);
+
        task_lock(current);
        ret = mpol_set_nodemask(new, nodes, scratch);
        if (ret) {
                task_unlock(current);
-               if (mm)
-                       up_write(&mm->mmap_sem);
                mpol_put(new);
                goto out;
        }
@@ -839,9 +829,6 @@ static long do_set_mempolicy(unsigned short mode, unsigned short flags,
            nodes_weight(new->v.nodes))
                current->il_next = first_node(new->v.nodes);
        task_unlock(current);
-       if (mm)
-               up_write(&mm->mmap_sem);
-
        mpol_put(old);
        ret = 0;
 out:
@@ -1605,32 +1592,14 @@ COMPAT_SYSCALL_DEFINE6(mbind, compat_ulong_t, start, compat_ulong_t, len,
 
 #endif
 
-/*
- * get_vma_policy(@task, @vma, @addr)
- * @task: task for fallback if vma policy == default
- * @vma: virtual memory area whose policy is sought
- * @addr: address in @vma for shared policy lookup
- *
- * Returns effective policy for a VMA at specified address.
- * Falls back to @task or system default policy, as necessary.
- * Current or other task's task mempolicy and non-shared vma policies must be
- * protected by task_lock(task) by the caller.
- * Shared policies [those marked as MPOL_F_SHARED] require an extra reference
- * count--added by the get_policy() vm_op, as appropriate--to protect against
- * freeing by another task.  It is the caller's responsibility to free the
- * extra reference for shared policies.
- */
-struct mempolicy *get_vma_policy(struct task_struct *task,
-               struct vm_area_struct *vma, unsigned long addr)
+struct mempolicy *__get_vma_policy(struct vm_area_struct *vma,
+                                               unsigned long addr)
 {
-       struct mempolicy *pol = get_task_policy(task);
+       struct mempolicy *pol = NULL;
 
        if (vma) {
                if (vma->vm_ops && vma->vm_ops->get_policy) {
-                       struct mempolicy *vpol = vma->vm_ops->get_policy(vma,
-                                                                       addr);
-                       if (vpol)
-                               pol = vpol;
+                       pol = vma->vm_ops->get_policy(vma, addr);
                } else if (vma->vm_policy) {
                        pol = vma->vm_policy;
 
@@ -1644,31 +1613,51 @@ struct mempolicy *get_vma_policy(struct task_struct *task,
                                mpol_get(pol);
                }
        }
+
+       return pol;
+}
+
+/*
+ * get_vma_policy(@vma, @addr)
+ * @vma: virtual memory area whose policy is sought
+ * @addr: address in @vma for shared policy lookup
+ *
+ * Returns effective policy for a VMA at specified address.
+ * Falls back to current->mempolicy or system default policy, as necessary.
+ * Shared policies [those marked as MPOL_F_SHARED] require an extra reference
+ * count--added by the get_policy() vm_op, as appropriate--to protect against
+ * freeing by another task.  It is the caller's responsibility to free the
+ * extra reference for shared policies.
+ */
+static struct mempolicy *get_vma_policy(struct vm_area_struct *vma,
+                                               unsigned long addr)
+{
+       struct mempolicy *pol = __get_vma_policy(vma, addr);
+
        if (!pol)
-               pol = &default_policy;
+               pol = get_task_policy(current);
+
        return pol;
 }
 
-bool vma_policy_mof(struct task_struct *task, struct vm_area_struct *vma)
+bool vma_policy_mof(struct vm_area_struct *vma)
 {
-       struct mempolicy *pol = get_task_policy(task);
-       if (vma) {
-               if (vma->vm_ops && vma->vm_ops->get_policy) {
-                       bool ret = false;
+       struct mempolicy *pol;
 
-                       pol = vma->vm_ops->get_policy(vma, vma->vm_start);
-                       if (pol && (pol->flags & MPOL_F_MOF))
-                               ret = true;
-                       mpol_cond_put(pol);
+       if (vma->vm_ops && vma->vm_ops->get_policy) {
+               bool ret = false;
 
-                       return ret;
-               } else if (vma->vm_policy) {
-                       pol = vma->vm_policy;
-               }
+               pol = vma->vm_ops->get_policy(vma, vma->vm_start);
+               if (pol && (pol->flags & MPOL_F_MOF))
+                       ret = true;
+               mpol_cond_put(pol);
+
+               return ret;
        }
 
+       pol = vma->vm_policy;
        if (!pol)
-               return default_policy.flags & MPOL_F_MOF;
+               pol = get_task_policy(current);
 
        return pol->flags & MPOL_F_MOF;
 }
@@ -1874,7 +1863,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 {
        struct zonelist *zl;
 
-       *mpol = get_vma_policy(current, vma, addr);
+       *mpol = get_vma_policy(vma, addr);
        *nodemask = NULL;       /* assume !MPOL_BIND */
 
        if (unlikely((*mpol)->mode == MPOL_INTERLEAVE)) {
@@ -2029,7 +2018,7 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
        unsigned int cpuset_mems_cookie;
 
 retry_cpuset:
-       pol = get_vma_policy(current, vma, addr);
+       pol = get_vma_policy(vma, addr);
        cpuset_mems_cookie = read_mems_allowed_begin();
 
        if (unlikely(pol->mode == MPOL_INTERLEAVE)) {
@@ -2046,8 +2035,7 @@ retry_cpuset:
        page = __alloc_pages_nodemask(gfp, order,
                                      policy_zonelist(gfp, pol, node),
                                      policy_nodemask(gfp, pol));
-       if (unlikely(mpol_needs_cond_ref(pol)))
-               __mpol_put(pol);
+       mpol_cond_put(pol);
        if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie)))
                goto retry_cpuset;
        return page;
@@ -2074,12 +2062,12 @@ retry_cpuset:
  */
 struct page *alloc_pages_current(gfp_t gfp, unsigned order)
 {
-       struct mempolicy *pol = get_task_policy(current);
+       struct mempolicy *pol = &default_policy;
        struct page *page;
        unsigned int cpuset_mems_cookie;
 
-       if (!pol || in_interrupt() || (gfp & __GFP_THISNODE))
-               pol = &default_policy;
+       if (!in_interrupt() && !(gfp & __GFP_THISNODE))
+               pol = get_task_policy(current);
 
 retry_cpuset:
        cpuset_mems_cookie = read_mems_allowed_begin();
@@ -2296,7 +2284,7 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long
 
        BUG_ON(!vma);
 
-       pol = get_vma_policy(current, vma, addr);
+       pol = get_vma_policy(vma, addr);
        if (!(pol->flags & MPOL_F_MOF))
                goto out;
 
index 2740360cd216ca45054387f481c7c31775d4ebff..01439953abf548690ac17f1b994511b587e9326a 100644 (file)
@@ -876,7 +876,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
                }
        }
 
-       if (unlikely(balloon_page_movable(page))) {
+       if (unlikely(isolated_balloon_page(page))) {
                /*
                 * A ballooned page does not need any special attention from
                 * physical to virtual reverse mapping procedures.
@@ -955,17 +955,6 @@ static int unmap_and_move(new_page_t get_new_page, free_page_t put_new_page,
 
        rc = __unmap_and_move(page, newpage, force, mode);
 
-       if (unlikely(rc == MIGRATEPAGE_BALLOON_SUCCESS)) {
-               /*
-                * A ballooned page has been migrated already.
-                * Now, it's the time to wrap-up counters,
-                * handle the page back to Buddy and return.
-                */
-               dec_zone_page_state(page, NR_ISOLATED_ANON +
-                                   page_is_file_cache(page));
-               balloon_page_free(page);
-               return MIGRATEPAGE_SUCCESS;
-       }
 out:
        if (rc != -EAGAIN) {
                /*
@@ -988,6 +977,9 @@ out:
        if (rc != MIGRATEPAGE_SUCCESS && put_new_page) {
                ClearPageSwapBacked(newpage);
                put_new_page(newpage, private);
+       } else if (unlikely(__is_movable_balloon_page(newpage))) {
+               /* drop our reference, page already in the balloon */
+               put_page(newpage);
        } else
                putback_lru_page(newpage);
 
index ce84cb0b83ef56179facdacc286bce88abd05fae..03aa8512723b3120277702d60e032031de0e425f 100644 (file)
@@ -233,9 +233,9 @@ long __mlock_vma_pages_range(struct vm_area_struct *vma,
 
        VM_BUG_ON(start & ~PAGE_MASK);
        VM_BUG_ON(end   & ~PAGE_MASK);
-       VM_BUG_ON(start < vma->vm_start);
-       VM_BUG_ON(end   > vma->vm_end);
-       VM_BUG_ON(!rwsem_is_locked(&mm->mmap_sem));
+       VM_BUG_ON_VMA(start < vma->vm_start, vma);
+       VM_BUG_ON_VMA(end   > vma->vm_end, vma);
+       VM_BUG_ON_MM(!rwsem_is_locked(&mm->mmap_sem), mm);
 
        gup_flags = FOLL_TOUCH | FOLL_MLOCK;
        /*
index c0a3637cdb645b0eed66c18edcd10431bfd5417b..93d28c7e54201de5549dab1a3dd696d785a06302 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -70,7 +70,7 @@ static void unmap_region(struct mm_struct *mm,
  * MAP_SHARED  r: (no) no      r: (yes) yes    r: (no) yes     r: (no) yes
  *             w: (no) no      w: (no) no      w: (yes) yes    w: (no) no
  *             x: (no) no      x: (no) yes     x: (no) yes     x: (yes) yes
- *             
+ *
  * MAP_PRIVATE r: (no) no      r: (yes) yes    r: (no) yes     r: (no) yes
  *             w: (no) no      w: (no) no      w: (copy) copy  w: (no) no
  *             x: (no) no      x: (no) yes     x: (no) yes     x: (yes) yes
@@ -268,7 +268,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len);
 
 SYSCALL_DEFINE1(brk, unsigned long, brk)
 {
-       unsigned long rlim, retval;
+       unsigned long retval;
        unsigned long newbrk, oldbrk;
        struct mm_struct *mm = current->mm;
        unsigned long min_brk;
@@ -298,9 +298,8 @@ SYSCALL_DEFINE1(brk, unsigned long, brk)
         * segment grow beyond its set limit the in case where the limit is
         * not page aligned -Ram Gupta
         */
-       rlim = rlimit(RLIMIT_DATA);
-       if (rlim < RLIM_INFINITY && (brk - mm->start_brk) +
-                       (mm->end_data - mm->start_data) > rlim)
+       if (check_data_rlimit(rlimit(RLIMIT_DATA), brk, mm->start_brk,
+                             mm->end_data, mm->start_data))
                goto out;
 
        newbrk = PAGE_ALIGN(brk);
@@ -369,16 +368,18 @@ static int browse_rb(struct rb_root *root)
                struct vm_area_struct *vma;
                vma = rb_entry(nd, struct vm_area_struct, vm_rb);
                if (vma->vm_start < prev) {
-                       pr_emerg("vm_start %lx prev %lx\n", vma->vm_start, prev);
+                       pr_emerg("vm_start %lx < prev %lx\n",
+                                 vma->vm_start, prev);
                        bug = 1;
                }
                if (vma->vm_start < pend) {
-                       pr_emerg("vm_start %lx pend %lx\n", vma->vm_start, pend);
+                       pr_emerg("vm_start %lx < pend %lx\n",
+                                 vma->vm_start, pend);
                        bug = 1;
                }
                if (vma->vm_start > vma->vm_end) {
-                       pr_emerg("vm_end %lx < vm_start %lx\n",
-                               vma->vm_end, vma->vm_start);
+                       pr_emerg("vm_start %lx > vm_end %lx\n",
+                                 vma->vm_start, vma->vm_end);
                        bug = 1;
                }
                if (vma->rb_subtree_gap != vma_compute_subtree_gap(vma)) {
@@ -409,8 +410,9 @@ static void validate_mm_rb(struct rb_root *root, struct vm_area_struct *ignore)
        for (nd = rb_first(root); nd; nd = rb_next(nd)) {
                struct vm_area_struct *vma;
                vma = rb_entry(nd, struct vm_area_struct, vm_rb);
-               BUG_ON(vma != ignore &&
-                      vma->rb_subtree_gap != vma_compute_subtree_gap(vma));
+               VM_BUG_ON_VMA(vma != ignore &&
+                       vma->rb_subtree_gap != vma_compute_subtree_gap(vma),
+                       vma);
        }
 }
 
@@ -420,8 +422,10 @@ static void validate_mm(struct mm_struct *mm)
        int i = 0;
        unsigned long highest_address = 0;
        struct vm_area_struct *vma = mm->mmap;
+
        while (vma) {
                struct anon_vma_chain *avc;
+
                vma_lock_anon_vma(vma);
                list_for_each_entry(avc, &vma->anon_vma_chain, same_vma)
                        anon_vma_interval_tree_verify(avc);
@@ -436,15 +440,16 @@ static void validate_mm(struct mm_struct *mm)
        }
        if (highest_address != mm->highest_vm_end) {
                pr_emerg("mm->highest_vm_end %lx, found %lx\n",
-                      mm->highest_vm_end, highest_address);
+                         mm->highest_vm_end, highest_address);
                bug = 1;
        }
        i = browse_rb(&mm->mm_rb);
        if (i != mm->map_count) {
-               pr_emerg("map_count %d rb %d\n", mm->map_count, i);
+               if (i != -1)
+                       pr_emerg("map_count %d rb %d\n", mm->map_count, i);
                bug = 1;
        }
-       BUG_ON(bug);
+       VM_BUG_ON_MM(bug, mm);
 }
 #else
 #define validate_mm_rb(root, ignore) do { } while (0)
@@ -741,7 +746,7 @@ again:                      remove_next = 1 + (end > next->vm_end);
                         * split_vma inserting another: so it must be
                         * mprotect case 4 shifting the boundary down.
                         */
-                       adjust_next = - ((vma->vm_end - end) >> PAGE_SHIFT);
+                       adjust_next = -((vma->vm_end - end) >> PAGE_SHIFT);
                        exporter = vma;
                        importer = next;
                }
@@ -787,8 +792,8 @@ again:                      remove_next = 1 + (end > next->vm_end);
        if (!anon_vma && adjust_next)
                anon_vma = next->anon_vma;
        if (anon_vma) {
-               VM_BUG_ON(adjust_next && next->anon_vma &&
-                         anon_vma != next->anon_vma);
+               VM_BUG_ON_VMA(adjust_next && next->anon_vma &&
+                         anon_vma != next->anon_vma, next);
                anon_vma_lock_write(anon_vma);
                anon_vma_interval_tree_pre_update_vma(vma);
                if (adjust_next)
@@ -1010,7 +1015,7 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags,
 struct vm_area_struct *vma_merge(struct mm_struct *mm,
                        struct vm_area_struct *prev, unsigned long addr,
                        unsigned long end, unsigned long vm_flags,
-                       struct anon_vma *anon_vma, struct file *file,
+                       struct anon_vma *anon_vma, struct file *file,
                        pgoff_t pgoff, struct mempolicy *policy)
 {
        pgoff_t pglen = (end - addr) >> PAGE_SHIFT;
@@ -1036,7 +1041,7 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
         * Can it merge with the predecessor?
         */
        if (prev && prev->vm_end == addr &&
-                       mpol_equal(vma_policy(prev), policy) &&
+                       mpol_equal(vma_policy(prev), policy) &&
                        can_vma_merge_after(prev, vm_flags,
                                                anon_vma, file, pgoff)) {
                /*
@@ -1064,7 +1069,7 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
         * Can this new request be merged in front of next?
         */
        if (next && end == next->vm_start &&
-                       mpol_equal(policy, vma_policy(next)) &&
+                       mpol_equal(policy, vma_policy(next)) &&
                        can_vma_merge_before(next, vm_flags,
                                        anon_vma, file, pgoff+pglen)) {
                if (prev && addr < prev->vm_end)        /* case 4 */
@@ -1235,7 +1240,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
                        unsigned long flags, unsigned long pgoff,
                        unsigned long *populate)
 {
-       struct mm_struct * mm = current->mm;
+       struct mm_struct *mm = current->mm;
        vm_flags_t vm_flags;
 
        *populate = 0;
@@ -1263,7 +1268,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
 
        /* offset overflow? */
        if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)
-               return -EOVERFLOW;
+               return -EOVERFLOW;
 
        /* Too many mappings? */
        if (mm->map_count > sysctl_max_map_count)
@@ -1921,7 +1926,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
        info.align_mask = 0;
        return vm_unmapped_area(&info);
 }
-#endif 
+#endif
 
 /*
  * This mmap-allocator allocates new areas top-down from below the
@@ -2321,13 +2326,13 @@ int expand_stack(struct vm_area_struct *vma, unsigned long address)
 }
 
 struct vm_area_struct *
-find_extend_vma(struct mm_struct * mm, unsigned long addr)
+find_extend_vma(struct mm_struct *mm, unsigned long addr)
 {
-       struct vm_area_struct * vma;
+       struct vm_area_struct *vma;
        unsigned long start;
 
        addr &= PAGE_MASK;
-       vma = find_vma(mm,addr);
+       vma = find_vma(mm, addr);
        if (!vma)
                return NULL;
        if (vma->vm_start <= addr)
@@ -2376,7 +2381,7 @@ static void unmap_region(struct mm_struct *mm,
                struct vm_area_struct *vma, struct vm_area_struct *prev,
                unsigned long start, unsigned long end)
 {
-       struct vm_area_struct *next = prev? prev->vm_next: mm->mmap;
+       struct vm_area_struct *next = prev ? prev->vm_next : mm->mmap;
        struct mmu_gather tlb;
 
        lru_add_drain();
@@ -2423,7 +2428,7 @@ detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma,
  * __split_vma() bypasses sysctl_max_map_count checking.  We use this on the
  * munmap path where it doesn't make sense to fail.
  */
-static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
+static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
              unsigned long addr, int new_below)
 {
        struct vm_area_struct *new;
@@ -2512,7 +2517,8 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
        if ((start & ~PAGE_MASK) || start > TASK_SIZE || len > TASK_SIZE-start)
                return -EINVAL;
 
-       if ((len = PAGE_ALIGN(len)) == 0)
+       len = PAGE_ALIGN(len);
+       if (len == 0)
                return -EINVAL;
 
        /* Find the first overlapping VMA */
@@ -2558,7 +2564,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
                if (error)
                        return error;
        }
-       vma = prev? prev->vm_next: mm->mmap;
+       vma = prev ? prev->vm_next : mm->mmap;
 
        /*
         * unlock any mlock()ed ranges before detaching vmas
@@ -2621,10 +2627,10 @@ static inline void verify_mm_writelocked(struct mm_struct *mm)
  */
 static unsigned long do_brk(unsigned long addr, unsigned long len)
 {
-       struct mm_struct * mm = current->mm;
-       struct vm_area_struct * vma, * prev;
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma, *prev;
        unsigned long flags;
-       struct rb_node ** rb_link, * rb_parent;
+       struct rb_node **rb_link, *rb_parent;
        pgoff_t pgoff = addr >> PAGE_SHIFT;
        int error;
 
@@ -2848,7 +2854,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
                         * safe. It is only safe to keep the vm_pgoff
                         * linear if there are no pages mapped yet.
                         */
-                       VM_BUG_ON(faulted_in_anon_vma);
+                       VM_BUG_ON_VMA(faulted_in_anon_vma, new_vma);
                        *vmap = vma = new_vma;
                }
                *need_rmap_locks = (new_vma->vm_pgoff <= vma->vm_pgoff);
@@ -3196,7 +3202,7 @@ void __init mmap_init(void)
 {
        int ret;
 
-       ret = percpu_counter_init(&vm_committed_as, 0);
+       ret = percpu_counter_init(&vm_committed_as, 0, GFP_KERNEL);
        VM_BUG_ON(ret);
 }
 
index 05f1180e9f21822e99a5f11a2a7a03af663a422c..b147f66f4c40f8a639def879df125f48e8c5595c 100644 (file)
@@ -21,8 +21,8 @@
 #include <linux/syscalls.h>
 #include <linux/mmu_notifier.h>
 #include <linux/sched/sysctl.h>
+#include <linux/uaccess.h>
 
-#include <asm/uaccess.h>
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
 
@@ -195,7 +195,8 @@ unsigned long move_page_tables(struct vm_area_struct *vma,
                if (pmd_trans_huge(*old_pmd)) {
                        int err = 0;
                        if (extent == HPAGE_PMD_SIZE) {
-                               VM_BUG_ON(vma->vm_file || !vma->anon_vma);
+                               VM_BUG_ON_VMA(vma->vm_file || !vma->anon_vma,
+                                             vma);
                                /* See comment in move_ptes() */
                                if (need_rmap_locks)
                                        anon_vma_lock_write(vma->anon_vma);
index a881d9673c6b165e5336e83bf2ab75bf3c03d938..bd1808e194a7f36a2e1cc418c51ea85dffc713fc 100644 (file)
@@ -539,7 +539,7 @@ void __init mmap_init(void)
 {
        int ret;
 
-       ret = percpu_counter_init(&vm_committed_as, 0);
+       ret = percpu_counter_init(&vm_committed_as, 0, GFP_KERNEL);
        VM_BUG_ON(ret);
        vm_region_jar = KMEM_CACHE(vm_region, SLAB_PANIC);
 }
index 1e11df8fa7ecaecd274a3d0aaa1fe0aea4bb38ab..bbf405a3a18f5acd8fbe57fabc06c3e5ce973e29 100644 (file)
@@ -565,7 +565,7 @@ bool oom_zonelist_trylock(struct zonelist *zonelist, gfp_t gfp_mask)
 
        spin_lock(&zone_scan_lock);
        for_each_zone_zonelist(zone, z, zonelist, gfp_zone(gfp_mask))
-               if (zone_is_oom_locked(zone)) {
+               if (test_bit(ZONE_OOM_LOCKED, &zone->flags)) {
                        ret = false;
                        goto out;
                }
@@ -575,7 +575,7 @@ bool oom_zonelist_trylock(struct zonelist *zonelist, gfp_t gfp_mask)
         * call to oom_zonelist_trylock() doesn't succeed when it shouldn't.
         */
        for_each_zone_zonelist(zone, z, zonelist, gfp_zone(gfp_mask))
-               zone_set_flag(zone, ZONE_OOM_LOCKED);
+               set_bit(ZONE_OOM_LOCKED, &zone->flags);
 
 out:
        spin_unlock(&zone_scan_lock);
@@ -594,7 +594,7 @@ void oom_zonelist_unlock(struct zonelist *zonelist, gfp_t gfp_mask)
 
        spin_lock(&zone_scan_lock);
        for_each_zone_zonelist(zone, z, zonelist, gfp_zone(gfp_mask))
-               zone_clear_flag(zone, ZONE_OOM_LOCKED);
+               clear_bit(ZONE_OOM_LOCKED, &zone->flags);
        spin_unlock(&zone_scan_lock);
 }
 
index 91d73ef1744d6fbc5c4bbdf9782beb3b3e22da6b..ff24c9d83112ece05dab0d3b5c941a92252deef0 100644 (file)
@@ -1075,13 +1075,13 @@ static void bdi_update_dirty_ratelimit(struct backing_dev_info *bdi,
        }
 
        if (dirty < setpoint) {
-               x = min(bdi->balanced_dirty_ratelimit,
-                        min(balanced_dirty_ratelimit, task_ratelimit));
+               x = min3(bdi->balanced_dirty_ratelimit,
+                        balanced_dirty_ratelimit, task_ratelimit);
                if (dirty_ratelimit < x)
                        step = x - dirty_ratelimit;
        } else {
-               x = max(bdi->balanced_dirty_ratelimit,
-                        max(balanced_dirty_ratelimit, task_ratelimit));
+               x = max3(bdi->balanced_dirty_ratelimit,
+                        balanced_dirty_ratelimit, task_ratelimit);
                if (dirty_ratelimit > x)
                        step = dirty_ratelimit - x;
        }
@@ -1777,7 +1777,7 @@ void __init page_writeback_init(void)
        writeback_set_ratelimit();
        register_cpu_notifier(&ratelimit_nb);
 
-       fprop_global_init(&writeout_completions);
+       fprop_global_init(&writeout_completions, GFP_KERNEL);
 }
 
 /**
index eee961958021e8c31e02ac6845b290a21d5a1690..c9710c9bbee2987569d6e56b5a2b9aea7e9d316a 100644 (file)
@@ -53,8 +53,6 @@
 #include <linux/kmemleak.h>
 #include <linux/compaction.h>
 #include <trace/events/kmem.h>
-#include <linux/ftrace_event.h>
-#include <linux/memcontrol.h>
 #include <linux/prefetch.h>
 #include <linux/mm_inline.h>
 #include <linux/migrate.h>
@@ -85,6 +83,7 @@ EXPORT_PER_CPU_SYMBOL(numa_node);
  */
 DEFINE_PER_CPU(int, _numa_mem_);               /* Kernel "local memory" node */
 EXPORT_PER_CPU_SYMBOL(_numa_mem_);
+int _node_numa_mem_[MAX_NUMNODES];
 #endif
 
 /*
@@ -1014,7 +1013,7 @@ int move_freepages(struct zone *zone,
         * Remove at a later date when no bug reports exist related to
         * grouping pages by mobility
         */
-       BUG_ON(page_zone(start_page) != page_zone(end_page));
+       VM_BUG_ON(page_zone(start_page) != page_zone(end_page));
 #endif
 
        for (page = start_page; page <= end_page;) {
@@ -1613,8 +1612,8 @@ again:
 
        __mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order));
        if (atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH]) <= 0 &&
-           !zone_is_fair_depleted(zone))
-               zone_set_flag(zone, ZONE_FAIR_DEPLETED);
+           !test_bit(ZONE_FAIR_DEPLETED, &zone->flags))
+               set_bit(ZONE_FAIR_DEPLETED, &zone->flags);
 
        __count_zone_vm_events(PGALLOC, zone, 1 << order);
        zone_statistics(preferred_zone, zone, gfp_flags);
@@ -1934,7 +1933,7 @@ static void reset_alloc_batches(struct zone *preferred_zone)
                mod_zone_page_state(zone, NR_ALLOC_BATCH,
                        high_wmark_pages(zone) - low_wmark_pages(zone) -
                        atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH]));
-               zone_clear_flag(zone, ZONE_FAIR_DEPLETED);
+               clear_bit(ZONE_FAIR_DEPLETED, &zone->flags);
        } while (zone++ != preferred_zone);
 }
 
@@ -1985,7 +1984,7 @@ zonelist_scan:
                if (alloc_flags & ALLOC_FAIR) {
                        if (!zone_local(preferred_zone, zone))
                                break;
-                       if (zone_is_fair_depleted(zone)) {
+                       if (test_bit(ZONE_FAIR_DEPLETED, &zone->flags)) {
                                nr_fair_skipped++;
                                continue;
                        }
@@ -2296,58 +2295,72 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
        struct zonelist *zonelist, enum zone_type high_zoneidx,
        nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
        int classzone_idx, int migratetype, enum migrate_mode mode,
-       bool *contended_compaction, bool *deferred_compaction,
-       unsigned long *did_some_progress)
+       int *contended_compaction, bool *deferred_compaction)
 {
-       if (!order)
-               return NULL;
+       struct zone *last_compact_zone = NULL;
+       unsigned long compact_result;
+       struct page *page;
 
-       if (compaction_deferred(preferred_zone, order)) {
-               *deferred_compaction = true;
+       if (!order)
                return NULL;
-       }
 
        current->flags |= PF_MEMALLOC;
-       *did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
+       compact_result = try_to_compact_pages(zonelist, order, gfp_mask,
                                                nodemask, mode,
-                                               contended_compaction);
+                                               contended_compaction,
+                                               &last_compact_zone);
        current->flags &= ~PF_MEMALLOC;
 
-       if (*did_some_progress != COMPACT_SKIPPED) {
-               struct page *page;
+       switch (compact_result) {
+       case COMPACT_DEFERRED:
+               *deferred_compaction = true;
+               /* fall-through */
+       case COMPACT_SKIPPED:
+               return NULL;
+       default:
+               break;
+       }
 
-               /* Page migration frees to the PCP lists but we want merging */
-               drain_pages(get_cpu());
-               put_cpu();
+       /*
+        * At least in one zone compaction wasn't deferred or skipped, so let's
+        * count a compaction stall
+        */
+       count_vm_event(COMPACTSTALL);
 
-               page = get_page_from_freelist(gfp_mask, nodemask,
-                               order, zonelist, high_zoneidx,
-                               alloc_flags & ~ALLOC_NO_WATERMARKS,
-                               preferred_zone, classzone_idx, migratetype);
-               if (page) {
-                       preferred_zone->compact_blockskip_flush = false;
-                       compaction_defer_reset(preferred_zone, order, true);
-                       count_vm_event(COMPACTSUCCESS);
-                       return page;
-               }
+       /* Page migration frees to the PCP lists but we want merging */
+       drain_pages(get_cpu());
+       put_cpu();
 
-               /*
-                * It's bad if compaction run occurs and fails.
-                * The most likely reason is that pages exist,
-                * but not enough to satisfy watermarks.
-                */
-               count_vm_event(COMPACTFAIL);
+       page = get_page_from_freelist(gfp_mask, nodemask,
+                       order, zonelist, high_zoneidx,
+                       alloc_flags & ~ALLOC_NO_WATERMARKS,
+                       preferred_zone, classzone_idx, migratetype);
 
-               /*
-                * As async compaction considers a subset of pageblocks, only
-                * defer if the failure was a sync compaction failure.
-                */
-               if (mode != MIGRATE_ASYNC)
-                       defer_compaction(preferred_zone, order);
+       if (page) {
+               struct zone *zone = page_zone(page);
 
-               cond_resched();
+               zone->compact_blockskip_flush = false;
+               compaction_defer_reset(zone, order, true);
+               count_vm_event(COMPACTSUCCESS);
+               return page;
        }
 
+       /*
+        * last_compact_zone is where try_to_compact_pages thought allocation
+        * should succeed, so it did not defer compaction. But here we know
+        * that it didn't succeed, so we do the defer.
+        */
+       if (last_compact_zone && mode != MIGRATE_ASYNC)
+               defer_compaction(last_compact_zone, order);
+
+       /*
+        * It's bad if compaction run occurs and fails. The most likely reason
+        * is that pages exist, but not enough to satisfy watermarks.
+        */
+       count_vm_event(COMPACTFAIL);
+
+       cond_resched();
+
        return NULL;
 }
 #else
@@ -2355,9 +2368,8 @@ static inline struct page *
 __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
        struct zonelist *zonelist, enum zone_type high_zoneidx,
        nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
-       int classzone_idx, int migratetype,
-       enum migrate_mode mode, bool *contended_compaction,
-       bool *deferred_compaction, unsigned long *did_some_progress)
+       int classzone_idx, int migratetype, enum migrate_mode mode,
+       int *contended_compaction, bool *deferred_compaction)
 {
        return NULL;
 }
@@ -2457,12 +2469,14 @@ __alloc_pages_high_priority(gfp_t gfp_mask, unsigned int order,
 static void wake_all_kswapds(unsigned int order,
                             struct zonelist *zonelist,
                             enum zone_type high_zoneidx,
-                            struct zone *preferred_zone)
+                            struct zone *preferred_zone,
+                            nodemask_t *nodemask)
 {
        struct zoneref *z;
        struct zone *zone;
 
-       for_each_zone_zonelist(zone, z, zonelist, high_zoneidx)
+       for_each_zone_zonelist_nodemask(zone, z, zonelist,
+                                               high_zoneidx, nodemask)
                wakeup_kswapd(zone, order, zone_idx(preferred_zone));
 }
 
@@ -2509,7 +2523,7 @@ gfp_to_alloc_flags(gfp_t gfp_mask)
                        alloc_flags |= ALLOC_NO_WATERMARKS;
        }
 #ifdef CONFIG_CMA
-       if (allocflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE)
+       if (gfpflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE)
                alloc_flags |= ALLOC_CMA;
 #endif
        return alloc_flags;
@@ -2533,7 +2547,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
        unsigned long did_some_progress;
        enum migrate_mode migration_mode = MIGRATE_ASYNC;
        bool deferred_compaction = false;
-       bool contended_compaction = false;
+       int contended_compaction = COMPACT_CONTENDED_NONE;
 
        /*
         * In the slowpath, we sanity check order to avoid ever trying to
@@ -2560,7 +2574,8 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
 
 restart:
        if (!(gfp_mask & __GFP_NO_KSWAPD))
-               wake_all_kswapds(order, zonelist, high_zoneidx, preferred_zone);
+               wake_all_kswapds(order, zonelist, high_zoneidx,
+                               preferred_zone, nodemask);
 
        /*
         * OK, we're below the kswapd watermark and have kicked background
@@ -2633,20 +2648,40 @@ rebalance:
                                        preferred_zone,
                                        classzone_idx, migratetype,
                                        migration_mode, &contended_compaction,
-                                       &deferred_compaction,
-                                       &did_some_progress);
+                                       &deferred_compaction);
        if (page)
                goto got_pg;
 
-       /*
-        * If compaction is deferred for high-order allocations, it is because
-        * sync compaction recently failed. In this is the case and the caller
-        * requested a movable allocation that does not heavily disrupt the
-        * system then fail the allocation instead of entering direct reclaim.
-        */
-       if ((deferred_compaction || contended_compaction) &&
-                                               (gfp_mask & __GFP_NO_KSWAPD))
-               goto nopage;
+       /* Checks for THP-specific high-order allocations */
+       if ((gfp_mask & GFP_TRANSHUGE) == GFP_TRANSHUGE) {
+               /*
+                * If compaction is deferred for high-order allocations, it is
+                * because sync compaction recently failed. If this is the case
+                * and the caller requested a THP allocation, we do not want
+                * to heavily disrupt the system, so we fail the allocation
+                * instead of entering direct reclaim.
+                */
+               if (deferred_compaction)
+                       goto nopage;
+
+               /*
+                * In all zones where compaction was attempted (and not
+                * deferred or skipped), lock contention has been detected.
+                * For THP allocation we do not want to disrupt the others
+                * so we fallback to base pages instead.
+                */
+               if (contended_compaction == COMPACT_CONTENDED_LOCK)
+                       goto nopage;
+
+               /*
+                * If compaction was aborted due to need_resched(), we do not
+                * want to further increase allocation latency, unless it is
+                * khugepaged trying to collapse.
+                */
+               if (contended_compaction == COMPACT_CONTENDED_SCHED
+                       && !(current->flags & PF_KTHREAD))
+                       goto nopage;
+       }
 
        /*
         * It can become very expensive to allocate transparent hugepages at
@@ -2726,8 +2761,7 @@ rebalance:
                                        preferred_zone,
                                        classzone_idx, migratetype,
                                        migration_mode, &contended_compaction,
-                                       &deferred_compaction,
-                                       &did_some_progress);
+                                       &deferred_compaction);
                if (page)
                        goto got_pg;
        }
@@ -2753,7 +2787,7 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
        struct zone *preferred_zone;
        struct zoneref *preferred_zoneref;
        struct page *page = NULL;
-       int migratetype = allocflags_to_migratetype(gfp_mask);
+       int migratetype = gfpflags_to_migratetype(gfp_mask);
        unsigned int cpuset_mems_cookie;
        int alloc_flags = ALLOC_WMARK_LOW|ALLOC_CPUSET|ALLOC_FAIR;
        int classzone_idx;
@@ -2775,6 +2809,9 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
        if (unlikely(!zonelist->_zonerefs->zone))
                return NULL;
 
+       if (IS_ENABLED(CONFIG_CMA) && migratetype == MIGRATE_MOVABLE)
+               alloc_flags |= ALLOC_CMA;
+
 retry_cpuset:
        cpuset_mems_cookie = read_mems_allowed_begin();
 
@@ -2786,10 +2823,6 @@ retry_cpuset:
                goto out;
        classzone_idx = zonelist_zone_idx(preferred_zoneref);
 
-#ifdef CONFIG_CMA
-       if (allocflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE)
-               alloc_flags |= ALLOC_CMA;
-#endif
        /* First allocation attempt */
        page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, nodemask, order,
                        zonelist, high_zoneidx, alloc_flags,
@@ -3579,68 +3612,30 @@ static void build_zonelists_in_zone_order(pg_data_t *pgdat, int nr_nodes)
        zonelist->_zonerefs[pos].zone_idx = 0;
 }
 
+#if defined(CONFIG_64BIT)
+/*
+ * Devices that require DMA32/DMA are relatively rare and do not justify a
+ * penalty to every machine in case the specialised case applies. Default
+ * to Node-ordering on 64-bit NUMA machines
+ */
+static int default_zonelist_order(void)
+{
+       return ZONELIST_ORDER_NODE;
+}
+#else
+/*
+ * On 32-bit, the Normal zone needs to be preserved for allocations accessible
+ * by the kernel. If processes running on node 0 deplete the low memory zone
+ * then reclaim will occur more frequency increasing stalls and potentially
+ * be easier to OOM if a large percentage of the zone is under writeback or
+ * dirty. The problem is significantly worse if CONFIG_HIGHPTE is not set.
+ * Hence, default to zone ordering on 32-bit.
+ */
 static int default_zonelist_order(void)
 {
-       int nid, zone_type;
-       unsigned long low_kmem_size, total_size;
-       struct zone *z;
-       int average_size;
-       /*
-        * ZONE_DMA and ZONE_DMA32 can be very small area in the system.
-        * If they are really small and used heavily, the system can fall
-        * into OOM very easily.
-        * This function detect ZONE_DMA/DMA32 size and configures zone order.
-        */
-       /* Is there ZONE_NORMAL ? (ex. ppc has only DMA zone..) */
-       low_kmem_size = 0;
-       total_size = 0;
-       for_each_online_node(nid) {
-               for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) {
-                       z = &NODE_DATA(nid)->node_zones[zone_type];
-                       if (populated_zone(z)) {
-                               if (zone_type < ZONE_NORMAL)
-                                       low_kmem_size += z->managed_pages;
-                               total_size += z->managed_pages;
-                       } else if (zone_type == ZONE_NORMAL) {
-                               /*
-                                * If any node has only lowmem, then node order
-                                * is preferred to allow kernel allocations
-                                * locally; otherwise, they can easily infringe
-                                * on other nodes when there is an abundance of
-                                * lowmem available to allocate from.
-                                */
-                               return ZONELIST_ORDER_NODE;
-                       }
-               }
-       }
-       if (!low_kmem_size ||  /* there are no DMA area. */
-           low_kmem_size > total_size/2) /* DMA/DMA32 is big. */
-               return ZONELIST_ORDER_NODE;
-       /*
-        * look into each node's config.
-        * If there is a node whose DMA/DMA32 memory is very big area on
-        * local memory, NODE_ORDER may be suitable.
-        */
-       average_size = total_size /
-                               (nodes_weight(node_states[N_MEMORY]) + 1);
-       for_each_online_node(nid) {
-               low_kmem_size = 0;
-               total_size = 0;
-               for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) {
-                       z = &NODE_DATA(nid)->node_zones[zone_type];
-                       if (populated_zone(z)) {
-                               if (zone_type < ZONE_NORMAL)
-                                       low_kmem_size += z->present_pages;
-                               total_size += z->present_pages;
-                       }
-               }
-               if (low_kmem_size &&
-                   total_size > average_size && /* ignore small node */
-                   low_kmem_size > total_size * 70/100)
-                       return ZONELIST_ORDER_NODE;
-       }
        return ZONELIST_ORDER_ZONE;
 }
+#endif /* CONFIG_64BIT */
 
 static void set_zonelist_order(void)
 {
@@ -6277,8 +6272,7 @@ static int __alloc_contig_migrate_range(struct compact_control *cc,
 
                if (list_empty(&cc->migratepages)) {
                        cc->nr_migratepages = 0;
-                       pfn = isolate_migratepages_range(cc->zone, cc,
-                                                        pfn, end, true);
+                       pfn = isolate_migratepages_range(cc, pfn, end);
                        if (!pfn) {
                                ret = -EINTR;
                                break;
@@ -6554,97 +6548,3 @@ bool is_free_buddy_page(struct page *page)
        return order < MAX_ORDER;
 }
 #endif
-
-static const struct trace_print_flags pageflag_names[] = {
-       {1UL << PG_locked,              "locked"        },
-       {1UL << PG_error,               "error"         },
-       {1UL << PG_referenced,          "referenced"    },
-       {1UL << PG_uptodate,            "uptodate"      },
-       {1UL << PG_dirty,               "dirty"         },
-       {1UL << PG_lru,                 "lru"           },
-       {1UL << PG_active,              "active"        },
-       {1UL << PG_slab,                "slab"          },
-       {1UL << PG_owner_priv_1,        "owner_priv_1"  },
-       {1UL << PG_arch_1,              "arch_1"        },
-       {1UL << PG_reserved,            "reserved"      },
-       {1UL << PG_private,             "private"       },
-       {1UL << PG_private_2,           "private_2"     },
-       {1UL << PG_writeback,           "writeback"     },
-#ifdef CONFIG_PAGEFLAGS_EXTENDED
-       {1UL << PG_head,                "head"          },
-       {1UL << PG_tail,                "tail"          },
-#else
-       {1UL << PG_compound,            "compound"      },
-#endif
-       {1UL << PG_swapcache,           "swapcache"     },
-       {1UL << PG_mappedtodisk,        "mappedtodisk"  },
-       {1UL << PG_reclaim,             "reclaim"       },
-       {1UL << PG_swapbacked,          "swapbacked"    },
-       {1UL << PG_unevictable,         "unevictable"   },
-#ifdef CONFIG_MMU
-       {1UL << PG_mlocked,             "mlocked"       },
-#endif
-#ifdef CONFIG_ARCH_USES_PG_UNCACHED
-       {1UL << PG_uncached,            "uncached"      },
-#endif
-#ifdef CONFIG_MEMORY_FAILURE
-       {1UL << PG_hwpoison,            "hwpoison"      },
-#endif
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-       {1UL << PG_compound_lock,       "compound_lock" },
-#endif
-};
-
-static void dump_page_flags(unsigned long flags)
-{
-       const char *delim = "";
-       unsigned long mask;
-       int i;
-
-       BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS);
-
-       printk(KERN_ALERT "page flags: %#lx(", flags);
-
-       /* remove zone id */
-       flags &= (1UL << NR_PAGEFLAGS) - 1;
-
-       for (i = 0; i < ARRAY_SIZE(pageflag_names) && flags; i++) {
-
-               mask = pageflag_names[i].mask;
-               if ((flags & mask) != mask)
-                       continue;
-
-               flags &= ~mask;
-               printk("%s%s", delim, pageflag_names[i].name);
-               delim = "|";
-       }
-
-       /* check for left over flags */
-       if (flags)
-               printk("%s%#lx", delim, flags);
-
-       printk(")\n");
-}
-
-void dump_page_badflags(struct page *page, const char *reason,
-               unsigned long badflags)
-{
-       printk(KERN_ALERT
-              "page:%p count:%d mapcount:%d mapping:%p index:%#lx\n",
-               page, atomic_read(&page->_count), page_mapcount(page),
-               page->mapping, page->index);
-       dump_page_flags(page->flags);
-       if (reason)
-               pr_alert("page dumped because: %s\n", reason);
-       if (page->flags & badflags) {
-               pr_alert("bad because of flags:\n");
-               dump_page_flags(page->flags & badflags);
-       }
-       mem_cgroup_print_bad_page(page);
-}
-
-void dump_page(struct page *page, const char *reason)
-{
-       dump_page_badflags(page, reason, 0);
-}
-EXPORT_SYMBOL(dump_page);
index 2beeabf502c50f1ff3069981bd5b3a84b7463b6f..ad83195521f2da08e136cc8674d3b37c54e3ab86 100644 (file)
@@ -177,7 +177,7 @@ int walk_page_range(unsigned long addr, unsigned long end,
        if (!walk->mm)
                return -EINVAL;
 
-       VM_BUG_ON(!rwsem_is_locked(&walk->mm->mmap_sem));
+       VM_BUG_ON_MM(!rwsem_is_locked(&walk->mm->mmap_sem), walk->mm);
 
        pgd = pgd_offset(walk->mm, addr);
        do {
index 89633fefc6a2b39d8d32d0204846f4ab8b303930..10e3d0b8a86d1bbbd9749bccb9666d4251155450 100644 (file)
 
 #include <linux/log2.h>
 
-static int pcpu_populate_chunk(struct pcpu_chunk *chunk, int off, int size)
+static int pcpu_populate_chunk(struct pcpu_chunk *chunk,
+                              int page_start, int page_end)
 {
-       unsigned int cpu;
-
-       for_each_possible_cpu(cpu)
-               memset((void *)pcpu_chunk_addr(chunk, cpu, 0) + off, 0, size);
-
        return 0;
 }
 
-static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk, int off, int size)
+static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk,
+                                 int page_start, int page_end)
 {
        /* nada */
 }
@@ -70,6 +67,11 @@ static struct pcpu_chunk *pcpu_create_chunk(void)
 
        chunk->data = pages;
        chunk->base_addr = page_address(pages) - pcpu_group_offsets[0];
+
+       spin_lock_irq(&pcpu_lock);
+       pcpu_chunk_populated(chunk, 0, nr_pages);
+       spin_unlock_irq(&pcpu_lock);
+
        return chunk;
 }
 
index 51108165f829d777e4c69abe6109cb88ca4d7e14..538998a137d24e069969dcc3ed00cedc6c25616f 100644 (file)
@@ -20,46 +20,25 @@ static struct page *pcpu_chunk_page(struct pcpu_chunk *chunk,
 }
 
 /**
- * pcpu_get_pages_and_bitmap - get temp pages array and bitmap
+ * pcpu_get_pages - get temp pages array
  * @chunk: chunk of interest
- * @bitmapp: output parameter for bitmap
- * @may_alloc: may allocate the array
  *
- * Returns pointer to array of pointers to struct page and bitmap,
- * both of which can be indexed with pcpu_page_idx().  The returned
- * array is cleared to zero and *@bitmapp is copied from
- * @chunk->populated.  Note that there is only one array and bitmap
- * and access exclusion is the caller's responsibility.
- *
- * CONTEXT:
- * pcpu_alloc_mutex and does GFP_KERNEL allocation if @may_alloc.
- * Otherwise, don't care.
+ * Returns pointer to array of pointers to struct page which can be indexed
+ * with pcpu_page_idx().  Note that there is only one array and accesses
+ * should be serialized by pcpu_alloc_mutex.
  *
  * RETURNS:
- * Pointer to temp pages array on success, NULL on failure.
+ * Pointer to temp pages array on success.
  */
-static struct page **pcpu_get_pages_and_bitmap(struct pcpu_chunk *chunk,
-                                              unsigned long **bitmapp,
-                                              bool may_alloc)
+static struct page **pcpu_get_pages(struct pcpu_chunk *chunk_alloc)
 {
        static struct page **pages;
-       static unsigned long *bitmap;
        size_t pages_size = pcpu_nr_units * pcpu_unit_pages * sizeof(pages[0]);
-       size_t bitmap_size = BITS_TO_LONGS(pcpu_unit_pages) *
-                            sizeof(unsigned long);
-
-       if (!pages || !bitmap) {
-               if (may_alloc && !pages)
-                       pages = pcpu_mem_zalloc(pages_size);
-               if (may_alloc && !bitmap)
-                       bitmap = pcpu_mem_zalloc(bitmap_size);
-               if (!pages || !bitmap)
-                       return NULL;
-       }
 
-       bitmap_copy(bitmap, chunk->populated, pcpu_unit_pages);
+       lockdep_assert_held(&pcpu_alloc_mutex);
 
-       *bitmapp = bitmap;
+       if (!pages)
+               pages = pcpu_mem_zalloc(pages_size);
        return pages;
 }
 
@@ -67,7 +46,6 @@ static struct page **pcpu_get_pages_and_bitmap(struct pcpu_chunk *chunk,
  * pcpu_free_pages - free pages which were allocated for @chunk
  * @chunk: chunk pages were allocated for
  * @pages: array of pages to be freed, indexed by pcpu_page_idx()
- * @populated: populated bitmap
  * @page_start: page index of the first page to be freed
  * @page_end: page index of the last page to be freed + 1
  *
@@ -75,8 +53,7 @@ static struct page **pcpu_get_pages_and_bitmap(struct pcpu_chunk *chunk,
  * The pages were allocated for @chunk.
  */
 static void pcpu_free_pages(struct pcpu_chunk *chunk,
-                           struct page **pages, unsigned long *populated,
-                           int page_start, int page_end)
+                           struct page **pages, int page_start, int page_end)
 {
        unsigned int cpu;
        int i;
@@ -95,7 +72,6 @@ static void pcpu_free_pages(struct pcpu_chunk *chunk,
  * pcpu_alloc_pages - allocates pages for @chunk
  * @chunk: target chunk
  * @pages: array to put the allocated pages into, indexed by pcpu_page_idx()
- * @populated: populated bitmap
  * @page_start: page index of the first page to be allocated
  * @page_end: page index of the last page to be allocated + 1
  *
@@ -104,8 +80,7 @@ static void pcpu_free_pages(struct pcpu_chunk *chunk,
  * content of @pages and will pass it verbatim to pcpu_map_pages().
  */
 static int pcpu_alloc_pages(struct pcpu_chunk *chunk,
-                           struct page **pages, unsigned long *populated,
-                           int page_start, int page_end)
+                           struct page **pages, int page_start, int page_end)
 {
        const gfp_t gfp = GFP_KERNEL | __GFP_HIGHMEM | __GFP_COLD;
        unsigned int cpu, tcpu;
@@ -164,7 +139,6 @@ static void __pcpu_unmap_pages(unsigned long addr, int nr_pages)
  * pcpu_unmap_pages - unmap pages out of a pcpu_chunk
  * @chunk: chunk of interest
  * @pages: pages array which can be used to pass information to free
- * @populated: populated bitmap
  * @page_start: page index of the first page to unmap
  * @page_end: page index of the last page to unmap + 1
  *
@@ -175,8 +149,7 @@ static void __pcpu_unmap_pages(unsigned long addr, int nr_pages)
  * proper pre/post flush functions.
  */
 static void pcpu_unmap_pages(struct pcpu_chunk *chunk,
-                            struct page **pages, unsigned long *populated,
-                            int page_start, int page_end)
+                            struct page **pages, int page_start, int page_end)
 {
        unsigned int cpu;
        int i;
@@ -192,8 +165,6 @@ static void pcpu_unmap_pages(struct pcpu_chunk *chunk,
                __pcpu_unmap_pages(pcpu_chunk_addr(chunk, cpu, page_start),
                                   page_end - page_start);
        }
-
-       bitmap_clear(populated, page_start, page_end - page_start);
 }
 
 /**
@@ -228,7 +199,6 @@ static int __pcpu_map_pages(unsigned long addr, struct page **pages,
  * pcpu_map_pages - map pages into a pcpu_chunk
  * @chunk: chunk of interest
  * @pages: pages array containing pages to be mapped
- * @populated: populated bitmap
  * @page_start: page index of the first page to map
  * @page_end: page index of the last page to map + 1
  *
@@ -236,13 +206,11 @@ static int __pcpu_map_pages(unsigned long addr, struct page **pages,
  * caller is responsible for calling pcpu_post_map_flush() after all
  * mappings are complete.
  *
- * This function is responsible for setting corresponding bits in
- * @chunk->populated bitmap and whatever is necessary for reverse
- * lookup (addr -> chunk).
+ * This function is responsible for setting up whatever is necessary for
+ * reverse lookup (addr -> chunk).
  */
 static int pcpu_map_pages(struct pcpu_chunk *chunk,
-                         struct page **pages, unsigned long *populated,
-                         int page_start, int page_end)
+                         struct page **pages, int page_start, int page_end)
 {
        unsigned int cpu, tcpu;
        int i, err;
@@ -253,18 +221,12 @@ static int pcpu_map_pages(struct pcpu_chunk *chunk,
                                       page_end - page_start);
                if (err < 0)
                        goto err;
-       }
 
-       /* mapping successful, link chunk and mark populated */
-       for (i = page_start; i < page_end; i++) {
-               for_each_possible_cpu(cpu)
+               for (i = page_start; i < page_end; i++)
                        pcpu_set_page_chunk(pages[pcpu_page_idx(cpu, i)],
                                            chunk);
-               __set_bit(i, populated);
        }
-
        return 0;
-
 err:
        for_each_possible_cpu(tcpu) {
                if (tcpu == cpu)
@@ -299,123 +261,69 @@ static void pcpu_post_map_flush(struct pcpu_chunk *chunk,
 /**
  * pcpu_populate_chunk - populate and map an area of a pcpu_chunk
  * @chunk: chunk of interest
- * @off: offset to the area to populate
- * @size: size of the area to populate in bytes
+ * @page_start: the start page
+ * @page_end: the end page
  *
  * For each cpu, populate and map pages [@page_start,@page_end) into
- * @chunk.  The area is cleared on return.
+ * @chunk.
  *
  * CONTEXT:
  * pcpu_alloc_mutex, does GFP_KERNEL allocation.
  */
-static int pcpu_populate_chunk(struct pcpu_chunk *chunk, int off, int size)
+static int pcpu_populate_chunk(struct pcpu_chunk *chunk,
+                              int page_start, int page_end)
 {
-       int page_start = PFN_DOWN(off);
-       int page_end = PFN_UP(off + size);
-       int free_end = page_start, unmap_end = page_start;
        struct page **pages;
-       unsigned long *populated;
-       unsigned int cpu;
-       int rs, re, rc;
-
-       /* quick path, check whether all pages are already there */
-       rs = page_start;
-       pcpu_next_pop(chunk, &rs, &re, page_end);
-       if (rs == page_start && re == page_end)
-               goto clear;
 
-       /* need to allocate and map pages, this chunk can't be immutable */
-       WARN_ON(chunk->immutable);
-
-       pages = pcpu_get_pages_and_bitmap(chunk, &populated, true);
+       pages = pcpu_get_pages(chunk);
        if (!pages)
                return -ENOMEM;
 
-       /* alloc and map */
-       pcpu_for_each_unpop_region(chunk, rs, re, page_start, page_end) {
-               rc = pcpu_alloc_pages(chunk, pages, populated, rs, re);
-               if (rc)
-                       goto err_free;
-               free_end = re;
-       }
+       if (pcpu_alloc_pages(chunk, pages, page_start, page_end))
+               return -ENOMEM;
 
-       pcpu_for_each_unpop_region(chunk, rs, re, page_start, page_end) {
-               rc = pcpu_map_pages(chunk, pages, populated, rs, re);
-               if (rc)
-                       goto err_unmap;
-               unmap_end = re;
+       if (pcpu_map_pages(chunk, pages, page_start, page_end)) {
+               pcpu_free_pages(chunk, pages, page_start, page_end);
+               return -ENOMEM;
        }
        pcpu_post_map_flush(chunk, page_start, page_end);
 
-       /* commit new bitmap */
-       bitmap_copy(chunk->populated, populated, pcpu_unit_pages);
-clear:
-       for_each_possible_cpu(cpu)
-               memset((void *)pcpu_chunk_addr(chunk, cpu, 0) + off, 0, size);
        return 0;
-
-err_unmap:
-       pcpu_pre_unmap_flush(chunk, page_start, unmap_end);
-       pcpu_for_each_unpop_region(chunk, rs, re, page_start, unmap_end)
-               pcpu_unmap_pages(chunk, pages, populated, rs, re);
-       pcpu_post_unmap_tlb_flush(chunk, page_start, unmap_end);
-err_free:
-       pcpu_for_each_unpop_region(chunk, rs, re, page_start, free_end)
-               pcpu_free_pages(chunk, pages, populated, rs, re);
-       return rc;
 }
 
 /**
  * pcpu_depopulate_chunk - depopulate and unmap an area of a pcpu_chunk
  * @chunk: chunk to depopulate
- * @off: offset to the area to depopulate
- * @size: size of the area to depopulate in bytes
+ * @page_start: the start page
+ * @page_end: the end page
  *
  * For each cpu, depopulate and unmap pages [@page_start,@page_end)
- * from @chunk.  If @flush is true, vcache is flushed before unmapping
- * and tlb after.
+ * from @chunk.
  *
  * CONTEXT:
  * pcpu_alloc_mutex.
  */
-static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk, int off, int size)
+static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk,
+                                 int page_start, int page_end)
 {
-       int page_start = PFN_DOWN(off);
-       int page_end = PFN_UP(off + size);
        struct page **pages;
-       unsigned long *populated;
-       int rs, re;
-
-       /* quick path, check whether it's empty already */
-       rs = page_start;
-       pcpu_next_unpop(chunk, &rs, &re, page_end);
-       if (rs == page_start && re == page_end)
-               return;
-
-       /* immutable chunks can't be depopulated */
-       WARN_ON(chunk->immutable);
 
        /*
         * If control reaches here, there must have been at least one
         * successful population attempt so the temp pages array must
         * be available now.
         */
-       pages = pcpu_get_pages_and_bitmap(chunk, &populated, false);
+       pages = pcpu_get_pages(chunk);
        BUG_ON(!pages);
 
        /* unmap and free */
        pcpu_pre_unmap_flush(chunk, page_start, page_end);
 
-       pcpu_for_each_pop_region(chunk, rs, re, page_start, page_end)
-               pcpu_unmap_pages(chunk, pages, populated, rs, re);
+       pcpu_unmap_pages(chunk, pages, page_start, page_end);
 
        /* no need to flush tlb, vmalloc will handle it lazily */
 
-       pcpu_for_each_pop_region(chunk, rs, re, page_start, page_end)
-               pcpu_free_pages(chunk, pages, populated, rs, re);
-
-       /* commit new bitmap */
-       bitmap_copy(chunk->populated, populated, pcpu_unit_pages);
+       pcpu_free_pages(chunk, pages, page_start, page_end);
 }
 
 static struct pcpu_chunk *pcpu_create_chunk(void)
index da997f9800bdeab14b47b694b09593de89db4a17..014bab65e0ffd82bac0e535cf267d28bfbd09902 100644 (file)
 
 #define PCPU_SLOT_BASE_SHIFT           5       /* 1-31 shares the same slot */
 #define PCPU_DFL_MAP_ALLOC             16      /* start a map with 16 ents */
+#define PCPU_ATOMIC_MAP_MARGIN_LOW     32
+#define PCPU_ATOMIC_MAP_MARGIN_HIGH    64
+#define PCPU_EMPTY_POP_PAGES_LOW       2
+#define PCPU_EMPTY_POP_PAGES_HIGH      4
 
 #ifdef CONFIG_SMP
 /* default addr <-> pcpu_ptr mapping, override in asm/percpu.h if necessary */
@@ -102,12 +106,16 @@ struct pcpu_chunk {
        int                     free_size;      /* free bytes in the chunk */
        int                     contig_hint;    /* max contiguous size hint */
        void                    *base_addr;     /* base address of this chunk */
+
        int                     map_used;       /* # of map entries used before the sentry */
        int                     map_alloc;      /* # of map entries allocated */
        int                     *map;           /* allocation map */
+       struct work_struct      map_extend_work;/* async ->map[] extension */
+
        void                    *data;          /* chunk data */
        int                     first_free;     /* no free below this */
        bool                    immutable;      /* no [de]population allowed */
+       int                     nr_populated;   /* # of populated pages */
        unsigned long           populated[];    /* populated bitmap */
 };
 
@@ -151,38 +159,33 @@ static struct pcpu_chunk *pcpu_first_chunk;
 static struct pcpu_chunk *pcpu_reserved_chunk;
 static int pcpu_reserved_chunk_limit;
 
+static DEFINE_SPINLOCK(pcpu_lock);     /* all internal data structures */
+static DEFINE_MUTEX(pcpu_alloc_mutex); /* chunk create/destroy, [de]pop */
+
+static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */
+
 /*
- * Synchronization rules.
- *
- * There are two locks - pcpu_alloc_mutex and pcpu_lock.  The former
- * protects allocation/reclaim paths, chunks, populated bitmap and
- * vmalloc mapping.  The latter is a spinlock and protects the index
- * data structures - chunk slots, chunks and area maps in chunks.
- *
- * During allocation, pcpu_alloc_mutex is kept locked all the time and
- * pcpu_lock is grabbed and released as necessary.  All actual memory
- * allocations are done using GFP_KERNEL with pcpu_lock released.  In
- * general, percpu memory can't be allocated with irq off but
- * irqsave/restore are still used in alloc path so that it can be used
- * from early init path - sched_init() specifically.
- *
- * Free path accesses and alters only the index data structures, so it
- * can be safely called from atomic context.  When memory needs to be
- * returned to the system, free path schedules reclaim_work which
- * grabs both pcpu_alloc_mutex and pcpu_lock, unlinks chunks to be
- * reclaimed, release both locks and frees the chunks.  Note that it's
- * necessary to grab both locks to remove a chunk from circulation as
- * allocation path might be referencing the chunk with only
- * pcpu_alloc_mutex locked.
+ * The number of empty populated pages, protected by pcpu_lock.  The
+ * reserved chunk doesn't contribute to the count.
  */
-static DEFINE_MUTEX(pcpu_alloc_mutex); /* protects whole alloc and reclaim */
-static DEFINE_SPINLOCK(pcpu_lock);     /* protects index data structures */
+static int pcpu_nr_empty_pop_pages;
 
-static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */
+/*
+ * Balance work is used to populate or destroy chunks asynchronously.  We
+ * try to keep the number of populated free pages between
+ * PCPU_EMPTY_POP_PAGES_LOW and HIGH for atomic allocations and at most one
+ * empty chunk.
+ */
+static void pcpu_balance_workfn(struct work_struct *work);
+static DECLARE_WORK(pcpu_balance_work, pcpu_balance_workfn);
+static bool pcpu_async_enabled __read_mostly;
+static bool pcpu_atomic_alloc_failed;
 
-/* reclaim work to release fully free chunks, scheduled from free path */
-static void pcpu_reclaim(struct work_struct *work);
-static DECLARE_WORK(pcpu_reclaim_work, pcpu_reclaim);
+static void pcpu_schedule_balance_work(void)
+{
+       if (pcpu_async_enabled)
+               schedule_work(&pcpu_balance_work);
+}
 
 static bool pcpu_addr_in_first_chunk(void *addr)
 {
@@ -314,6 +317,38 @@ static void pcpu_mem_free(void *ptr, size_t size)
                vfree(ptr);
 }
 
+/**
+ * pcpu_count_occupied_pages - count the number of pages an area occupies
+ * @chunk: chunk of interest
+ * @i: index of the area in question
+ *
+ * Count the number of pages chunk's @i'th area occupies.  When the area's
+ * start and/or end address isn't aligned to page boundary, the straddled
+ * page is included in the count iff the rest of the page is free.
+ */
+static int pcpu_count_occupied_pages(struct pcpu_chunk *chunk, int i)
+{
+       int off = chunk->map[i] & ~1;
+       int end = chunk->map[i + 1] & ~1;
+
+       if (!PAGE_ALIGNED(off) && i > 0) {
+               int prev = chunk->map[i - 1];
+
+               if (!(prev & 1) && prev <= round_down(off, PAGE_SIZE))
+                       off = round_down(off, PAGE_SIZE);
+       }
+
+       if (!PAGE_ALIGNED(end) && i + 1 < chunk->map_used) {
+               int next = chunk->map[i + 1];
+               int nend = chunk->map[i + 2] & ~1;
+
+               if (!(next & 1) && nend >= round_up(end, PAGE_SIZE))
+                       end = round_up(end, PAGE_SIZE);
+       }
+
+       return max_t(int, PFN_DOWN(end) - PFN_UP(off), 0);
+}
+
 /**
  * pcpu_chunk_relocate - put chunk in the appropriate chunk slot
  * @chunk: chunk of interest
@@ -342,9 +377,14 @@ static void pcpu_chunk_relocate(struct pcpu_chunk *chunk, int oslot)
 /**
  * pcpu_need_to_extend - determine whether chunk area map needs to be extended
  * @chunk: chunk of interest
+ * @is_atomic: the allocation context
  *
- * Determine whether area map of @chunk needs to be extended to
- * accommodate a new allocation.
+ * Determine whether area map of @chunk needs to be extended.  If
+ * @is_atomic, only the amount necessary for a new allocation is
+ * considered; however, async extension is scheduled if the left amount is
+ * low.  If !@is_atomic, it aims for more empty space.  Combined, this
+ * ensures that the map is likely to have enough available space to
+ * accomodate atomic allocations which can't extend maps directly.
  *
  * CONTEXT:
  * pcpu_lock.
@@ -353,15 +393,26 @@ static void pcpu_chunk_relocate(struct pcpu_chunk *chunk, int oslot)
  * New target map allocation length if extension is necessary, 0
  * otherwise.
  */
-static int pcpu_need_to_extend(struct pcpu_chunk *chunk)
+static int pcpu_need_to_extend(struct pcpu_chunk *chunk, bool is_atomic)
 {
-       int new_alloc;
+       int margin, new_alloc;
+
+       if (is_atomic) {
+               margin = 3;
+
+               if (chunk->map_alloc <
+                   chunk->map_used + PCPU_ATOMIC_MAP_MARGIN_LOW &&
+                   pcpu_async_enabled)
+                       schedule_work(&chunk->map_extend_work);
+       } else {
+               margin = PCPU_ATOMIC_MAP_MARGIN_HIGH;
+       }
 
-       if (chunk->map_alloc >= chunk->map_used + 3)
+       if (chunk->map_alloc >= chunk->map_used + margin)
                return 0;
 
        new_alloc = PCPU_DFL_MAP_ALLOC;
-       while (new_alloc < chunk->map_used + 3)
+       while (new_alloc < chunk->map_used + margin)
                new_alloc *= 2;
 
        return new_alloc;
@@ -418,11 +469,76 @@ out_unlock:
        return 0;
 }
 
+static void pcpu_map_extend_workfn(struct work_struct *work)
+{
+       struct pcpu_chunk *chunk = container_of(work, struct pcpu_chunk,
+                                               map_extend_work);
+       int new_alloc;
+
+       spin_lock_irq(&pcpu_lock);
+       new_alloc = pcpu_need_to_extend(chunk, false);
+       spin_unlock_irq(&pcpu_lock);
+
+       if (new_alloc)
+               pcpu_extend_area_map(chunk, new_alloc);
+}
+
+/**
+ * pcpu_fit_in_area - try to fit the requested allocation in a candidate area
+ * @chunk: chunk the candidate area belongs to
+ * @off: the offset to the start of the candidate area
+ * @this_size: the size of the candidate area
+ * @size: the size of the target allocation
+ * @align: the alignment of the target allocation
+ * @pop_only: only allocate from already populated region
+ *
+ * We're trying to allocate @size bytes aligned at @align.  @chunk's area
+ * at @off sized @this_size is a candidate.  This function determines
+ * whether the target allocation fits in the candidate area and returns the
+ * number of bytes to pad after @off.  If the target area doesn't fit, -1
+ * is returned.
+ *
+ * If @pop_only is %true, this function only considers the already
+ * populated part of the candidate area.
+ */
+static int pcpu_fit_in_area(struct pcpu_chunk *chunk, int off, int this_size,
+                           int size, int align, bool pop_only)
+{
+       int cand_off = off;
+
+       while (true) {
+               int head = ALIGN(cand_off, align) - off;
+               int page_start, page_end, rs, re;
+
+               if (this_size < head + size)
+                       return -1;
+
+               if (!pop_only)
+                       return head;
+
+               /*
+                * If the first unpopulated page is beyond the end of the
+                * allocation, the whole allocation is populated;
+                * otherwise, retry from the end of the unpopulated area.
+                */
+               page_start = PFN_DOWN(head + off);
+               page_end = PFN_UP(head + off + size);
+
+               rs = page_start;
+               pcpu_next_unpop(chunk, &rs, &re, PFN_UP(off + this_size));
+               if (rs >= page_end)
+                       return head;
+               cand_off = re * PAGE_SIZE;
+       }
+}
+
 /**
  * pcpu_alloc_area - allocate area from a pcpu_chunk
  * @chunk: chunk of interest
  * @size: wanted size in bytes
  * @align: wanted align
+ * @pop_only: allocate only from the populated area
+ * @occ_pages_p: out param for the number of pages the area occupies
  *
  * Try to allocate @size bytes area aligned at @align from @chunk.
  * Note that this function only allocates the offset.  It doesn't
@@ -437,7 +553,8 @@ out_unlock:
  * Allocated offset in @chunk on success, -1 if no matching area is
  * found.
  */
-static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align)
+static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align,
+                          bool pop_only, int *occ_pages_p)
 {
        int oslot = pcpu_chunk_slot(chunk);
        int max_contig = 0;
@@ -453,11 +570,11 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align)
                if (off & 1)
                        continue;
 
-               /* extra for alignment requirement */
-               head = ALIGN(off, align) - off;
-
                this_size = (p[1] & ~1) - off;
-               if (this_size < head + size) {
+
+               head = pcpu_fit_in_area(chunk, off, this_size, size, align,
+                                       pop_only);
+               if (head < 0) {
                        if (!seen_free) {
                                chunk->first_free = i;
                                seen_free = true;
@@ -526,6 +643,7 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align)
                chunk->free_size -= size;
                *p |= 1;
 
+               *occ_pages_p = pcpu_count_occupied_pages(chunk, i);
                pcpu_chunk_relocate(chunk, oslot);
                return off;
        }
@@ -541,6 +659,7 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align)
  * pcpu_free_area - free area to a pcpu_chunk
  * @chunk: chunk of interest
  * @freeme: offset of area to free
+ * @occ_pages_p: out param for the number of pages the area occupies
  *
  * Free area starting from @freeme to @chunk.  Note that this function
  * only modifies the allocation map.  It doesn't depopulate or unmap
@@ -549,7 +668,8 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align)
  * CONTEXT:
  * pcpu_lock.
  */
-static void pcpu_free_area(struct pcpu_chunk *chunk, int freeme)
+static void pcpu_free_area(struct pcpu_chunk *chunk, int freeme,
+                          int *occ_pages_p)
 {
        int oslot = pcpu_chunk_slot(chunk);
        int off = 0;
@@ -580,6 +700,8 @@ static void pcpu_free_area(struct pcpu_chunk *chunk, int freeme)
        *p = off &= ~1;
        chunk->free_size += (p[1] & ~1) - off;
 
+       *occ_pages_p = pcpu_count_occupied_pages(chunk, i);
+
        /* merge with next? */
        if (!(p[1] & 1))
                to_free++;
@@ -620,6 +742,7 @@ static struct pcpu_chunk *pcpu_alloc_chunk(void)
        chunk->map_used = 1;
 
        INIT_LIST_HEAD(&chunk->list);
+       INIT_WORK(&chunk->map_extend_work, pcpu_map_extend_workfn);
        chunk->free_size = pcpu_unit_size;
        chunk->contig_hint = pcpu_unit_size;
 
@@ -634,6 +757,50 @@ static void pcpu_free_chunk(struct pcpu_chunk *chunk)
        pcpu_mem_free(chunk, pcpu_chunk_struct_size);
 }
 
+/**
+ * pcpu_chunk_populated - post-population bookkeeping
+ * @chunk: pcpu_chunk which got populated
+ * @page_start: the start page
+ * @page_end: the end page
+ *
+ * Pages in [@page_start,@page_end) have been populated to @chunk.  Update
+ * the bookkeeping information accordingly.  Must be called after each
+ * successful population.
+ */
+static void pcpu_chunk_populated(struct pcpu_chunk *chunk,
+                                int page_start, int page_end)
+{
+       int nr = page_end - page_start;
+
+       lockdep_assert_held(&pcpu_lock);
+
+       bitmap_set(chunk->populated, page_start, nr);
+       chunk->nr_populated += nr;
+       pcpu_nr_empty_pop_pages += nr;
+}
+
+/**
+ * pcpu_chunk_depopulated - post-depopulation bookkeeping
+ * @chunk: pcpu_chunk which got depopulated
+ * @page_start: the start page
+ * @page_end: the end page
+ *
+ * Pages in [@page_start,@page_end) have been depopulated from @chunk.
+ * Update the bookkeeping information accordingly.  Must be called after
+ * each successful depopulation.
+ */
+static void pcpu_chunk_depopulated(struct pcpu_chunk *chunk,
+                                  int page_start, int page_end)
+{
+       int nr = page_end - page_start;
+
+       lockdep_assert_held(&pcpu_lock);
+
+       bitmap_clear(chunk->populated, page_start, nr);
+       chunk->nr_populated -= nr;
+       pcpu_nr_empty_pop_pages -= nr;
+}
+
 /*
  * Chunk management implementation.
  *
@@ -695,21 +862,23 @@ static struct pcpu_chunk *pcpu_chunk_addr_search(void *addr)
  * @size: size of area to allocate in bytes
  * @align: alignment of area (max PAGE_SIZE)
  * @reserved: allocate from the reserved chunk if available
+ * @gfp: allocation flags
  *
- * Allocate percpu area of @size bytes aligned at @align.
- *
- * CONTEXT:
- * Does GFP_KERNEL allocation.
+ * Allocate percpu area of @size bytes aligned at @align.  If @gfp doesn't
+ * contain %GFP_KERNEL, the allocation is atomic.
  *
  * RETURNS:
  * Percpu pointer to the allocated area on success, NULL on failure.
  */
-static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
+static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved,
+                                gfp_t gfp)
 {
        static int warn_limit = 10;
        struct pcpu_chunk *chunk;
        const char *err;
-       int slot, off, new_alloc;
+       bool is_atomic = (gfp & GFP_KERNEL) != GFP_KERNEL;
+       int occ_pages = 0;
+       int slot, off, new_alloc, cpu, ret;
        unsigned long flags;
        void __percpu *ptr;
 
@@ -728,7 +897,6 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
                return NULL;
        }
 
-       mutex_lock(&pcpu_alloc_mutex);
        spin_lock_irqsave(&pcpu_lock, flags);
 
        /* serve reserved allocations from the reserved chunk if available */
@@ -740,16 +908,18 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
                        goto fail_unlock;
                }
 
-               while ((new_alloc = pcpu_need_to_extend(chunk))) {
+               while ((new_alloc = pcpu_need_to_extend(chunk, is_atomic))) {
                        spin_unlock_irqrestore(&pcpu_lock, flags);
-                       if (pcpu_extend_area_map(chunk, new_alloc) < 0) {
+                       if (is_atomic ||
+                           pcpu_extend_area_map(chunk, new_alloc) < 0) {
                                err = "failed to extend area map of reserved chunk";
-                               goto fail_unlock_mutex;
+                               goto fail;
                        }
                        spin_lock_irqsave(&pcpu_lock, flags);
                }
 
-               off = pcpu_alloc_area(chunk, size, align);
+               off = pcpu_alloc_area(chunk, size, align, is_atomic,
+                                     &occ_pages);
                if (off >= 0)
                        goto area_found;
 
@@ -764,13 +934,15 @@ restart:
                        if (size > chunk->contig_hint)
                                continue;
 
-                       new_alloc = pcpu_need_to_extend(chunk);
+                       new_alloc = pcpu_need_to_extend(chunk, is_atomic);
                        if (new_alloc) {
+                               if (is_atomic)
+                                       continue;
                                spin_unlock_irqrestore(&pcpu_lock, flags);
                                if (pcpu_extend_area_map(chunk,
                                                         new_alloc) < 0) {
                                        err = "failed to extend area map";
-                                       goto fail_unlock_mutex;
+                                       goto fail;
                                }
                                spin_lock_irqsave(&pcpu_lock, flags);
                                /*
@@ -780,74 +952,134 @@ restart:
                                goto restart;
                        }
 
-                       off = pcpu_alloc_area(chunk, size, align);
+                       off = pcpu_alloc_area(chunk, size, align, is_atomic,
+                                             &occ_pages);
                        if (off >= 0)
                                goto area_found;
                }
        }
 
-       /* hmmm... no space left, create a new chunk */
        spin_unlock_irqrestore(&pcpu_lock, flags);
 
-       chunk = pcpu_create_chunk();
-       if (!chunk) {
-               err = "failed to allocate new chunk";
-               goto fail_unlock_mutex;
+       /*
+        * No space left.  Create a new chunk.  We don't want multiple
+        * tasks to create chunks simultaneously.  Serialize and create iff
+        * there's still no empty chunk after grabbing the mutex.
+        */
+       if (is_atomic)
+               goto fail;
+
+       mutex_lock(&pcpu_alloc_mutex);
+
+       if (list_empty(&pcpu_slot[pcpu_nr_slots - 1])) {
+               chunk = pcpu_create_chunk();
+               if (!chunk) {
+                       mutex_unlock(&pcpu_alloc_mutex);
+                       err = "failed to allocate new chunk";
+                       goto fail;
+               }
+
+               spin_lock_irqsave(&pcpu_lock, flags);
+               pcpu_chunk_relocate(chunk, -1);
+       } else {
+               spin_lock_irqsave(&pcpu_lock, flags);
        }
 
-       spin_lock_irqsave(&pcpu_lock, flags);
-       pcpu_chunk_relocate(chunk, -1);
+       mutex_unlock(&pcpu_alloc_mutex);
        goto restart;
 
 area_found:
        spin_unlock_irqrestore(&pcpu_lock, flags);
 
-       /* populate, map and clear the area */
-       if (pcpu_populate_chunk(chunk, off, size)) {
-               spin_lock_irqsave(&pcpu_lock, flags);
-               pcpu_free_area(chunk, off);
-               err = "failed to populate";
-               goto fail_unlock;
+       /* populate if not all pages are already there */
+       if (!is_atomic) {
+               int page_start, page_end, rs, re;
+
+               mutex_lock(&pcpu_alloc_mutex);
+
+               page_start = PFN_DOWN(off);
+               page_end = PFN_UP(off + size);
+
+               pcpu_for_each_unpop_region(chunk, rs, re, page_start, page_end) {
+                       WARN_ON(chunk->immutable);
+
+                       ret = pcpu_populate_chunk(chunk, rs, re);
+
+                       spin_lock_irqsave(&pcpu_lock, flags);
+                       if (ret) {
+                               mutex_unlock(&pcpu_alloc_mutex);
+                               pcpu_free_area(chunk, off, &occ_pages);
+                               err = "failed to populate";
+                               goto fail_unlock;
+                       }
+                       pcpu_chunk_populated(chunk, rs, re);
+                       spin_unlock_irqrestore(&pcpu_lock, flags);
+               }
+
+               mutex_unlock(&pcpu_alloc_mutex);
        }
 
-       mutex_unlock(&pcpu_alloc_mutex);
+       if (chunk != pcpu_reserved_chunk)
+               pcpu_nr_empty_pop_pages -= occ_pages;
+
+       if (pcpu_nr_empty_pop_pages < PCPU_EMPTY_POP_PAGES_LOW)
+               pcpu_schedule_balance_work();
+
+       /* clear the areas and return address relative to base address */
+       for_each_possible_cpu(cpu)
+               memset((void *)pcpu_chunk_addr(chunk, cpu, 0) + off, 0, size);
 
-       /* return address relative to base address */
        ptr = __addr_to_pcpu_ptr(chunk->base_addr + off);
        kmemleak_alloc_percpu(ptr, size);
        return ptr;
 
 fail_unlock:
        spin_unlock_irqrestore(&pcpu_lock, flags);
-fail_unlock_mutex:
-       mutex_unlock(&pcpu_alloc_mutex);
-       if (warn_limit) {
-               pr_warning("PERCPU: allocation failed, size=%zu align=%zu, "
-                          "%s\n", size, align, err);
+fail:
+       if (!is_atomic && warn_limit) {
+               pr_warning("PERCPU: allocation failed, size=%zu align=%zu atomic=%d, %s\n",
+                          size, align, is_atomic, err);
                dump_stack();
                if (!--warn_limit)
                        pr_info("PERCPU: limit reached, disable warning\n");
        }
+       if (is_atomic) {
+               /* see the flag handling in pcpu_blance_workfn() */
+               pcpu_atomic_alloc_failed = true;
+               pcpu_schedule_balance_work();
+       }
        return NULL;
 }
 
 /**
- * __alloc_percpu - allocate dynamic percpu area
+ * __alloc_percpu_gfp - allocate dynamic percpu area
  * @size: size of area to allocate in bytes
  * @align: alignment of area (max PAGE_SIZE)
+ * @gfp: allocation flags
  *
- * Allocate zero-filled percpu area of @size bytes aligned at @align.
- * Might sleep.  Might trigger writeouts.
- *
- * CONTEXT:
- * Does GFP_KERNEL allocation.
+ * Allocate zero-filled percpu area of @size bytes aligned at @align.  If
+ * @gfp doesn't contain %GFP_KERNEL, the allocation doesn't block and can
+ * be called from any context but is a lot more likely to fail.
  *
  * RETURNS:
  * Percpu pointer to the allocated area on success, NULL on failure.
  */
+void __percpu *__alloc_percpu_gfp(size_t size, size_t align, gfp_t gfp)
+{
+       return pcpu_alloc(size, align, false, gfp);
+}
+EXPORT_SYMBOL_GPL(__alloc_percpu_gfp);
+
+/**
+ * __alloc_percpu - allocate dynamic percpu area
+ * @size: size of area to allocate in bytes
+ * @align: alignment of area (max PAGE_SIZE)
+ *
+ * Equivalent to __alloc_percpu_gfp(size, align, %GFP_KERNEL).
+ */
 void __percpu *__alloc_percpu(size_t size, size_t align)
 {
-       return pcpu_alloc(size, align, false);
+       return pcpu_alloc(size, align, false, GFP_KERNEL);
 }
 EXPORT_SYMBOL_GPL(__alloc_percpu);
 
@@ -869,44 +1101,121 @@ EXPORT_SYMBOL_GPL(__alloc_percpu);
  */
 void __percpu *__alloc_reserved_percpu(size_t size, size_t align)
 {
-       return pcpu_alloc(size, align, true);
+       return pcpu_alloc(size, align, true, GFP_KERNEL);
 }
 
 /**
- * pcpu_reclaim - reclaim fully free chunks, workqueue function
+ * pcpu_balance_workfn - manage the amount of free chunks and populated pages
  * @work: unused
  *
  * Reclaim all fully free chunks except for the first one.
- *
- * CONTEXT:
- * workqueue context.
  */
-static void pcpu_reclaim(struct work_struct *work)
+static void pcpu_balance_workfn(struct work_struct *work)
 {
-       LIST_HEAD(todo);
-       struct list_head *head = &pcpu_slot[pcpu_nr_slots - 1];
+       LIST_HEAD(to_free);
+       struct list_head *free_head = &pcpu_slot[pcpu_nr_slots - 1];
        struct pcpu_chunk *chunk, *next;
+       int slot, nr_to_pop, ret;
 
+       /*
+        * There's no reason to keep around multiple unused chunks and VM
+        * areas can be scarce.  Destroy all free chunks except for one.
+        */
        mutex_lock(&pcpu_alloc_mutex);
        spin_lock_irq(&pcpu_lock);
 
-       list_for_each_entry_safe(chunk, next, head, list) {
+       list_for_each_entry_safe(chunk, next, free_head, list) {
                WARN_ON(chunk->immutable);
 
                /* spare the first one */
-               if (chunk == list_first_entry(head, struct pcpu_chunk, list))
+               if (chunk == list_first_entry(free_head, struct pcpu_chunk, list))
                        continue;
 
-               list_move(&chunk->list, &todo);
+               list_move(&chunk->list, &to_free);
        }
 
        spin_unlock_irq(&pcpu_lock);
 
-       list_for_each_entry_safe(chunk, next, &todo, list) {
-               pcpu_depopulate_chunk(chunk, 0, pcpu_unit_size);
+       list_for_each_entry_safe(chunk, next, &to_free, list) {
+               int rs, re;
+
+               pcpu_for_each_pop_region(chunk, rs, re, 0, pcpu_unit_pages) {
+                       pcpu_depopulate_chunk(chunk, rs, re);
+                       spin_lock_irq(&pcpu_lock);
+                       pcpu_chunk_depopulated(chunk, rs, re);
+                       spin_unlock_irq(&pcpu_lock);
+               }
                pcpu_destroy_chunk(chunk);
        }
 
+       /*
+        * Ensure there are certain number of free populated pages for
+        * atomic allocs.  Fill up from the most packed so that atomic
+        * allocs don't increase fragmentation.  If atomic allocation
+        * failed previously, always populate the maximum amount.  This
+        * should prevent atomic allocs larger than PAGE_SIZE from keeping
+        * failing indefinitely; however, large atomic allocs are not
+        * something we support properly and can be highly unreliable and
+        * inefficient.
+        */
+retry_pop:
+       if (pcpu_atomic_alloc_failed) {
+               nr_to_pop = PCPU_EMPTY_POP_PAGES_HIGH;
+               /* best effort anyway, don't worry about synchronization */
+               pcpu_atomic_alloc_failed = false;
+       } else {
+               nr_to_pop = clamp(PCPU_EMPTY_POP_PAGES_HIGH -
+                                 pcpu_nr_empty_pop_pages,
+                                 0, PCPU_EMPTY_POP_PAGES_HIGH);
+       }
+
+       for (slot = pcpu_size_to_slot(PAGE_SIZE); slot < pcpu_nr_slots; slot++) {
+               int nr_unpop = 0, rs, re;
+
+               if (!nr_to_pop)
+                       break;
+
+               spin_lock_irq(&pcpu_lock);
+               list_for_each_entry(chunk, &pcpu_slot[slot], list) {
+                       nr_unpop = pcpu_unit_pages - chunk->nr_populated;
+                       if (nr_unpop)
+                               break;
+               }
+               spin_unlock_irq(&pcpu_lock);
+
+               if (!nr_unpop)
+                       continue;
+
+               /* @chunk can't go away while pcpu_alloc_mutex is held */
+               pcpu_for_each_unpop_region(chunk, rs, re, 0, pcpu_unit_pages) {
+                       int nr = min(re - rs, nr_to_pop);
+
+                       ret = pcpu_populate_chunk(chunk, rs, rs + nr);
+                       if (!ret) {
+                               nr_to_pop -= nr;
+                               spin_lock_irq(&pcpu_lock);
+                               pcpu_chunk_populated(chunk, rs, rs + nr);
+                               spin_unlock_irq(&pcpu_lock);
+                       } else {
+                               nr_to_pop = 0;
+                       }
+
+                       if (!nr_to_pop)
+                               break;
+               }
+       }
+
+       if (nr_to_pop) {
+               /* ran out of chunks to populate, create a new one and retry */
+               chunk = pcpu_create_chunk();
+               if (chunk) {
+                       spin_lock_irq(&pcpu_lock);
+                       pcpu_chunk_relocate(chunk, -1);
+                       spin_unlock_irq(&pcpu_lock);
+                       goto retry_pop;
+               }
+       }
+
        mutex_unlock(&pcpu_alloc_mutex);
 }
 
@@ -924,7 +1233,7 @@ void free_percpu(void __percpu *ptr)
        void *addr;
        struct pcpu_chunk *chunk;
        unsigned long flags;
-       int off;
+       int off, occ_pages;
 
        if (!ptr)
                return;
@@ -938,7 +1247,10 @@ void free_percpu(void __percpu *ptr)
        chunk = pcpu_chunk_addr_search(addr);
        off = addr - chunk->base_addr;
 
-       pcpu_free_area(chunk, off);
+       pcpu_free_area(chunk, off, &occ_pages);
+
+       if (chunk != pcpu_reserved_chunk)
+               pcpu_nr_empty_pop_pages += occ_pages;
 
        /* if there are more than one fully free chunks, wake up grim reaper */
        if (chunk->free_size == pcpu_unit_size) {
@@ -946,7 +1258,7 @@ void free_percpu(void __percpu *ptr)
 
                list_for_each_entry(pos, &pcpu_slot[pcpu_nr_slots - 1], list)
                        if (pos != chunk) {
-                               schedule_work(&pcpu_reclaim_work);
+                               pcpu_schedule_balance_work();
                                break;
                        }
        }
@@ -1336,11 +1648,13 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
         */
        schunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0);
        INIT_LIST_HEAD(&schunk->list);
+       INIT_WORK(&schunk->map_extend_work, pcpu_map_extend_workfn);
        schunk->base_addr = base_addr;
        schunk->map = smap;
        schunk->map_alloc = ARRAY_SIZE(smap);
        schunk->immutable = true;
        bitmap_fill(schunk->populated, pcpu_unit_pages);
+       schunk->nr_populated = pcpu_unit_pages;
 
        if (ai->reserved_size) {
                schunk->free_size = ai->reserved_size;
@@ -1364,11 +1678,13 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
        if (dyn_size) {
                dchunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0);
                INIT_LIST_HEAD(&dchunk->list);
+               INIT_WORK(&dchunk->map_extend_work, pcpu_map_extend_workfn);
                dchunk->base_addr = base_addr;
                dchunk->map = dmap;
                dchunk->map_alloc = ARRAY_SIZE(dmap);
                dchunk->immutable = true;
                bitmap_fill(dchunk->populated, pcpu_unit_pages);
+               dchunk->nr_populated = pcpu_unit_pages;
 
                dchunk->contig_hint = dchunk->free_size = dyn_size;
                dchunk->map[0] = 1;
@@ -1379,6 +1695,8 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
 
        /* link the first chunk in */
        pcpu_first_chunk = dchunk ?: schunk;
+       pcpu_nr_empty_pop_pages +=
+               pcpu_count_occupied_pages(pcpu_first_chunk, 1);
        pcpu_chunk_relocate(pcpu_first_chunk, -1);
 
        /* we're done */
@@ -1932,8 +2250,6 @@ void __init setup_per_cpu_areas(void)
 
        if (pcpu_setup_first_chunk(ai, fc) < 0)
                panic("Failed to initialize percpu areas.");
-
-       pcpu_free_alloc_info(ai);
 }
 
 #endif /* CONFIG_SMP */
@@ -1967,3 +2283,15 @@ void __init percpu_init_late(void)
                spin_unlock_irqrestore(&pcpu_lock, flags);
        }
 }
+
+/*
+ * Percpu allocator is initialized early during boot when neither slab or
+ * workqueue is available.  Plug async management until everything is up
+ * and running.
+ */
+static int __init percpu_enable_async(void)
+{
+       pcpu_async_enabled = true;
+       return 0;
+}
+subsys_initcall(percpu_enable_async);
index bc74e0012809943930ab52fcc3857e080f152049..116a5053415b8dbc41c39622316e6b8b417a3727 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -527,7 +527,7 @@ vma_address(struct page *page, struct vm_area_struct *vma)
        unsigned long address = __vma_address(page, vma);
 
        /* page should be within @vma mapping range */
-       VM_BUG_ON(address < vma->vm_start || address >= vma->vm_end);
+       VM_BUG_ON_VMA(address < vma->vm_start || address >= vma->vm_end, vma);
 
        return address;
 }
@@ -897,7 +897,7 @@ void page_move_anon_rmap(struct page *page,
        struct anon_vma *anon_vma = vma->anon_vma;
 
        VM_BUG_ON_PAGE(!PageLocked(page), page);
-       VM_BUG_ON(!anon_vma);
+       VM_BUG_ON_VMA(!anon_vma, vma);
        VM_BUG_ON_PAGE(page->index != linear_page_index(vma, address), page);
 
        anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON;
@@ -1024,7 +1024,7 @@ void do_page_add_anon_rmap(struct page *page,
 void page_add_new_anon_rmap(struct page *page,
        struct vm_area_struct *vma, unsigned long address)
 {
-       VM_BUG_ON(address < vma->vm_start || address >= vma->vm_end);
+       VM_BUG_ON_VMA(address < vma->vm_start || address >= vma->vm_end, vma);
        SetPageSwapBacked(page);
        atomic_set(&page->_mapcount, 0); /* increment count (starts at -1) */
        if (PageTransHuge(page))
@@ -1670,7 +1670,7 @@ static int rmap_walk_file(struct page *page, struct rmap_walk_control *rwc)
         * structure at mapping cannot be freed and reused yet,
         * so we can safely take mapping->i_mmap_mutex.
         */
-       VM_BUG_ON(!PageLocked(page));
+       VM_BUG_ON_PAGE(!PageLocked(page), page);
 
        if (!mapping)
                return ret;
index 469f90d56051984c674f395042029ea737608d5b..cd6fc7590e54f758adacdcfc9cdcb402c7cd3005 100644 (file)
@@ -2995,7 +2995,7 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
 #endif
 
        spin_lock_init(&sbinfo->stat_lock);
-       if (percpu_counter_init(&sbinfo->used_blocks, 0))
+       if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL))
                goto failed;
        sbinfo->free_inodes = sbinfo->max_inodes;
 
@@ -3077,7 +3077,9 @@ static const struct address_space_operations shmem_aops = {
        .write_begin    = shmem_write_begin,
        .write_end      = shmem_write_end,
 #endif
+#ifdef CONFIG_MIGRATION
        .migratepage    = migrate_page,
+#endif
        .error_remove_page = generic_error_remove_page,
 };
 
index 7c52b3890d25378198341101242c671e585649bf..154aac8411c592816891e4897408e509d9b24785 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -237,11 +237,10 @@ struct arraycache_init {
 /*
  * Need this for bootstrapping a per node allocator.
  */
-#define NUM_INIT_LISTS (3 * MAX_NUMNODES)
+#define NUM_INIT_LISTS (2 * MAX_NUMNODES)
 static struct kmem_cache_node __initdata init_kmem_cache_node[NUM_INIT_LISTS];
 #define        CACHE_CACHE 0
-#define        SIZE_AC MAX_NUMNODES
-#define        SIZE_NODE (2 * MAX_NUMNODES)
+#define        SIZE_NODE (MAX_NUMNODES)
 
 static int drain_freelist(struct kmem_cache *cache,
                        struct kmem_cache_node *n, int tofree);
@@ -253,7 +252,6 @@ static void cache_reap(struct work_struct *unused);
 
 static int slab_early_init = 1;
 
-#define INDEX_AC kmalloc_index(sizeof(struct arraycache_init))
 #define INDEX_NODE kmalloc_index(sizeof(struct kmem_cache_node))
 
 static void kmem_cache_node_init(struct kmem_cache_node *parent)
@@ -458,9 +456,6 @@ static inline unsigned int obj_to_index(const struct kmem_cache *cache,
        return reciprocal_divide(offset, cache->reciprocal_buffer_size);
 }
 
-static struct arraycache_init initarray_generic =
-    { {0, BOOT_CPUCACHE_ENTRIES, 1, 0} };
-
 /* internal cache of cache description objs */
 static struct kmem_cache kmem_cache_boot = {
        .batchcount = 1,
@@ -476,7 +471,7 @@ static DEFINE_PER_CPU(struct delayed_work, slab_reap_work);
 
 static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep)
 {
-       return cachep->array[smp_processor_id()];
+       return this_cpu_ptr(cachep->cpu_cache);
 }
 
 static size_t calculate_freelist_size(int nr_objs, size_t align)
@@ -785,8 +780,8 @@ static inline void *ac_get_obj(struct kmem_cache *cachep,
        return objp;
 }
 
-static void *__ac_put_obj(struct kmem_cache *cachep, struct array_cache *ac,
-                                                               void *objp)
+static noinline void *__ac_put_obj(struct kmem_cache *cachep,
+                       struct array_cache *ac, void *objp)
 {
        if (unlikely(pfmemalloc_active)) {
                /* Some pfmemalloc slabs exist, check if this is one */
@@ -984,46 +979,50 @@ static void drain_alien_cache(struct kmem_cache *cachep,
        }
 }
 
-static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
+static int __cache_free_alien(struct kmem_cache *cachep, void *objp,
+                               int node, int page_node)
 {
-       int nodeid = page_to_nid(virt_to_page(objp));
        struct kmem_cache_node *n;
        struct alien_cache *alien = NULL;
        struct array_cache *ac;
-       int node;
        LIST_HEAD(list);
 
-       node = numa_mem_id();
-
-       /*
-        * Make sure we are not freeing a object from another node to the array
-        * cache on this cpu.
-        */
-       if (likely(nodeid == node))
-               return 0;
-
        n = get_node(cachep, node);
        STATS_INC_NODEFREES(cachep);
-       if (n->alien && n->alien[nodeid]) {
-               alien = n->alien[nodeid];
+       if (n->alien && n->alien[page_node]) {
+               alien = n->alien[page_node];
                ac = &alien->ac;
                spin_lock(&alien->lock);
                if (unlikely(ac->avail == ac->limit)) {
                        STATS_INC_ACOVERFLOW(cachep);
-                       __drain_alien_cache(cachep, ac, nodeid, &list);
+                       __drain_alien_cache(cachep, ac, page_node, &list);
                }
                ac_put_obj(cachep, ac, objp);
                spin_unlock(&alien->lock);
                slabs_destroy(cachep, &list);
        } else {
-               n = get_node(cachep, nodeid);
+               n = get_node(cachep, page_node);
                spin_lock(&n->list_lock);
-               free_block(cachep, &objp, 1, nodeid, &list);
+               free_block(cachep, &objp, 1, page_node, &list);
                spin_unlock(&n->list_lock);
                slabs_destroy(cachep, &list);
        }
        return 1;
 }
+
+static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
+{
+       int page_node = page_to_nid(virt_to_page(objp));
+       int node = numa_mem_id();
+       /*
+        * Make sure we are not freeing a object from another node to the array
+        * cache on this cpu.
+        */
+       if (likely(node == page_node))
+               return 0;
+
+       return __cache_free_alien(cachep, objp, node, page_node);
+}
 #endif
 
 /*
@@ -1092,24 +1091,25 @@ static void cpuup_canceled(long cpu)
                struct alien_cache **alien;
                LIST_HEAD(list);
 
-               /* cpu is dead; no one can alloc from it. */
-               nc = cachep->array[cpu];
-               cachep->array[cpu] = NULL;
                n = get_node(cachep, node);
-
                if (!n)
-                       goto free_array_cache;
+                       continue;
 
                spin_lock_irq(&n->list_lock);
 
                /* Free limit for this kmem_cache_node */
                n->free_limit -= cachep->batchcount;
-               if (nc)
+
+               /* cpu is dead; no one can alloc from it. */
+               nc = per_cpu_ptr(cachep->cpu_cache, cpu);
+               if (nc) {
                        free_block(cachep, nc->entry, nc->avail, node, &list);
+                       nc->avail = 0;
+               }
 
                if (!cpumask_empty(mask)) {
                        spin_unlock_irq(&n->list_lock);
-                       goto free_array_cache;
+                       goto free_slab;
                }
 
                shared = n->shared;
@@ -1129,9 +1129,9 @@ static void cpuup_canceled(long cpu)
                        drain_alien_cache(cachep, alien);
                        free_alien_cache(alien);
                }
-free_array_cache:
+
+free_slab:
                slabs_destroy(cachep, &list);
-               kfree(nc);
        }
        /*
         * In the previous loop, all the objects were freed to
@@ -1168,32 +1168,23 @@ static int cpuup_prepare(long cpu)
         * array caches
         */
        list_for_each_entry(cachep, &slab_caches, list) {
-               struct array_cache *nc;
                struct array_cache *shared = NULL;
                struct alien_cache **alien = NULL;
 
-               nc = alloc_arraycache(node, cachep->limit,
-                                       cachep->batchcount, GFP_KERNEL);
-               if (!nc)
-                       goto bad;
                if (cachep->shared) {
                        shared = alloc_arraycache(node,
                                cachep->shared * cachep->batchcount,
                                0xbaadf00d, GFP_KERNEL);
-                       if (!shared) {
-                               kfree(nc);
+                       if (!shared)
                                goto bad;
-                       }
                }
                if (use_alien_caches) {
                        alien = alloc_alien_cache(node, cachep->limit, GFP_KERNEL);
                        if (!alien) {
                                kfree(shared);
-                               kfree(nc);
                                goto bad;
                        }
                }
-               cachep->array[cpu] = nc;
                n = get_node(cachep, node);
                BUG_ON(!n);
 
@@ -1384,15 +1375,6 @@ static void __init set_up_node(struct kmem_cache *cachep, int index)
        }
 }
 
-/*
- * The memory after the last cpu cache pointer is used for the
- * the node pointer.
- */
-static void setup_node_pointer(struct kmem_cache *cachep)
-{
-       cachep->node = (struct kmem_cache_node **)&cachep->array[nr_cpu_ids];
-}
-
 /*
  * Initialisation.  Called after the page allocator have been initialised and
  * before smp_init().
@@ -1404,7 +1386,6 @@ void __init kmem_cache_init(void)
        BUILD_BUG_ON(sizeof(((struct page *)NULL)->lru) <
                                        sizeof(struct rcu_head));
        kmem_cache = &kmem_cache_boot;
-       setup_node_pointer(kmem_cache);
 
        if (num_possible_nodes() == 1)
                use_alien_caches = 0;
@@ -1412,8 +1393,6 @@ void __init kmem_cache_init(void)
        for (i = 0; i < NUM_INIT_LISTS; i++)
                kmem_cache_node_init(&init_kmem_cache_node[i]);
 
-       set_up_node(kmem_cache, CACHE_CACHE);
-
        /*
         * Fragmentation resistance on low memory - only use bigger
         * page orders on machines with more than 32MB of memory if
@@ -1448,49 +1427,22 @@ void __init kmem_cache_init(void)
         * struct kmem_cache size depends on nr_node_ids & nr_cpu_ids
         */
        create_boot_cache(kmem_cache, "kmem_cache",
-               offsetof(struct kmem_cache, array[nr_cpu_ids]) +
+               offsetof(struct kmem_cache, node) +
                                  nr_node_ids * sizeof(struct kmem_cache_node *),
                                  SLAB_HWCACHE_ALIGN);
        list_add(&kmem_cache->list, &slab_caches);
-
-       /* 2+3) create the kmalloc caches */
+       slab_state = PARTIAL;
 
        /*
-        * Initialize the caches that provide memory for the array cache and the
-        * kmem_cache_node structures first.  Without this, further allocations will
-        * bug.
+        * Initialize the caches that provide memory for the  kmem_cache_node
+        * structures first.  Without this, further allocations will bug.
         */
-
-       kmalloc_caches[INDEX_AC] = create_kmalloc_cache("kmalloc-ac",
-                                       kmalloc_size(INDEX_AC), ARCH_KMALLOC_FLAGS);
-
-       if (INDEX_AC != INDEX_NODE)
-               kmalloc_caches[INDEX_NODE] =
-                       create_kmalloc_cache("kmalloc-node",
+       kmalloc_caches[INDEX_NODE] = create_kmalloc_cache("kmalloc-node",
                                kmalloc_size(INDEX_NODE), ARCH_KMALLOC_FLAGS);
+       slab_state = PARTIAL_NODE;
 
        slab_early_init = 0;
 
-       /* 4) Replace the bootstrap head arrays */
-       {
-               struct array_cache *ptr;
-
-               ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);
-
-               memcpy(ptr, cpu_cache_get(kmem_cache),
-                      sizeof(struct arraycache_init));
-
-               kmem_cache->array[smp_processor_id()] = ptr;
-
-               ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);
-
-               BUG_ON(cpu_cache_get(kmalloc_caches[INDEX_AC])
-                      != &initarray_generic.cache);
-               memcpy(ptr, cpu_cache_get(kmalloc_caches[INDEX_AC]),
-                      sizeof(struct arraycache_init));
-
-               kmalloc_caches[INDEX_AC]->array[smp_processor_id()] = ptr;
-       }
        /* 5) Replace the bootstrap kmem_cache_node */
        {
                int nid;
@@ -1498,13 +1450,8 @@ void __init kmem_cache_init(void)
                for_each_online_node(nid) {
                        init_list(kmem_cache, &init_kmem_cache_node[CACHE_CACHE + nid], nid);
 
-                       init_list(kmalloc_caches[INDEX_AC],
-                                 &init_kmem_cache_node[SIZE_AC + nid], nid);
-
-                       if (INDEX_AC != INDEX_NODE) {
-                               init_list(kmalloc_caches[INDEX_NODE],
+                       init_list(kmalloc_caches[INDEX_NODE],
                                          &init_kmem_cache_node[SIZE_NODE + nid], nid);
-                       }
                }
        }
 
@@ -2037,56 +1984,53 @@ static size_t calculate_slab_order(struct kmem_cache *cachep,
        return left_over;
 }
 
+static struct array_cache __percpu *alloc_kmem_cache_cpus(
+               struct kmem_cache *cachep, int entries, int batchcount)
+{
+       int cpu;
+       size_t size;
+       struct array_cache __percpu *cpu_cache;
+
+       size = sizeof(void *) * entries + sizeof(struct array_cache);
+       cpu_cache = __alloc_percpu(size, 0);
+
+       if (!cpu_cache)
+               return NULL;
+
+       for_each_possible_cpu(cpu) {
+               init_arraycache(per_cpu_ptr(cpu_cache, cpu),
+                               entries, batchcount);
+       }
+
+       return cpu_cache;
+}
+
 static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
 {
        if (slab_state >= FULL)
                return enable_cpucache(cachep, gfp);
 
+       cachep->cpu_cache = alloc_kmem_cache_cpus(cachep, 1, 1);
+       if (!cachep->cpu_cache)
+               return 1;
+
        if (slab_state == DOWN) {
-               /*
-                * Note: Creation of first cache (kmem_cache).
-                * The setup_node is taken care
-                * of by the caller of __kmem_cache_create
-                */
-               cachep->array[smp_processor_id()] = &initarray_generic.cache;
-               slab_state = PARTIAL;
+               /* Creation of first cache (kmem_cache). */
+               set_up_node(kmem_cache, CACHE_CACHE);
        } else if (slab_state == PARTIAL) {
-               /*
-                * Note: the second kmem_cache_create must create the cache
-                * that's used by kmalloc(24), otherwise the creation of
-                * further caches will BUG().
-                */
-               cachep->array[smp_processor_id()] = &initarray_generic.cache;
-
-               /*
-                * If the cache that's used by kmalloc(sizeof(kmem_cache_node)) is
-                * the second cache, then we need to set up all its node/,
-                * otherwise the creation of further caches will BUG().
-                */
-               set_up_node(cachep, SIZE_AC);
-               if (INDEX_AC == INDEX_NODE)
-                       slab_state = PARTIAL_NODE;
-               else
-                       slab_state = PARTIAL_ARRAYCACHE;
+               /* For kmem_cache_node */
+               set_up_node(cachep, SIZE_NODE);
        } else {
-               /* Remaining boot caches */
-               cachep->array[smp_processor_id()] =
-                       kmalloc(sizeof(struct arraycache_init), gfp);
+               int node;
 
-               if (slab_state == PARTIAL_ARRAYCACHE) {
-                       set_up_node(cachep, SIZE_NODE);
-                       slab_state = PARTIAL_NODE;
-               } else {
-                       int node;
-                       for_each_online_node(node) {
-                               cachep->node[node] =
-                                   kmalloc_node(sizeof(struct kmem_cache_node),
-                                               gfp, node);
-                               BUG_ON(!cachep->node[node]);
-                               kmem_cache_node_init(cachep->node[node]);
-                       }
+               for_each_online_node(node) {
+                       cachep->node[node] = kmalloc_node(
+                               sizeof(struct kmem_cache_node), gfp, node);
+                       BUG_ON(!cachep->node[node]);
+                       kmem_cache_node_init(cachep->node[node]);
                }
        }
+
        cachep->node[numa_mem_id()]->next_reap =
                        jiffies + REAPTIMEOUT_NODE +
                        ((unsigned long)cachep) % REAPTIMEOUT_NODE;
@@ -2100,6 +2044,32 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
        return 0;
 }
 
+unsigned long kmem_cache_flags(unsigned long object_size,
+       unsigned long flags, const char *name,
+       void (*ctor)(void *))
+{
+       return flags;
+}
+
+struct kmem_cache *
+__kmem_cache_alias(const char *name, size_t size, size_t align,
+                  unsigned long flags, void (*ctor)(void *))
+{
+       struct kmem_cache *cachep;
+
+       cachep = find_mergeable(size, align, flags, name, ctor);
+       if (cachep) {
+               cachep->refcount++;
+
+               /*
+                * Adjust the object sizes so that we clear
+                * the complete object on kzalloc.
+                */
+               cachep->object_size = max_t(int, cachep->object_size, size);
+       }
+       return cachep;
+}
+
 /**
  * __kmem_cache_create - Create a cache.
  * @cachep: cache management descriptor
@@ -2183,7 +2153,6 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags)
        else
                gfp = GFP_NOWAIT;
 
-       setup_node_pointer(cachep);
 #if DEBUG
 
        /*
@@ -2440,8 +2409,7 @@ int __kmem_cache_shutdown(struct kmem_cache *cachep)
        if (rc)
                return rc;
 
-       for_each_online_cpu(i)
-           kfree(cachep->array[i]);
+       free_percpu(cachep->cpu_cache);
 
        /* NUMA: free the node structures */
        for_each_kmem_cache_node(cachep, i, n) {
@@ -3399,7 +3367,7 @@ static inline void __cache_free(struct kmem_cache *cachep, void *objp,
        if (nr_online_nodes > 1 && cache_free_alien(cachep, objp))
                return;
 
-       if (likely(ac->avail < ac->limit)) {
+       if (ac->avail < ac->limit) {
                STATS_INC_FREEHIT(cachep);
        } else {
                STATS_INC_FREEMISS(cachep);
@@ -3496,7 +3464,6 @@ __do_kmalloc_node(size_t size, gfp_t flags, int node, unsigned long caller)
        return kmem_cache_alloc_node_trace(cachep, flags, node, size);
 }
 
-#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_TRACING)
 void *__kmalloc_node(size_t size, gfp_t flags, int node)
 {
        return __do_kmalloc_node(size, flags, node, _RET_IP_);
@@ -3509,13 +3476,6 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t flags,
        return __do_kmalloc_node(size, flags, node, caller);
 }
 EXPORT_SYMBOL(__kmalloc_node_track_caller);
-#else
-void *__kmalloc_node(size_t size, gfp_t flags, int node)
-{
-       return __do_kmalloc_node(size, flags, node, 0);
-}
-EXPORT_SYMBOL(__kmalloc_node);
-#endif /* CONFIG_DEBUG_SLAB || CONFIG_TRACING */
 #endif /* CONFIG_NUMA */
 
 /**
@@ -3541,8 +3501,6 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
        return ret;
 }
 
-
-#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_TRACING)
 void *__kmalloc(size_t size, gfp_t flags)
 {
        return __do_kmalloc(size, flags, _RET_IP_);
@@ -3555,14 +3513,6 @@ void *__kmalloc_track_caller(size_t size, gfp_t flags, unsigned long caller)
 }
 EXPORT_SYMBOL(__kmalloc_track_caller);
 
-#else
-void *__kmalloc(size_t size, gfp_t flags)
-{
-       return __do_kmalloc(size, flags, 0);
-}
-EXPORT_SYMBOL(__kmalloc);
-#endif
-
 /**
  * kmem_cache_free - Deallocate an object
  * @cachep: The cache the allocation was from.
@@ -3707,72 +3657,45 @@ fail:
        return -ENOMEM;
 }
 
-struct ccupdate_struct {
-       struct kmem_cache *cachep;
-       struct array_cache *new[0];
-};
-
-static void do_ccupdate_local(void *info)
-{
-       struct ccupdate_struct *new = info;
-       struct array_cache *old;
-
-       check_irq_off();
-       old = cpu_cache_get(new->cachep);
-
-       new->cachep->array[smp_processor_id()] = new->new[smp_processor_id()];
-       new->new[smp_processor_id()] = old;
-}
-
 /* Always called with the slab_mutex held */
 static int __do_tune_cpucache(struct kmem_cache *cachep, int limit,
                                int batchcount, int shared, gfp_t gfp)
 {
-       struct ccupdate_struct *new;
-       int i;
+       struct array_cache __percpu *cpu_cache, *prev;
+       int cpu;
 
-       new = kzalloc(sizeof(*new) + nr_cpu_ids * sizeof(struct array_cache *),
-                     gfp);
-       if (!new)
+       cpu_cache = alloc_kmem_cache_cpus(cachep, limit, batchcount);
+       if (!cpu_cache)
                return -ENOMEM;
 
-       for_each_online_cpu(i) {
-               new->new[i] = alloc_arraycache(cpu_to_mem(i), limit,
-                                               batchcount, gfp);
-               if (!new->new[i]) {
-                       for (i--; i >= 0; i--)
-                               kfree(new->new[i]);
-                       kfree(new);
-                       return -ENOMEM;
-               }
-       }
-       new->cachep = cachep;
-
-       on_each_cpu(do_ccupdate_local, (void *)new, 1);
+       prev = cachep->cpu_cache;
+       cachep->cpu_cache = cpu_cache;
+       kick_all_cpus_sync();
 
        check_irq_on();
        cachep->batchcount = batchcount;
        cachep->limit = limit;
        cachep->shared = shared;
 
-       for_each_online_cpu(i) {
+       if (!prev)
+               goto alloc_node;
+
+       for_each_online_cpu(cpu) {
                LIST_HEAD(list);
-               struct array_cache *ccold = new->new[i];
                int node;
                struct kmem_cache_node *n;
+               struct array_cache *ac = per_cpu_ptr(prev, cpu);
 
-               if (!ccold)
-                       continue;
-
-               node = cpu_to_mem(i);
+               node = cpu_to_mem(cpu);
                n = get_node(cachep, node);
                spin_lock_irq(&n->list_lock);
-               free_block(cachep, ccold->entry, ccold->avail, node, &list);
+               free_block(cachep, ac->entry, ac->avail, node, &list);
                spin_unlock_irq(&n->list_lock);
                slabs_destroy(cachep, &list);
-               kfree(ccold);
        }
-       kfree(new);
+       free_percpu(prev);
+
+alloc_node:
        return alloc_kmem_cache_node(cachep, gfp);
 }
 
@@ -4255,19 +4178,15 @@ static const struct seq_operations slabstats_op = {
 
 static int slabstats_open(struct inode *inode, struct file *file)
 {
-       unsigned long *n = kzalloc(PAGE_SIZE, GFP_KERNEL);
-       int ret = -ENOMEM;
-       if (n) {
-               ret = seq_open(file, &slabstats_op);
-               if (!ret) {
-                       struct seq_file *m = file->private_data;
-                       *n = PAGE_SIZE / (2 * sizeof(unsigned long));
-                       m->private = n;
-                       n = NULL;
-               }
-               kfree(n);
-       }
-       return ret;
+       unsigned long *n;
+
+       n = __seq_open_private(file, &slabstats_op, PAGE_SIZE);
+       if (!n)
+               return -ENOMEM;
+
+       *n = PAGE_SIZE / (2 * sizeof(unsigned long));
+
+       return 0;
 }
 
 static const struct file_operations proc_slabstats_operations = {
index 0e0fdd3658409e0eee9a89fa51b1ea45cfc96466..ab019e63e3c204d13b36c3957957e4310587d18b 100644 (file)
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -4,6 +4,41 @@
  * Internal slab definitions
  */
 
+#ifdef CONFIG_SLOB
+/*
+ * Common fields provided in kmem_cache by all slab allocators
+ * This struct is either used directly by the allocator (SLOB)
+ * or the allocator must include definitions for all fields
+ * provided in kmem_cache_common in their definition of kmem_cache.
+ *
+ * Once we can do anonymous structs (C11 standard) we could put a
+ * anonymous struct definition in these allocators so that the
+ * separate allocations in the kmem_cache structure of SLAB and
+ * SLUB is no longer needed.
+ */
+struct kmem_cache {
+       unsigned int object_size;/* The original size of the object */
+       unsigned int size;      /* The aligned/padded/added on size  */
+       unsigned int align;     /* Alignment as calculated */
+       unsigned long flags;    /* Active flags on the slab */
+       const char *name;       /* Slab name for sysfs */
+       int refcount;           /* Use counter */
+       void (*ctor)(void *);   /* Called on object slot creation */
+       struct list_head list;  /* List of all slab caches on the system */
+};
+
+#endif /* CONFIG_SLOB */
+
+#ifdef CONFIG_SLAB
+#include <linux/slab_def.h>
+#endif
+
+#ifdef CONFIG_SLUB
+#include <linux/slub_def.h>
+#endif
+
+#include <linux/memcontrol.h>
+
 /*
  * State of the slab allocator.
  *
@@ -15,7 +50,6 @@
 enum slab_state {
        DOWN,                   /* No slab functionality yet */
        PARTIAL,                /* SLUB: kmem_cache_node available */
-       PARTIAL_ARRAYCACHE,     /* SLAB: kmalloc size for arraycache available */
        PARTIAL_NODE,           /* SLAB: kmalloc size for node struct available */
        UP,                     /* Slab caches usable but not all extras yet */
        FULL                    /* Everything is working */
@@ -53,15 +87,30 @@ extern void create_boot_cache(struct kmem_cache *, const char *name,
                        size_t size, unsigned long flags);
 
 struct mem_cgroup;
-#ifdef CONFIG_SLUB
+
+int slab_unmergeable(struct kmem_cache *s);
+struct kmem_cache *find_mergeable(size_t size, size_t align,
+               unsigned long flags, const char *name, void (*ctor)(void *));
+#ifndef CONFIG_SLOB
 struct kmem_cache *
 __kmem_cache_alias(const char *name, size_t size, size_t align,
                   unsigned long flags, void (*ctor)(void *));
+
+unsigned long kmem_cache_flags(unsigned long object_size,
+       unsigned long flags, const char *name,
+       void (*ctor)(void *));
 #else
 static inline struct kmem_cache *
 __kmem_cache_alias(const char *name, size_t size, size_t align,
                   unsigned long flags, void (*ctor)(void *))
 { return NULL; }
+
+static inline unsigned long kmem_cache_flags(unsigned long object_size,
+       unsigned long flags, const char *name,
+       void (*ctor)(void *))
+{
+       return flags;
+}
 #endif
 
 
@@ -303,8 +352,8 @@ static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node)
  * a kmem_cache_node structure allocated (which is true for all online nodes)
  */
 #define for_each_kmem_cache_node(__s, __node, __n) \
-       for (__node = 0; __n = get_node(__s, __node), __node < nr_node_ids; __node++) \
-                if (__n)
+       for (__node = 0; __node < nr_node_ids; __node++) \
+                if ((__n = get_node(__s, __node)))
 
 #endif
 
index d319502b24038b7ad0aee1023b88c5c6501fc39e..3a6e0cfdf03add7f19f4dc9ebdf7cc8bf6fad695 100644 (file)
@@ -30,6 +30,43 @@ LIST_HEAD(slab_caches);
 DEFINE_MUTEX(slab_mutex);
 struct kmem_cache *kmem_cache;
 
+/*
+ * Set of flags that will prevent slab merging
+ */
+#define SLAB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
+               SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_NOLEAKTRACE | \
+               SLAB_FAILSLAB)
+
+#define SLAB_MERGE_SAME (SLAB_DEBUG_FREE | SLAB_RECLAIM_ACCOUNT | \
+               SLAB_CACHE_DMA | SLAB_NOTRACK)
+
+/*
+ * Merge control. If this is set then no merging of slab caches will occur.
+ * (Could be removed. This was introduced to pacify the merge skeptics.)
+ */
+static int slab_nomerge;
+
+static int __init setup_slab_nomerge(char *str)
+{
+       slab_nomerge = 1;
+       return 1;
+}
+
+#ifdef CONFIG_SLUB
+__setup_param("slub_nomerge", slub_nomerge, setup_slab_nomerge, 0);
+#endif
+
+__setup("slab_nomerge", setup_slab_nomerge);
+
+/*
+ * Determine the size of a slab object
+ */
+unsigned int kmem_cache_size(struct kmem_cache *s)
+{
+       return s->object_size;
+}
+EXPORT_SYMBOL(kmem_cache_size);
+
 #ifdef CONFIG_DEBUG_VM
 static int kmem_cache_sanity_check(const char *name, size_t size)
 {
@@ -79,6 +116,65 @@ static inline int kmem_cache_sanity_check(const char *name, size_t size)
 #endif
 
 #ifdef CONFIG_MEMCG_KMEM
+static int memcg_alloc_cache_params(struct mem_cgroup *memcg,
+               struct kmem_cache *s, struct kmem_cache *root_cache)
+{
+       size_t size;
+
+       if (!memcg_kmem_enabled())
+               return 0;
+
+       if (!memcg) {
+               size = offsetof(struct memcg_cache_params, memcg_caches);
+               size += memcg_limited_groups_array_size * sizeof(void *);
+       } else
+               size = sizeof(struct memcg_cache_params);
+
+       s->memcg_params = kzalloc(size, GFP_KERNEL);
+       if (!s->memcg_params)
+               return -ENOMEM;
+
+       if (memcg) {
+               s->memcg_params->memcg = memcg;
+               s->memcg_params->root_cache = root_cache;
+       } else
+               s->memcg_params->is_root_cache = true;
+
+       return 0;
+}
+
+static void memcg_free_cache_params(struct kmem_cache *s)
+{
+       kfree(s->memcg_params);
+}
+
+static int memcg_update_cache_params(struct kmem_cache *s, int num_memcgs)
+{
+       int size;
+       struct memcg_cache_params *new_params, *cur_params;
+
+       BUG_ON(!is_root_cache(s));
+
+       size = offsetof(struct memcg_cache_params, memcg_caches);
+       size += num_memcgs * sizeof(void *);
+
+       new_params = kzalloc(size, GFP_KERNEL);
+       if (!new_params)
+               return -ENOMEM;
+
+       cur_params = s->memcg_params;
+       memcpy(new_params->memcg_caches, cur_params->memcg_caches,
+              memcg_limited_groups_array_size * sizeof(void *));
+
+       new_params->is_root_cache = true;
+
+       rcu_assign_pointer(s->memcg_params, new_params);
+       if (cur_params)
+               kfree_rcu(cur_params, rcu_head);
+
+       return 0;
+}
+
 int memcg_update_all_caches(int num_memcgs)
 {
        struct kmem_cache *s;
@@ -89,9 +185,8 @@ int memcg_update_all_caches(int num_memcgs)
                if (!is_root_cache(s))
                        continue;
 
-               ret = memcg_update_cache_size(s, num_memcgs);
+               ret = memcg_update_cache_params(s, num_memcgs);
                /*
-                * See comment in memcontrol.c, memcg_update_cache_size:
                 * Instead of freeing the memory, we'll just leave the caches
                 * up to this point in an updated state.
                 */
@@ -104,7 +199,80 @@ out:
        mutex_unlock(&slab_mutex);
        return ret;
 }
-#endif
+#else
+static inline int memcg_alloc_cache_params(struct mem_cgroup *memcg,
+               struct kmem_cache *s, struct kmem_cache *root_cache)
+{
+       return 0;
+}
+
+static inline void memcg_free_cache_params(struct kmem_cache *s)
+{
+}
+#endif /* CONFIG_MEMCG_KMEM */
+
+/*
+ * Find a mergeable slab cache
+ */
+int slab_unmergeable(struct kmem_cache *s)
+{
+       if (slab_nomerge || (s->flags & SLAB_NEVER_MERGE))
+               return 1;
+
+       if (!is_root_cache(s))
+               return 1;
+
+       if (s->ctor)
+               return 1;
+
+       /*
+        * We may have set a slab to be unmergeable during bootstrap.
+        */
+       if (s->refcount < 0)
+               return 1;
+
+       return 0;
+}
+
+struct kmem_cache *find_mergeable(size_t size, size_t align,
+               unsigned long flags, const char *name, void (*ctor)(void *))
+{
+       struct kmem_cache *s;
+
+       if (slab_nomerge || (flags & SLAB_NEVER_MERGE))
+               return NULL;
+
+       if (ctor)
+               return NULL;
+
+       size = ALIGN(size, sizeof(void *));
+       align = calculate_alignment(flags, align, size);
+       size = ALIGN(size, align);
+       flags = kmem_cache_flags(size, flags, name, NULL);
+
+       list_for_each_entry(s, &slab_caches, list) {
+               if (slab_unmergeable(s))
+                       continue;
+
+               if (size > s->size)
+                       continue;
+
+               if ((flags & SLAB_MERGE_SAME) != (s->flags & SLAB_MERGE_SAME))
+                       continue;
+               /*
+                * Check if alignment is compatible.
+                * Courtesy of Adrian Drzewiecki
+                */
+               if ((s->size & ~(align - 1)) != s->size)
+                       continue;
+
+               if (s->size - size >= sizeof(void *))
+                       continue;
+
+               return s;
+       }
+       return NULL;
+}
 
 /*
  * Figure out what the alignment of the objects will be given a set of
@@ -211,8 +379,10 @@ kmem_cache_create(const char *name, size_t size, size_t align,
        mutex_lock(&slab_mutex);
 
        err = kmem_cache_sanity_check(name, size);
-       if (err)
+       if (err) {
+               s = NULL;       /* suppress uninit var warning */
                goto out_unlock;
+       }
 
        /*
         * Some allocators will constraint the set of valid flags to a subset
index 21980e0f39a8a81f8e28e8ff891fd19c7f1b6fcf..96a86206a26b2ce6ea4fc8a71441a5e4d4ded26a 100644 (file)
--- a/mm/slob.c
+++ b/mm/slob.c
@@ -468,7 +468,6 @@ void *__kmalloc(size_t size, gfp_t gfp)
 }
 EXPORT_SYMBOL(__kmalloc);
 
-#ifdef CONFIG_TRACING
 void *__kmalloc_track_caller(size_t size, gfp_t gfp, unsigned long caller)
 {
        return __do_kmalloc_node(size, gfp, NUMA_NO_NODE, caller);
@@ -481,7 +480,6 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfp,
        return __do_kmalloc_node(size, gfp, node, caller);
 }
 #endif
-#endif
 
 void kfree(const void *block)
 {
index 3e8afcc07a760c552135cfb3c79ac924ee9e5494..ae7b9f1ad394ca9c65350dac175efa8cab199771 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -169,16 +169,6 @@ static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s)
  */
 #define DEBUG_METADATA_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER)
 
-/*
- * Set of flags that will prevent slab merging
- */
-#define SLUB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
-               SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_NOLEAKTRACE | \
-               SLAB_FAILSLAB)
-
-#define SLUB_MERGE_SAME (SLAB_DEBUG_FREE | SLAB_RECLAIM_ACCOUNT | \
-               SLAB_CACHE_DMA | SLAB_NOTRACK)
-
 #define OO_SHIFT       16
 #define OO_MASK                ((1 << OO_SHIFT) - 1)
 #define MAX_OBJS_PER_PAGE      32767 /* since page.objects is u15 */
@@ -1176,7 +1166,7 @@ out:
 
 __setup("slub_debug", setup_slub_debug);
 
-static unsigned long kmem_cache_flags(unsigned long object_size,
+unsigned long kmem_cache_flags(unsigned long object_size,
        unsigned long flags, const char *name,
        void (*ctor)(void *))
 {
@@ -1208,7 +1198,7 @@ static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n,
                                        struct page *page) {}
 static inline void remove_full(struct kmem_cache *s, struct kmem_cache_node *n,
                                        struct page *page) {}
-static inline unsigned long kmem_cache_flags(unsigned long object_size,
+unsigned long kmem_cache_flags(unsigned long object_size,
        unsigned long flags, const char *name,
        void (*ctor)(void *))
 {
@@ -1699,7 +1689,12 @@ static void *get_partial(struct kmem_cache *s, gfp_t flags, int node,
                struct kmem_cache_cpu *c)
 {
        void *object;
-       int searchnode = (node == NUMA_NO_NODE) ? numa_mem_id() : node;
+       int searchnode = node;
+
+       if (node == NUMA_NO_NODE)
+               searchnode = numa_mem_id();
+       else if (!node_present_pages(node))
+               searchnode = node_to_mem_node(node);
 
        object = get_partial_node(s, get_node(s, searchnode), c, flags);
        if (object || node != NUMA_NO_NODE)
@@ -2280,11 +2275,18 @@ static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
 redo:
 
        if (unlikely(!node_match(page, node))) {
-               stat(s, ALLOC_NODE_MISMATCH);
-               deactivate_slab(s, page, c->freelist);
-               c->page = NULL;
-               c->freelist = NULL;
-               goto new_slab;
+               int searchnode = node;
+
+               if (node != NUMA_NO_NODE && !node_present_pages(node))
+                       searchnode = node_to_mem_node(node);
+
+               if (unlikely(!node_match(page, searchnode))) {
+                       stat(s, ALLOC_NODE_MISMATCH);
+                       deactivate_slab(s, page, c->freelist);
+                       c->page = NULL;
+                       c->freelist = NULL;
+                       goto new_slab;
+               }
        }
 
        /*
@@ -2706,12 +2708,6 @@ static int slub_min_order;
 static int slub_max_order = PAGE_ALLOC_COSTLY_ORDER;
 static int slub_min_objects;
 
-/*
- * Merge control. If this is set then no merging of slab caches will occur.
- * (Could be removed. This was introduced to pacify the merge skeptics.)
- */
-static int slub_nomerge;
-
 /*
  * Calculate the order of allocation given an slab object size.
  *
@@ -3240,14 +3236,6 @@ static int __init setup_slub_min_objects(char *str)
 
 __setup("slub_min_objects=", setup_slub_min_objects);
 
-static int __init setup_slub_nomerge(char *str)
-{
-       slub_nomerge = 1;
-       return 1;
-}
-
-__setup("slub_nomerge", setup_slub_nomerge);
-
 void *__kmalloc(size_t size, gfp_t flags)
 {
        struct kmem_cache *s;
@@ -3625,69 +3613,6 @@ void __init kmem_cache_init_late(void)
 {
 }
 
-/*
- * Find a mergeable slab cache
- */
-static int slab_unmergeable(struct kmem_cache *s)
-{
-       if (slub_nomerge || (s->flags & SLUB_NEVER_MERGE))
-               return 1;
-
-       if (!is_root_cache(s))
-               return 1;
-
-       if (s->ctor)
-               return 1;
-
-       /*
-        * We may have set a slab to be unmergeable during bootstrap.
-        */
-       if (s->refcount < 0)
-               return 1;
-
-       return 0;
-}
-
-static struct kmem_cache *find_mergeable(size_t size, size_t align,
-               unsigned long flags, const char *name, void (*ctor)(void *))
-{
-       struct kmem_cache *s;
-
-       if (slub_nomerge || (flags & SLUB_NEVER_MERGE))
-               return NULL;
-
-       if (ctor)
-               return NULL;
-
-       size = ALIGN(size, sizeof(void *));
-       align = calculate_alignment(flags, align, size);
-       size = ALIGN(size, align);
-       flags = kmem_cache_flags(size, flags, name, NULL);
-
-       list_for_each_entry(s, &slab_caches, list) {
-               if (slab_unmergeable(s))
-                       continue;
-
-               if (size > s->size)
-                       continue;
-
-               if ((flags & SLUB_MERGE_SAME) != (s->flags & SLUB_MERGE_SAME))
-                       continue;
-               /*
-                * Check if alignment is compatible.
-                * Courtesy of Adrian Drzewiecki
-                */
-               if ((s->size & ~(align - 1)) != s->size)
-                       continue;
-
-               if (s->size - size >= sizeof(void *))
-                       continue;
-
-               return s;
-       }
-       return NULL;
-}
-
 struct kmem_cache *
 __kmem_cache_alias(const char *name, size_t size, size_t align,
                   unsigned long flags, void (*ctor)(void *))
@@ -4604,6 +4529,14 @@ static ssize_t trace_show(struct kmem_cache *s, char *buf)
 static ssize_t trace_store(struct kmem_cache *s, const char *buf,
                                                        size_t length)
 {
+       /*
+        * Tracing a merged cache is going to give confusing results
+        * as well as cause other issues like converting a mergeable
+        * cache into an umergeable one.
+        */
+       if (s->refcount > 1)
+               return -EINVAL;
+
        s->flags &= ~SLAB_TRACE;
        if (buf[0] == '1') {
                s->flags &= ~__CMPXCHG_DOUBLE;
@@ -4721,6 +4654,9 @@ static ssize_t failslab_show(struct kmem_cache *s, char *buf)
 static ssize_t failslab_store(struct kmem_cache *s, const char *buf,
                                                        size_t length)
 {
+       if (s->refcount > 1)
+               return -EINVAL;
+
        s->flags &= ~SLAB_FAILSLAB;
        if (buf[0] == '1')
                s->flags |= SLAB_FAILSLAB;
index 6b2dc3897cd575f94873df4e6bdaadebd7a1f2a1..8a12b33936b45c1c49a312ea04e08338472b98fe 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -887,18 +887,14 @@ void lru_add_drain_all(void)
        mutex_unlock(&lock);
 }
 
-/*
- * Batched page_cache_release().  Decrement the reference count on all the
- * passed pages.  If it fell to zero then remove the page from the LRU and
- * free it.
- *
- * Avoid taking zone->lru_lock if possible, but if it is taken, retain it
- * for the remainder of the operation.
+/**
+ * release_pages - batched page_cache_release()
+ * @pages: array of pages to release
+ * @nr: number of pages
+ * @cold: whether the pages are cache cold
  *
- * The locking in this function is against shrink_inactive_list(): we recheck
- * the page count inside the lock to see whether shrink_inactive_list()
- * grabbed the page via the LRU.  If it did, give up: shrink_inactive_list()
- * will free it.
+ * Decrement the reference count on all the pages in @pages.  If it
+ * fell to zero, remove the page from the LRU and free it.
  */
 void release_pages(struct page **pages, int nr, bool cold)
 {
@@ -907,6 +903,7 @@ void release_pages(struct page **pages, int nr, bool cold)
        struct zone *zone = NULL;
        struct lruvec *lruvec;
        unsigned long uninitialized_var(flags);
+       unsigned int uninitialized_var(lock_batch);
 
        for (i = 0; i < nr; i++) {
                struct page *page = pages[i];
@@ -920,6 +917,16 @@ void release_pages(struct page **pages, int nr, bool cold)
                        continue;
                }
 
+               /*
+                * Make sure the IRQ-safe lock-holding time does not get
+                * excessive with a continuous string of pages from the
+                * same zone. The lock is held only if zone != NULL.
+                */
+               if (zone && ++lock_batch == SWAP_CLUSTER_MAX) {
+                       spin_unlock_irqrestore(&zone->lru_lock, flags);
+                       zone = NULL;
+               }
+
                if (!put_page_testzero(page))
                        continue;
 
@@ -930,6 +937,7 @@ void release_pages(struct page **pages, int nr, bool cold)
                                if (zone)
                                        spin_unlock_irqrestore(&zone->lru_lock,
                                                                        flags);
+                               lock_batch = 0;
                                zone = pagezone;
                                spin_lock_irqsave(&zone->lru_lock, flags);
                        }
index 3e0ec83d000cdf3f7a65e620dc6696f039dc8d98..1544449186858a42f5cb5076e028f269e238b3a9 100644 (file)
@@ -28,7 +28,9 @@
 static const struct address_space_operations swap_aops = {
        .writepage      = swap_writepage,
        .set_page_dirty = swap_set_page_dirty,
+#ifdef CONFIG_MIGRATION
        .migratepage    = migrate_page,
+#endif
 };
 
 static struct backing_dev_info swap_backing_dev_info = {
@@ -263,18 +265,12 @@ void free_page_and_swap_cache(struct page *page)
 void free_pages_and_swap_cache(struct page **pages, int nr)
 {
        struct page **pagep = pages;
+       int i;
 
        lru_add_drain();
-       while (nr) {
-               int todo = min(nr, PAGEVEC_SIZE);
-               int i;
-
-               for (i = 0; i < todo; i++)
-                       free_swap_cache(pagep[i]);
-               release_pages(pagep, todo, false);
-               pagep += todo;
-               nr -= todo;
-       }
+       for (i = 0; i < nr; i++)
+               free_swap_cache(pagep[i]);
+       release_pages(pagep, nr, false);
 }
 
 /*
index 093c973f1697dd39545c6c93aeac8e9c3d60cf52..fec39d4509a958763685fb6b70499590f9e363fb 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -170,32 +170,25 @@ static int vm_is_stack_for_task(struct task_struct *t,
 /*
  * Check if the vma is being used as a stack.
  * If is_group is non-zero, check in the entire thread group or else
- * just check in the current task. Returns the pid of the task that
- * the vma is stack for.
+ * just check in the current task. Returns the task_struct of the task
+ * that the vma is stack for. Must be called under rcu_read_lock().
  */
-pid_t vm_is_stack(struct task_struct *task,
-                 struct vm_area_struct *vma, int in_group)
+struct task_struct *task_of_stack(struct task_struct *task,
+                               struct vm_area_struct *vma, bool in_group)
 {
-       pid_t ret = 0;
-
        if (vm_is_stack_for_task(task, vma))
-               return task->pid;
+               return task;
 
        if (in_group) {
                struct task_struct *t;
 
-               rcu_read_lock();
                for_each_thread(task, t) {
-                       if (vm_is_stack_for_task(t, vma)) {
-                               ret = t->pid;
-                               goto done;
-                       }
+                       if (vm_is_stack_for_task(t, vma))
+                               return t;
                }
-done:
-               rcu_read_unlock();
        }
 
-       return ret;
+       return NULL;
 }
 
 #if defined(CONFIG_MMU) && !defined(HAVE_ARCH_PICK_MMAP_LAYOUT)
index 2b0aa5486092dca2745c2ec201cda44db033550c..90520af7f18604ccd825d2595e6103dccbbf3f32 100644 (file)
@@ -2646,21 +2646,11 @@ static const struct seq_operations vmalloc_op = {
 
 static int vmalloc_open(struct inode *inode, struct file *file)
 {
-       unsigned int *ptr = NULL;
-       int ret;
-
-       if (IS_ENABLED(CONFIG_NUMA)) {
-               ptr = kmalloc(nr_node_ids * sizeof(unsigned int), GFP_KERNEL);
-               if (ptr == NULL)
-                       return -ENOMEM;
-       }
-       ret = seq_open(file, &vmalloc_op);
-       if (!ret) {
-               struct seq_file *m = file->private_data;
-               m->private = ptr;
-       } else
-               kfree(ptr);
-       return ret;
+       if (IS_ENABLED(CONFIG_NUMA))
+               return seq_open_private(file, &vmalloc_op,
+                                       nr_node_ids * sizeof(unsigned int));
+       else
+               return seq_open(file, &vmalloc_op);
 }
 
 static const struct file_operations proc_vmalloc_operations = {
index 2836b5373b2e7623a1143a98bf3997fa11865731..dcb47074ae03cdbe7ff4d823e116776b6dbc8673 100644 (file)
@@ -920,7 +920,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
                        /* Case 1 above */
                        if (current_is_kswapd() &&
                            PageReclaim(page) &&
-                           zone_is_reclaim_writeback(zone)) {
+                           test_bit(ZONE_WRITEBACK, &zone->flags)) {
                                nr_immediate++;
                                goto keep_locked;
 
@@ -1002,7 +1002,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
                         */
                        if (page_is_file_cache(page) &&
                                        (!current_is_kswapd() ||
-                                        !zone_is_reclaim_dirty(zone))) {
+                                        !test_bit(ZONE_DIRTY, &zone->flags))) {
                                /*
                                 * Immediately reclaim when written back.
                                 * Similar in principal to deactivate_page()
@@ -1563,7 +1563,7 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec,
         * are encountered in the nr_immediate check below.
         */
        if (nr_writeback && nr_writeback == nr_taken)
-               zone_set_flag(zone, ZONE_WRITEBACK);
+               set_bit(ZONE_WRITEBACK, &zone->flags);
 
        /*
         * memcg will stall in page writeback so only consider forcibly
@@ -1575,16 +1575,16 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec,
                 * backed by a congested BDI and wait_iff_congested will stall.
                 */
                if (nr_dirty && nr_dirty == nr_congested)
-                       zone_set_flag(zone, ZONE_CONGESTED);
+                       set_bit(ZONE_CONGESTED, &zone->flags);
 
                /*
                 * If dirty pages are scanned that are not queued for IO, it
                 * implies that flushers are not keeping up. In this case, flag
-                * the zone ZONE_TAIL_LRU_DIRTY and kswapd will start writing
-                * pages from reclaim context.
+                * the zone ZONE_DIRTY and kswapd will start writing pages from
+                * reclaim context.
                 */
                if (nr_unqueued_dirty == nr_taken)
-                       zone_set_flag(zone, ZONE_TAIL_LRU_DIRTY);
+                       set_bit(ZONE_DIRTY, &zone->flags);
 
                /*
                 * If kswapd scans pages marked marked for immediate
@@ -2315,7 +2315,10 @@ static bool shrink_zone(struct zone *zone, struct scan_control *sc)
        return reclaimable;
 }
 
-/* Returns true if compaction should go ahead for a high-order request */
+/*
+ * Returns true if compaction should go ahead for a high-order request, or
+ * the high-order allocation would succeed without compaction.
+ */
 static inline bool compaction_ready(struct zone *zone, int order)
 {
        unsigned long balance_gap, watermark;
@@ -2339,8 +2342,11 @@ static inline bool compaction_ready(struct zone *zone, int order)
        if (compaction_deferred(zone, order))
                return watermark_ok;
 
-       /* If compaction is not ready to start, keep reclaiming */
-       if (!compaction_suitable(zone, order))
+       /*
+        * If compaction is not ready to start and allocation is not likely
+        * to succeed without it, then keep reclaiming.
+        */
+       if (compaction_suitable(zone, order) == COMPACT_SKIPPED)
                return false;
 
        return watermark_ok;
@@ -2753,21 +2759,22 @@ unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *memcg,
 }
 
 unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
+                                          unsigned long nr_pages,
                                           gfp_t gfp_mask,
-                                          bool noswap)
+                                          bool may_swap)
 {
        struct zonelist *zonelist;
        unsigned long nr_reclaimed;
        int nid;
        struct scan_control sc = {
-               .nr_to_reclaim = SWAP_CLUSTER_MAX,
+               .nr_to_reclaim = max(nr_pages, SWAP_CLUSTER_MAX),
                .gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
                                (GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK),
                .target_mem_cgroup = memcg,
                .priority = DEF_PRIORITY,
                .may_writepage = !laptop_mode,
                .may_unmap = 1,
-               .may_swap = !noswap,
+               .may_swap = may_swap,
        };
 
        /*
@@ -2818,7 +2825,7 @@ static bool zone_balanced(struct zone *zone, int order,
                return false;
 
        if (IS_ENABLED(CONFIG_COMPACTION) && order &&
-           !compaction_suitable(zone, order))
+           compaction_suitable(zone, order) == COMPACT_SKIPPED)
                return false;
 
        return true;
@@ -2978,7 +2985,7 @@ static bool kswapd_shrink_zone(struct zone *zone,
        /* Account for the number of pages attempted to reclaim */
        *nr_attempted += sc->nr_to_reclaim;
 
-       zone_clear_flag(zone, ZONE_WRITEBACK);
+       clear_bit(ZONE_WRITEBACK, &zone->flags);
 
        /*
         * If a zone reaches its high watermark, consider it to be no longer
@@ -2988,8 +2995,8 @@ static bool kswapd_shrink_zone(struct zone *zone,
         */
        if (zone_reclaimable(zone) &&
            zone_balanced(zone, testorder, 0, classzone_idx)) {
-               zone_clear_flag(zone, ZONE_CONGESTED);
-               zone_clear_flag(zone, ZONE_TAIL_LRU_DIRTY);
+               clear_bit(ZONE_CONGESTED, &zone->flags);
+               clear_bit(ZONE_DIRTY, &zone->flags);
        }
 
        return sc->nr_scanned >= sc->nr_to_reclaim;
@@ -3080,8 +3087,8 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order,
                                 * If balanced, clear the dirty and congested
                                 * flags
                                 */
-                               zone_clear_flag(zone, ZONE_CONGESTED);
-                               zone_clear_flag(zone, ZONE_TAIL_LRU_DIRTY);
+                               clear_bit(ZONE_CONGESTED, &zone->flags);
+                               clear_bit(ZONE_DIRTY, &zone->flags);
                        }
                }
 
@@ -3708,11 +3715,11 @@ int zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)
        if (node_state(node_id, N_CPU) && node_id != numa_node_id())
                return ZONE_RECLAIM_NOSCAN;
 
-       if (zone_test_and_set_flag(zone, ZONE_RECLAIM_LOCKED))
+       if (test_and_set_bit(ZONE_RECLAIM_LOCKED, &zone->flags))
                return ZONE_RECLAIM_NOSCAN;
 
        ret = __zone_reclaim(zone, gfp_mask, order);
-       zone_clear_flag(zone, ZONE_RECLAIM_LOCKED);
+       clear_bit(ZONE_RECLAIM_LOCKED, &zone->flags);
 
        if (!ret)
                count_vm_event(PGSCAN_ZONE_RECLAIM_FAILED);
@@ -3791,66 +3798,3 @@ void check_move_unevictable_pages(struct page **pages, int nr_pages)
        }
 }
 #endif /* CONFIG_SHMEM */
-
-static void warn_scan_unevictable_pages(void)
-{
-       printk_once(KERN_WARNING
-                   "%s: The scan_unevictable_pages sysctl/node-interface has been "
-                   "disabled for lack of a legitimate use case.  If you have "
-                   "one, please send an email to linux-mm@kvack.org.\n",
-                   current->comm);
-}
-
-/*
- * scan_unevictable_pages [vm] sysctl handler.  On demand re-scan of
- * all nodes' unevictable lists for evictable pages
- */
-unsigned long scan_unevictable_pages;
-
-int scan_unevictable_handler(struct ctl_table *table, int write,
-                          void __user *buffer,
-                          size_t *length, loff_t *ppos)
-{
-       warn_scan_unevictable_pages();
-       proc_doulongvec_minmax(table, write, buffer, length, ppos);
-       scan_unevictable_pages = 0;
-       return 0;
-}
-
-#ifdef CONFIG_NUMA
-/*
- * per node 'scan_unevictable_pages' attribute.  On demand re-scan of
- * a specified node's per zone unevictable lists for evictable pages.
- */
-
-static ssize_t read_scan_unevictable_node(struct device *dev,
-                                         struct device_attribute *attr,
-                                         char *buf)
-{
-       warn_scan_unevictable_pages();
-       return sprintf(buf, "0\n");     /* always zero; should fit... */
-}
-
-static ssize_t write_scan_unevictable_node(struct device *dev,
-                                          struct device_attribute *attr,
-                                       const char *buf, size_t count)
-{
-       warn_scan_unevictable_pages();
-       return 1;
-}
-
-
-static DEVICE_ATTR(scan_unevictable_pages, S_IRUGO | S_IWUSR,
-                       read_scan_unevictable_node,
-                       write_scan_unevictable_node);
-
-int scan_unevictable_register_node(struct node *node)
-{
-       return device_create_file(&node->dev, &dev_attr_scan_unevictable_pages);
-}
-
-void scan_unevictable_unregister_node(struct node *node)
-{
-       device_remove_file(&node->dev, &dev_attr_scan_unevictable_pages);
-}
-#endif
index e9ab104b956f127f79598231863bd62b7f559147..1b12d390dc6815d136cb0c812d00169c6f52895f 100644 (file)
@@ -7,6 +7,7 @@
  *  zoned VM statistics
  *  Copyright (C) 2006 Silicon Graphics, Inc.,
  *             Christoph Lameter <christoph@lameter.com>
+ *  Copyright (C) 2008-2014 Christoph Lameter
  */
 #include <linux/fs.h>
 #include <linux/mm.h>
@@ -14,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/cpu.h>
+#include <linux/cpumask.h>
 #include <linux/vmstat.h>
 #include <linux/sched.h>
 #include <linux/math64.h>
@@ -419,13 +421,22 @@ void dec_zone_page_state(struct page *page, enum zone_stat_item item)
 EXPORT_SYMBOL(dec_zone_page_state);
 #endif
 
-static inline void fold_diff(int *diff)
+
+/*
+ * Fold a differential into the global counters.
+ * Returns the number of counters updated.
+ */
+static int fold_diff(int *diff)
 {
        int i;
+       int changes = 0;
 
        for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
-               if (diff[i])
+               if (diff[i]) {
                        atomic_long_add(diff[i], &vm_stat[i]);
+                       changes++;
+       }
+       return changes;
 }
 
 /*
@@ -441,12 +452,15 @@ static inline void fold_diff(int *diff)
  * statistics in the remote zone struct as well as the global cachelines
  * with the global counters. These could cause remote node cache line
  * bouncing and will have to be only done when necessary.
+ *
+ * The function returns the number of global counters updated.
  */
-static void refresh_cpu_vm_stats(void)
+static int refresh_cpu_vm_stats(void)
 {
        struct zone *zone;
        int i;
        int global_diff[NR_VM_ZONE_STAT_ITEMS] = { 0, };
+       int changes = 0;
 
        for_each_populated_zone(zone) {
                struct per_cpu_pageset __percpu *p = zone->pageset;
@@ -486,15 +500,17 @@ static void refresh_cpu_vm_stats(void)
                        continue;
                }
 
-
                if (__this_cpu_dec_return(p->expire))
                        continue;
 
-               if (__this_cpu_read(p->pcp.count))
+               if (__this_cpu_read(p->pcp.count)) {
                        drain_zone_pages(zone, this_cpu_ptr(&p->pcp));
+                       changes++;
+               }
 #endif
        }
-       fold_diff(global_diff);
+       changes += fold_diff(global_diff);
+       return changes;
 }
 
 /*
@@ -735,7 +751,7 @@ static void walk_zones_in_node(struct seq_file *m, pg_data_t *pgdat,
                                        TEXT_FOR_HIGHMEM(xx) xx "_movable",
 
 const char * const vmstat_text[] = {
-       /* Zoned VM counters */
+       /* enum zone_stat_item countes */
        "nr_free_pages",
        "nr_alloc_batch",
        "nr_inactive_anon",
@@ -778,10 +794,13 @@ const char * const vmstat_text[] = {
        "workingset_nodereclaim",
        "nr_anon_transparent_hugepages",
        "nr_free_cma",
+
+       /* enum writeback_stat_item counters */
        "nr_dirty_threshold",
        "nr_dirty_background_threshold",
 
 #ifdef CONFIG_VM_EVENT_COUNTERS
+       /* enum vm_event_item counters */
        "pgpgin",
        "pgpgout",
        "pswpin",
@@ -860,6 +879,13 @@ const char * const vmstat_text[] = {
        "thp_zero_page_alloc",
        "thp_zero_page_alloc_failed",
 #endif
+#ifdef CONFIG_MEMORY_BALLOON
+       "balloon_inflate",
+       "balloon_deflate",
+#ifdef CONFIG_BALLOON_COMPACTION
+       "balloon_migrate",
+#endif
+#endif /* CONFIG_MEMORY_BALLOON */
 #ifdef CONFIG_DEBUG_TLBFLUSH
 #ifdef CONFIG_SMP
        "nr_tlb_remote_flush",
@@ -1229,20 +1255,108 @@ static const struct file_operations proc_vmstat_file_operations = {
 #ifdef CONFIG_SMP
 static DEFINE_PER_CPU(struct delayed_work, vmstat_work);
 int sysctl_stat_interval __read_mostly = HZ;
+static cpumask_var_t cpu_stat_off;
 
 static void vmstat_update(struct work_struct *w)
 {
-       refresh_cpu_vm_stats();
-       schedule_delayed_work(this_cpu_ptr(&vmstat_work),
+       if (refresh_cpu_vm_stats())
+               /*
+                * Counters were updated so we expect more updates
+                * to occur in the future. Keep on running the
+                * update worker thread.
+                */
+               schedule_delayed_work(this_cpu_ptr(&vmstat_work),
+                       round_jiffies_relative(sysctl_stat_interval));
+       else {
+               /*
+                * We did not update any counters so the app may be in
+                * a mode where it does not cause counter updates.
+                * We may be uselessly running vmstat_update.
+                * Defer the checking for differentials to the
+                * shepherd thread on a different processor.
+                */
+               int r;
+               /*
+                * Shepherd work thread does not race since it never
+                * changes the bit if its zero but the cpu
+                * online / off line code may race if
+                * worker threads are still allowed during
+                * shutdown / startup.
+                */
+               r = cpumask_test_and_set_cpu(smp_processor_id(),
+                       cpu_stat_off);
+               VM_BUG_ON(r);
+       }
+}
+
+/*
+ * Check if the diffs for a certain cpu indicate that
+ * an update is needed.
+ */
+static bool need_update(int cpu)
+{
+       struct zone *zone;
+
+       for_each_populated_zone(zone) {
+               struct per_cpu_pageset *p = per_cpu_ptr(zone->pageset, cpu);
+
+               BUILD_BUG_ON(sizeof(p->vm_stat_diff[0]) != 1);
+               /*
+                * The fast way of checking if there are any vmstat diffs.
+                * This works because the diffs are byte sized items.
+                */
+               if (memchr_inv(p->vm_stat_diff, 0, NR_VM_ZONE_STAT_ITEMS))
+                       return true;
+
+       }
+       return false;
+}
+
+
+/*
+ * Shepherd worker thread that checks the
+ * differentials of processors that have their worker
+ * threads for vm statistics updates disabled because of
+ * inactivity.
+ */
+static void vmstat_shepherd(struct work_struct *w);
+
+static DECLARE_DELAYED_WORK(shepherd, vmstat_shepherd);
+
+static void vmstat_shepherd(struct work_struct *w)
+{
+       int cpu;
+
+       get_online_cpus();
+       /* Check processors whose vmstat worker threads have been disabled */
+       for_each_cpu(cpu, cpu_stat_off)
+               if (need_update(cpu) &&
+                       cpumask_test_and_clear_cpu(cpu, cpu_stat_off))
+
+                       schedule_delayed_work_on(cpu, &per_cpu(vmstat_work, cpu),
+                               __round_jiffies_relative(sysctl_stat_interval, cpu));
+
+       put_online_cpus();
+
+       schedule_delayed_work(&shepherd,
                round_jiffies_relative(sysctl_stat_interval));
+
 }
 
-static void start_cpu_timer(int cpu)
+static void __init start_shepherd_timer(void)
 {
-       struct delayed_work *work = &per_cpu(vmstat_work, cpu);
+       int cpu;
+
+       for_each_possible_cpu(cpu)
+               INIT_DEFERRABLE_WORK(per_cpu_ptr(&vmstat_work, cpu),
+                       vmstat_update);
+
+       if (!alloc_cpumask_var(&cpu_stat_off, GFP_KERNEL))
+               BUG();
+       cpumask_copy(cpu_stat_off, cpu_online_mask);
 
-       INIT_DEFERRABLE_WORK(work, vmstat_update);
-       schedule_delayed_work_on(cpu, work, __round_jiffies_relative(HZ, cpu));
+       schedule_delayed_work(&shepherd,
+               round_jiffies_relative(sysctl_stat_interval));
 }
 
 static void vmstat_cpu_dead(int node)
@@ -1273,17 +1387,17 @@ static int vmstat_cpuup_callback(struct notifier_block *nfb,
        case CPU_ONLINE:
        case CPU_ONLINE_FROZEN:
                refresh_zone_stat_thresholds();
-               start_cpu_timer(cpu);
                node_set_state(cpu_to_node(cpu), N_CPU);
+               cpumask_set_cpu(cpu, cpu_stat_off);
                break;
        case CPU_DOWN_PREPARE:
        case CPU_DOWN_PREPARE_FROZEN:
                cancel_delayed_work_sync(&per_cpu(vmstat_work, cpu));
-               per_cpu(vmstat_work, cpu).work.func = NULL;
+               cpumask_clear_cpu(cpu, cpu_stat_off);
                break;
        case CPU_DOWN_FAILED:
        case CPU_DOWN_FAILED_FROZEN:
-               start_cpu_timer(cpu);
+               cpumask_set_cpu(cpu, cpu_stat_off);
                break;
        case CPU_DEAD:
        case CPU_DEAD_FROZEN:
@@ -1303,15 +1417,10 @@ static struct notifier_block vmstat_notifier =
 static int __init setup_vmstat(void)
 {
 #ifdef CONFIG_SMP
-       int cpu;
-
        cpu_notifier_register_begin();
        __register_cpu_notifier(&vmstat_notifier);
 
-       for_each_online_cpu(cpu) {
-               start_cpu_timer(cpu);
-               node_set_state(cpu_to_node(cpu), N_CPU);
-       }
+       start_shepherd_timer();
        cpu_notifier_register_done();
 #endif
 #ifdef CONFIG_PROC_FS
index f26e7fcc7fa25c7316f60235e8c402ff6bec4f26..ecf1dbef69833f56fbde1c0e2cb167e8e7b1eff7 100644 (file)
--- a/mm/zbud.c
+++ b/mm/zbud.c
  * NCHUNKS_ORDER determines the internal allocation granularity, effectively
  * adjusting internal fragmentation.  It also determines the number of
  * freelists maintained in each pool. NCHUNKS_ORDER of 6 means that the
- * allocation granularity will be in chunks of size PAGE_SIZE/64, and there
- * will be 64 freelists per pool.
+ * allocation granularity will be in chunks of size PAGE_SIZE/64. As one chunk
+ * in allocated page is occupied by zbud header, NCHUNKS will be calculated to
+ * 63 which shows the max number of free chunks in zbud page, also there will be
+ * 63 freelists per pool.
  */
 #define NCHUNKS_ORDER  6
 
 #define CHUNK_SHIFT    (PAGE_SHIFT - NCHUNKS_ORDER)
 #define CHUNK_SIZE     (1 << CHUNK_SHIFT)
-#define NCHUNKS                (PAGE_SIZE >> CHUNK_SHIFT)
 #define ZHDR_SIZE_ALIGNED CHUNK_SIZE
+#define NCHUNKS                ((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT)
 
 /**
  * struct zbud_pool - stores metadata for each zbud pool
@@ -268,10 +270,9 @@ static int num_free_chunks(struct zbud_header *zhdr)
 {
        /*
         * Rather than branch for different situations, just use the fact that
-        * free buddies have a length of zero to simplify everything. -1 at the
-        * end for the zbud header.
+        * free buddies have a length of zero to simplify everything.
         */
-       return NCHUNKS - zhdr->first_chunks - zhdr->last_chunks - 1;
+       return NCHUNKS - zhdr->first_chunks - zhdr->last_chunks;
 }
 
 /*****************
index 94f38fac5e81eb451d61e6561000c354103c0589..839a48c3ca27b2bf008496c89867f7210aeb6610 100644 (file)
@@ -175,7 +175,7 @@ enum fullness_group {
  *     n <= N / f, where
  * n = number of allocated objects
  * N = total number of objects zspage can store
- * f = 1/fullness_threshold_frac
+ * f = fullness_threshold_frac
  *
  * Similarly, we assign zspage to:
  *     ZS_ALMOST_FULL  when n > N / f
@@ -199,9 +199,6 @@ struct size_class {
 
        spinlock_t lock;
 
-       /* stats */
-       u64 pages_allocated;
-
        struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS];
 };
 
@@ -220,6 +217,7 @@ struct zs_pool {
        struct size_class size_class[ZS_SIZE_CLASSES];
 
        gfp_t flags;    /* allocation flags used when growing pool */
+       atomic_long_t pages_allocated;
 };
 
 /*
@@ -299,7 +297,7 @@ static void zs_zpool_unmap(void *pool, unsigned long handle)
 
 static u64 zs_zpool_total_size(void *pool)
 {
-       return zs_get_total_size_bytes(pool);
+       return zs_get_total_pages(pool) << PAGE_SHIFT;
 }
 
 static struct zpool_driver zs_zpool_driver = {
@@ -630,7 +628,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
        while (page) {
                struct page *next_page;
                struct link_free *link;
-               unsigned int i, objs_on_page;
+               unsigned int i = 1;
 
                /*
                 * page->index stores offset of first object starting
@@ -643,14 +641,10 @@ static void init_zspage(struct page *first_page, struct size_class *class)
 
                link = (struct link_free *)kmap_atomic(page) +
                                                off / sizeof(*link);
-               objs_on_page = (PAGE_SIZE - off) / class->size;
 
-               for (i = 1; i <= objs_on_page; i++) {
-                       off += class->size;
-                       if (off < PAGE_SIZE) {
-                               link->next = obj_location_to_handle(page, i);
-                               link += class->size / sizeof(*link);
-                       }
+               while ((off += class->size) < PAGE_SIZE) {
+                       link->next = obj_location_to_handle(page, i++);
+                       link += class->size / sizeof(*link);
                }
 
                /*
@@ -662,7 +656,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
                link->next = obj_location_to_handle(next_page, 0);
                kunmap_atomic(link);
                page = next_page;
-               off = (off + class->size) % PAGE_SIZE;
+               off %= PAGE_SIZE;
        }
 }
 
@@ -1028,8 +1022,9 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
                        return 0;
 
                set_zspage_mapping(first_page, class->index, ZS_EMPTY);
+               atomic_long_add(class->pages_per_zspage,
+                                       &pool->pages_allocated);
                spin_lock(&class->lock);
-               class->pages_allocated += class->pages_per_zspage;
        }
 
        obj = (unsigned long)first_page->freelist;
@@ -1082,14 +1077,13 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 
        first_page->inuse--;
        fullness = fix_fullness_group(pool, first_page);
-
-       if (fullness == ZS_EMPTY)
-               class->pages_allocated -= class->pages_per_zspage;
-
        spin_unlock(&class->lock);
 
-       if (fullness == ZS_EMPTY)
+       if (fullness == ZS_EMPTY) {
+               atomic_long_sub(class->pages_per_zspage,
+                               &pool->pages_allocated);
                free_zspage(first_page);
+       }
 }
 EXPORT_SYMBOL_GPL(zs_free);
 
@@ -1183,17 +1177,11 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
 }
 EXPORT_SYMBOL_GPL(zs_unmap_object);
 
-u64 zs_get_total_size_bytes(struct zs_pool *pool)
+unsigned long zs_get_total_pages(struct zs_pool *pool)
 {
-       int i;
-       u64 npages = 0;
-
-       for (i = 0; i < ZS_SIZE_CLASSES; i++)
-               npages += pool->size_class[i].pages_allocated;
-
-       return npages << PAGE_SHIFT;
+       return atomic_long_read(&pool->pages_allocated);
 }
-EXPORT_SYMBOL_GPL(zs_get_total_size_bytes);
+EXPORT_SYMBOL_GPL(zs_get_total_pages);
 
 module_init(zs_init);
 module_exit(zs_exit);
index 97b0fcc79547aa48476feda4e0c6ef95054bd5e7..5ab6627cf3704a8479d35170546d1d683185dbbd 100644 (file)
@@ -1115,7 +1115,7 @@ static int __init dccp_init(void)
 
        BUILD_BUG_ON(sizeof(struct dccp_skb_cb) >
                     FIELD_SIZEOF(struct sk_buff, cb));
-       rc = percpu_counter_init(&dccp_orphan_count, 0);
+       rc = percpu_counter_init(&dccp_orphan_count, 0, GFP_KERNEL);
        if (rc)
                goto out_fail;
        rc = -ENOBUFS;
index 461003d258ba4030b1a8aa1c49930b7a1ac94b9c..86023b9be47f46eeef7373935af024c1e7c4fa9d 100644 (file)
@@ -3071,8 +3071,8 @@ void __init tcp_init(void)
 
        BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb));
 
-       percpu_counter_init(&tcp_sockets_allocated, 0);
-       percpu_counter_init(&tcp_orphan_count, 0);
+       percpu_counter_init(&tcp_sockets_allocated, 0, GFP_KERNEL);
+       percpu_counter_init(&tcp_orphan_count, 0, GFP_KERNEL);
        tcp_hashinfo.bind_bucket_cachep =
                kmem_cache_create("tcp_bind_bucket",
                                  sizeof(struct inet_bind_bucket), 0,
index 3af522622fad1fe7b400cb7caf07425502358550..1d191357bf8801c97ee27e5f10481b8d229a3c12 100644 (file)
@@ -32,7 +32,7 @@ int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
                res_parent = &parent_cg->memory_allocated;
 
        res_counter_init(&cg_proto->memory_allocated, res_parent);
-       percpu_counter_init(&cg_proto->sockets_allocated, 0);
+       percpu_counter_init(&cg_proto->sockets_allocated, 0, GFP_KERNEL);
 
        return 0;
 }
index 9d2c6c9facb6a4f4dabee29d8430aaf55b323636..8f34b27d5775f053ffde8a763f724c0d8b4f6e1f 100644 (file)
@@ -1341,7 +1341,7 @@ static __init int sctp_init(void)
        if (!sctp_chunk_cachep)
                goto err_chunk_cachep;
 
-       status = percpu_counter_init(&sctp_sockets_allocated, 0);
+       status = percpu_counter_init(&sctp_sockets_allocated, 0, GFP_KERNEL);
        if (status)
                goto err_percpu_counter_init;
 
index ffd9cb46902b3640c5a52947391ad16c5d0c69b9..fe20c319a0bb37dd475b7a8a32dccd57ac4e15d9 100644 (file)
@@ -1065,7 +1065,8 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
                        err = -EFAULT;
                        if (get_user(pid, (int __user *)argp))
                                break;
-                       err = f_setown(sock->file, pid, 1);
+                       f_setown(sock->file, pid, 1);
+                       err = 0;
                        break;
                case FIOGETOWN:
                case SIOCGPGRP:
index a74fde6a7468cda37e0eacee7fb7608aabfdce52..d68c57a62bcf72694b7705292ac3a61592dc67af 100644 (file)
@@ -343,9 +343,9 @@ static int cap_file_fcntl(struct file *file, unsigned int cmd,
        return 0;
 }
 
-static int cap_file_set_fowner(struct file *file)
+static void cap_file_set_fowner(struct file *file)
 {
-       return 0;
+       return;
 }
 
 static int cap_file_send_sigiotask(struct task_struct *tsk,
index e41b1a8d7644a674d8e9c02ae09fc3562d6a8d84..18b35c63fc0c80cb3f0ce9dfc734f42c33efe98b 100644 (file)
@@ -775,9 +775,9 @@ int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
        return security_ops->file_fcntl(file, cmd, arg);
 }
 
-int security_file_set_fowner(struct file *file)
+void security_file_set_fowner(struct file *file)
 {
-       return security_ops->file_set_fowner(file);
+       security_ops->file_set_fowner(file);
 }
 
 int security_file_send_sigiotask(struct task_struct *tsk,
index b0e940497e23bb47a0460e57a65952f2b4dc7e03..ada0d0bf3463cb67f3cfceeb510ef7f162a9a2ea 100644 (file)
@@ -3346,14 +3346,12 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
        return err;
 }
 
-static int selinux_file_set_fowner(struct file *file)
+static void selinux_file_set_fowner(struct file *file)
 {
        struct file_security_struct *fsec;
 
        fsec = file->f_security;
        fsec->fown_sid = current_sid();
-
-       return 0;
 }
 
 static int selinux_file_send_sigiotask(struct task_struct *tsk,
index e6ab307ce86e2fc1d1d04d35d2869fe1385ea724..69e5635d89e55f2e7d7007b0b7253ef3ef659eb6 100644 (file)
@@ -1390,12 +1390,11 @@ static int smack_mmap_file(struct file *file,
  * Returns 0
  * Further research may be required on this one.
  */
-static int smack_file_set_fowner(struct file *file)
+static void smack_file_set_fowner(struct file *file)
 {
        struct smack_known *skp = smk_of_current();
 
        file->f_security = skp->smk_known;
-       return 0;
 }
 
 /**
index 30e027ecf4da7383c7e6045f0b9a9f9905ca63b4..f2e8226c88fbb962b68fdab5ff40cae30813e873 100644 (file)
@@ -145,6 +145,8 @@ EXPORT_SYMBOL(snd_pci_quirk_lookup_id);
 const struct snd_pci_quirk *
 snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list)
 {
+       if (!pci)
+               return NULL;
        return snd_pci_quirk_lookup_id(pci->subsystem_vendor,
                                       pci->subsystem_device,
                                       list);
index 43932e8dce669a57a0908ffbd57585a858d1616b..42ded997b223b7ece3d8535000d4defeea2ba8f5 100644 (file)
@@ -215,6 +215,7 @@ static char *snd_pcm_format_names[] = {
        FORMAT(G723_40_1B),
        FORMAT(DSD_U8),
        FORMAT(DSD_U16_LE),
+       FORMAT(DSD_U32_LE),
 };
 
 const char *snd_pcm_format_name(snd_pcm_format_t format)
@@ -698,6 +699,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
                }
                substream->group = &substream->self_group;
                spin_lock_init(&substream->self_group.lock);
+               mutex_init(&substream->self_group.mutex);
                INIT_LIST_HEAD(&substream->self_group.substreams);
                list_add_tail(&substream->link_list, &substream->self_group.substreams);
                atomic_set(&substream->mmap_count, 0);
index 0032278567ad8c0ecc4228a6c2f065d6cd4c85e8..dfc28542a007e6ef44f9ea670e3f1aec59032a0b 100644 (file)
@@ -1113,18 +1113,20 @@ int snd_interval_list(struct snd_interval *i, unsigned int count,
 
 EXPORT_SYMBOL(snd_interval_list);
 
-static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step)
+static int snd_interval_step(struct snd_interval *i, unsigned int step)
 {
        unsigned int n;
        int changed = 0;
-       n = (i->min - min) % step;
+       n = i->min % step;
        if (n != 0 || i->openmin) {
                i->min += step - n;
+               i->openmin = 0;
                changed = 1;
        }
-       n = (i->max - min) % step;
+       n = i->max % step;
        if (n != 0 || i->openmax) {
                i->max -= n;
+               i->openmax = 0;
                changed = 1;
        }
        if (snd_interval_checkempty(i)) {
@@ -1427,7 +1429,7 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
                                struct snd_pcm_hw_rule *rule)
 {
        unsigned long step = (unsigned long) rule->private;
-       return snd_interval_step(hw_param_interval(params, rule->var), 0, step);
+       return snd_interval_step(hw_param_interval(params, rule->var), step);
 }
 
 /**
index 2c6fd80e0bd1fd0042848bdc3e68bd2734c4a0c5..ae7a0feb3b76001f54555187c19343bce352f0c8 100644 (file)
@@ -148,6 +148,10 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
                .width = 16, .phys = 16, .le = 1, .signd = 0,
                .silence = { 0x69, 0x69 },
        },
+       [SNDRV_PCM_FORMAT_DSD_U32_LE] = {
+               .width = 32, .phys = 32, .le = 1, .signd = 0,
+               .silence = { 0x69, 0x69, 0x69, 0x69 },
+       },
        /* FIXME: the following three formats are not defined properly yet */
        [SNDRV_PCM_FORMAT_MPEG] = {
                .le = -1, .signd = -1,
index 8cd2f930ad0baf1db6646101b89e41f3d5947d57..85fe1a216225cdd14165b3c947d1bf184d23ecd8 100644 (file)
@@ -74,11 +74,68 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
  *
  */
 
-DEFINE_RWLOCK(snd_pcm_link_rwlock);
-EXPORT_SYMBOL(snd_pcm_link_rwlock);
-
+static DEFINE_RWLOCK(snd_pcm_link_rwlock);
 static DECLARE_RWSEM(snd_pcm_link_rwsem);
 
+void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
+{
+       if (substream->pcm->nonatomic) {
+               down_read(&snd_pcm_link_rwsem);
+               mutex_lock(&substream->self_group.mutex);
+       } else {
+               read_lock(&snd_pcm_link_rwlock);
+               spin_lock(&substream->self_group.lock);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
+
+void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
+{
+       if (substream->pcm->nonatomic) {
+               mutex_unlock(&substream->self_group.mutex);
+               up_read(&snd_pcm_link_rwsem);
+       } else {
+               spin_unlock(&substream->self_group.lock);
+               read_unlock(&snd_pcm_link_rwlock);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
+
+void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
+{
+       if (!substream->pcm->nonatomic)
+               local_irq_disable();
+       snd_pcm_stream_lock(substream);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
+
+void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
+{
+       snd_pcm_stream_unlock(substream);
+       if (!substream->pcm->nonatomic)
+               local_irq_enable();
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq);
+
+unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
+{
+       unsigned long flags = 0;
+       if (!substream->pcm->nonatomic)
+               local_irq_save(flags);
+       snd_pcm_stream_lock(substream);
+       return flags;
+}
+EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
+
+void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
+                                     unsigned long flags)
+{
+       snd_pcm_stream_unlock(substream);
+       if (!substream->pcm->nonatomic)
+               local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
+
 static inline mm_segment_t snd_enter_user(void)
 {
        mm_segment_t fs = get_fs();
@@ -727,9 +784,14 @@ static int snd_pcm_action_group(struct action_ops *ops,
        int res = 0;
 
        snd_pcm_group_for_each_entry(s, substream) {
-               if (do_lock && s != substream)
-                       spin_lock_nested(&s->self_group.lock,
-                                        SINGLE_DEPTH_NESTING);
+               if (do_lock && s != substream) {
+                       if (s->pcm->nonatomic)
+                               mutex_lock_nested(&s->self_group.mutex,
+                                                 SINGLE_DEPTH_NESTING);
+                       else
+                               spin_lock_nested(&s->self_group.lock,
+                                                SINGLE_DEPTH_NESTING);
+               }
                res = ops->pre_action(s, state);
                if (res < 0)
                        goto _unlock;
@@ -755,8 +817,12 @@ static int snd_pcm_action_group(struct action_ops *ops,
        if (do_lock) {
                /* unlock streams */
                snd_pcm_group_for_each_entry(s1, substream) {
-                       if (s1 != substream)
-                               spin_unlock(&s1->self_group.lock);
+                       if (s1 != substream) {
+                               if (s->pcm->nonatomic)
+                                       mutex_unlock(&s1->self_group.mutex);
+                               else
+                                       spin_unlock(&s1->self_group.lock);
+                       }
                        if (s1 == s)    /* end */
                                break;
                }
@@ -784,6 +850,27 @@ static int snd_pcm_action_single(struct action_ops *ops,
        return res;
 }
 
+/* call in mutex-protected context */
+static int snd_pcm_action_mutex(struct action_ops *ops,
+                               struct snd_pcm_substream *substream,
+                               int state)
+{
+       int res;
+
+       if (snd_pcm_stream_linked(substream)) {
+               if (!mutex_trylock(&substream->group->mutex)) {
+                       mutex_unlock(&substream->self_group.mutex);
+                       mutex_lock(&substream->group->mutex);
+                       mutex_lock(&substream->self_group.mutex);
+               }
+               res = snd_pcm_action_group(ops, substream, state, 1);
+               mutex_unlock(&substream->group->mutex);
+       } else {
+               res = snd_pcm_action_single(ops, substream, state);
+       }
+       return res;
+}
+
 /*
  *  Note: call with stream lock
  */
@@ -793,6 +880,9 @@ static int snd_pcm_action(struct action_ops *ops,
 {
        int res;
 
+       if (substream->pcm->nonatomic)
+               return snd_pcm_action_mutex(ops, substream, state);
+
        if (snd_pcm_stream_linked(substream)) {
                if (!spin_trylock(&substream->group->lock)) {
                        spin_unlock(&substream->self_group.lock);
@@ -807,6 +897,29 @@ static int snd_pcm_action(struct action_ops *ops,
        return res;
 }
 
+static int snd_pcm_action_lock_mutex(struct action_ops *ops,
+                                    struct snd_pcm_substream *substream,
+                                    int state)
+{
+       int res;
+
+       down_read(&snd_pcm_link_rwsem);
+       if (snd_pcm_stream_linked(substream)) {
+               mutex_lock(&substream->group->mutex);
+               mutex_lock_nested(&substream->self_group.mutex,
+                                 SINGLE_DEPTH_NESTING);
+               res = snd_pcm_action_group(ops, substream, state, 1);
+               mutex_unlock(&substream->self_group.mutex);
+               mutex_unlock(&substream->group->mutex);
+       } else {
+               mutex_lock(&substream->self_group.mutex);
+               res = snd_pcm_action_single(ops, substream, state);
+               mutex_unlock(&substream->self_group.mutex);
+       }
+       up_read(&snd_pcm_link_rwsem);
+       return res;
+}
+
 /*
  *  Note: don't use any locks before
  */
@@ -816,6 +929,9 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops,
 {
        int res;
 
+       if (substream->pcm->nonatomic)
+               return snd_pcm_action_lock_mutex(ops, substream, state);
+
        read_lock_irq(&snd_pcm_link_rwlock);
        if (snd_pcm_stream_linked(substream)) {
                spin_lock(&substream->group->lock);
@@ -1634,7 +1750,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
        down_write(&snd_pcm_link_rwsem);
        write_lock_irq(&snd_pcm_link_rwlock);
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
-           substream->runtime->status->state != substream1->runtime->status->state) {
+           substream->runtime->status->state != substream1->runtime->status->state ||
+           substream->pcm->nonatomic != substream1->pcm->nonatomic) {
                res = -EBADFD;
                goto _end;
        }
@@ -1646,6 +1763,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
                substream->group = group;
                group = NULL;
                spin_lock_init(&substream->group->lock);
+               mutex_init(&substream->group->mutex);
                INIT_LIST_HEAD(&substream->group->substreams);
                list_add_tail(&substream->link_list, &substream->group->substreams);
                substream->group->count = 1;
index 83596891cde4f1d6b1cb9fb7115d4646265bf85e..e8cc169939038a4512172ad5cd61bdb8c6969d2e 100644 (file)
@@ -117,7 +117,7 @@ static int vx_reset_chk(struct vx_core *chip)
  *
  * returns 0 if successful, or a negative error code.
  * the error code can be VX-specific, retrieved via vx_get_error().
- * NB: call with spinlock held!
+ * NB: call with mutex held!
  */
 static int vx_transfer_end(struct vx_core *chip, int cmd)
 {
@@ -155,7 +155,7 @@ static int vx_transfer_end(struct vx_core *chip, int cmd)
  *
  * returns 0 if successful, or a negative error code.
  * the error code can be VX-specific, retrieved via vx_get_error().
- * NB: call with spinlock held!
+ * NB: call with mutex held!
  */
 static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
 {
@@ -236,7 +236,7 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
  * returns 0 if successful, or a negative error code.
  * the error code can be VX-specific, retrieved via vx_get_error().
  * 
- * this function doesn't call spinlock at all.
+ * this function doesn't call mutex lock at all.
  */
 int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
 {
@@ -337,7 +337,7 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
 
 
 /*
- * vx_send_msg - send a DSP message with spinlock
+ * vx_send_msg - send a DSP message with mutex
  * @rmh: the rmh record to send and receive
  *
  * returns 0 if successful, or a negative error code.
@@ -345,12 +345,11 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
  */
 int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh)
 {
-       unsigned long flags;
        int err;
 
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        err = vx_send_msg_nolock(chip, rmh);
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
        return err;
 }
 
@@ -362,7 +361,7 @@ int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh)
  * returns 0 if successful, or a negative error code.
  * the error code can be VX-specific, retrieved via vx_get_error().
  *
- * this function doesn't call spinlock at all.
+ * this function doesn't call mutex at all.
  *
  * unlike RMH, no command is sent to DSP.
  */
@@ -398,19 +397,18 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd)
 
 
 /*
- * vx_send_rih - send an RIH with spinlock
+ * vx_send_rih - send an RIH with mutex
  * @cmd: the command to send
  *
  * see vx_send_rih_nolock().
  */
 int vx_send_rih(struct vx_core *chip, int cmd)
 {
-       unsigned long flags;
        int err;
 
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        err = vx_send_rih_nolock(chip, cmd);
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
        return err;
 }
 
@@ -482,30 +480,30 @@ static int vx_test_irq_src(struct vx_core *chip, unsigned int *ret)
        int err;
 
        vx_init_rmh(&chip->irq_rmh, CMD_TEST_IT);
-       spin_lock(&chip->lock);
+       mutex_lock(&chip->lock);
        err = vx_send_msg_nolock(chip, &chip->irq_rmh);
        if (err < 0)
                *ret = 0;
        else
                *ret = chip->irq_rmh.Stat[0];
-       spin_unlock(&chip->lock);
+       mutex_unlock(&chip->lock);
        return err;
 }
 
 
 /*
- * vx_interrupt - soft irq handler
+ * snd_vx_threaded_irq_handler - threaded irq handler
  */
-static void vx_interrupt(unsigned long private_data)
+irqreturn_t snd_vx_threaded_irq_handler(int irq, void *dev)
 {
-       struct vx_core *chip = (struct vx_core *) private_data;
+       struct vx_core *chip = dev;
        unsigned int events;
                
        if (chip->chip_status & VX_STAT_IS_STALE)
-               return;
+               return IRQ_HANDLED;
 
        if (vx_test_irq_src(chip, &events) < 0)
-               return;
+               return IRQ_HANDLED;
     
 #if 0
        if (events & 0x000800)
@@ -519,7 +517,7 @@ static void vx_interrupt(unsigned long private_data)
         */
        if (events & FATAL_DSP_ERROR) {
                snd_printk(KERN_ERR "vx_core: fatal DSP error!!\n");
-               return;
+               return IRQ_HANDLED;
        }
 
        /* The start on time code conditions are filled (ie the time code
@@ -534,8 +532,9 @@ static void vx_interrupt(unsigned long private_data)
 
        /* update the pcm streams */
        vx_pcm_update_intr(chip, events);
+       return IRQ_HANDLED;
 }
-
+EXPORT_SYMBOL(snd_vx_threaded_irq_handler);
 
 /**
  * snd_vx_irq_handler - interrupt handler
@@ -548,8 +547,8 @@ irqreturn_t snd_vx_irq_handler(int irq, void *dev)
            (chip->chip_status & VX_STAT_IS_STALE))
                return IRQ_NONE;
        if (! vx_test_and_ack(chip))
-               tasklet_schedule(&chip->tq);
-       return IRQ_HANDLED;
+               return IRQ_WAKE_THREAD;
+       return IRQ_NONE;
 }
 
 EXPORT_SYMBOL(snd_vx_irq_handler);
@@ -790,13 +789,11 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
                snd_printk(KERN_ERR "vx_core: no memory\n");
                return NULL;
        }
-       spin_lock_init(&chip->lock);
-       spin_lock_init(&chip->irq_lock);
+       mutex_init(&chip->lock);
        chip->irq = -1;
        chip->hw = hw;
        chip->type = hw->type;
        chip->ops = ops;
-       tasklet_init(&chip->tq, vx_interrupt, (unsigned long)chip);
        mutex_init(&chip->mixer_mutex);
 
        chip->card = card;
index c71b8d148d7fa82324a18863da0b98459488582a..3b6823fc0606537d6659b2bb5541eb88165d1349 100644 (file)
  */
 static void vx_write_codec_reg(struct vx_core *chip, int codec, unsigned int data)
 {
-       unsigned long flags;
-
        if (snd_BUG_ON(!chip->ops->write_codec))
                return;
 
        if (chip->chip_status & VX_STAT_IS_STALE)
                return;
 
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        chip->ops->write_codec(chip, codec, data);
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
 }
 
 /*
@@ -178,14 +176,12 @@ void vx_reset_codec(struct vx_core *chip, int cold_reset)
  */
 static void vx_change_audio_source(struct vx_core *chip, int src)
 {
-       unsigned long flags;
-
        if (chip->chip_status & VX_STAT_IS_STALE)
                return;
 
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        chip->ops->change_audio_source(chip, src);
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
 }
 
 
index deed5efff33c9c57c2d462f7d05de89dc31bf2d7..11467272089e45632935912c4e2b94f8cb604978 100644 (file)
@@ -229,7 +229,7 @@ static int vx_get_pipe_state(struct vx_core *chip, struct vx_pipe *pipe, int *st
 
        vx_init_rmh(&rmh, CMD_PIPE_STATE);
        vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
-       err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+       err = vx_send_msg(chip, &rmh);
        if (! err)
                *state = (rmh.Stat[0] & (1 << pipe->number)) ? 1 : 0;
        return err;
@@ -280,7 +280,7 @@ static int vx_pipe_can_start(struct vx_core *chip, struct vx_pipe *pipe)
        vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
        rmh.Cmd[0] |= 1;
 
-       err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+       err = vx_send_msg(chip, &rmh);
        if (! err) {
                if (rmh.Stat[0])
                        err = 1;
@@ -300,7 +300,7 @@ static int vx_conf_pipe(struct vx_core *chip, struct vx_pipe *pipe)
        if (pipe->is_capture)
                rmh.Cmd[0] |= COMMAND_RECORD_MASK;
        rmh.Cmd[1] = 1 << pipe->number;
-       return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+       return vx_send_msg(chip, &rmh);
 }
 
 /*
@@ -311,7 +311,7 @@ static int vx_send_irqa(struct vx_core *chip)
        struct vx_rmh rmh;
 
        vx_init_rmh(&rmh, CMD_SEND_IRQA);
-       return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+       return vx_send_msg(chip, &rmh);
 }
 
 
@@ -389,7 +389,7 @@ static int vx_stop_pipe(struct vx_core *chip, struct vx_pipe *pipe)
        struct vx_rmh rmh;
        vx_init_rmh(&rmh, CMD_STOP_PIPE);
        vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
-       return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+       return vx_send_msg(chip, &rmh);
 }
 
 
@@ -477,7 +477,7 @@ static int vx_start_stream(struct vx_core *chip, struct vx_pipe *pipe)
        vx_init_rmh(&rmh, CMD_START_ONE_STREAM);
        vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number);
        vx_set_differed_time(chip, &rmh, pipe);
-       return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+       return vx_send_msg(chip, &rmh);
 }
 
 
@@ -492,7 +492,7 @@ static int vx_stop_stream(struct vx_core *chip, struct vx_pipe *pipe)
 
        vx_init_rmh(&rmh, CMD_STOP_STREAM);
        vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number);
-       return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+       return vx_send_msg(chip, &rmh);
 }
 
 
@@ -520,8 +520,6 @@ static struct snd_pcm_hardware vx_pcm_playback_hw = {
 };
 
 
-static void vx_pcm_delayed_start(unsigned long arg);
-
 /*
  * vx_pcm_playback_open - open callback for playback
  */
@@ -553,7 +551,6 @@ static int vx_pcm_playback_open(struct snd_pcm_substream *subs)
        pipe->references++;
 
        pipe->substream = subs;
-       tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs);
        chip->playback_pipes[audio] = pipe;
 
        runtime->hw = vx_pcm_playback_hw;
@@ -646,12 +643,12 @@ static int vx_pcm_playback_transfer_chunk(struct vx_core *chip,
        /* we don't need irqsave here, because this function
         * is called from either trigger callback or irq handler
         */
-       spin_lock(&chip->lock); 
+       mutex_lock(&chip->lock);
        vx_pseudo_dma_write(chip, runtime, pipe, size);
        err = vx_notify_end_of_buffer(chip, pipe);
        /* disconnect the host, SIZE_HBUF command always switches to the stream mode */
        vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
-       spin_unlock(&chip->lock);
+       mutex_unlock(&chip->lock);
        return err;
 }
 
@@ -727,31 +724,6 @@ static void vx_pcm_playback_update(struct vx_core *chip,
        }
 }
 
-/*
- * start the stream and pipe.
- * this function is called from tasklet, which is invoked by the trigger
- * START callback.
- */
-static void vx_pcm_delayed_start(unsigned long arg)
-{
-       struct snd_pcm_substream *subs = (struct snd_pcm_substream *)arg;
-       struct vx_core *chip = subs->pcm->private_data;
-       struct vx_pipe *pipe = subs->runtime->private_data;
-       int err;
-
-       /*  printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/
-
-       if ((err = vx_start_stream(chip, pipe)) < 0) {
-               snd_printk(KERN_ERR "vx: cannot start stream\n");
-               return;
-       }
-       if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) {
-               snd_printk(KERN_ERR "vx: cannot start pipe\n");
-               return;
-       }
-       /*   printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/
-}
-
 /*
  * vx_pcm_playback_trigger - trigger callback for playback
  */
@@ -769,11 +741,17 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
        case SNDRV_PCM_TRIGGER_RESUME:
                if (! pipe->is_capture)
                        vx_pcm_playback_transfer(chip, subs, pipe, 2);
-               /* FIXME:
-                * we trigger the pipe using tasklet, so that the interrupts are
-                * issued surely after the trigger is completed.
-                */ 
-               tasklet_schedule(&pipe->start_tq);
+               err = vx_start_stream(chip, pipe);
+               if (err < 0) {
+                       pr_debug("vx: cannot start stream\n");
+                       return err;
+               }
+               err = vx_toggle_pipe(chip, pipe, 1);
+               if (err < 0) {
+                       pr_debug("vx: cannot start pipe\n");
+                       vx_stop_stream(chip, pipe);
+                       return err;
+               }
                chip->pcm_running++;
                pipe->running = 1;
                break;
@@ -955,7 +933,6 @@ static int vx_pcm_capture_open(struct snd_pcm_substream *subs)
        if (err < 0)
                return err;
        pipe->substream = subs;
-       tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs);
        chip->capture_pipes[audio] = pipe;
 
        /* check if monitoring is needed */
@@ -1082,7 +1059,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
                count -= 3;
        }
        /* disconnect the host, SIZE_HBUF command always switches to the stream mode */
-       vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
+       vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
        /* read the last pending 6 bytes */
        count = DMA_READ_ALIGN;
        while (count > 0) {
@@ -1099,7 +1076,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
 
  _error:
        /* disconnect the host, SIZE_HBUF command always switches to the stream mode */
-       vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
+       vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
        return;
 }
 
@@ -1275,6 +1252,7 @@ int snd_vx_pcm_new(struct vx_core *chip)
                pcm->private_data = chip;
                pcm->private_free = snd_vx_pcm_free;
                pcm->info_flags = 0;
+               pcm->nonatomic = true;
                strcpy(pcm->name, chip->card->shortname);
                chip->pcm[i] = pcm;
        }
index b0560fec6bba68fb8dc516a92c84263101ed98c2..ef0b40c0a594274b097c3bdaa7dd6e3a11698b89 100644 (file)
@@ -60,9 +60,9 @@ static int vx_modify_board_inputs(struct vx_core *chip)
  */
 static int vx_read_one_cbit(struct vx_core *chip, int index)
 {
-       unsigned long flags;
        int val;
-       spin_lock_irqsave(&chip->lock, flags);
+
+       mutex_lock(&chip->lock);
        if (chip->type >= VX_TYPE_VXPOCKET) {
                vx_outb(chip, CSUER, 1); /* read */
                vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
@@ -72,7 +72,7 @@ static int vx_read_one_cbit(struct vx_core *chip, int index)
                vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
                val = (vx_inl(chip, RUER) >> 7) & 0x01;
        }
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
        return val;
 }
 
@@ -83,9 +83,8 @@ static int vx_read_one_cbit(struct vx_core *chip, int index)
  */
 static void vx_write_one_cbit(struct vx_core *chip, int index, int val)
 {
-       unsigned long flags;
        val = !!val;    /* 0 or 1 */
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        if (vx_is_pcmcia(chip)) {
                vx_outb(chip, CSUER, 0); /* write */
                vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
@@ -93,7 +92,7 @@ static void vx_write_one_cbit(struct vx_core *chip, int index, int val)
                vx_outl(chip, CSUER, 0); /* write */
                vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
        }
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
 }
 
 /*
@@ -190,14 +189,12 @@ static int vx_calc_clock_from_freq(struct vx_core *chip, int freq)
  */
 static void vx_change_clock_source(struct vx_core *chip, int source)
 {
-       unsigned long flags;
-
        /* we mute DAC to prevent clicks */
        vx_toggle_dac_mute(chip, 1);
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        chip->ops->set_clock_source(chip, source);
        chip->clock_source = source;
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
        /* unmute */
        vx_toggle_dac_mute(chip, 0);
 }
@@ -209,11 +206,11 @@ static void vx_change_clock_source(struct vx_core *chip, int source)
 void vx_set_internal_clock(struct vx_core *chip, unsigned int freq)
 {
        int clock;
-       unsigned long flags;
+
        /* Get real clock value */
        clock = vx_calc_clock_from_freq(chip, freq);
        snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq);
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        if (vx_is_pcmcia(chip)) {
                vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f);
                vx_outb(chip, LOFREQ, clock & 0xff);
@@ -221,7 +218,7 @@ void vx_set_internal_clock(struct vx_core *chip, unsigned int freq)
                vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f);
                vx_outl(chip, LOFREQ, clock & 0xff);
        }
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
 }
 
 
index afb1b44b741e0d31c51ad0afa92150cb9bf0ec54..21ce31f636bc55c939970be2bcdbcc9953500680 100644 (file)
@@ -48,10 +48,10 @@ static void vortex_fix_latency(struct pci_dev *vortex)
 {
        int rc;
        if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) {
-                       printk(KERN_INFO CARD_NAME
+                       pr_info( CARD_NAME
                               ": vortex latency is 0xff\n");
        } else {
-               printk(KERN_WARNING CARD_NAME
+               pr_warn( CARD_NAME
                                ": could not set vortex latency: pci error 0x%x\n", rc);
        }
 }
@@ -70,10 +70,10 @@ static void vortex_fix_agp_bridge(struct pci_dev *via)
        if (!(rc = pci_read_config_byte(via, 0x42, &value))
                        && ((value & 0x10)
                                || !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) {
-               printk(KERN_INFO CARD_NAME
+               pr_info( CARD_NAME
                                ": bridge config is 0x%x\n", value | 0x10);
        } else {
-               printk(KERN_WARNING CARD_NAME
+               pr_warn( CARD_NAME
                                ": could not set vortex latency: pci error 0x%x\n", rc);
        }
 }
@@ -97,7 +97,7 @@ static void snd_vortex_workaround(struct pci_dev *vortex, int fix)
                                        PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
                }
                if (via) {
-                       printk(KERN_INFO CARD_NAME ": Activating latency workaround...\n");
+                       pr_info( CARD_NAME ": Activating latency workaround...\n");
                        vortex_fix_latency(vortex);
                        vortex_fix_agp_bridge(via);
                }
@@ -153,7 +153,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
                return err;
        if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
            pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
-               printk(KERN_ERR "error to set DMA mask\n");
+               pr_err( "error to set DMA mask\n");
                pci_disable_device(pci);
                return -ENXIO;
        }
@@ -182,7 +182,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
 
        chip->mmio = pci_ioremap_bar(pci, 0);
        if (!chip->mmio) {
-               printk(KERN_ERR "MMIO area remap failed.\n");
+               pr_err( "MMIO area remap failed.\n");
                err = -ENOMEM;
                goto ioremap_out;
        }
@@ -191,14 +191,14 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
         * This must be done before we do request_irq otherwise we can get spurious
         * interrupts that we do not handle properly and make a mess of things */
        if ((err = vortex_core_init(chip)) != 0) {
-               printk(KERN_ERR "hw core init failed\n");
+               pr_err( "hw core init failed\n");
                goto core_out;
        }
 
        if ((err = request_irq(pci->irq, vortex_interrupt,
                               IRQF_SHARED, KBUILD_MODNAME,
                               chip)) != 0) {
-               printk(KERN_ERR "cannot grab irq\n");
+               pr_err( "cannot grab irq\n");
                goto irq_out;
        }
        chip->irq = pci->irq;
@@ -342,10 +342,10 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
        chip->rev = pci->revision;
 #ifdef CHIP_AU8830
        if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) {
-               printk(KERN_ALERT
+               pr_alert(
                       "vortex: The revision (%x) of your card has not been seen before.\n",
                       chip->rev);
-               printk(KERN_ALERT
+               pr_alert(
                       "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
                snd_card_free(card);
                err = -ENODEV;
index aad831acbb170f282e1287863037ff81095b14d2..30f760e3d2c0100df9a04341d531d4b3c29b3b68 100644 (file)
@@ -463,7 +463,7 @@ static void a3dsrc_ZeroSliceIO(a3dsrc_t * a)
 static void a3dsrc_ZeroState(a3dsrc_t * a)
 {
        /*
-       printk(KERN_DEBUG "vortex: ZeroState slice: %d, source %d\n",
+       pr_debug( "vortex: ZeroState slice: %d, source %d\n",
               a->slice, a->source);
        */
        a3dsrc_SetAtmosState(a, 0, 0, 0, 0);
@@ -489,7 +489,7 @@ static void a3dsrc_ZeroStateA3D(a3dsrc_t * a)
        int i, var, var2;
 
        if ((a->vortex) == NULL) {
-               printk(KERN_ERR "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n");
+               pr_err( "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n");
                return;
        }
 
@@ -628,14 +628,14 @@ static void vortex_Vort3D_connect(vortex_t * v, int en)
        v->mixxtlk[0] =
            vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
        if (v->mixxtlk[0] < 0) {
-               printk
+               pr_warn
                    ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
                return;
        }
        v->mixxtlk[1] =
            vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
        if (v->mixxtlk[1] < 0) {
-               printk
+               pr_warn
                    ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
                return;
        }
@@ -679,7 +679,7 @@ static void vortex_Vort3D_connect(vortex_t * v, int en)
 static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en)
 {
        if (a->vortex == NULL) {
-               printk
+               pr_warn
                    ("vortex: Vort3D_InitializeSource: A3D source not initialized\n");
                return;
        }
index ae59dbaa53d919dcdc12de63f24cd1cddbebe3d9..72e81286b70eefcce14f05aaf1ae4fdb83658c74 100644 (file)
@@ -285,7 +285,7 @@ vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
                temp = hwread(vortex->mmio, prev);
                //printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp);
                if ((++lifeboat) > 0xf) {
-                       printk(KERN_ERR
+                       pr_err(
                               "vortex_mixer_addWTD: lifeboat overflow\n");
                        return 0;
                }
@@ -303,7 +303,7 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
 
        eax = hwread(vortex->mmio, VORTEX_MIXER_SR);
        if (((1 << ch) & eax) == 0) {
-               printk(KERN_ERR "mix ALARM %x\n", eax);
+               pr_err( "mix ALARM %x\n", eax);
                return 0;
        }
        ebp = VORTEX_MIXER_CHNBASE + (ch << 2);
@@ -324,7 +324,7 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
                        //printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
                        while ((edx & 0xf) != mix) {
                                if ((esi) > 0xf) {
-                                       printk(KERN_ERR
+                                       pr_err(
                                               "vortex: mixdelWTD: error lifeboat overflow\n");
                                        return 0;
                                }
@@ -492,7 +492,7 @@ vortex_src_persist_convratio(vortex_t * vortex, unsigned char src, int ratio)
                hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio);
                temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
                if ((++lifeboat) > 0x9) {
-                       printk(KERN_ERR "Vortex: Src cvr fail\n");
+                       pr_err( "Vortex: Src cvr fail\n");
                        break;
                }
        }
@@ -545,7 +545,7 @@ vortex_src_checkratio(vortex_t * vortex, unsigned char src,
                hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), desired_ratio);
 
                if ((lifeboat++) > 15) {
-                       printk(KERN_ERR "Vortex: could not set src-%d from %d to %d\n",
+                       pr_err( "Vortex: could not set src-%d from %d to %d\n",
                               src, hw_ratio, desired_ratio);
                        break;
                }
@@ -684,7 +684,7 @@ vortex_src_addWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
                temp = hwread(vortex->mmio, prev);
                //printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp);
                if ((++lifeboat) > 0xf) {
-                       printk(KERN_ERR
+                       pr_err(
                               "vortex_src_addWTD: lifeboat overflow\n");
                        return 0;
                }
@@ -703,7 +703,7 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
 
        eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
        if (((1 << ch) & eax) == 0) {
-               printk(KERN_ERR "src alarm\n");
+               pr_err( "src alarm\n");
                return 0;
        }
        ebp = VORTEX_SRC_CHNBASE + (ch << 2);
@@ -724,7 +724,7 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
                        //printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
                        while ((edx & 0xf) != src) {
                                if ((esi) > 0xf) {
-                                       printk
+                                       pr_warn
                                            ("vortex: srcdelWTD: error, lifeboat overflow\n");
                                        return 0;
                                }
@@ -819,7 +819,7 @@ vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int stereo, int priority,
        do {
                temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
                if (lifeboat++ > 0xbb8) {
-                       printk(KERN_ERR
+                       pr_err(
                               "Vortex: vortex_fifo_setadbctrl fail\n");
                        break;
                }
@@ -915,7 +915,7 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority,
        do {
                temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
                if (lifeboat++ > 0xbb8) {
-                       printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail\n");
+                       pr_err( "Vortex: vortex_fifo_setwtctrl fail\n");
                        break;
                }
        }
@@ -970,7 +970,7 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority,
     do {
                temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
                if (lifeboat++ > 0xbb8) {
-                       printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail (hanging)\n");
+                       pr_err( "Vortex: vortex_fifo_setwtctrl fail (hanging)\n");
                        break;
                }
     } while ((temp & FIFO_RDONLY)&&(temp & FIFO_VALID)&&(temp != 0xFFFFFFFF));
@@ -1042,7 +1042,7 @@ static void vortex_fifo_init(vortex_t * vortex)
        for (x = NR_ADB - 1; x >= 0; x--) {
                hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1));
                if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1))
-                       printk(KERN_ERR "bad adb fifo reset!");
+                       pr_err( "bad adb fifo reset!");
                vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE);
                addr -= 4;
        }
@@ -1053,7 +1053,7 @@ static void vortex_fifo_init(vortex_t * vortex)
        for (x = NR_WT - 1; x >= 0; x--) {
                hwwrite(vortex->mmio, addr, FIFO_U0);
                if (hwread(vortex->mmio, addr) != FIFO_U0)
-                       printk(KERN_ERR
+                       pr_err(
                               "bad wt fifo reset (0x%08x, 0x%08x)!\n",
                               addr, hwread(vortex->mmio, addr));
                vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE);
@@ -1136,7 +1136,7 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
                break;
        }
        /*
-       printk(KERN_DEBUG "vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n",
+       pr_debug( "vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n",
               dma->cfg0, dma->cfg1);
        */
        hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG0 + (adbdma << 3), dma->cfg0);
@@ -1213,7 +1213,7 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
        if (dma->period_virt >= dma->nr_periods)
                dma->period_virt -= dma->nr_periods;
        if (delta != 1)
-               printk(KERN_INFO "vortex: %d virt=%d, real=%d, delta=%d\n",
+               pr_info( "vortex: %d virt=%d, real=%d, delta=%d\n",
                       adbdma, dma->period_virt, dma->period_real, delta);
 
        return delta;
@@ -1482,7 +1482,7 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
        dma->period_real = page;
 
        if (delta != 1)
-               printk(KERN_WARNING "vortex: wt virt = %d, delta = %d\n",
+               pr_warn( "vortex: wt virt = %d, delta = %d\n",
                       dma->period_virt, delta);
 
        return delta;
@@ -1667,7 +1667,7 @@ vortex_adb_addroutes(vortex_t * vortex, unsigned char channel,
                    hwread(vortex->mmio,
                           VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK;
                if ((lifeboat++) > ADB_MASK) {
-                       printk(KERN_ERR
+                       pr_err(
                               "vortex_adb_addroutes: unending route! 0x%x\n",
                               *route);
                        return;
@@ -1703,7 +1703,7 @@ vortex_adb_delroutes(vortex_t * vortex, unsigned char channel,
                    hwread(vortex->mmio,
                           VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK;
                if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) {
-                       printk(KERN_ERR
+                       pr_err(
                               "vortex_adb_delroutes: route not found! 0x%x\n",
                               route0);
                        return;
@@ -1967,7 +1967,7 @@ vortex_connect_codecplay(vortex_t * vortex, int en, unsigned char mixers[])
                                          ADB_CODECOUT(0 + 4));
                vortex_connection_mix_adb(vortex, en, 0x11, mixers[3],
                                          ADB_CODECOUT(1 + 4));
-               /* printk(KERN_DEBUG "SDAC detected "); */
+               /* pr_debug( "SDAC detected "); */
        }
 #else
        // Use plain direct output to codec.
@@ -2022,7 +2022,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
                                else
                                        vortex->dma_adb[i].resources[restype] |= (1 << i);
                                /*
-                               printk(KERN_DEBUG
+                               pr_debug(
                                       "vortex: ResManager: type %d out %d\n",
                                       restype, i);
                                */
@@ -2037,7 +2037,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
                        if (resmap[restype] & (1 << i)) {
                                resmap[restype] &= ~(1 << i);
                                /*
-                               printk(KERN_DEBUG
+                               pr_debug(
                                       "vortex: ResManager: type %d in %d\n",
                                       restype, i);
                                */
@@ -2045,7 +2045,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
                        }
                }
        }
-       printk(KERN_ERR "vortex: FATAL: ResManager: resource type %d exhausted.\n", restype);
+       pr_err( "vortex: FATAL: ResManager: resource type %d exhausted.\n", restype);
        return -ENOMEM;
 }
 
@@ -2173,7 +2173,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
                                memset(stream->resources, 0,
                                       sizeof(unsigned char) *
                                       VORTEX_RESOURCE_LAST);
-                               printk(KERN_ERR "vortex: out of A3D sources. Sorry\n");
+                               pr_err( "vortex: out of A3D sources. Sorry\n");
                                return -EBUSY;
                        }
                        /* (De)Initialize A3D hardware source. */
@@ -2421,7 +2421,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
        hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
        // Is at least one IRQ flag set?
        if (source == 0) {
-               printk(KERN_ERR "vortex: missing irq source\n");
+               pr_err( "vortex: missing irq source\n");
                return IRQ_NONE;
        }
 
@@ -2429,19 +2429,19 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
        // Attend every interrupt source.
        if (unlikely(source & IRQ_ERR_MASK)) {
                if (source & IRQ_FATAL) {
-                       printk(KERN_ERR "vortex: IRQ fatal error\n");
+                       pr_err( "vortex: IRQ fatal error\n");
                }
                if (source & IRQ_PARITY) {
-                       printk(KERN_ERR "vortex: IRQ parity error\n");
+                       pr_err( "vortex: IRQ parity error\n");
                }
                if (source & IRQ_REG) {
-                       printk(KERN_ERR "vortex: IRQ reg error\n");
+                       pr_err( "vortex: IRQ reg error\n");
                }
                if (source & IRQ_FIFO) {
-                       printk(KERN_ERR "vortex: IRQ fifo error\n");
+                       pr_err( "vortex: IRQ fifo error\n");
                }
                if (source & IRQ_DMA) {
-                       printk(KERN_ERR "vortex: IRQ dma error\n");
+                       pr_err( "vortex: IRQ dma error\n");
                }
                handled = 1;
        }
@@ -2489,7 +2489,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
        }
 
        if (!handled) {
-               printk(KERN_ERR "vortex: unknown irq source %x\n", source);
+               pr_err( "vortex: unknown irq source %x\n", source);
        }
        return IRQ_RETVAL(handled);
 }
@@ -2546,7 +2546,7 @@ vortex_codec_write(struct snd_ac97 * codec, unsigned short addr, unsigned short
        while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
                udelay(100);
                if (lifeboat++ > POLL_COUNT) {
-                       printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+                       pr_err( "vortex: ac97 codec stuck busy\n");
                        return;
                }
        }
@@ -2572,7 +2572,7 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short
        while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
                udelay(100);
                if (lifeboat++ > POLL_COUNT) {
-                       printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+                       pr_err( "vortex: ac97 codec stuck busy\n");
                        return 0xffff;
                }
        }
@@ -2586,7 +2586,7 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short
                udelay(100);
                data = hwread(card->mmio, VORTEX_CODEC_IO);
                if (lifeboat++ > POLL_COUNT) {
-                       printk(KERN_ERR "vortex: ac97 address never arrived\n");
+                       pr_err( "vortex: ac97 address never arrived\n");
                        return 0xffff;
                }
        } while ((data & VORTEX_CODEC_ADDMASK) !=
@@ -2683,7 +2683,7 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode)
 static int vortex_core_init(vortex_t *vortex)
 {
 
-       printk(KERN_INFO "Vortex: init.... ");
+       pr_info( "Vortex: init.... ");
        /* Hardware Init. */
        hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff);
        msleep(5);
@@ -2728,7 +2728,7 @@ static int vortex_core_init(vortex_t *vortex)
        //vortex_enable_timer_int(vortex);
        //vortex_disable_timer_int(vortex);
 
-       printk(KERN_INFO "done.\n");
+       pr_info( "done.\n");
        spin_lock_init(&vortex->lock);
 
        return 0;
@@ -2737,7 +2737,7 @@ static int vortex_core_init(vortex_t *vortex)
 static int vortex_core_shutdown(vortex_t * vortex)
 {
 
-       printk(KERN_INFO "Vortex: shutdown...");
+       pr_info( "Vortex: shutdown...");
 #ifndef CHIP_AU8820
        vortex_eq_free(vortex);
        vortex_Vort3D_disable(vortex);
@@ -2759,7 +2759,7 @@ static int vortex_core_shutdown(vortex_t * vortex)
        msleep(5);
        hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff);
 
-       printk(KERN_INFO "done.\n");
+       pr_info( "done.\n");
        return 0;
 }
 
@@ -2793,7 +2793,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt)
                break;
        default:
                fmt = 0x8;
-               printk(KERN_ERR "vortex: format unsupported %d\n", alsafmt);
+               pr_err( "vortex: format unsupported %d\n", alsafmt);
                break;
        }
        return fmt;
index e7220533ecfc79a46f9ab05f3d54d083216dc16d..9404ba73eaf6140ccdabf0576ab04f096fde94f9 100644 (file)
@@ -845,7 +845,7 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
 
        vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count);
        if (count != 20) {
-               printk(KERN_ERR "vortex: peak count error 20 != %d \n", count);
+               pr_err( "vortex: peak count error 20 != %d \n", count);
                return -1;
        }
        for (i = 0; i < 20; i++)
index 280f86de223086ab1b26d2639e12334ab2dceb37..72daf6cf81695602ce77efd7f7e7aeeab9069af2 100644 (file)
@@ -98,7 +98,7 @@ static int vortex_gameport_register(vortex_t *vortex)
 
        vortex->gameport = gp = gameport_allocate_port();
        if (!gp) {
-               printk(KERN_ERR "vortex: cannot allocate memory for gameport\n");
+               pr_err( "vortex: cannot allocate memory for gameport\n");
                return -ENOMEM;
        }
 
index 29e5945eef60dea7af07e4cea22202d68526b616..328c1943c0c341725a55c926229c40c23cad1668 100644 (file)
@@ -73,7 +73,7 @@ static int snd_vortex_midi(vortex_t *vortex)
        /* Check if anything is OK. */
        temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
        if (temp != MPU401_ACK /*0xfe */ ) {
-               printk(KERN_ERR "midi port doesn't acknowledge!\n");
+               pr_err( "midi port doesn't acknowledge!\n");
                return -ENODEV;
        }
        /* Enable MPU401 interrupts. */
index 9fb03b4ea925cb173e3e898950c9b4736c0ff787..5adc6b92ffabc9f893cc4b7f4c0b29f8428d5f96 100644 (file)
@@ -227,11 +227,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
        err =
            snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
        if (err < 0) {
-               printk(KERN_ERR "Vortex: pcm page alloc failed!\n");
+               pr_err( "Vortex: pcm page alloc failed!\n");
                return err;
        }
        /*
-          printk(KERN_INFO "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
+          pr_info( "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
           params_period_bytes(hw_params), params_channels(hw_params));
         */
        spin_lock_irq(&chip->lock);
@@ -371,7 +371,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                }
 #ifndef CHIP_AU8810
                else {
-                       printk(KERN_INFO "vortex: wt start %d\n", dma);
+                       pr_info( "vortex: wt start %d\n", dma);
                        vortex_wtdma_startfifo(chip, dma);
                }
 #endif
@@ -384,7 +384,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                        vortex_adbdma_stopfifo(chip, dma);
 #ifndef CHIP_AU8810
                else {
-                       printk(KERN_INFO "vortex: wt stop %d\n", dma);
+                       pr_info( "vortex: wt stop %d\n", dma);
                        vortex_wtdma_stopfifo(chip, dma);
                }
 #endif
index 922a84bba2ef4738006e02dcc1c3920525ce8ec6..f094bac24291297e2d82441247733a9e2d7f5f6b 100644 (file)
@@ -90,7 +90,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
        hwwrite(vortex->mmio, WT_PARM(wt, 2), 0);
 
        temp = hwread(vortex->mmio, WT_PARM(wt, 3));
-       printk(KERN_DEBUG "vortex: WT PARM3: %x\n", temp);
+       pr_debug( "vortex: WT PARM3: %x\n", temp);
        //hwwrite(vortex->mmio, WT_PARM(wt, 3), temp);
 
        hwwrite(vortex->mmio, WT_DELAY(wt, 0), 0);
@@ -98,7 +98,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
        hwwrite(vortex->mmio, WT_DELAY(wt, 2), 0);
        hwwrite(vortex->mmio, WT_DELAY(wt, 3), 0);
 
-       printk(KERN_DEBUG "vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+       pr_debug( "vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
 
        hwwrite(vortex->mmio, WT_PARM(wt, 2), 0xffffffff);
        hwwrite(vortex->mmio, WT_PARM(wt, 3), 0xcff1c810);
@@ -106,7 +106,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
        voice->parm0 = voice->parm1 = 0xcfb23e2f;
        hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0);
        hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1);
-       printk(KERN_DEBUG "vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+       pr_debug( "vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
        return 0;
 }
 
@@ -196,14 +196,14 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
 
        if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) {
                if (wt >= (NR_WT / NR_WT_PB)) {
-                       printk
+                       pr_warn
                            ("vortex: WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
                             reg, wt);
                        return 0;
                }
        } else {
                if (wt >= NR_WT) {
-                       printk(KERN_ERR "vortex: WT SetReg: voice out of range\n");
+                       pr_err( "vortex: WT SetReg: voice out of range\n");
                        return 0;
                }
        }
@@ -214,42 +214,42 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
                /* Voice specific parameters */
        case 0:         /* running */
                /*
-               printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+               pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
                       WT_RUN(wt), (int)val);
                */
                hwwrite(vortex->mmio, WT_RUN(wt), val);
                return 0xc;
        case 1:         /* param 0 */
                /*
-               printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+               pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
                       WT_PARM(wt,0), (int)val);
                */
                hwwrite(vortex->mmio, WT_PARM(wt, 0), val);
                return 0xc;
        case 2:         /* param 1 */
                /*
-               printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+               pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
                       WT_PARM(wt,1), (int)val);
                */
                hwwrite(vortex->mmio, WT_PARM(wt, 1), val);
                return 0xc;
        case 3:         /* param 2 */
                /*
-               printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+               pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
                       WT_PARM(wt,2), (int)val);
                */
                hwwrite(vortex->mmio, WT_PARM(wt, 2), val);
                return 0xc;
        case 4:         /* param 3 */
                /*
-               printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+               pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
                       WT_PARM(wt,3), (int)val);
                */
                hwwrite(vortex->mmio, WT_PARM(wt, 3), val);
                return 0xc;
        case 6:         /* mute */
                /*
-               printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+               pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
                       WT_MUTE(wt), (int)val);
                */
                hwwrite(vortex->mmio, WT_MUTE(wt), val);
@@ -257,7 +257,7 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
        case 0xb:
                        /* delay */
                /*
-               printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+               pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
                       WT_DELAY(wt,0), (int)val);
                */
                hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
@@ -285,7 +285,7 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
                return 0;
        }
        /*
-       printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
+       pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
        */
        hwwrite(vortex->mmio, ecx, val);
        return 1;
index fee35cfc0c7f0c62895aad3b9bb3748ab0df7fa7..c7dc38d41b7f4678691e5a492b93b5087bddddce 100644 (file)
@@ -258,7 +258,8 @@ static int get_amixer_rsc(struct amixer_mgr *mgr,
        }
        spin_unlock_irqrestore(&mgr->mgr_lock, flags);
        if (err) {
-               printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n");
+               dev_err(mgr->card->dev,
+                       "Can't meet AMIXER resource request!\n");
                goto error;
        }
 
@@ -296,7 +297,7 @@ static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
        return 0;
 }
 
-int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
+int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr)
 {
        int err;
        struct amixer_mgr *amixer_mgr;
@@ -314,6 +315,7 @@ int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
 
        amixer_mgr->get_amixer = get_amixer_rsc;
        amixer_mgr->put_amixer = put_amixer_rsc;
+       amixer_mgr->card = hw->card;
 
        *ramixer_mgr = amixer_mgr;
 
@@ -411,7 +413,8 @@ static int get_sum_rsc(struct sum_mgr *mgr,
        }
        spin_unlock_irqrestore(&mgr->mgr_lock, flags);
        if (err) {
-               printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n");
+               dev_err(mgr->card->dev,
+                       "Can't meet SUM resource request!\n");
                goto error;
        }
 
@@ -449,7 +452,7 @@ static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
        return 0;
 }
 
-int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
+int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr)
 {
        int err;
        struct sum_mgr *sum_mgr;
@@ -467,6 +470,7 @@ int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
 
        sum_mgr->get_sum = get_sum_rsc;
        sum_mgr->put_sum = put_sum_rsc;
+       sum_mgr->card = hw->card;
 
        *rsum_mgr = sum_mgr;
 
index cc49e5ab4750308c3116401b67518aa05106b2c9..72f42f27434eb7f4dcada14fe737be047d03d7f0 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "ctresource.h"
 #include <linux/spinlock.h>
+#include <sound/core.h>
 
 /* Define the descriptor of a summation node resource */
 struct sum {
@@ -35,6 +36,7 @@ struct sum_desc {
 
 struct sum_mgr {
        struct rsc_mgr mgr;     /* Basic resource manager info */
+       struct snd_card *card;  /* pointer to this card */
        spinlock_t mgr_lock;
 
         /* request one sum resource */
@@ -45,7 +47,7 @@ struct sum_mgr {
 };
 
 /* Constructor and destructor of daio resource manager */
-int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr);
+int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr);
 int sum_mgr_destroy(struct sum_mgr *sum_mgr);
 
 /* Define the descriptor of a amixer resource */
@@ -79,6 +81,7 @@ struct amixer_desc {
 
 struct amixer_mgr {
        struct rsc_mgr mgr;     /* Basic resource manager info */
+       struct snd_card *card;  /* pointer to this card */
        spinlock_t mgr_lock;
 
         /* request one amixer resource */
@@ -90,7 +93,7 @@ struct amixer_mgr {
 };
 
 /* Constructor and destructor of amixer resource manager */
-int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr);
+int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr);
 int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
 
 #endif /* CTAMIXER_H */
index af632bd08323feaf0f9ab2d3098375e12f38d46b..45465907439002b4bf879462f4c6f97955dc9255 100644 (file)
@@ -106,11 +106,11 @@ static struct {
                            .public_name = "Mixer"}
 };
 
-typedef int (*create_t)(void *, void **);
+typedef int (*create_t)(struct hw *, void **);
 typedef int (*destroy_t)(void *);
 
 static struct {
-       int (*create)(void *hw, void **rmgr);
+       int (*create)(struct hw *hw, void **rmgr);
        int (*destroy)(void *mgr);
 } rsc_mgr_funcs[NUM_RSCTYP] = {
        [SRC]           = { .create     = (create_t)src_mgr_create,
@@ -171,7 +171,8 @@ static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index)
        return atc->vm->get_ptp_phys(atc->vm, index);
 }
 
-static unsigned int convert_format(snd_pcm_format_t snd_format)
+static unsigned int convert_format(snd_pcm_format_t snd_format,
+                                  struct snd_card *card)
 {
        switch (snd_format) {
        case SNDRV_PCM_FORMAT_U8:
@@ -185,7 +186,7 @@ static unsigned int convert_format(snd_pcm_format_t snd_format)
        case SNDRV_PCM_FORMAT_FLOAT_LE:
                return SRC_SF_F32;
        default:
-               printk(KERN_ERR "ctxfi: not recognized snd format is %d \n",
+               dev_err(card->dev, "not recognized snd format is %d\n",
                        snd_format);
                return SRC_SF_S16;
        }
@@ -268,7 +269,8 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
        src = apcm->src;
        src->ops->set_pitch(src, pitch);
        src->ops->set_rom(src, select_rom(pitch));
-       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format,
+                                            atc->card));
        src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
 
        /* Get AMIXER resource */
@@ -738,7 +740,8 @@ static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 
        /*  Set up recording SRC */
        src = apcm->src;
-       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format,
+                                            atc->card));
        src->ops->set_sa(src, apcm->vm_block->addr);
        src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size);
        src->ops->set_ca(src, apcm->vm_block->addr);
@@ -807,7 +810,8 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
        src = apcm->src;
        src->ops->set_pitch(src, pitch);
        src->ops->set_rom(src, select_rom(pitch));
-       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format,
+                                            atc->card));
        src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
        src->ops->set_bp(src, 1);
 
@@ -1235,7 +1239,7 @@ static int ct_atc_destroy(struct ct_atc *atc)
        }
 
        if (atc->hw)
-               destroy_hw_obj((struct hw *)atc->hw);
+               destroy_hw_obj(atc->hw);
 
        /* Destroy device virtual memory manager object */
        if (atc->vm) {
@@ -1282,9 +1286,9 @@ static int atc_identify_card(struct ct_atc *atc, unsigned int ssid)
        p = snd_pci_quirk_lookup_id(vendor_id, device_id, list);
        if (p) {
                if (p->value < 0) {
-                       printk(KERN_ERR "ctxfi: "
-                              "Device %04x:%04x is black-listed\n",
-                              vendor_id, device_id);
+                       dev_err(atc->card->dev,
+                               "Device %04x:%04x is black-listed\n",
+                               vendor_id, device_id);
                        return -ENOENT;
                }
                atc->model = p->value;
@@ -1315,8 +1319,8 @@ int ct_atc_create_alsa_devs(struct ct_atc *atc)
                err = alsa_dev_funcs[i].create(atc, i,
                                alsa_dev_funcs[i].public_name);
                if (err) {
-                       printk(KERN_ERR "ctxfi: "
-                              "Creating alsa device %d failed!\n", i);
+                       dev_err(atc->card->dev,
+                               "Creating alsa device %d failed!\n", i);
                        return err;
                }
        }
@@ -1332,9 +1336,10 @@ static int atc_create_hw_devs(struct ct_atc *atc)
 
        err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw);
        if (err) {
-               printk(KERN_ERR "Failed to create hw obj!!!\n");
+               dev_err(atc->card->dev, "Failed to create hw obj!!!\n");
                return err;
        }
+       hw->card = atc->card;
        atc->hw = hw;
 
        /* Initialize card hardware. */
@@ -1351,8 +1356,8 @@ static int atc_create_hw_devs(struct ct_atc *atc)
 
                err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
                if (err) {
-                       printk(KERN_ERR "ctxfi: "
-                              "Failed to create rsc_mgr %d!!!\n", i);
+                       dev_err(atc->card->dev,
+                               "Failed to create rsc_mgr %d!!!\n", i);
                        return err;
                }
        }
@@ -1399,8 +1404,9 @@ static int atc_get_resources(struct ct_atc *atc)
                err = daio_mgr->get_daio(daio_mgr, &da_desc,
                                        (struct daio **)&atc->daios[i]);
                if (err) {
-                       printk(KERN_ERR "ctxfi: Failed to get DAIO "
-                                       "resource %d!!!\n", i);
+                       dev_err(atc->card->dev,
+                               "Failed to get DAIO resource %d!!!\n",
+                               i);
                        return err;
                }
                atc->n_daio++;
@@ -1603,8 +1609,8 @@ static int atc_resume(struct ct_atc *atc)
        /* Do hardware resume. */
        err = atc_hw_resume(atc);
        if (err < 0) {
-               printk(KERN_ERR "ctxfi: pci_enable_device failed, "
-                      "disabling device\n");
+               dev_err(atc->card->dev,
+                       "pci_enable_device failed, disabling device\n");
                snd_card_disconnect(atc->card);
                return err;
        }
@@ -1701,7 +1707,7 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
        /* Find card model */
        err = atc_identify_card(atc, ssid);
        if (err < 0) {
-               printk(KERN_ERR "ctatc: Card not recognised\n");
+               dev_err(card->dev, "ctatc: Card not recognised\n");
                goto error1;
        }
 
@@ -1717,7 +1723,7 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
 
        err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer);
        if (err) {
-               printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n");
+               dev_err(card->dev, "Failed to create mixer obj!!!\n");
                goto error1;
        }
 
@@ -1744,6 +1750,6 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
 
 error1:
        ct_atc_destroy(atc);
-       printk(KERN_ERR "ctxfi: Something wrong!!!\n");
+       dev_err(card->dev, "Something wrong!!!\n");
        return err;
 }
index 5f11ca22fcdefaca7706cb7a81256d16c5894d2e..56413343a9e831dd1bbdfac07b9529428fbd979e 100644 (file)
@@ -131,7 +131,7 @@ struct ct_atc {
        /* Don't touch! Used for internal object. */
        void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
        void *mixer;            /* internal mixer object */
-       void *hw;               /* chip specific hardware access object */
+       struct hw *hw;          /* chip specific hardware access object */
        void **daios;           /* digital audio io resources */
        void **pcm;             /* SUMs for collecting all pcm stream */
        void **srcs;            /* Sample Rate Converters for input signal */
index 84f86bf63b8fc31dc27700013608a65a7af970bb..c1c3f8816fff671962a1392672b6e127f26f694a 100644 (file)
@@ -140,19 +140,19 @@ static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc);
 
 static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos)
 {
-       ((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos);
+       dao->hw->dao_get_spos(dao->ctrl_blk, spos);
        return 0;
 }
 
 static int dao_spdif_set_spos(struct dao *dao, unsigned int spos)
 {
-       ((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos);
+       dao->hw->dao_set_spos(dao->ctrl_blk, spos);
        return 0;
 }
 
 static int dao_commit_write(struct dao *dao)
 {
-       ((struct hw *)dao->hw)->dao_commit_write(dao->hw,
+       dao->hw->dao_commit_write(dao->hw,
                daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk);
        return 0;
 }
@@ -277,16 +277,14 @@ static struct dao_rsc_ops dao_ops = {
 static int dai_set_srt_srcl(struct dai *dai, struct rsc *src)
 {
        src->ops->master(src);
-       ((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk,
-                                               src->ops->index(src));
+       dai->hw->dai_srt_set_srcm(dai->ctrl_blk, src->ops->index(src));
        return 0;
 }
 
 static int dai_set_srt_srcr(struct dai *dai, struct rsc *src)
 {
        src->ops->master(src);
-       ((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk,
-                                               src->ops->index(src));
+       dai->hw->dai_srt_set_srco(dai->ctrl_blk, src->ops->index(src));
        return 0;
 }
 
@@ -297,25 +295,25 @@ static int dai_set_srt_msr(struct dai *dai, unsigned int msr)
        for (rsr = 0; msr > 1; msr >>= 1)
                rsr++;
 
-       ((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr);
+       dai->hw->dai_srt_set_rsr(dai->ctrl_blk, rsr);
        return 0;
 }
 
 static int dai_set_enb_src(struct dai *dai, unsigned int enb)
 {
-       ((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb);
+       dai->hw->dai_srt_set_ec(dai->ctrl_blk, enb);
        return 0;
 }
 
 static int dai_set_enb_srt(struct dai *dai, unsigned int enb)
 {
-       ((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb);
+       dai->hw->dai_srt_set_et(dai->ctrl_blk, enb);
        return 0;
 }
 
 static int dai_commit_write(struct dai *dai)
 {
-       ((struct hw *)dai->hw)->dai_commit_write(dai->hw,
+       dai->hw->dai_commit_write(dai->hw,
                daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
        return 0;
 }
@@ -331,12 +329,12 @@ static struct dai_rsc_ops dai_ops = {
 
 static int daio_rsc_init(struct daio *daio,
                         const struct daio_desc *desc,
-                        void *hw)
+                        struct hw *hw)
 {
        int err;
        unsigned int idx_l, idx_r;
 
-       switch (((struct hw *)hw)->chip_type) {
+       switch (hw->chip_type) {
        case ATC20K1:
                idx_l = idx_20k1[desc->type].left;
                idx_r = idx_20k1[desc->type].right;
@@ -360,7 +358,7 @@ static int daio_rsc_init(struct daio *daio,
        if (desc->type <= DAIO_OUT_MAX) {
                daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops;
        } else {
-               switch (((struct hw *)hw)->chip_type) {
+               switch (hw->chip_type) {
                case ATC20K1:
                        daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1;
                        break;
@@ -445,7 +443,7 @@ static int dao_rsc_uninit(struct dao *dao)
                kfree(dao->imappers);
                dao->imappers = NULL;
        }
-       ((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk);
+       dao->hw->dao_put_ctrl_blk(dao->ctrl_blk);
        dao->hw = dao->ctrl_blk = NULL;
        daio_rsc_uninit(&dao->daio);
 
@@ -502,7 +500,7 @@ error1:
 
 static int dai_rsc_uninit(struct dai *dai)
 {
-       ((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk);
+       dai->hw->dai_put_ctrl_blk(dai->ctrl_blk);
        dai->hw = dai->ctrl_blk = NULL;
        daio_rsc_uninit(&dai->daio);
        return 0;
@@ -541,7 +539,8 @@ static int get_daio_rsc(struct daio_mgr *mgr,
        err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
        spin_unlock_irqrestore(&mgr->mgr_lock, flags);
        if (err) {
-               printk(KERN_ERR "Can't meet DAIO resource request!\n");
+               dev_err(mgr->card->dev,
+                       "Can't meet DAIO resource request!\n");
                return err;
        }
 
@@ -692,7 +691,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr)
        return 0;
 }
 
-int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
+int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr)
 {
        int err, i;
        struct daio_mgr *daio_mgr;
@@ -727,12 +726,13 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
        daio_mgr->imap_add = daio_imap_add;
        daio_mgr->imap_delete = daio_imap_delete;
        daio_mgr->commit_write = daio_mgr_commit_write;
+       daio_mgr->card = hw->card;
 
        for (i = 0; i < 8; i++) {
-               ((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
-               ((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
+               hw->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
+               hw->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
        }
-       ((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
+       hw->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
 
        *rdaio_mgr = daio_mgr;
 
index 85ccb6ee1ab4e2c3f7df0ca5378a7e2bc54c71de..0ebbf350f51aa8a6b3a64e91aa3a278e0b44ebf0 100644 (file)
@@ -23,6 +23,7 @@
 #include "ctimap.h"
 #include <linux/spinlock.h>
 #include <linux/list.h>
+#include <sound/core.h>
 
 /* Define the descriptor of a daio resource */
 enum DAIOTYP {
@@ -53,14 +54,14 @@ struct dao {
        struct dao_rsc_ops *ops;        /* DAO specific operations */
        struct imapper **imappers;
        struct daio_mgr *mgr;
-       void *hw;
+       struct hw *hw;
        void *ctrl_blk;
 };
 
 struct dai {
        struct daio daio;
        struct dai_rsc_ops *ops;        /* DAI specific operations */
-       void *hw;
+       struct hw *hw;
        void *ctrl_blk;
 };
 
@@ -98,6 +99,7 @@ struct daio_desc {
 
 struct daio_mgr {
        struct rsc_mgr mgr;     /* Basic resource manager info */
+       struct snd_card *card;  /* pointer to this card */
        spinlock_t mgr_lock;
        spinlock_t imap_lock;
        struct list_head imappers;
@@ -117,7 +119,7 @@ struct daio_mgr {
 };
 
 /* Constructor and destructor of daio resource manager */
-int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr);
+int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr);
 int daio_mgr_destroy(struct daio_mgr *daio_mgr);
 
 #endif /* CTDAIO_H */
index 5977e9a24b5ca68971e1f5f0dc17f17b03257098..54cc9cb75f00827ac18b231e3773f36a7a951c10 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/types.h>
 #include <linux/pci.h>
+#include <sound/core.h>
 
 enum CHIPTYP {
        ATC20K1,
@@ -184,9 +185,10 @@ struct hw {
        void *irq_callback_data;
 
        struct pci_dev *pci;    /* the pci kernel structure of this card */
+       struct snd_card *card;  /* pointer to this card */
        int irq;
        unsigned long io_base;
-       unsigned long mem_base;
+       void __iomem *mem_base;
 
        enum CHIPTYP chip_type;
        enum CTCARDS model;
index 6ac40beb49dabea2b54e3de48a9eb721cfc3a5ae..b425aa8ee57858fb11c43f5c1b56bfa6adc8bbca 100644 (file)
@@ -1268,7 +1268,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
 
        /* Set up device page table */
        if ((~0UL) == info->vm_pgt_phys) {
-               printk(KERN_ERR "Wrong device page table page address!\n");
+               dev_err(hw->card->dev,
+                       "Wrong device page table page address!\n");
                return -1;
        }
 
@@ -1327,7 +1328,7 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
                mdelay(40);
        }
        if (i >= 3) {
-               printk(KERN_ALERT "PLL initialization failed!!!\n");
+               dev_alert(hw->card->dev, "PLL initialization failed!!!\n");
                return -EBUSY;
        }
 
@@ -1351,7 +1352,7 @@ static int hw_auto_init(struct hw *hw)
                        break;
        }
        if (!get_field(gctl, GCTL_AID)) {
-               printk(KERN_ALERT "Card Auto-init failed!!!\n");
+               dev_alert(hw->card->dev, "Card Auto-init failed!!!\n");
                return -EBUSY;
        }
 
@@ -1802,7 +1803,7 @@ static int uaa_to_xfi(struct pci_dev *pci)
        unsigned int is_uaa;
        unsigned int data[4] = {0};
        unsigned int io_base;
-       void *mem_base;
+       void __iomem *mem_base;
        int i;
        const u32 CTLX = CTLBITS('C', 'T', 'L', 'X');
        const u32 CTL_ = CTLBITS('C', 'T', 'L', '-');
@@ -1911,9 +1912,9 @@ static int hw_card_start(struct hw *hw)
        /* Set DMA transfer mask */
        if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
            pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
-               printk(KERN_ERR "architecture does not support PCI "
-                               "busmaster DMA with mask 0x%llx\n",
-                      CT_XFI_DMA_MASK);
+               dev_err(hw->card->dev,
+                       "architecture does not support PCI busmaster DMA with mask 0x%llx\n",
+                       CT_XFI_DMA_MASK);
                err = -ENXIO;
                goto error1;
        }
@@ -1942,7 +1943,8 @@ static int hw_card_start(struct hw *hw)
                err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
                                  KBUILD_MODNAME, hw);
                if (err < 0) {
-                       printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+                       dev_err(hw->card->dev,
+                               "XFi: Cannot get irq %d\n", pci->irq);
                        goto error2;
                }
                hw->irq = pci->irq;
@@ -1985,9 +1987,9 @@ static int hw_card_shutdown(struct hw *hw)
        hw->irq = -1;
 
        if (hw->mem_base)
-               iounmap((void *)hw->mem_base);
+               iounmap(hw->mem_base);
 
-       hw->mem_base = (unsigned long)NULL;
+       hw->mem_base = NULL;
 
        if (hw->io_base)
                pci_release_regions(hw->pci);
index b1438861d38ade68112572dbec342240a24d9e34..253899d1379096ea3108c5588aa012b3e4bd9a80 100644 (file)
@@ -1187,7 +1187,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
                hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121);
                hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
        } else {
-               printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n");
+               dev_alert(hw->card->dev,
+                         "ERROR!!! Invalid sampling rate!!!\n");
                return -EINVAL;
        }
 
@@ -1246,8 +1247,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
 
        /* Set up device page table */
        if ((~0UL) == info->vm_pgt_phys) {
-               printk(KERN_ALERT "ctxfi: "
-                      "Wrong device page table page address!!!\n");
+               dev_alert(hw->card->dev,
+                         "Wrong device page table page address!!!\n");
                return -1;
        }
 
@@ -1352,7 +1353,8 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
                break;
        }
        if (i >= 1000) {
-               printk(KERN_ALERT "ctxfi: PLL initialization failed!!!\n");
+               dev_alert(hw->card->dev,
+                         "PLL initialization failed!!!\n");
                return -EBUSY;
        }
 
@@ -1376,7 +1378,7 @@ static int hw_auto_init(struct hw *hw)
                        break;
        }
        if (!get_field(gctl, GCTL_AID)) {
-               printk(KERN_ALERT "ctxfi: Card Auto-init failed!!!\n");
+               dev_alert(hw->card->dev, "Card Auto-init failed!!!\n");
                return -EBUSY;
        }
 
@@ -1847,7 +1849,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
        /* Initialize I2C */
        err = hw20k2_i2c_init(hw, 0x1A, 1, 1);
        if (err < 0) {
-               printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n");
+               dev_alert(hw->card->dev, "Failure to acquire I2C!!!\n");
                goto error;
        }
 
@@ -1890,8 +1892,9 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
                hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A),
                                                MAKE_WM8775_DATA(0x0A));
        } else {
-               printk(KERN_ALERT "ctxfi: Invalid master sampling "
-                                 "rate (msr %d)!!!\n", info->msr);
+               dev_alert(hw->card->dev,
+                         "Invalid master sampling rate (msr %d)!!!\n",
+                         info->msr);
                err = -EINVAL;
                goto error;
        }
@@ -2034,8 +2037,9 @@ static int hw_card_start(struct hw *hw)
        /* Set DMA transfer mask */
        if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
            pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
-               printk(KERN_ERR "ctxfi: architecture does not support PCI "
-               "busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK);
+               dev_err(hw->card->dev,
+                       "architecture does not support PCI busmaster DMA with mask 0x%llx\n",
+                       CT_XFI_DMA_MASK);
                err = -ENXIO;
                goto error1;
        }
@@ -2046,8 +2050,8 @@ static int hw_card_start(struct hw *hw)
                        goto error1;
 
                hw->io_base = pci_resource_start(hw->pci, 2);
-               hw->mem_base = (unsigned long)ioremap(hw->io_base,
-                                       pci_resource_len(hw->pci, 2));
+               hw->mem_base = ioremap(hw->io_base,
+                                      pci_resource_len(hw->pci, 2));
                if (!hw->mem_base) {
                        err = -ENOENT;
                        goto error2;
@@ -2063,7 +2067,8 @@ static int hw_card_start(struct hw *hw)
                err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED,
                                  KBUILD_MODNAME, hw);
                if (err < 0) {
-                       printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+                       dev_err(hw->card->dev,
+                               "XFi: Cannot get irq %d\n", pci->irq);
                        goto error2;
                }
                hw->irq = pci->irq;
@@ -2107,9 +2112,9 @@ static int hw_card_shutdown(struct hw *hw)
        hw->irq = -1;
 
        if (hw->mem_base)
-               iounmap((void *)hw->mem_base);
+               iounmap(hw->mem_base);
 
-       hw->mem_base = (unsigned long)NULL;
+       hw->mem_base = NULL;
 
        if (hw->io_base)
                pci_release_regions(hw->pci);
@@ -2229,12 +2234,12 @@ static int hw_resume(struct hw *hw, struct card_conf *info)
 
 static u32 hw_read_20kx(struct hw *hw, u32 reg)
 {
-       return readl((void *)(hw->mem_base + reg));
+       return readl(hw->mem_base + reg);
 }
 
 static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
 {
-       writel(data, (void *)(hw->mem_base + reg));
+       writel(data, hw->mem_base + reg);
 }
 
 static struct hw ct20k2_preset = {
index 48fe0e39c2be273a95762698999dd4cf5508b019..4f4a2a5dedb8f0fd8eb5b1679cfbab08c5441e12 100644 (file)
@@ -854,8 +854,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer)
        for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
                err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum);
                if (err) {
-                       printk(KERN_ERR "ctxfi:Failed to get sum resources for "
-                                         "front output!\n");
+                       dev_err(mixer->atc->card->dev,
+                               "Failed to get sum resources for front output!\n");
                        break;
                }
                mixer->sums[i] = sum;
@@ -869,8 +869,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer)
        for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
                err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer);
                if (err) {
-                       printk(KERN_ERR "ctxfi:Failed to get amixer resources "
-                              "for mixer obj!\n");
+                       dev_err(mixer->atc->card->dev,
+                               "Failed to get amixer resources for mixer obj!\n");
                        break;
                }
                mixer->amixers[i] = amixer;
index e8a4feb1ed86ba59396368601f1cdbfeb364ce0a..d86c474ca5b62da74aee826b4021bdccb4f09088 100644 (file)
@@ -217,7 +217,8 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
                err = atc->pcm_playback_prepare(atc, apcm);
 
        if (err < 0) {
-               printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n");
+               dev_err(atc->card->dev,
+                       "Preparing pcm playback failed!!!\n");
                return err;
        }
 
@@ -324,7 +325,8 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
 
        err = atc->pcm_capture_prepare(atc, apcm);
        if (err < 0) {
-               printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n");
+               dev_err(atc->card->dev,
+                       "Preparing pcm capture failed!!!\n");
                return err;
        }
 
@@ -435,7 +437,8 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
        err = snd_pcm_new(atc->card, "ctxfi", device,
                          playback_count, capture_count, &pcm);
        if (err < 0) {
-               printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err);
+               dev_err(atc->card->dev, "snd_pcm_new failed!! Err=%d\n",
+                       err);
                return err;
        }
 
index 7dfaf67344d4a0ded780ff575922250fe8df6950..1a97e406d8ec00aaa36b430a8d8d02419cb30fca 100644 (file)
@@ -134,7 +134,8 @@ static struct rsc_ops rsc_generic_ops = {
        .next_conj      = rsc_next_conj,
 };
 
-int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
+int
+rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, struct hw *hw)
 {
        int err = 0;
 
@@ -151,25 +152,24 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
 
        switch (type) {
        case SRC:
-               err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+               err = hw->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
                break;
        case AMIXER:
-               err = ((struct hw *)hw)->
-                               amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+               err = hw->amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
                break;
        case SRCIMP:
        case SUM:
        case DAIO:
                break;
        default:
-               printk(KERN_ERR
-                      "ctxfi: Invalid resource type value %d!\n", type);
+               dev_err(((struct hw *)hw)->card->dev,
+                       "Invalid resource type value %d!\n", type);
                return -EINVAL;
        }
 
        if (err) {
-               printk(KERN_ERR
-                      "ctxfi: Failed to get resource control block!\n");
+               dev_err(((struct hw *)hw)->card->dev,
+                       "Failed to get resource control block!\n");
                return err;
        }
 
@@ -181,19 +181,18 @@ int rsc_uninit(struct rsc *rsc)
        if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) {
                switch (rsc->type) {
                case SRC:
-                       ((struct hw *)rsc->hw)->
-                               src_rsc_put_ctrl_blk(rsc->ctrl_blk);
+                       rsc->hw->src_rsc_put_ctrl_blk(rsc->ctrl_blk);
                        break;
                case AMIXER:
-                       ((struct hw *)rsc->hw)->
-                               amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
+                       rsc->hw->amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
                        break;
                case SUM:
                case DAIO:
                        break;
                default:
-                       printk(KERN_ERR "ctxfi: "
-                              "Invalid resource type value %d!\n", rsc->type);
+                       dev_err(((struct hw *)rsc->hw)->card->dev,
+                               "Invalid resource type value %d!\n",
+                               rsc->type);
                        break;
                }
 
@@ -208,10 +207,9 @@ int rsc_uninit(struct rsc *rsc)
 }
 
 int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
-                unsigned int amount, void *hw_obj)
+                unsigned int amount, struct hw *hw)
 {
        int err = 0;
-       struct hw *hw = hw_obj;
 
        mgr->type = NUM_RSCTYP;
 
@@ -235,15 +233,15 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
        case SUM:
                break;
        default:
-               printk(KERN_ERR
-                      "ctxfi: Invalid resource type value %d!\n", type);
+               dev_err(hw->card->dev,
+                       "Invalid resource type value %d!\n", type);
                err = -EINVAL;
                goto error;
        }
 
        if (err) {
-               printk(KERN_ERR
-                      "ctxfi: Failed to get manager control block!\n");
+               dev_err(hw->card->dev,
+                       "Failed to get manager control block!\n");
                goto error;
        }
 
@@ -268,26 +266,23 @@ int rsc_mgr_uninit(struct rsc_mgr *mgr)
        if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) {
                switch (mgr->type) {
                case SRC:
-                       ((struct hw *)mgr->hw)->
-                               src_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       mgr->hw->src_mgr_put_ctrl_blk(mgr->ctrl_blk);
                        break;
                case SRCIMP:
-                       ((struct hw *)mgr->hw)->
-                               srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       mgr->hw->srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
                        break;
                case AMIXER:
-                       ((struct hw *)mgr->hw)->
-                               amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       mgr->hw->amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
                        break;
                case DAIO:
-                       ((struct hw *)mgr->hw)->
-                               daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       mgr->hw->daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
                        break;
                case SUM:
                        break;
                default:
-                       printk(KERN_ERR "ctxfi: "
-                              "Invalid resource type value %d!\n", mgr->type);
+                       dev_err(((struct hw *)mgr->hw)->card->dev,
+                               "Invalid resource type value %d!\n",
+                               mgr->type);
                        break;
                }
 
index 0838c2e84f8b42ec8bb7b2372b0fa72f037f2762..9b746c3719e6219faa30b116b762675376867631 100644 (file)
@@ -38,7 +38,7 @@ struct rsc {
        u32 conj:12;    /* Current conjugate index */
        u32 msr:4;      /* The Master Sample Rate a resource working on */
        void *ctrl_blk; /* Chip specific control info block for a resource */
-       void *hw;       /* Chip specific object for hardware access means */
+       struct hw *hw;  /* Chip specific object for hardware access means */
        struct rsc_ops *ops;    /* Generic resource operations */
 };
 
@@ -50,7 +50,8 @@ struct rsc_ops {
        int (*output_slot)(const struct rsc *rsc);
 };
 
-int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw);
+int
+rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, struct hw *hw);
 int rsc_uninit(struct rsc *rsc);
 
 struct rsc_mgr {
@@ -59,12 +60,12 @@ struct rsc_mgr {
        unsigned int avail; /* The amount of currently available resources */
        unsigned char *rscs; /* The bit-map for resource allocation */
        void *ctrl_blk; /* Chip specific control info block */
-       void *hw; /* Chip specific object for hardware access */
+       struct hw *hw; /* Chip specific object for hardware access */
 };
 
 /* Resource management is based on bit-map mechanism */
 int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
-                unsigned int amount, void *hw);
+                unsigned int amount, struct hw *hw);
 int rsc_mgr_uninit(struct rsc_mgr *mgr);
 int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx);
 int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx);
index 6e77e86307c2c90fd8ca8d3dde53dceebd0499af..ec1f08464d938ce0adc3660f3ae8efbb799d7cde 100644 (file)
@@ -431,7 +431,8 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
 
        spin_unlock_irqrestore(&mgr->mgr_lock, flags);
        if (err) {
-               printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n");
+               dev_err(mgr->card->dev,
+                       "Can't meet SRC resource request!\n");
                return err;
        }
 
@@ -543,7 +544,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr)
        return 0;
 }
 
-int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
+int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr)
 {
        int err, i;
        struct src_mgr *src_mgr;
@@ -558,7 +559,7 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
                goto error1;
 
        spin_lock_init(&src_mgr->mgr_lock);
-       conj_mask = ((struct hw *)hw)->src_dirty_conj_mask();
+       conj_mask = hw->src_dirty_conj_mask();
 
        src_mgr->get_src = get_src_rsc;
        src_mgr->put_src = put_src_rsc;
@@ -566,12 +567,13 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
        src_mgr->src_enable = src_enable;
        src_mgr->src_disable = src_disable;
        src_mgr->commit_write = src_mgr_commit_write;
+       src_mgr->card = hw->card;
 
        /* Disable all SRC resources. */
        for (i = 0; i < 256; i++)
-               ((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
+               hw->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
 
-       ((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
+       hw->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
 
        *rsrc_mgr = src_mgr;
 
@@ -739,7 +741,8 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr,
        }
        spin_unlock_irqrestore(&mgr->mgr_lock, flags);
        if (err) {
-               printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n");
+               dev_err(mgr->card->dev,
+                       "Can't meet SRCIMP resource request!\n");
                goto error1;
        }
 
@@ -825,7 +828,7 @@ static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
        return err;
 }
 
-int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
+int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrcimp_mgr)
 {
        int err;
        struct srcimp_mgr *srcimp_mgr;
@@ -857,6 +860,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
        srcimp_mgr->put_srcimp = put_srcimp_rsc;
        srcimp_mgr->imap_add = srcimp_imap_add;
        srcimp_mgr->imap_delete = srcimp_imap_delete;
+       srcimp_mgr->card = hw->card;
 
        *rsrcimp_mgr = srcimp_mgr;
 
index 259366aabcaca35db91fa5ff2bf978045fb35908..da7573c5db9bb6bdfd974c03c758a33c32170e65 100644 (file)
@@ -23,6 +23,7 @@
 #include "ctimap.h"
 #include <linux/spinlock.h>
 #include <linux/list.h>
+#include <sound/core.h>
 
 #define SRC_STATE_OFF  0x0
 #define SRC_STATE_INIT 0x4
@@ -85,6 +86,7 @@ struct src_desc {
 /* Define src manager object */
 struct src_mgr {
        struct rsc_mgr mgr;     /* Basic resource manager info */
+       struct snd_card *card;  /* pointer to this card */
        spinlock_t mgr_lock;
 
         /* request src resource */
@@ -123,6 +125,7 @@ struct srcimp_desc {
 
 struct srcimp_mgr {
        struct rsc_mgr mgr;     /* Basic resource manager info */
+       struct snd_card *card;  /* pointer to this card */
        spinlock_t mgr_lock;
        spinlock_t imap_lock;
        struct list_head imappers;
@@ -140,10 +143,10 @@ struct srcimp_mgr {
 };
 
 /* Constructor and destructor of SRC resource manager */
-int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr);
+int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr);
 int src_mgr_destroy(struct src_mgr *src_mgr);
 /* Constructor and destructor of SRCIMP resource manager */
-int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr);
+int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrc_mgr);
 int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
 
 #endif /* CTSRC_H */
index 6109490b83e83294373f36956a5a8a01dd923e20..419306ef825f61c51c50a507c3c249784f613337 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include "ctvmem.h"
+#include "ctatc.h"
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/io.h>
  * @size must be page aligned.
  * */
 static struct ct_vm_block *
-get_vm_block(struct ct_vm *vm, unsigned int size)
+get_vm_block(struct ct_vm *vm, unsigned int size, struct ct_atc *atc)
 {
        struct ct_vm_block *block = NULL, *entry;
        struct list_head *pos;
 
        size = CT_PAGE_ALIGN(size);
        if (size > vm->size) {
-               printk(KERN_ERR "ctxfi: Fail! No sufficient device virtual "
-                                 "memory space available!\n");
+               dev_err(atc->card->dev,
+                       "Fail! No sufficient device virtual memory space available!\n");
                return NULL;
        }
 
@@ -129,11 +130,12 @@ ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size)
        unsigned int pte_start;
        unsigned i, pages;
        unsigned long *ptp;
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
 
-       block = get_vm_block(vm, size);
+       block = get_vm_block(vm, size, atc);
        if (block == NULL) {
-               printk(KERN_ERR "ctxfi: No virtual memory block that is big "
-                                 "enough to allocate!\n");
+               dev_err(atc->card->dev,
+                       "No virtual memory block that is big enough to allocate!\n");
                return NULL;
        }
 
index 8f8b566a1b35ea0c8d32755b0d6240d351787922..f2f32779de986ff9d50b6853f024113d79ef9752 100644 (file)
@@ -76,17 +76,18 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
        if (err)
                return err;
        if ((reference_rate != 48000) && (reference_rate != 44100)) {
-               printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n",
-                      reference_rate);
-               printk(KERN_ERR "ctxfi: The valid values for reference_rate "
-                      "are 48000 and 44100, Value 48000 is assumed.\n");
+               dev_err(card->dev,
+                       "Invalid reference_rate value %u!!!\n",
+                       reference_rate);
+               dev_err(card->dev,
+                       "The valid values for reference_rate are 48000 and 44100, Value 48000 is assumed.\n");
                reference_rate = 48000;
        }
        if ((multiple != 1) && (multiple != 2) && (multiple != 4)) {
-               printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
-                      multiple);
-               printk(KERN_ERR "ctxfi: The valid values for multiple are "
-                      "1, 2 and 4, Value 2 is assumed.\n");
+               dev_err(card->dev, "Invalid multiple value %u!!!\n",
+                       multiple);
+               dev_err(card->dev,
+                       "The valid values for multiple are 1, 2 and 4, Value 2 is assumed.\n");
                multiple = 2;
        }
        err = ct_atc_create(card, pci, reference_rate, multiple,
index 51dea49aadd4212b83263766d01f44a40bc0ac75..fcc5e478c9a1a10a42db7c69e24560de42c0cdbe 100644 (file)
@@ -57,12 +57,14 @@ static void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list,
 
 
 /* add the found input-pin to the cfg->inputs[] table */
-static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
-                                  int type)
+static void add_auto_cfg_input_pin(struct hda_codec *codec, struct auto_pin_cfg *cfg,
+                                  hda_nid_t nid, int type)
 {
        if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
                cfg->inputs[cfg->num_inputs].pin = nid;
                cfg->inputs[cfg->num_inputs].type = type;
+               cfg->inputs[cfg->num_inputs].has_boost_on_pin =
+                       nid_has_volume(codec, nid, HDA_INPUT);
                cfg->num_inputs++;
        }
 }
@@ -71,7 +73,12 @@ static int compare_input_type(const void *ap, const void *bp)
 {
        const struct auto_pin_cfg_item *a = ap;
        const struct auto_pin_cfg_item *b = bp;
-       return (int)(a->type - b->type);
+       if (a->type != b->type)
+               return (int)(a->type - b->type);
+
+       /* In case one has boost and the other one has not,
+          pick the one with boost first. */
+       return (int)(b->has_boost_on_pin - a->has_boost_on_pin);
 }
 
 /* Reorder the surround channels
@@ -268,16 +275,16 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
                        cfg->hp_outs++;
                        break;
                case AC_JACK_MIC_IN:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
+                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_MIC);
                        break;
                case AC_JACK_LINE_IN:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
+                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_LINE_IN);
                        break;
                case AC_JACK_CD:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
+                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_CD);
                        break;
                case AC_JACK_AUX:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
+                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_AUX);
                        break;
                case AC_JACK_SPDIF_OUT:
                case AC_JACK_DIG_OTHER_OUT:
index e941f604f5e5d450c2ac204a2a401842d5f6d4a8..2b8e29fd73e7ee0ea23762e59b9250ffb0283757 100644 (file)
@@ -38,6 +38,7 @@ struct auto_pin_cfg_item {
        int type;
        unsigned int is_headset_mic:1;
        unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */
+       unsigned int has_boost_on_pin:1;
 };
 
 struct auto_pin_cfg;
index ec6a7d0d1886c691fc5cf356c6d078e4cea435c0..15e0089492f735f55814d6caf1e6735080358602 100644 (file)
@@ -2001,6 +2001,26 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 }
 EXPORT_SYMBOL_GPL(query_amp_caps);
 
+/**
+ * snd_hda_check_amp_caps - query AMP capabilities
+ * @codec: the HD-audio codec
+ * @nid: the NID to query
+ * @dir: either #HDA_INPUT or #HDA_OUTPUT
+ *
+ * Check whether the widget has the given amp capability for the direction.
+ */
+bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+                          int dir, unsigned int bits)
+{
+       if (!nid)
+               return false;
+       if (get_wcaps(codec, nid) & (1 << (dir + 1)))
+               if (query_amp_caps(codec, nid, dir) & bits)
+                       return true;
+       return false;
+}
+EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps);
+
 /**
  * snd_hda_override_amp_caps - Override the AMP capabilities
  * @codec: the CODEC to clean up
@@ -4816,121 +4836,6 @@ int snd_hda_build_pcms(struct hda_bus *bus)
 }
 EXPORT_SYMBOL_GPL(snd_hda_build_pcms);
 
-/**
- * snd_hda_check_board_config - compare the current codec with the config table
- * @codec: the HDA codec
- * @num_configs: number of config enums
- * @models: array of model name strings
- * @tbl: configuration table, terminated by null entries
- *
- * Compares the modelname or PCI subsystem id of the current codec with the
- * given configuration table.  If a matching entry is found, returns its
- * config value (supposed to be 0 or positive).
- *
- * If no entries are matching, the function returns a negative value.
- */
-int snd_hda_check_board_config(struct hda_codec *codec,
-                              int num_configs, const char * const *models,
-                              const struct snd_pci_quirk *tbl)
-{
-       if (codec->modelname && models) {
-               int i;
-               for (i = 0; i < num_configs; i++) {
-                       if (models[i] &&
-                           !strcmp(codec->modelname, models[i])) {
-                               codec_info(codec, "model '%s' is selected\n",
-                                          models[i]);
-                               return i;
-                       }
-               }
-       }
-
-       if (!codec->bus->pci || !tbl)
-               return -1;
-
-       tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl);
-       if (!tbl)
-               return -1;
-       if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-               char tmp[10];
-               const char *model = NULL;
-               if (models)
-                       model = models[tbl->value];
-               if (!model) {
-                       sprintf(tmp, "#%d", tbl->value);
-                       model = tmp;
-               }
-               codec_info(codec, "model '%s' is selected for config %x:%x (%s)\n",
-                          model, tbl->subvendor, tbl->subdevice,
-                          (tbl->name ? tbl->name : "Unknown device"));
-#endif
-               return tbl->value;
-       }
-       return -1;
-}
-EXPORT_SYMBOL_GPL(snd_hda_check_board_config);
-
-/**
- * snd_hda_check_board_codec_sid_config - compare the current codec
-                                       subsystem ID with the
-                                       config table
-
-          This is important for Gateway notebooks with SB450 HDA Audio
-          where the vendor ID of the PCI device is:
-               ATI Technologies Inc SB450 HDA Audio [1002:437b]
-          and the vendor/subvendor are found only at the codec.
-
- * @codec: the HDA codec
- * @num_configs: number of config enums
- * @models: array of model name strings
- * @tbl: configuration table, terminated by null entries
- *
- * Compares the modelname or PCI subsystem id of the current codec with the
- * given configuration table.  If a matching entry is found, returns its
- * config value (supposed to be 0 or positive).
- *
- * If no entries are matching, the function returns a negative value.
- */
-int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
-                              int num_configs, const char * const *models,
-                              const struct snd_pci_quirk *tbl)
-{
-       const struct snd_pci_quirk *q;
-
-       /* Search for codec ID */
-       for (q = tbl; q->subvendor; q++) {
-               unsigned int mask = 0xffff0000 | q->subdevice_mask;
-               unsigned int id = (q->subdevice | (q->subvendor << 16)) & mask;
-               if ((codec->subsystem_id & mask) == id)
-                       break;
-       }
-
-       if (!q->subvendor)
-               return -1;
-
-       tbl = q;
-
-       if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-               char tmp[10];
-               const char *model = NULL;
-               if (models)
-                       model = models[tbl->value];
-               if (!model) {
-                       sprintf(tmp, "#%d", tbl->value);
-                       model = tmp;
-               }
-               codec_info(codec, "model '%s' is selected for config %x:%x (%s)\n",
-                          model, tbl->subvendor, tbl->subdevice,
-                          (tbl->name ? tbl->name : "Unknown device"));
-#endif
-               return tbl->value;
-       }
-       return -1;
-}
-EXPORT_SYMBOL_GPL(snd_hda_check_board_codec_sid_config);
-
 /**
  * snd_hda_add_new_ctls - create controls from the array
  * @codec: the HDA codec
index bbc5a1392c7522ce1cd961064d972a4e60c49561..9c8820f21f948ffb98bea96a0804d6e9b80e1aa8 100644 (file)
@@ -687,6 +687,4 @@ snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
                                struct snd_dma_buffer *dmab) {}
 #endif
 
-#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym)
-
 #endif /* __SOUND_HDA_CODEC_H */
index b956449ddada67751297b9154422509dc065a388..64220c08bd982b01851ab13b70660104d2d6e2d2 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/module.h>
 #include <sound/core.h>
 #include <sound/jack.h>
+#include <sound/tlv.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_auto_parser.h"
@@ -518,18 +519,6 @@ static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
        return val;
 }
 
-/* check whether the widget has the given amp capability for the direction */
-static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
-                          int dir, unsigned int bits)
-{
-       if (!nid)
-               return false;
-       if (get_wcaps(codec, nid) & (1 << (dir + 1)))
-               if (query_amp_caps(codec, nid, dir) & bits)
-                       return true;
-       return false;
-}
-
 static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1,
                          hda_nid_t nid2, int dir)
 {
@@ -539,11 +528,6 @@ static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1,
                query_amp_caps(codec, nid2, dir));
 }
 
-#define nid_has_mute(codec, nid, dir) \
-       check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))
-#define nid_has_volume(codec, nid, dir) \
-       check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
-
 /* look for a widget suitable for assigning a mute switch in the path */
 static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec,
                                       struct nid_path *path)
@@ -1105,6 +1089,7 @@ enum {
  */
 static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path)
 {
+       struct hda_gen_spec *spec = codec->spec;
        hda_nid_t nid;
        unsigned int val;
        int badness = 0;
@@ -1119,6 +1104,8 @@ static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path)
        nid = look_for_out_vol_nid(codec, path);
        if (nid) {
                val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+               if (spec->dac_min_mute)
+                       val |= HDA_AMP_VAL_MIN_MUTE;
                if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
                        badness += BAD_SHARED_VOL;
                else
@@ -1880,9 +1867,12 @@ static int parse_output_paths(struct hda_codec *codec)
                path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
                if (path)
                        spec->vmaster_nid = look_for_out_vol_nid(codec, path);
-               if (spec->vmaster_nid)
+               if (spec->vmaster_nid) {
                        snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
                                                HDA_OUTPUT, spec->vmaster_tlv);
+                       if (spec->dac_min_mute)
+                               spec->vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
+               }
        }
 
        /* set initial pinctl targets */
@@ -2025,7 +2015,8 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
  * independent HP controls
  */
 
-static void call_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack);
+static void call_hp_automute(struct hda_codec *codec,
+                            struct hda_jack_callback *jack);
 static int indep_hp_info(struct snd_kcontrol *kcontrol,
                         struct snd_ctl_elem_info *uinfo)
 {
@@ -3941,7 +3932,8 @@ static void call_update_outputs(struct hda_codec *codec)
 }
 
 /* standard HP-automute helper */
-void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+void snd_hda_gen_hp_automute(struct hda_codec *codec,
+                            struct hda_jack_callback *jack)
 {
        struct hda_gen_spec *spec = codec->spec;
        hda_nid_t *pins = spec->autocfg.hp_pins;
@@ -3961,7 +3953,8 @@ void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 EXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute);
 
 /* standard line-out-automute helper */
-void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+void snd_hda_gen_line_automute(struct hda_codec *codec,
+                              struct hda_jack_callback *jack)
 {
        struct hda_gen_spec *spec = codec->spec;
 
@@ -3981,7 +3974,8 @@ void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jac
 EXPORT_SYMBOL_GPL(snd_hda_gen_line_automute);
 
 /* standard mic auto-switch helper */
-void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack)
+void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
+                               struct hda_jack_callback *jack)
 {
        struct hda_gen_spec *spec = codec->spec;
        int i;
@@ -4004,7 +3998,8 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja
 EXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch);
 
 /* call appropriate hooks */
-static void call_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+static void call_hp_automute(struct hda_codec *codec,
+                            struct hda_jack_callback *jack)
 {
        struct hda_gen_spec *spec = codec->spec;
        if (spec->hp_automute_hook)
@@ -4014,7 +4009,7 @@ static void call_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 }
 
 static void call_line_automute(struct hda_codec *codec,
-                              struct hda_jack_tbl *jack)
+                              struct hda_jack_callback *jack)
 {
        struct hda_gen_spec *spec = codec->spec;
        if (spec->line_automute_hook)
@@ -4024,7 +4019,7 @@ static void call_line_automute(struct hda_codec *codec,
 }
 
 static void call_mic_autoswitch(struct hda_codec *codec,
-                               struct hda_jack_tbl *jack)
+                               struct hda_jack_callback *jack)
 {
        struct hda_gen_spec *spec = codec->spec;
        if (spec->mic_autoswitch_hook)
@@ -4173,7 +4168,7 @@ static int check_auto_mute_availability(struct hda_codec *codec)
                if (!is_jack_detectable(codec, nid))
                        continue;
                codec_dbg(codec, "Enable HP auto-muting on NID 0x%x\n", nid);
-               snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT,
+               snd_hda_jack_detect_enable_callback(codec, nid,
                                                    call_hp_automute);
                spec->detect_hp = 1;
        }
@@ -4186,7 +4181,6 @@ static int check_auto_mute_availability(struct hda_codec *codec)
                                        continue;
                                codec_dbg(codec, "Enable Line-Out auto-muting on NID 0x%x\n", nid);
                                snd_hda_jack_detect_enable_callback(codec, nid,
-                                                                   HDA_GEN_FRONT_EVENT,
                                                                    call_line_automute);
                                spec->detect_lo = 1;
                        }
@@ -4228,7 +4222,6 @@ static bool auto_mic_check_imux(struct hda_codec *codec)
        for (i = 1; i < spec->am_num_entries; i++)
                snd_hda_jack_detect_enable_callback(codec,
                                                    spec->am_entry[i].pin,
-                                                   HDA_GEN_MIC_EVENT,
                                                    call_mic_autoswitch);
        return true;
 }
index bb2dea743986b365813a9e55ed2342ecd4f25c07..61dd5153f512b99a26d44305dc2e17b723322077 100644 (file)
 #ifndef __SOUND_HDA_GENERIC_H
 #define __SOUND_HDA_GENERIC_H
 
-/* unsol event tags */
-enum {
-       HDA_GEN_HP_EVENT = 1, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT,
-       HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT
-};
-
 /* table entry for multi-io paths */
 struct hda_multi_io {
        hda_nid_t pin;          /* multi-io widget pin NID */
@@ -231,6 +225,7 @@ struct hda_gen_spec {
        unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */
        unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */
        unsigned int power_down_unused:1; /* power down unused widgets */
+       unsigned int dac_min_mute:1; /* minimal = mute for DACs */
 
        /* other internal flags */
        unsigned int no_analog:1; /* digital I/O only */
@@ -289,11 +284,11 @@ struct hda_gen_spec {
 
        /* automute / autoswitch hooks */
        void (*hp_automute_hook)(struct hda_codec *codec,
-                                struct hda_jack_tbl *tbl);
+                                struct hda_jack_callback *cb);
        void (*line_automute_hook)(struct hda_codec *codec,
-                                  struct hda_jack_tbl *tbl);
+                                  struct hda_jack_callback *cb);
        void (*mic_autoswitch_hook)(struct hda_codec *codec,
-                                   struct hda_jack_tbl *tbl);
+                                   struct hda_jack_callback *cb);
 };
 
 int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
@@ -325,11 +320,11 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec);
 
 /* standard jack event callbacks */
 void snd_hda_gen_hp_automute(struct hda_codec *codec,
-                            struct hda_jack_tbl *jack);
+                            struct hda_jack_callback *jack);
 void snd_hda_gen_line_automute(struct hda_codec *codec,
-                              struct hda_jack_tbl *jack);
+                              struct hda_jack_callback *jack);
 void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
-                               struct hda_jack_tbl *jack);
+                               struct hda_jack_callback *jack);
 void snd_hda_gen_update_outputs(struct hda_codec *codec);
 
 #ifdef CONFIG_PM
index 9746d73cec52b113b83e7e694f854cdac719e574..f56765ae73a7ed064d820c88f24a243097ea147e 100644 (file)
@@ -94,7 +94,7 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
 /**
  * snd_hda_jack_tbl_new - create a jack-table entry for the given NID
  */
-struct hda_jack_tbl *
+static struct hda_jack_tbl *
 snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid)
 {
        struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
@@ -108,21 +108,24 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid)
        jack->tag = codec->jacktbl.used;
        return jack;
 }
-EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_new);
 
 void snd_hda_jack_tbl_clear(struct hda_codec *codec)
 {
+       struct hda_jack_tbl *jack = codec->jacktbl.list;
+       int i;
+
+       for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+               struct hda_jack_callback *cb, *next;
 #ifdef CONFIG_SND_HDA_INPUT_JACK
-       /* free jack instances manually when clearing/reconfiguring */
-       if (!codec->bus->shutdown && codec->jacktbl.list) {
-               struct hda_jack_tbl *jack = codec->jacktbl.list;
-               int i;
-               for (i = 0; i < codec->jacktbl.used; i++, jack++) {
-                       if (jack->jack)
-                               snd_device_free(codec->bus->card, jack->jack);
+               /* free jack instances manually when clearing/reconfiguring */
+               if (!codec->bus->shutdown && jack->jack)
+                       snd_device_free(codec->bus->card, jack->jack);
+#endif
+               for (cb = jack->callback; cb; cb = next) {
+                       next = cb->next;
+                       kfree(cb);
                }
        }
-#endif
        snd_array_free(&codec->jacktbl);
 }
 
@@ -215,33 +218,49 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state);
 
 /**
  * snd_hda_jack_detect_enable - enable the jack-detection
+ *
+ * In the case of error, the return value will be a pointer embedded with
+ * errno.  Check and handle the return value appropriately with standard
+ * macros such as @IS_ERR() and @PTR_ERR().
  */
-int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
-                                       unsigned char action,
-                                       hda_jack_callback cb)
+struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
+                                   hda_jack_callback_fn func)
 {
-       struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid);
+       struct hda_jack_tbl *jack;
+       struct hda_jack_callback *callback = NULL;
+       int err;
+
+       jack = snd_hda_jack_tbl_new(codec, nid);
        if (!jack)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
+       if (func) {
+               callback = kzalloc(sizeof(*callback), GFP_KERNEL);
+               if (!callback)
+                       return ERR_PTR(-ENOMEM);
+               callback->func = func;
+               callback->tbl = jack;
+               callback->next = jack->callback;
+               jack->callback = callback;
+       }
+
        if (jack->jack_detect)
-               return 0; /* already registered */
+               return callback; /* already registered */
        jack->jack_detect = 1;
-       if (action)
-               jack->action = action;
-       if (cb)
-               jack->callback = cb;
        if (codec->jackpoll_interval > 0)
-               return 0; /* No unsol if we're polling instead */
-       return snd_hda_codec_write_cache(codec, nid, 0,
+               return callback; /* No unsol if we're polling instead */
+       err = snd_hda_codec_write_cache(codec, nid, 0,
                                         AC_VERB_SET_UNSOLICITED_ENABLE,
                                         AC_USRSP_EN | jack->tag);
+       if (err < 0)
+               return ERR_PTR(err);
+       return callback;
 }
 EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback);
 
-int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
-                              unsigned char action)
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid)
 {
-       return snd_hda_jack_detect_enable_callback(codec, nid, action, NULL);
+       return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback(codec, nid, NULL));
 }
 EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
 
@@ -431,7 +450,7 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
                return err;
 
        if (!phantom_jack)
-               return snd_hda_jack_detect_enable(codec, nid, 0);
+               return snd_hda_jack_detect_enable(codec, nid);
        return 0;
 }
 
@@ -498,13 +517,17 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls);
 static void call_jack_callback(struct hda_codec *codec,
                               struct hda_jack_tbl *jack)
 {
-       if (jack->callback)
-               jack->callback(codec, jack);
+       struct hda_jack_callback *cb;
+
+       for (cb = jack->callback; cb; cb = cb->next)
+               cb->func(codec, cb);
        if (jack->gated_jack) {
                struct hda_jack_tbl *gated =
                        snd_hda_jack_tbl_get(codec, jack->gated_jack);
-               if (gated && gated->callback)
-                       gated->callback(codec, gated);
+               if (gated) {
+                       for (cb = gated->callback; cb; cb = cb->next)
+                               cb->func(codec, cb);
+               }
        }
 }
 
index 46e1ea83ce3c208da42afb98e8668ae3b48e8591..13cb375454f665218517cc8b3456ff2e1beb808c 100644 (file)
 #ifndef __SOUND_HDA_JACK_H
 #define __SOUND_HDA_JACK_H
 
+#include <linux/err.h>
+
 struct auto_pin_cfg;
 struct hda_jack_tbl;
+struct hda_jack_callback;
+
+typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *);
 
-typedef void (*hda_jack_callback) (struct hda_codec *, struct hda_jack_tbl *);
+struct hda_jack_callback {
+       struct hda_jack_tbl *tbl;
+       hda_jack_callback_fn func;
+       unsigned int private_data;      /* arbitrary data */
+       struct hda_jack_callback *next;
+};
 
 struct hda_jack_tbl {
        hda_nid_t nid;
-       unsigned char action;           /* event action (0 = none) */
        unsigned char tag;              /* unsol event tag */
-       unsigned int private_data;      /* arbitrary data */
-       hda_jack_callback callback;
+       struct hda_jack_callback *callback;
        /* jack-detection stuff */
        unsigned int pin_sense;         /* cached pin-sense value */
        unsigned int jack_detect:1;     /* capable of jack-detection? */
@@ -43,34 +51,14 @@ snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid);
 struct hda_jack_tbl *
 snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag);
 
-struct hda_jack_tbl *
-snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid);
 void snd_hda_jack_tbl_clear(struct hda_codec *codec);
 
-/**
- * snd_hda_jack_get_action - get jack-tbl entry for the tag
- *
- * Call this from the unsol event handler to get the assigned action for the
- * event.  This will mark the dirty flag for the later reporting, too.
- */
-static inline unsigned char
-snd_hda_jack_get_action(struct hda_codec *codec, unsigned int tag)
-{
-       struct hda_jack_tbl *jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
-       if (jack) {
-               jack->jack_dirty = 1;
-               return jack->action;
-       }
-       return 0;
-}
-
 void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
 
-int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
-                              unsigned char action);
-int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
-                                       unsigned char action,
-                                       hda_jack_callback cb);
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid);
+struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
+                                   hda_jack_callback_fn cb);
 
 int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
                                 hda_nid_t gating_nid);
index 364bb413e02aa9d934bd970f1c4a0d0bd5c61be5..7eb44e78e141fcd0bcb98d2e9ce3358f9c4c80ae 100644 (file)
@@ -371,12 +371,6 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen);
 /*
  * Misc
  */
-int snd_hda_check_board_config(struct hda_codec *codec, int num_configs,
-                              const char * const *modelnames,
-                              const struct snd_pci_quirk *pci_list);
-int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
-                               int num_configs, const char * const *models,
-                               const struct snd_pci_quirk *tbl);
 int snd_hda_add_new_ctls(struct hda_codec *codec,
                         const struct snd_kcontrol_new *knew);
 
@@ -609,6 +603,14 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
 int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
                              unsigned int caps);
+bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+                          int dir, unsigned int bits);
+
+#define nid_has_mute(codec, nid, dir) \
+       snd_hda_check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))
+#define nid_has_volume(codec, nid, dir) \
+       snd_hda_check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
+
 
 /* flags for hda_nid_item */
 #define HDA_NID_ITEM_AMP       (1<<0)
index e2079090ca6f479ee04c3cd67f4d281165651481..9b49f156a12e6b424432909237694e624757ee24 100644 (file)
@@ -514,7 +514,7 @@ enum {
 
 static inline int strmatch(const char *a, const char *b)
 {
-       return strnicmp(a, b, strlen(b)) == 0;
+       return strncasecmp(a, b, strlen(b)) == 0;
 }
 
 /* parse the contents after the line "[codec]"
index 5d8455e2dacd709c308d215a44155134e15319cd..4f7ffa8c4a0db610f3bc20d570afe7d8770fe3de 100644 (file)
@@ -3224,8 +3224,14 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work)
 {
        struct ca0132_spec *spec = container_of(
                to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
+       struct hda_jack_tbl *jack;
+
        ca0132_select_out(spec->codec);
-       snd_hda_jack_report_sync(spec->codec);
+       jack = snd_hda_jack_tbl_get(spec->codec, UNSOL_TAG_HP);
+       if (jack) {
+               jack->block_report = 0;
+               snd_hda_jack_report_sync(spec->codec);
+       }
 }
 
 static void ca0132_set_dmic(struct hda_codec *codec, int enable);
@@ -4114,12 +4120,6 @@ static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
        }
 }
 
-static void ca0132_init_unsol(struct hda_codec *codec)
-{
-       snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP);
-       snd_hda_jack_detect_enable(codec, UNSOL_TAG_AMIC1, UNSOL_TAG_AMIC1);
-}
-
 static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir)
 {
        unsigned int caps;
@@ -4390,7 +4390,8 @@ static void ca0132_download_dsp(struct hda_codec *codec)
                ca0132_set_dsp_msr(codec, true);
 }
 
-static void ca0132_process_dsp_response(struct hda_codec *codec)
+static void ca0132_process_dsp_response(struct hda_codec *codec,
+                                       struct hda_jack_callback *callback)
 {
        struct ca0132_spec *spec = codec->spec;
 
@@ -4403,36 +4404,31 @@ static void ca0132_process_dsp_response(struct hda_codec *codec)
        dspio_clear_response_queue(codec);
 }
 
-static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res)
+static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
 {
        struct ca0132_spec *spec = codec->spec;
 
-       if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) {
-               ca0132_process_dsp_response(codec);
-       } else {
-               res = snd_hda_jack_get_action(codec,
-                               (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f);
-
-               codec_dbg(codec, "snd_hda_jack_get_action: 0x%x\n", res);
-
-               switch (res) {
-               case UNSOL_TAG_HP:
-                       /* Delay enabling the HP amp, to let the mic-detection
-                        * state machine run.
-                        */
-                       cancel_delayed_work_sync(&spec->unsol_hp_work);
-                       queue_delayed_work(codec->bus->workq,
-                                          &spec->unsol_hp_work,
-                                          msecs_to_jiffies(500));
-                       break;
-               case UNSOL_TAG_AMIC1:
-                       ca0132_select_mic(codec);
-                       snd_hda_jack_report_sync(codec);
-                       break;
-               default:
-                       break;
-               }
-       }
+       /* Delay enabling the HP amp, to let the mic-detection
+        * state machine run.
+        */
+       cancel_delayed_work_sync(&spec->unsol_hp_work);
+       queue_delayed_work(codec->bus->workq, &spec->unsol_hp_work,
+                          msecs_to_jiffies(500));
+       cb->tbl->block_report = 1;
+}
+
+static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
+{
+       ca0132_select_mic(codec);
+}
+
+static void ca0132_init_unsol(struct hda_codec *codec)
+{
+       snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_HP, hp_callback);
+       snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_AMIC1,
+                                           amic_callback);
+       snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP,
+                                           ca0132_process_dsp_response);
 }
 
 /*
@@ -4443,8 +4439,6 @@ static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res)
 static struct hda_verb ca0132_base_init_verbs[] = {
        /*enable ct extension*/
        {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1},
-       /*enable DSP node unsol, needed for DSP download*/
-       {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_DSP},
        {}
 };
 
@@ -4561,6 +4555,8 @@ static int ca0132_init(struct hda_codec *codec)
 
        snd_hda_power_up(codec);
 
+       ca0132_init_unsol(codec);
+
        ca0132_init_params(codec);
        ca0132_init_flags(codec);
        snd_hda_sequence_write(codec, spec->base_init_verbs);
@@ -4583,8 +4579,6 @@ static int ca0132_init(struct hda_codec *codec)
        for (i = 0; i < spec->num_init_verbs; i++)
                snd_hda_sequence_write(codec, spec->init_verbs[i]);
 
-       ca0132_init_unsol(codec);
-
        ca0132_select_out(codec);
        ca0132_select_mic(codec);
 
@@ -4612,7 +4606,7 @@ static struct hda_codec_ops ca0132_patch_ops = {
        .build_pcms = ca0132_build_pcms,
        .init = ca0132_init,
        .free = ca0132_free,
-       .unsol_event = ca0132_unsol_event,
+       .unsol_event = snd_hda_jack_unsol_event,
 };
 
 static void ca0132_config(struct hda_codec *codec)
index 3db724eaa53c1e154cb550b52bb0e245b2eca2e4..1589c9bcce3e15a230f87d352f2ae165252c358d 100644 (file)
@@ -135,8 +135,6 @@ enum {
 #define CS421X_IDX_DAC_CFG     0x03
 #define CS421X_IDX_SPK_CTL     0x04
 
-#define SPDIF_EVENT            0x04
-
 /* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */
 #define CS4213_VENDOR_NID      0x09
 
@@ -984,7 +982,7 @@ static void cs4210_pinmux_init(struct hda_codec *codec)
 }
 
 static void cs4210_spdif_automute(struct hda_codec *codec,
-                                 struct hda_jack_tbl *tbl)
+                                 struct hda_jack_callback *tbl)
 {
        struct cs_spec *spec = codec->spec;
        bool spdif_present = false;
@@ -1019,7 +1017,6 @@ static void parse_cs421x_digital(struct hda_codec *codec)
                if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
                        spec->spdif_detect = 1;
                        snd_hda_jack_detect_enable_callback(codec, nid,
-                                                           SPDIF_EVENT,
                                                            cs4210_spdif_automute);
                }
        }
index 47ccb8f44adb7ab5e0353a17cd0e68c2e1c363f0..71e4bad06345c856fcfb80e9e75578d444db117e 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/module.h>
 #include <sound/core.h>
 #include <sound/jack.h>
-#include <sound/tlv.h>
 
 #include "hda_codec.h"
 #include "hda_local.h"
@@ -394,7 +393,8 @@ static void olpc_xo_update_mic_pins(struct hda_codec *codec)
 }
 
 /* mic_autoswitch hook */
-static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack)
+static void olpc_xo_automic(struct hda_codec *codec,
+                           struct hda_jack_callback *jack)
 {
        struct conexant_spec *spec = codec->spec;
        int saved_cached_write = codec->cached_write;
@@ -752,6 +752,7 @@ static const struct hda_model_fixup cxt5051_fixup_models[] = {
 static const struct snd_pci_quirk cxt5066_fixups[] = {
        SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC),
+       SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC),
        SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
        SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
@@ -787,6 +788,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
  */
 static void add_cx5051_fake_mutes(struct hda_codec *codec)
 {
+       struct conexant_spec *spec = codec->spec;
        static hda_nid_t out_nids[] = {
                0x10, 0x11, 0
        };
@@ -796,6 +798,7 @@ static void add_cx5051_fake_mutes(struct hda_codec *codec)
                snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT,
                                          AC_AMPCAP_MIN_MUTE |
                                          query_amp_caps(codec, *p, HDA_OUTPUT));
+       spec->gen.dac_min_mute = true;
 }
 
 static int patch_conexant_auto(struct hda_codec *codec)
@@ -868,11 +871,6 @@ static int patch_conexant_auto(struct hda_codec *codec)
        if (err < 0)
                goto error;
 
-       if (codec->vendor_id == 0x14f15051) {
-               /* minimum value is actually mute */
-               spec->gen.vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
-       }
-
        codec->patch_ops = cx_auto_patch_ops;
 
        /* Some laptops with Conexant chips show stalls in S3 resume,
index 99d7d7fecaad09934090eb6575cfb43131f2dfb4..39862e98551c41466505ee7b1764496e04799880 100644 (file)
@@ -1163,17 +1163,23 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
 
 static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
 
-static void jack_callback(struct hda_codec *codec, struct hda_jack_tbl *jack)
+static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid)
 {
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx = pin_nid_to_pin_index(codec, jack->nid);
+       int pin_idx = pin_nid_to_pin_index(codec, nid);
+
        if (pin_idx < 0)
                return;
-
        if (hdmi_present_sense(get_pin(spec, pin_idx), 1))
                snd_hda_jack_report_sync(codec);
 }
 
+static void jack_callback(struct hda_codec *codec,
+                         struct hda_jack_callback *jack)
+{
+       check_presence_and_report(codec, jack->tbl->nid);
+}
+
 static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
 {
        int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
@@ -1190,7 +1196,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
                codec->addr, jack->nid, dev_entry, !!(res & AC_UNSOL_RES_IA),
                !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
 
-       jack_callback(codec, jack);
+       check_presence_and_report(codec, jack->nid);
 }
 
 static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -2165,7 +2171,7 @@ static int generic_hdmi_init(struct hda_codec *codec)
                hda_nid_t pin_nid = per_pin->pin_nid;
 
                hdmi_init_pin(codec, pin_nid);
-               snd_hda_jack_detect_enable_callback(codec, pin_nid, pin_nid,
+               snd_hda_jack_detect_enable_callback(codec, pin_nid,
                        codec->jackpoll_interval > 0 ? jack_callback : NULL);
        }
        return 0;
@@ -2428,7 +2434,7 @@ static int simple_playback_init(struct hda_codec *codec)
        if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
                snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
                                    AMP_OUT_UNMUTE);
-       snd_hda_jack_detect_enable(codec, pin, pin);
+       snd_hda_jack_detect_enable(codec, pin);
        return 0;
 }
 
index 1ba22fb527c288b751d3064e041759e881013605..bc86c36b4bfa39b383f113b5dbd377b0c8ffd22d 100644 (file)
@@ -40,9 +40,6 @@
 /* keep halting ALC5505 DSP, for power saving */
 #define HALT_REALTEK_ALC5505
 
-/* unsol event tags */
-#define ALC_DCVOL_EVENT                0x08
-
 /* for GPIO Poll */
 #define GPIO_MASK      0x03
 
@@ -93,11 +90,6 @@ struct alc_spec {
        struct alc_customize_define cdefine;
        unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
 
-       /* inverted dmic fix */
-       unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
-       unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
-       hda_nid_t inv_dmic_pin;
-
        /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */
        int mute_led_polarity;
        hda_nid_t mute_led_nid;
@@ -128,6 +120,83 @@ struct alc_spec {
        unsigned int coef0;
 };
 
+/*
+ * COEF access helper functions
+ */
+
+static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+                              unsigned int coef_idx)
+{
+       unsigned int val;
+
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx);
+       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0);
+       return val;
+}
+
+#define alc_read_coef_idx(codec, coef_idx) \
+       alc_read_coefex_idx(codec, 0x20, coef_idx)
+
+static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+                                unsigned int coef_idx, unsigned int coef_val)
+{
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val);
+}
+
+#define alc_write_coef_idx(codec, coef_idx, coef_val) \
+       alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
+
+static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+                                 unsigned int coef_idx, unsigned int mask,
+                                 unsigned int bits_set)
+{
+       unsigned int val = alc_read_coefex_idx(codec, nid, coef_idx);
+
+       if (val != -1)
+               alc_write_coefex_idx(codec, nid, coef_idx,
+                                    (val & ~mask) | bits_set);
+}
+
+#define alc_update_coef_idx(codec, coef_idx, mask, bits_set)   \
+       alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set)
+
+/* a special bypass for COEF 0; read the cached value at the second time */
+static unsigned int alc_get_coef0(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (!spec->coef0)
+               spec->coef0 = alc_read_coef_idx(codec, 0);
+       return spec->coef0;
+}
+
+/* coef writes/updates batch */
+struct coef_fw {
+       unsigned char nid;
+       unsigned char idx;
+       unsigned short mask;
+       unsigned short val;
+};
+
+#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \
+       { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) }
+#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val)
+#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val)
+#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val)
+
+static void alc_process_coef_fw(struct hda_codec *codec,
+                               const struct coef_fw *fw)
+{
+       for (; fw->nid; fw++) {
+               if (fw->mask == (unsigned short)-1)
+                       alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val);
+               else
+                       alc_update_coefex_idx(codec, fw->nid, fw->idx,
+                                             fw->mask, fw->val);
+       }
+}
+
 /*
  * Append the given mixer and verb elements for the later use
  * The mixer array is referred in build_controls(), and init_verbs are
@@ -173,20 +242,10 @@ static const struct hda_verb alc_gpio3_init_verbs[] = {
 static void alc_fix_pll(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int val;
 
-       if (!spec->pll_nid)
-               return;
-       snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
-                           spec->pll_coef_idx);
-       val = snd_hda_codec_read(codec, spec->pll_nid, 0,
-                                AC_VERB_GET_PROC_COEF, 0);
-       if (val == -1)
-               return;
-       snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
-                           spec->pll_coef_idx);
-       snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_PROC_COEF,
-                           val & ~(1 << spec->pll_coef_bit));
+       if (spec->pll_nid)
+               alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx,
+                                     1 << spec->pll_coef_bit, 0);
 }
 
 static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
@@ -200,7 +259,8 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
 }
 
 /* update the master volume per volume-knob's unsol event */
-static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack)
+static void alc_update_knob_master(struct hda_codec *codec,
+                                  struct hda_jack_callback *jack)
 {
        unsigned int val;
        struct snd_kcontrol *kctl;
@@ -212,7 +272,7 @@ static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl
        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
        if (!uctl)
                return;
-       val = snd_hda_codec_read(codec, jack->nid, 0,
+       val = snd_hda_codec_read(codec, jack->tbl->nid, 0,
                                 AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
        val &= HDA_AMP_VOLMASK;
        uctl->value.integer.value[0] = val;
@@ -231,30 +291,18 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
 /* additional initialization for ALC888 variants */
 static void alc888_coef_init(struct hda_codec *codec)
 {
-       unsigned int tmp;
-
-       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0);
-       tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
-       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
-       if ((tmp & 0xf0) == 0x20)
+       if (alc_get_coef0(codec) == 0x20)
                /* alc888S-VC */
-               snd_hda_codec_read(codec, 0x20, 0,
-                                  AC_VERB_SET_PROC_COEF, 0x830);
+               alc_write_coef_idx(codec, 7, 0x830);
         else
                 /* alc888-VB */
-                snd_hda_codec_read(codec, 0x20, 0,
-                                   AC_VERB_SET_PROC_COEF, 0x3030);
+               alc_write_coef_idx(codec, 7, 0x3030);
 }
 
 /* additional initialization for ALC889 variants */
 static void alc889_coef_init(struct hda_codec *codec)
 {
-       unsigned int tmp;
-
-       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
-       tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
-       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
-       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010);
+       alc_update_coef_idx(codec, 7, 0, 0x2010);
 }
 
 /* turn on/off EAPD control (only if available) */
@@ -295,8 +343,6 @@ static void alc_eapd_shutup(struct hda_codec *codec)
 /* generic EAPD initialization */
 static void alc_auto_init_amp(struct hda_codec *codec, int type)
 {
-       unsigned int tmp;
-
        alc_auto_setup_eapd(codec, true);
        switch (type) {
        case ALC_INIT_GPIO1:
@@ -311,15 +357,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
        case ALC_INIT_DEFAULT:
                switch (codec->vendor_id) {
                case 0x10ec0260:
-                       snd_hda_codec_write(codec, 0x1a, 0,
-                                           AC_VERB_SET_COEF_INDEX, 7);
-                       tmp = snd_hda_codec_read(codec, 0x1a, 0,
-                                                AC_VERB_GET_PROC_COEF, 0);
-                       snd_hda_codec_write(codec, 0x1a, 0,
-                                           AC_VERB_SET_COEF_INDEX, 7);
-                       snd_hda_codec_write(codec, 0x1a, 0,
-                                           AC_VERB_SET_PROC_COEF,
-                                           tmp | 0x2010);
+                       alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010);
                        break;
                case 0x10ec0262:
                case 0x10ec0880:
@@ -337,15 +375,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
 #if 0 /* XXX: This may cause the silent output on speaker on some machines */
                case 0x10ec0267:
                case 0x10ec0268:
-                       snd_hda_codec_write(codec, 0x20, 0,
-                                           AC_VERB_SET_COEF_INDEX, 7);
-                       tmp = snd_hda_codec_read(codec, 0x20, 0,
-                                                AC_VERB_GET_PROC_COEF, 0);
-                       snd_hda_codec_write(codec, 0x20, 0,
-                                           AC_VERB_SET_COEF_INDEX, 7);
-                       snd_hda_codec_write(codec, 0x20, 0,
-                                           AC_VERB_SET_PROC_COEF,
-                                           tmp | 0x3000);
+                       alc_update_coef_idx(codec, 7, 0, 0x3000);
                        break;
 #endif /* XXX */
                }
@@ -587,191 +617,15 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
        }
 }
 
-/*
- * COEF access helper functions
- */
-
-static int alc_read_coefex_idx(struct hda_codec *codec,
-                                       hda_nid_t nid,
-                                       unsigned int coef_idx)
-{
-       unsigned int val;
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX,
-                               coef_idx);
-       val = snd_hda_codec_read(codec, nid, 0,
-                               AC_VERB_GET_PROC_COEF, 0);
-       return val;
-}
-
-#define alc_read_coef_idx(codec, coef_idx) \
-       alc_read_coefex_idx(codec, 0x20, coef_idx)
-
-static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
-                                                       unsigned int coef_idx,
-                                                       unsigned int coef_val)
-{
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX,
-                           coef_idx);
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF,
-                           coef_val);
-}
-
-#define alc_write_coef_idx(codec, coef_idx, coef_val) \
-       alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
-
-/* a special bypass for COEF 0; read the cached value at the second time */
-static unsigned int alc_get_coef0(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       if (!spec->coef0)
-               spec->coef0 = alc_read_coef_idx(codec, 0);
-       return spec->coef0;
-}
-
 /*
  */
 
-static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
-{
-       struct hda_gen_spec *spec = codec->spec;
-       if (spec->dyn_adc_switch)
-               adc_idx = spec->dyn_adc_idx[imux_idx];
-       return spec->adc_nids[adc_idx];
-}
-
-static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
-{
-       struct alc_spec *spec = codec->spec;
-       struct hda_input_mux *imux = &spec->gen.input_mux;
-       struct nid_path *path;
-       hda_nid_t nid;
-       int i, dir, parm;
-       unsigned int val;
-
-       for (i = 0; i < imux->num_items; i++) {
-               if (spec->gen.imux_pins[i] == spec->inv_dmic_pin)
-                       break;
-       }
-       if (i >= imux->num_items)
-               return;
-
-       path = snd_hda_get_nid_path(codec, spec->inv_dmic_pin,
-                                   get_adc_nid(codec, adc_idx, i));
-       val = path->ctls[NID_PATH_MUTE_CTL];
-       if (!val)
-               return;
-       nid = get_amp_nid_(val);
-       dir = get_amp_direction_(val);
-       parm = AC_AMP_SET_RIGHT |
-               (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT);
-
-       /* flush all cached amps at first */
-       snd_hda_codec_flush_cache(codec);
-
-       /* we care only right channel */
-       val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
-       if (val & 0x80) /* if already muted, we don't need to touch */
-               return;
-       val |= 0x80;
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                           parm | val);
-}
-
-/*
- * Inverted digital-mic handling
- *
- * First off, it's a bit tricky.  The "Inverted Internal Mic Capture Switch"
- * gives the additional mute only to the right channel of the digital mic
- * capture stream.  This is a workaround for avoiding the almost silence
- * by summing the stereo stream from some (known to be ForteMedia)
- * digital mic unit.
- *
- * The logic is to call alc_inv_dmic_sync() after each action (possibly)
- * modifying ADC amp.  When the mute flag is set, it mutes the R-channel
- * without caching so that the cache can still keep the original value.
- * The cached value is then restored when the flag is set off or any other
- * than d-mic is used as the current input source.
- */
-static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
-{
-       struct alc_spec *spec = codec->spec;
-       int src, nums;
-
-       if (!spec->inv_dmic_fixup)
-               return;
-       if (!spec->inv_dmic_muted && !force)
-               return;
-       nums = spec->gen.dyn_adc_switch ? 1 : spec->gen.num_adc_nids;
-       for (src = 0; src < nums; src++) {
-               bool dmic_fixup = false;
-
-               if (spec->inv_dmic_muted &&
-                   spec->gen.imux_pins[spec->gen.cur_mux[src]] == spec->inv_dmic_pin)
-                       dmic_fixup = true;
-               if (!dmic_fixup && !force)
-                       continue;
-               alc_inv_dmic_sync_adc(codec, src);
-       }
-}
-
-static void alc_inv_dmic_hook(struct hda_codec *codec,
-                             struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       alc_inv_dmic_sync(codec, false);
-}
-
-static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct alc_spec *spec = codec->spec;
-
-       ucontrol->value.integer.value[0] = !spec->inv_dmic_muted;
-       return 0;
-}
-
-static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct alc_spec *spec = codec->spec;
-       unsigned int val = !ucontrol->value.integer.value[0];
-
-       if (val == spec->inv_dmic_muted)
-               return 0;
-       spec->inv_dmic_muted = val;
-       alc_inv_dmic_sync(codec, true);
-       return 0;
-}
-
-static const struct snd_kcontrol_new alc_inv_dmic_sw = {
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "Inverted Internal Mic Capture Switch",
-       .info = snd_ctl_boolean_mono_info,
-       .get = alc_inv_dmic_sw_get,
-       .put = alc_inv_dmic_sw_put,
-};
-
-static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
+static void alc_fixup_inv_dmic(struct hda_codec *codec,
+                              const struct hda_fixup *fix, int action)
 {
        struct alc_spec *spec = codec->spec;
 
-       if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &alc_inv_dmic_sw))
-               return -ENOMEM;
-       spec->inv_dmic_fixup = 1;
-       spec->inv_dmic_muted = 0;
-       spec->inv_dmic_pin = nid;
-       spec->gen.cap_sync_hook = alc_inv_dmic_hook;
-       return 0;
-}
-
-/* typically the digital mic is put at node 0x12 */
-static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec,
-                                   const struct hda_fixup *fix, int action)
-{
-       if (action == HDA_FIXUP_ACT_PROBE)
-               alc_add_inv_dmic_mixer(codec, 0x12);
+       spec->gen.inv_dmic_split = 1;
 }
 
 
@@ -880,7 +734,6 @@ static int alc_resume(struct hda_codec *codec)
        codec->patch_ops.init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
-       alc_inv_dmic_sync(codec, true);
        hda_call_check_power_status(codec, 0x01);
        return 0;
 }
@@ -1134,7 +987,8 @@ static void alc880_fixup_vol_knob(struct hda_codec *codec,
                                  const struct hda_fixup *fix, int action)
 {
        if (action == HDA_FIXUP_ACT_PROBE)
-               snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master);
+               snd_hda_jack_detect_enable_callback(codec, 0x21,
+                                                   alc_update_knob_master);
 }
 
 static const struct hda_fixup alc880_fixups[] = {
@@ -1597,7 +1451,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
                spec->gen.detect_hp = 1;
                spec->gen.automute_speaker = 1;
                spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
-               snd_hda_jack_detect_enable_callback(codec, 0x0f, HDA_GEN_HP_EVENT,
+               snd_hda_jack_detect_enable_callback(codec, 0x0f,
                                                    snd_hda_gen_hp_automute);
                snd_hda_add_verbs(codec, alc_gpio1_init_verbs);
        }
@@ -2222,7 +2076,7 @@ static const struct hda_fixup alc882_fixups[] = {
        },
        [ALC882_FIXUP_INV_DMIC] = {
                .type = HDA_FIXUP_FUNC,
-               .v.func = alc_fixup_inv_dmic_0x12,
+               .v.func = alc_fixup_inv_dmic,
        },
        [ALC882_FIXUP_NO_PRIMARY_HP] = {
                .type = HDA_FIXUP_FUNC,
@@ -2473,7 +2327,7 @@ static const struct hda_fixup alc262_fixups[] = {
        },
        [ALC262_FIXUP_INV_DMIC] = {
                .type = HDA_FIXUP_FUNC,
-               .v.func = alc_fixup_inv_dmic_0x12,
+               .v.func = alc_fixup_inv_dmic,
        },
        [ALC262_FIXUP_INTEL_BAYLEYBAY] = {
                .type = HDA_FIXUP_FUNC,
@@ -2517,13 +2371,7 @@ static int patch_alc262(struct hda_codec *codec)
        /* pshou 07/11/05  set a zero PCM sample to DAC when FIFO is
         * under-run
         */
-       {
-       int tmp;
-       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
-       tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
-       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
-       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
-       }
+       alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x80);
 #endif
        alc_fix_pll_init(codec, 0x20, 0x0a, 10);
 
@@ -2592,7 +2440,7 @@ enum {
 static const struct hda_fixup alc268_fixups[] = {
        [ALC268_FIXUP_INV_DMIC] = {
                .type = HDA_FIXUP_FUNC,
-               .v.func = alc_fixup_inv_dmic_0x12,
+               .v.func = alc_fixup_inv_dmic,
        },
        [ALC268_FIXUP_HP_EAPD] = {
                .type = HDA_FIXUP_VERBS,
@@ -2809,14 +2657,7 @@ static void alc286_shutup(struct hda_codec *codec)
 
 static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up)
 {
-       int val = alc_read_coef_idx(codec, 0x04);
-       if (val == -1)
-               return;
-       if (power_up)
-               val |= 1 << 11;
-       else
-               val &= ~(1 << 11);
-       alc_write_coef_idx(codec, 0x04, val);
+       alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0);
 }
 
 static void alc269_shutup(struct hda_codec *codec)
@@ -2832,79 +2673,42 @@ static void alc269_shutup(struct hda_codec *codec)
        snd_hda_shutup_pins(codec);
 }
 
+static struct coef_fw alc282_coefs[] = {
+       WRITE_COEF(0x03, 0x0002), /* Power Down Control */
+       WRITE_COEF(0x05, 0x0700), /* FIFO and filter clock */
+       WRITE_COEF(0x07, 0x0200), /* DMIC control */
+       UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
+       UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
+       WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */
+       WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */
+       WRITE_COEF(0x0e, 0x6e00), /* LDO1/2/3, DAC/ADC */
+       UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */
+       UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */
+       WRITE_COEF(0x6f, 0x0), /* Class D test 4 */
+       UPDATE_COEF(0x0c, 0xfe00, 0), /* IO power down directly */
+       WRITE_COEF(0x34, 0xa0c0), /* ANC */
+       UPDATE_COEF(0x16, 0x0008, 0), /* AGC MUX */
+       UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */
+       UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */
+       WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */
+       WRITE_COEF(0x63, 0x2902), /* PLL */
+       WRITE_COEF(0x68, 0xa080), /* capless control 2 */
+       WRITE_COEF(0x69, 0x3400), /* capless control 3 */
+       WRITE_COEF(0x6a, 0x2f3e), /* capless control 4 */
+       WRITE_COEF(0x6b, 0x0), /* capless control 5 */
+       UPDATE_COEF(0x6d, 0x0fff, 0x0900), /* class D test 2 */
+       WRITE_COEF(0x6e, 0x110a), /* class D test 3 */
+       UPDATE_COEF(0x70, 0x00f8, 0x00d8), /* class D test 5 */
+       WRITE_COEF(0x71, 0x0014), /* class D test 6 */
+       WRITE_COEF(0x72, 0xc2ba), /* classD OCP */
+       UPDATE_COEF(0x77, 0x0f80, 0), /* classD pure DC test */
+       WRITE_COEF(0x6c, 0xfc06), /* Class D amp control */
+       {}
+};
+
 static void alc282_restore_default_value(struct hda_codec *codec)
 {
-       int val;
-
-       /* Power Down Control */
-       alc_write_coef_idx(codec, 0x03, 0x0002);
-       /* FIFO and filter clock */
-       alc_write_coef_idx(codec, 0x05, 0x0700);
-       /* DMIC control */
-       alc_write_coef_idx(codec, 0x07, 0x0200);
-       /* Analog clock */
-       val = alc_read_coef_idx(codec, 0x06);
-       alc_write_coef_idx(codec, 0x06, (val & ~0x00f0) | 0x0);
-       /* JD */
-       val = alc_read_coef_idx(codec, 0x08);
-       alc_write_coef_idx(codec, 0x08, (val & ~0xfffc) | 0x0c2c);
-       /* JD offset1 */
-       alc_write_coef_idx(codec, 0x0a, 0xcccc);
-       /* JD offset2 */
-       alc_write_coef_idx(codec, 0x0b, 0xcccc);
-       /* LDO1/2/3, DAC/ADC */
-       alc_write_coef_idx(codec, 0x0e, 0x6e00);
-       /* JD */
-       val = alc_read_coef_idx(codec, 0x0f);
-       alc_write_coef_idx(codec, 0x0f, (val & ~0xf800) | 0x1000);
-       /* Capless */
-       val = alc_read_coef_idx(codec, 0x10);
-       alc_write_coef_idx(codec, 0x10, (val & ~0xfc00) | 0x0c00);
-       /* Class D test 4 */
-       alc_write_coef_idx(codec, 0x6f, 0x0);
-       /* IO power down directly */
-       val = alc_read_coef_idx(codec, 0x0c);
-       alc_write_coef_idx(codec, 0x0c, (val & ~0xfe00) | 0x0);
-       /* ANC */
-       alc_write_coef_idx(codec, 0x34, 0xa0c0);
-       /* AGC MUX */
-       val = alc_read_coef_idx(codec, 0x16);
-       alc_write_coef_idx(codec, 0x16, (val & ~0x0008) | 0x0);
-       /* DAC simple content protection */
-       val = alc_read_coef_idx(codec, 0x1d);
-       alc_write_coef_idx(codec, 0x1d, (val & ~0x00e0) | 0x0);
-       /* ADC simple content protection */
-       val = alc_read_coef_idx(codec, 0x1f);
-       alc_write_coef_idx(codec, 0x1f, (val & ~0x00e0) | 0x0);
-       /* DAC ADC Zero Detection */
-       alc_write_coef_idx(codec, 0x21, 0x8804);
-       /* PLL */
-       alc_write_coef_idx(codec, 0x63, 0x2902);
-       /* capless control 2 */
-       alc_write_coef_idx(codec, 0x68, 0xa080);
-       /* capless control 3 */
-       alc_write_coef_idx(codec, 0x69, 0x3400);
-       /* capless control 4 */
-       alc_write_coef_idx(codec, 0x6a, 0x2f3e);
-       /* capless control 5 */
-       alc_write_coef_idx(codec, 0x6b, 0x0);
-       /* class D test 2 */
-       val = alc_read_coef_idx(codec, 0x6d);
-       alc_write_coef_idx(codec, 0x6d, (val & ~0x0fff) | 0x0900);
-       /* class D test 3 */
-       alc_write_coef_idx(codec, 0x6e, 0x110a);
-       /* class D test 5 */
-       val = alc_read_coef_idx(codec, 0x70);
-       alc_write_coef_idx(codec, 0x70, (val & ~0x00f8) | 0x00d8);
-       /* class D test 6 */
-       alc_write_coef_idx(codec, 0x71, 0x0014);
-       /* classD OCP */
-       alc_write_coef_idx(codec, 0x72, 0xc2ba);
-       /* classD pure DC test */
-       val = alc_read_coef_idx(codec, 0x77);
-       alc_write_coef_idx(codec, 0x77, (val & ~0x0f80) | 0x0);
-       /* Class D amp control */
-       alc_write_coef_idx(codec, 0x6c, 0xfc06);
+       alc_process_coef_fw(codec, alc282_coefs);
 }
 
 static void alc282_init(struct hda_codec *codec)
@@ -2980,87 +2784,45 @@ static void alc282_shutup(struct hda_codec *codec)
        alc_write_coef_idx(codec, 0x78, coef78);
 }
 
+static struct coef_fw alc283_coefs[] = {
+       WRITE_COEF(0x03, 0x0002), /* Power Down Control */
+       WRITE_COEF(0x05, 0x0700), /* FIFO and filter clock */
+       WRITE_COEF(0x07, 0x0200), /* DMIC control */
+       UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
+       UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
+       WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */
+       WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */
+       WRITE_COEF(0x0e, 0x6fc0), /* LDO1/2/3, DAC/ADC */
+       UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */
+       UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */
+       WRITE_COEF(0x3a, 0x0), /* Class D test 4 */
+       UPDATE_COEF(0x0c, 0xfe00, 0x0), /* IO power down directly */
+       WRITE_COEF(0x22, 0xa0c0), /* ANC */
+       UPDATE_COEFEX(0x53, 0x01, 0x000f, 0x0008), /* AGC MUX */
+       UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */
+       UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */
+       WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */
+       WRITE_COEF(0x2e, 0x2902), /* PLL */
+       WRITE_COEF(0x33, 0xa080), /* capless control 2 */
+       WRITE_COEF(0x34, 0x3400), /* capless control 3 */
+       WRITE_COEF(0x35, 0x2f3e), /* capless control 4 */
+       WRITE_COEF(0x36, 0x0), /* capless control 5 */
+       UPDATE_COEF(0x38, 0x0fff, 0x0900), /* class D test 2 */
+       WRITE_COEF(0x39, 0x110a), /* class D test 3 */
+       UPDATE_COEF(0x3b, 0x00f8, 0x00d8), /* class D test 5 */
+       WRITE_COEF(0x3c, 0x0014), /* class D test 6 */
+       WRITE_COEF(0x3d, 0xc2ba), /* classD OCP */
+       UPDATE_COEF(0x42, 0x0f80, 0x0), /* classD pure DC test */
+       WRITE_COEF(0x49, 0x0), /* test mode */
+       UPDATE_COEF(0x40, 0xf800, 0x9800), /* Class D DC enable */
+       UPDATE_COEF(0x42, 0xf000, 0x2000), /* DC offset */
+       WRITE_COEF(0x37, 0xfc06), /* Class D amp control */
+       {}
+};
+
 static void alc283_restore_default_value(struct hda_codec *codec)
 {
-       int val;
-
-       /* Power Down Control */
-       alc_write_coef_idx(codec, 0x03, 0x0002);
-       /* FIFO and filter clock */
-       alc_write_coef_idx(codec, 0x05, 0x0700);
-       /* DMIC control */
-       alc_write_coef_idx(codec, 0x07, 0x0200);
-       /* Analog clock */
-       val = alc_read_coef_idx(codec, 0x06);
-       alc_write_coef_idx(codec, 0x06, (val & ~0x00f0) | 0x0);
-       /* JD */
-       val = alc_read_coef_idx(codec, 0x08);
-       alc_write_coef_idx(codec, 0x08, (val & ~0xfffc) | 0x0c2c);
-       /* JD offset1 */
-       alc_write_coef_idx(codec, 0x0a, 0xcccc);
-       /* JD offset2 */
-       alc_write_coef_idx(codec, 0x0b, 0xcccc);
-       /* LDO1/2/3, DAC/ADC */
-       alc_write_coef_idx(codec, 0x0e, 0x6fc0);
-       /* JD */
-       val = alc_read_coef_idx(codec, 0x0f);
-       alc_write_coef_idx(codec, 0x0f, (val & ~0xf800) | 0x1000);
-       /* Capless */
-       val = alc_read_coef_idx(codec, 0x10);
-       alc_write_coef_idx(codec, 0x10, (val & ~0xfc00) | 0x0c00);
-       /* Class D test 4 */
-       alc_write_coef_idx(codec, 0x3a, 0x0);
-       /* IO power down directly */
-       val = alc_read_coef_idx(codec, 0x0c);
-       alc_write_coef_idx(codec, 0x0c, (val & ~0xfe00) | 0x0);
-       /* ANC */
-       alc_write_coef_idx(codec, 0x22, 0xa0c0);
-       /* AGC MUX */
-       val = alc_read_coefex_idx(codec, 0x53, 0x01);
-       alc_write_coefex_idx(codec, 0x53, 0x01, (val & ~0x000f) | 0x0008);
-       /* DAC simple content protection */
-       val = alc_read_coef_idx(codec, 0x1d);
-       alc_write_coef_idx(codec, 0x1d, (val & ~0x00e0) | 0x0);
-       /* ADC simple content protection */
-       val = alc_read_coef_idx(codec, 0x1f);
-       alc_write_coef_idx(codec, 0x1f, (val & ~0x00e0) | 0x0);
-       /* DAC ADC Zero Detection */
-       alc_write_coef_idx(codec, 0x21, 0x8804);
-       /* PLL */
-       alc_write_coef_idx(codec, 0x2e, 0x2902);
-       /* capless control 2 */
-       alc_write_coef_idx(codec, 0x33, 0xa080);
-       /* capless control 3 */
-       alc_write_coef_idx(codec, 0x34, 0x3400);
-       /* capless control 4 */
-       alc_write_coef_idx(codec, 0x35, 0x2f3e);
-       /* capless control 5 */
-       alc_write_coef_idx(codec, 0x36, 0x0);
-       /* class D test 2 */
-       val = alc_read_coef_idx(codec, 0x38);
-       alc_write_coef_idx(codec, 0x38, (val & ~0x0fff) | 0x0900);
-       /* class D test 3 */
-       alc_write_coef_idx(codec, 0x39, 0x110a);
-       /* class D test 5 */
-       val = alc_read_coef_idx(codec, 0x3b);
-       alc_write_coef_idx(codec, 0x3b, (val & ~0x00f8) | 0x00d8);
-       /* class D test 6 */
-       alc_write_coef_idx(codec, 0x3c, 0x0014);
-       /* classD OCP */
-       alc_write_coef_idx(codec, 0x3d, 0xc2ba);
-       /* classD pure DC test */
-       val = alc_read_coef_idx(codec, 0x42);
-       alc_write_coef_idx(codec, 0x42, (val & ~0x0f80) | 0x0);
-       /* test mode */
-       alc_write_coef_idx(codec, 0x49, 0x0);
-       /* Class D DC enable */
-       val = alc_read_coef_idx(codec, 0x40);
-       alc_write_coef_idx(codec, 0x40, (val & ~0xf800) | 0x9800);
-       /* DC offset */
-       val = alc_read_coef_idx(codec, 0x42);
-       alc_write_coef_idx(codec, 0x42, (val & ~0xf000) | 0x2000);
-       /* Class D amp control */
-       alc_write_coef_idx(codec, 0x37, 0xfc06);
+       alc_process_coef_fw(codec, alc283_coefs);
 }
 
 static void alc283_init(struct hda_codec *codec)
@@ -3068,7 +2830,6 @@ static void alc283_init(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
        bool hp_pin_sense;
-       int val;
 
        if (!spec->gen.autocfg.hp_outs) {
                if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
@@ -3098,8 +2859,7 @@ static void alc283_init(struct hda_codec *codec)
                msleep(85);
        /* Index 0x46 Combo jack auto switch control 2 */
        /* 3k pull low control for Headset jack. */
-       val = alc_read_coef_idx(codec, 0x46);
-       alc_write_coef_idx(codec, 0x46, val & ~(3 << 12));
+       alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
        /* Headphone capless set to normal mode */
        alc_write_coef_idx(codec, 0x43, 0x9614);
 }
@@ -3109,7 +2869,6 @@ static void alc283_shutup(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
        bool hp_pin_sense;
-       int val;
 
        if (!spec->gen.autocfg.hp_outs) {
                if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
@@ -3134,8 +2893,7 @@ static void alc283_shutup(struct hda_codec *codec)
        snd_hda_codec_write(codec, hp_pin, 0,
                            AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
 
-       val = alc_read_coef_idx(codec, 0x46);
-       alc_write_coef_idx(codec, 0x46, val | (3 << 12));
+       alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
 
        if (hp_pin_sense)
                msleep(100);
@@ -3268,7 +3026,6 @@ static int alc269_resume(struct hda_codec *codec)
 
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
-       alc_inv_dmic_sync(codec, true);
        hda_call_check_power_status(codec, 0x01);
 
        /* on some machine, the BIOS will clear the codec gpio data when enter
@@ -3298,12 +3055,8 @@ static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec,
 static void alc269_fixup_hweq(struct hda_codec *codec,
                               const struct hda_fixup *fix, int action)
 {
-       int coef;
-
-       if (action != HDA_FIXUP_ACT_INIT)
-               return;
-       coef = alc_read_coef_idx(codec, 0x1e);
-       alc_write_coef_idx(codec, 0x1e, coef | 0x80);
+       if (action == HDA_FIXUP_ACT_INIT)
+               alc_update_coef_idx(codec, 0x1e, 0, 0x80);
 }
 
 static void alc269_fixup_headset_mic(struct hda_codec *codec,
@@ -3351,32 +3104,21 @@ static void alc269_fixup_pcm_44k(struct hda_codec *codec,
 static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
                                     const struct hda_fixup *fix, int action)
 {
-       int coef;
-
-       if (action != HDA_FIXUP_ACT_INIT)
-               return;
        /* The digital-mic unit sends PDM (differential signal) instead of
         * the standard PCM, thus you can't record a valid mono stream as is.
         * Below is a workaround specific to ALC269 to control the dmic
         * signal source as mono.
         */
-       coef = alc_read_coef_idx(codec, 0x07);
-       alc_write_coef_idx(codec, 0x07, coef | 0x80);
+       if (action == HDA_FIXUP_ACT_INIT)
+               alc_update_coef_idx(codec, 0x07, 0, 0x80);
 }
 
 static void alc269_quanta_automute(struct hda_codec *codec)
 {
        snd_hda_gen_update_outputs(codec);
 
-       snd_hda_codec_write(codec, 0x20, 0,
-                       AC_VERB_SET_COEF_INDEX, 0x0c);
-       snd_hda_codec_write(codec, 0x20, 0,
-                       AC_VERB_SET_PROC_COEF, 0x680);
-
-       snd_hda_codec_write(codec, 0x20, 0,
-                       AC_VERB_SET_COEF_INDEX, 0x0c);
-       snd_hda_codec_write(codec, 0x20, 0,
-                       AC_VERB_SET_PROC_COEF, 0x480);
+       alc_write_coef_idx(codec, 0x0c, 0x680);
+       alc_write_coef_idx(codec, 0x0c, 0x480);
 }
 
 static void alc269_fixup_quanta_mute(struct hda_codec *codec,
@@ -3389,7 +3131,7 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec,
 }
 
 static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
-                                        struct hda_jack_tbl *jack)
+                                        struct hda_jack_callback *jack)
 {
        struct alc_spec *spec = codec->spec;
        int vref;
@@ -3622,61 +3364,62 @@ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
 
 static void alc_headset_mode_unplugged(struct hda_codec *codec)
 {
-       int val;
+       static struct coef_fw coef0255[] = {
+               WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */
+               WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */
+               UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
+               WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */
+               WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */
+               {}
+       };
+       static struct coef_fw coef0233[] = {
+               WRITE_COEF(0x1b, 0x0c0b),
+               WRITE_COEF(0x45, 0xc429),
+               UPDATE_COEF(0x35, 0x4000, 0),
+               WRITE_COEF(0x06, 0x2104),
+               WRITE_COEF(0x1a, 0x0001),
+               WRITE_COEF(0x26, 0x0004),
+               WRITE_COEF(0x32, 0x42a3),
+               {}
+       };
+       static struct coef_fw coef0292[] = {
+               WRITE_COEF(0x76, 0x000e),
+               WRITE_COEF(0x6c, 0x2400),
+               WRITE_COEF(0x18, 0x7308),
+               WRITE_COEF(0x6b, 0xc429),
+               {}
+       };
+       static struct coef_fw coef0293[] = {
+               UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */
+               UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */
+               UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */
+               UPDATE_COEF(0x1a, 1<<3, 1<<3), /* Combo JD gating with LINE1-VREFO */
+               WRITE_COEF(0x45, 0xc429), /* Set to TRS type */
+               UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */
+               {}
+       };
+       static struct coef_fw coef0668[] = {
+               WRITE_COEF(0x15, 0x0d40),
+               WRITE_COEF(0xb7, 0x802b),
+               {}
+       };
 
        switch (codec->vendor_id) {
        case 0x10ec0255:
-               /* LDO and MISC control */
-               alc_write_coef_idx(codec, 0x1b, 0x0c0b);
-               /* UAJ function set to menual mode */
-               alc_write_coef_idx(codec, 0x45, 0xd089);
-               /* Direct Drive HP Amp control(Set to verb control)*/
-               val = alc_read_coefex_idx(codec, 0x57, 0x05);
-               alc_write_coefex_idx(codec, 0x57, 0x05, val & ~(1<<14));
-               /* Set MIC2 Vref gate with HP */
-               alc_write_coef_idx(codec, 0x06, 0x6104);
-               /* Direct Drive HP Amp control */
-               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
+               alc_process_coef_fw(codec, coef0255);
                break;
        case 0x10ec0233:
        case 0x10ec0283:
-               alc_write_coef_idx(codec, 0x1b, 0x0c0b);
-               alc_write_coef_idx(codec, 0x45, 0xc429);
-               val = alc_read_coef_idx(codec, 0x35);
-               alc_write_coef_idx(codec, 0x35, val & 0xbfff);
-               alc_write_coef_idx(codec, 0x06, 0x2104);
-               alc_write_coef_idx(codec, 0x1a, 0x0001);
-               alc_write_coef_idx(codec, 0x26, 0x0004);
-               alc_write_coef_idx(codec, 0x32, 0x42a3);
+               alc_process_coef_fw(codec, coef0233);
                break;
        case 0x10ec0292:
-               alc_write_coef_idx(codec, 0x76, 0x000e);
-               alc_write_coef_idx(codec, 0x6c, 0x2400);
-               alc_write_coef_idx(codec, 0x18, 0x7308);
-               alc_write_coef_idx(codec, 0x6b, 0xc429);
+               alc_process_coef_fw(codec, coef0292);
                break;
        case 0x10ec0293:
-               /* SET Line1 JD to 0 */
-               val = alc_read_coef_idx(codec, 0x10);
-               alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 6<<8);
-               /* SET charge pump by verb */
-               val = alc_read_coefex_idx(codec, 0x57, 0x05);
-               alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | 0x0);
-               /* SET EN_OSW to 1 */
-               val = alc_read_coefex_idx(codec, 0x57, 0x03);
-               alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | (1<<10) );
-               /* Combo JD gating with LINE1-VREFO */
-               val = alc_read_coef_idx(codec, 0x1a);
-               alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | (1<<3));
-               /* Set to TRS type */
-               alc_write_coef_idx(codec, 0x45, 0xc429);
-               /* Combo Jack auto detect */
-               val = alc_read_coef_idx(codec, 0x4a);
-               alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
+               alc_process_coef_fw(codec, coef0293);
                break;
        case 0x10ec0668:
-               alc_write_coef_idx(codec, 0x15, 0x0d40);
-               alc_write_coef_idx(codec, 0xb7, 0x802b);
+               alc_process_coef_fw(codec, coef0668);
                break;
        }
        codec_dbg(codec, "Headset jack set to unplugged mode.\n");
@@ -3686,55 +3429,65 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
 static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
                                    hda_nid_t mic_pin)
 {
-       int val;
+       static struct coef_fw coef0255[] = {
+               WRITE_COEFEX(0x57, 0x03, 0x8aa6),
+               WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */
+               {}
+       };
+       static struct coef_fw coef0233[] = {
+               UPDATE_COEF(0x35, 0, 1<<14),
+               WRITE_COEF(0x06, 0x2100),
+               WRITE_COEF(0x1a, 0x0021),
+               WRITE_COEF(0x26, 0x008c),
+               {}
+       };
+       static struct coef_fw coef0292[] = {
+               WRITE_COEF(0x19, 0xa208),
+               WRITE_COEF(0x2e, 0xacf0),
+               {}
+       };
+       static struct coef_fw coef0293[] = {
+               UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */
+               UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */
+               UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */
+               {}
+       };
+       static struct coef_fw coef0688[] = {
+               WRITE_COEF(0xb7, 0x802b),
+               WRITE_COEF(0xb5, 0x1040),
+               UPDATE_COEF(0xc3, 0, 1<<12),
+               {}
+       };
 
        switch (codec->vendor_id) {
        case 0x10ec0255:
                alc_write_coef_idx(codec, 0x45, 0xc489);
                snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
-               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
-               /* Set MIC2 Vref gate to normal */
-               alc_write_coef_idx(codec, 0x06, 0x6100);
+               alc_process_coef_fw(codec, coef0255);
                snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
                break;
        case 0x10ec0233:
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x45, 0xc429);
                snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
-               val = alc_read_coef_idx(codec, 0x35);
-               alc_write_coef_idx(codec, 0x35, val | 1<<14);
-               alc_write_coef_idx(codec, 0x06, 0x2100);
-               alc_write_coef_idx(codec, 0x1a, 0x0021);
-               alc_write_coef_idx(codec, 0x26, 0x008c);
+               alc_process_coef_fw(codec, coef0233);
                snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
                break;
        case 0x10ec0292:
                snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
-               alc_write_coef_idx(codec, 0x19, 0xa208);
-               alc_write_coef_idx(codec, 0x2e, 0xacf0);
+               alc_process_coef_fw(codec, coef0292);
                break;
        case 0x10ec0293:
                /* Set to TRS mode */
                alc_write_coef_idx(codec, 0x45, 0xc429);
                snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
-               /* SET charge pump by verb */
-               val = alc_read_coefex_idx(codec, 0x57, 0x05);
-               alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | (1<<15|1<<13));
-               /* SET EN_OSW to 0 */
-               val = alc_read_coefex_idx(codec, 0x57, 0x03);
-               alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | 0x0);
-               /* Combo JD gating without LINE1-VREFO */
-               val = alc_read_coef_idx(codec, 0x1a);
-               alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
+               alc_process_coef_fw(codec, coef0293);
                snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
                break;
        case 0x10ec0668:
                alc_write_coef_idx(codec, 0x11, 0x0001);
                snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
-               alc_write_coef_idx(codec, 0xb7, 0x802b);
-               alc_write_coef_idx(codec, 0xb5, 0x1040);
-               val = alc_read_coef_idx(codec, 0xc3);
-               alc_write_coef_idx(codec, 0xc3, val | 1<<12);
+               alc_process_coef_fw(codec, coef0688);
                snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
                break;
        }
@@ -3743,40 +3496,54 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
 
 static void alc_headset_mode_default(struct hda_codec *codec)
 {
-       int val;
+       static struct coef_fw coef0255[] = {
+               WRITE_COEF(0x45, 0xc089),
+               WRITE_COEF(0x45, 0xc489),
+               WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+               WRITE_COEF(0x49, 0x0049),
+               {}
+       };
+       static struct coef_fw coef0233[] = {
+               WRITE_COEF(0x06, 0x2100),
+               WRITE_COEF(0x32, 0x4ea3),
+               {}
+       };
+       static struct coef_fw coef0292[] = {
+               WRITE_COEF(0x76, 0x000e),
+               WRITE_COEF(0x6c, 0x2400),
+               WRITE_COEF(0x6b, 0xc429),
+               WRITE_COEF(0x18, 0x7308),
+               {}
+       };
+       static struct coef_fw coef0293[] = {
+               UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */
+               WRITE_COEF(0x45, 0xC429), /* Set to TRS type */
+               UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */
+               {}
+       };
+       static struct coef_fw coef0688[] = {
+               WRITE_COEF(0x11, 0x0041),
+               WRITE_COEF(0x15, 0x0d40),
+               WRITE_COEF(0xb7, 0x802b),
+               {}
+       };
 
        switch (codec->vendor_id) {
        case 0x10ec0255:
-               alc_write_coef_idx(codec, 0x45, 0xc089);
-               alc_write_coef_idx(codec, 0x45, 0xc489);
-               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
-               alc_write_coef_idx(codec, 0x49, 0x0049);
+               alc_process_coef_fw(codec, coef0255);
                break;
        case 0x10ec0233:
        case 0x10ec0283:
-               alc_write_coef_idx(codec, 0x06, 0x2100);
-               alc_write_coef_idx(codec, 0x32, 0x4ea3);
+               alc_process_coef_fw(codec, coef0233);
                break;
        case 0x10ec0292:
-               alc_write_coef_idx(codec, 0x76, 0x000e);
-               alc_write_coef_idx(codec, 0x6c, 0x2400);
-               alc_write_coef_idx(codec, 0x6b, 0xc429);
-               alc_write_coef_idx(codec, 0x18, 0x7308);
+               alc_process_coef_fw(codec, coef0292);
                break;
        case 0x10ec0293:
-               /* Combo Jack auto detect */
-               val = alc_read_coef_idx(codec, 0x4a);
-               alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
-               /* Set to TRS type */
-               alc_write_coef_idx(codec, 0x45, 0xC429);
-               /* Combo JD gating without LINE1-VREFO */
-               val = alc_read_coef_idx(codec, 0x1a);
-               alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
+               alc_process_coef_fw(codec, coef0293);
                break;
        case 0x10ec0668:
-               alc_write_coef_idx(codec, 0x11, 0x0041);
-               alc_write_coef_idx(codec, 0x15, 0x0d40);
-               alc_write_coef_idx(codec, 0xb7, 0x802b);
+               alc_process_coef_fw(codec, coef0688);
                break;
        }
        codec_dbg(codec, "Headset jack set to headphone (default) mode.\n");
@@ -3785,37 +3552,52 @@ static void alc_headset_mode_default(struct hda_codec *codec)
 /* Iphone type */
 static void alc_headset_mode_ctia(struct hda_codec *codec)
 {
-       int val;
+       static struct coef_fw coef0255[] = {
+               WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
+               WRITE_COEF(0x1b, 0x0c2b),
+               WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+               {}
+       };
+       static struct coef_fw coef0233[] = {
+               WRITE_COEF(0x45, 0xd429),
+               WRITE_COEF(0x1b, 0x0c2b),
+               WRITE_COEF(0x32, 0x4ea3),
+               {}
+       };
+       static struct coef_fw coef0292[] = {
+               WRITE_COEF(0x6b, 0xd429),
+               WRITE_COEF(0x76, 0x0008),
+               WRITE_COEF(0x18, 0x7388),
+               {}
+       };
+       static struct coef_fw coef0293[] = {
+               WRITE_COEF(0x45, 0xd429), /* Set to ctia type */
+               UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */
+               {}
+       };
+       static struct coef_fw coef0688[] = {
+               WRITE_COEF(0x11, 0x0001),
+               WRITE_COEF(0x15, 0x0d60),
+               WRITE_COEF(0xc3, 0x0000),
+               {}
+       };
 
        switch (codec->vendor_id) {
        case 0x10ec0255:
-               /* Set to CTIA type */
-               alc_write_coef_idx(codec, 0x45, 0xd489);
-               alc_write_coef_idx(codec, 0x1b, 0x0c2b);
-               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
+               alc_process_coef_fw(codec, coef0255);
                break;
        case 0x10ec0233:
        case 0x10ec0283:
-               alc_write_coef_idx(codec, 0x45, 0xd429);
-               alc_write_coef_idx(codec, 0x1b, 0x0c2b);
-               alc_write_coef_idx(codec, 0x32, 0x4ea3);
+               alc_process_coef_fw(codec, coef0233);
                break;
        case 0x10ec0292:
-               alc_write_coef_idx(codec, 0x6b, 0xd429);
-               alc_write_coef_idx(codec, 0x76, 0x0008);
-               alc_write_coef_idx(codec, 0x18, 0x7388);
+               alc_process_coef_fw(codec, coef0292);
                break;
        case 0x10ec0293:
-               /* Set to ctia type */
-               alc_write_coef_idx(codec, 0x45, 0xd429);
-               /* SET Line1 JD to 1 */
-               val = alc_read_coef_idx(codec, 0x10);
-               alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
+               alc_process_coef_fw(codec, coef0293);
                break;
        case 0x10ec0668:
-               alc_write_coef_idx(codec, 0x11, 0x0001);
-               alc_write_coef_idx(codec, 0x15, 0x0d60);
-               alc_write_coef_idx(codec, 0xc3, 0x0000);
+               alc_process_coef_fw(codec, coef0688);
                break;
        }
        codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n");
@@ -3824,37 +3606,52 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
 /* Nokia type */
 static void alc_headset_mode_omtp(struct hda_codec *codec)
 {
-       int val;
+       static struct coef_fw coef0255[] = {
+               WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
+               WRITE_COEF(0x1b, 0x0c2b),
+               WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+               {}
+       };
+       static struct coef_fw coef0233[] = {
+               WRITE_COEF(0x45, 0xe429),
+               WRITE_COEF(0x1b, 0x0c2b),
+               WRITE_COEF(0x32, 0x4ea3),
+               {}
+       };
+       static struct coef_fw coef0292[] = {
+               WRITE_COEF(0x6b, 0xe429),
+               WRITE_COEF(0x76, 0x0008),
+               WRITE_COEF(0x18, 0x7388),
+               {}
+       };
+       static struct coef_fw coef0293[] = {
+               WRITE_COEF(0x45, 0xe429), /* Set to omtp type */
+               UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */
+               {}
+       };
+       static struct coef_fw coef0688[] = {
+               WRITE_COEF(0x11, 0x0001),
+               WRITE_COEF(0x15, 0x0d50),
+               WRITE_COEF(0xc3, 0x0000),
+               {}
+       };
 
        switch (codec->vendor_id) {
        case 0x10ec0255:
-               /* Set to OMTP Type */
-               alc_write_coef_idx(codec, 0x45, 0xe489);
-               alc_write_coef_idx(codec, 0x1b, 0x0c2b);
-               alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
+               alc_process_coef_fw(codec, coef0255);
                break;
        case 0x10ec0233:
        case 0x10ec0283:
-               alc_write_coef_idx(codec, 0x45, 0xe429);
-               alc_write_coef_idx(codec, 0x1b, 0x0c2b);
-               alc_write_coef_idx(codec, 0x32, 0x4ea3);
+               alc_process_coef_fw(codec, coef0233);
                break;
        case 0x10ec0292:
-               alc_write_coef_idx(codec, 0x6b, 0xe429);
-               alc_write_coef_idx(codec, 0x76, 0x0008);
-               alc_write_coef_idx(codec, 0x18, 0x7388);
+               alc_process_coef_fw(codec, coef0292);
                break;
        case 0x10ec0293:
-               /* Set to omtp type */
-               alc_write_coef_idx(codec, 0x45, 0xe429);
-               /* SET Line1 JD to 1 */
-               val = alc_read_coef_idx(codec, 0x10);
-               alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
+               alc_process_coef_fw(codec, coef0293);
                break;
        case 0x10ec0668:
-               alc_write_coef_idx(codec, 0x11, 0x0001);
-               alc_write_coef_idx(codec, 0x15, 0x0d50);
-               alc_write_coef_idx(codec, 0xc3, 0x0000);
+               alc_process_coef_fw(codec, coef0688);
                break;
        }
        codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n");
@@ -3865,13 +3662,28 @@ static void alc_determine_headset_type(struct hda_codec *codec)
        int val;
        bool is_ctia = false;
        struct alc_spec *spec = codec->spec;
+       static struct coef_fw coef0255[] = {
+               WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/
+               WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref
+ conteol) */
+               {}
+       };
+       static struct coef_fw coef0293[] = {
+               UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */
+               WRITE_COEF(0x45, 0xD429), /* Set to ctia type */
+               {}
+       };
+       static struct coef_fw coef0688[] = {
+               WRITE_COEF(0x11, 0x0001),
+               WRITE_COEF(0xb7, 0x802b),
+               WRITE_COEF(0x15, 0x0d60),
+               WRITE_COEF(0xc3, 0x0c00),
+               {}
+       };
 
        switch (codec->vendor_id) {
        case 0x10ec0255:
-               /* combo jack auto switch control(Check type)*/
-               alc_write_coef_idx(codec, 0x45, 0xd089);
-               /* combo jack auto switch control(Vref conteol) */
-               alc_write_coef_idx(codec, 0x49, 0x0149);
+               alc_process_coef_fw(codec, coef0255);
                msleep(300);
                val = alc_read_coef_idx(codec, 0x46);
                is_ctia = (val & 0x0070) == 0x0070;
@@ -3890,20 +3702,13 @@ static void alc_determine_headset_type(struct hda_codec *codec)
                is_ctia = (val & 0x001c) == 0x001c;
                break;
        case 0x10ec0293:
-               /* Combo Jack auto detect */
-               val = alc_read_coef_idx(codec, 0x4a);
-               alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x0008);
-               /* Set to ctia type */
-               alc_write_coef_idx(codec, 0x45, 0xD429);
+               alc_process_coef_fw(codec, coef0293);
                msleep(300);
                val = alc_read_coef_idx(codec, 0x46);
                is_ctia = (val & 0x0070) == 0x0070;
                break;
        case 0x10ec0668:
-               alc_write_coef_idx(codec, 0x11, 0x0001);
-               alc_write_coef_idx(codec, 0xb7, 0x802b);
-               alc_write_coef_idx(codec, 0x15, 0x0d60);
-               alc_write_coef_idx(codec, 0xc3, 0x0c00);
+               alc_process_coef_fw(codec, coef0688);
                msleep(300);
                val = alc_read_coef_idx(codec, 0xbe);
                is_ctia = (val & 0x1c02) == 0x1c02;
@@ -3980,7 +3785,8 @@ static void alc_update_headset_mode_hook(struct hda_codec *codec,
        alc_update_headset_mode(codec);
 }
 
-static void alc_update_headset_jack_cb(struct hda_codec *codec, struct hda_jack_tbl *jack)
+static void alc_update_headset_jack_cb(struct hda_codec *codec,
+                                      struct hda_jack_callback *jack)
 {
        struct alc_spec *spec = codec->spec;
        spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
@@ -4039,11 +3845,15 @@ static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
 static void alc255_set_default_jack_type(struct hda_codec *codec)
 {
        /* Set to iphone type */
-       alc_write_coef_idx(codec, 0x1b, 0x880b);
-       alc_write_coef_idx(codec, 0x45, 0xd089);
-       alc_write_coef_idx(codec, 0x1b, 0x080b);
-       alc_write_coef_idx(codec, 0x46, 0x0004);
-       alc_write_coef_idx(codec, 0x1b, 0x0c0b);
+       static struct coef_fw fw[] = {
+               WRITE_COEF(0x1b, 0x880b),
+               WRITE_COEF(0x45, 0xd089),
+               WRITE_COEF(0x1b, 0x080b),
+               WRITE_COEF(0x46, 0x0004),
+               WRITE_COEF(0x1b, 0x0c0b),
+               {}
+       };
+       alc_process_coef_fw(codec, fw);
        msleep(30);
 }
 
@@ -4138,10 +3948,8 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
                                const struct hda_fixup *fix, int action)
 {
        if (action == HDA_FIXUP_ACT_PRE_PROBE) {
-               int val;
                alc_write_coef_idx(codec, 0xc4, 0x8000);
-               val = alc_read_coef_idx(codec, 0xc2);
-               alc_write_coef_idx(codec, 0xc2, val & 0xfe);
+               alc_update_coef_idx(codec, 0xc2, ~0xfe, 0);
                snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
        }
        alc_fixup_headset_mode(codec, fix, action);
@@ -4218,7 +4026,7 @@ static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
 }
 
 static void alc283_hp_automute_hook(struct hda_codec *codec,
-                                   struct hda_jack_tbl *jack)
+                                   struct hda_jack_callback *jack)
 {
        struct alc_spec *spec = codec->spec;
        int vref;
@@ -4237,7 +4045,6 @@ static void alc283_fixup_chromebook(struct hda_codec *codec,
                                    const struct hda_fixup *fix, int action)
 {
        struct alc_spec *spec = codec->spec;
-       int val;
 
        switch (action) {
        case HDA_FIXUP_ACT_PRE_PROBE:
@@ -4248,11 +4055,9 @@ static void alc283_fixup_chromebook(struct hda_codec *codec,
        case HDA_FIXUP_ACT_INIT:
                /* MIC2-VREF control */
                /* Set to manual mode */
-               val = alc_read_coef_idx(codec, 0x06);
-               alc_write_coef_idx(codec, 0x06, val & ~0x000c);
+               alc_update_coef_idx(codec, 0x06, 0x000c, 0);
                /* Enable Line1 input control by verb */
-               val = alc_read_coef_idx(codec, 0x1a);
-               alc_write_coef_idx(codec, 0x1a, val | (1 << 4));
+               alc_update_coef_idx(codec, 0x1a, 0, 1 << 4);
                break;
        }
 }
@@ -4261,7 +4066,6 @@ static void alc283_fixup_sense_combo_jack(struct hda_codec *codec,
                                    const struct hda_fixup *fix, int action)
 {
        struct alc_spec *spec = codec->spec;
-       int val;
 
        switch (action) {
        case HDA_FIXUP_ACT_PRE_PROBE:
@@ -4270,8 +4074,7 @@ static void alc283_fixup_sense_combo_jack(struct hda_codec *codec,
        case HDA_FIXUP_ACT_INIT:
                /* MIC2-VREF control */
                /* Set to manual mode */
-               val = alc_read_coef_idx(codec, 0x06);
-               alc_write_coef_idx(codec, 0x06, val & ~0x000c);
+               alc_update_coef_idx(codec, 0x06, 0x000c, 0);
                break;
        }
 }
@@ -4309,7 +4112,6 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec,
                spec->gen.auto_mute_via_amp = 1;
                spec->gen.automute_hook = asus_tx300_automute;
                snd_hda_jack_detect_enable_callback(codec, 0x1b,
-                                                   HDA_GEN_HP_EVENT,
                                                    snd_hda_gen_hp_automute);
                break;
        case HDA_FIXUP_ACT_BUILD:
@@ -4576,7 +4378,7 @@ static const struct hda_fixup alc269_fixups[] = {
        },
        [ALC269_FIXUP_INV_DMIC] = {
                .type = HDA_FIXUP_FUNC,
-               .v.func = alc_fixup_inv_dmic_0x12,
+               .v.func = alc_fixup_inv_dmic,
        },
        [ALC269_FIXUP_NO_SHUTUP] = {
                .type = HDA_FIXUP_FUNC,
@@ -4887,122 +4689,45 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
        SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
        SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
-       SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05c5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05c6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05c7, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05c8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05c9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05ca, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05cb, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER),
-       SND_PCI_QUIRK(0x1028, 0x05de, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05e0, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05e9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05ea, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05eb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05ec, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05ed, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05ee, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05f3, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05f8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05f9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x05fb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0606, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0610, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
-       SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0614, "Dell Inspiron 3135", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
        SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
        SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
        SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
-       SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0668, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0669, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0684, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
        SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
-       SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
        /* ALC282 */
-       SND_PCI_QUIRK(0x103c, 0x21f8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x220d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x220e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x220f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2211, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2212, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2213, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2234, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2235, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2236, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2237, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2238, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2239, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2246, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2247, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2248, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2249, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x224a, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x224b, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x224c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x224d, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2266, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2267, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2268, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2269, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x226c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x226d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x226f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x227a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x227b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22a0, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22c0, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22c1, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22c2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22cd, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22ce, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22d0, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22da, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x8004, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        /* ALC290 */
        SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x221c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x221d, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2220, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2222, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2223, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2224, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2246, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2247, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
@@ -5017,8 +4742,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2261, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2262, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2263, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2264, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -5026,23 +4749,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2277, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x227d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x227e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2280, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2281, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x2289, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x228a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x228b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x228c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x228d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x228e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22c5, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22c6, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22c7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22c8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
-       SND_PCI_QUIRK(0x103c, 0x22c3, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2334, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -5070,6 +4783,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX),
        SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
        SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
+       SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
        SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_BXBT2807_MIC),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
        SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
@@ -5085,12 +4799,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK),
-       SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
        SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
+       SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
@@ -5173,28 +4888,58 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
        {}
 };
 
+#define ALC255_STANDARD_PINS \
+       {0x18, 0x411111f0}, \
+       {0x19, 0x411111f0}, \
+       {0x1a, 0x411111f0}, \
+       {0x1b, 0x411111f0}, \
+       {0x1e, 0x411111f0}
+
+#define ALC282_STANDARD_PINS \
+       {0x14, 0x90170110}, \
+       {0x18, 0x411111f0}, \
+       {0x1a, 0x411111f0}, \
+       {0x1b, 0x411111f0}, \
+       {0x1e, 0x411111f0}
+
+#define ALC290_STANDARD_PINS \
+       {0x12, 0x99a30130}, \
+       {0x13, 0x40000000}, \
+       {0x16, 0x411111f0}, \
+       {0x17, 0x411111f0}, \
+       {0x19, 0x411111f0}, \
+       {0x1b, 0x411111f0}, \
+       {0x1e, 0x411111f0}
+
+#define ALC292_STANDARD_PINS \
+       {0x14, 0x90170110}, \
+       {0x15, 0x0221401f}, \
+       {0x1a, 0x411111f0}, \
+       {0x1b, 0x411111f0}, \
+       {0x1d, 0x40700001}, \
+       {0x1e, 0x411111f0}
+
 static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+       SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
+               ALC255_STANDARD_PINS,
+               {0x12, 0x40300000},
+               {0x14, 0x90170110},
+               {0x17, 0x411111f0},
+               {0x1d, 0x40538029},
+               {0x21, 0x02211020}),
        SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC255_STANDARD_PINS,
                {0x12, 0x90a60140},
                {0x14, 0x90170110},
                {0x17, 0x40000000},
-               {0x18, 0x411111f0},
-               {0x19, 0x411111f0},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
                {0x1d, 0x40700001},
-               {0x1e, 0x411111f0},
                {0x21, 0x02211020}),
        SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC255_STANDARD_PINS,
                {0x12, 0x90a60160},
                {0x14, 0x90170120},
                {0x17, 0x40000000},
-               {0x18, 0x411111f0},
-               {0x19, 0x411111f0},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
                {0x1d, 0x40700001},
-               {0x1e, 0x411111f0},
                {0x21, 0x02211030}),
        SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
                {0x12, 0x90a60160},
@@ -5208,70 +4953,101 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
                {0x1e, 0x411111f0},
                {0x21, 0x0321102f}),
        SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC255_STANDARD_PINS,
                {0x12, 0x90a60160},
                {0x14, 0x90170130},
                {0x17, 0x40000000},
-               {0x18, 0x411111f0},
-               {0x19, 0x411111f0},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
                {0x1d, 0x40700001},
-               {0x1e, 0x411111f0},
                {0x21, 0x02211040}),
        SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC255_STANDARD_PINS,
                {0x12, 0x90a60160},
                {0x14, 0x90170140},
                {0x17, 0x40000000},
-               {0x18, 0x411111f0},
-               {0x19, 0x411111f0},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
                {0x1d, 0x40700001},
-               {0x1e, 0x411111f0},
                {0x21, 0x02211050}),
        SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC255_STANDARD_PINS,
                {0x12, 0x90a60170},
                {0x14, 0x90170120},
                {0x17, 0x40000000},
-               {0x18, 0x411111f0},
-               {0x19, 0x411111f0},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
                {0x1d, 0x40700001},
-               {0x1e, 0x411111f0},
                {0x21, 0x02211030}),
        SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC255_STANDARD_PINS,
                {0x12, 0x90a60170},
                {0x14, 0x90170130},
                {0x17, 0x40000000},
-               {0x18, 0x411111f0},
-               {0x19, 0x411111f0},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
                {0x1d, 0x40700001},
-               {0x1e, 0x411111f0},
                {0x21, 0x02211040}),
+       SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED,
+               {0x12, 0x90a60140},
+               {0x13, 0x40000000},
+               {0x14, 0x90170110},
+               {0x15, 0x0421101f},
+               {0x16, 0x411111f0},
+               {0x17, 0x411111f0},
+               {0x18, 0x02811030},
+               {0x19, 0x411111f0},
+               {0x1a, 0x04a1103f},
+               {0x1b, 0x02011020},
+               {0x1d, 0x40700001},
+               {0x1e, 0x411111f0}),
        SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP 15 Touchsmart", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC282_STANDARD_PINS,
                {0x12, 0x99a30130},
-               {0x14, 0x90170110},
                {0x17, 0x40000000},
-               {0x18, 0x411111f0},
                {0x19, 0x03a11020},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
                {0x1d, 0x40f41905},
-               {0x1e, 0x411111f0},
                {0x21, 0x0321101f}),
+       SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC282_STANDARD_PINS,
+               {0x12, 0x99a30130},
+               {0x17, 0x40020008},
+               {0x19, 0x03a11020},
+               {0x1d, 0x40e00001},
+               {0x21, 0x03211040}),
+       SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC282_STANDARD_PINS,
+               {0x12, 0x99a30130},
+               {0x17, 0x40000000},
+               {0x19, 0x03a11030},
+               {0x1d, 0x40e00001},
+               {0x21, 0x03211020}),
+       SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC282_STANDARD_PINS,
+               {0x12, 0x99a30130},
+               {0x17, 0x40000000},
+               {0x19, 0x03a11030},
+               {0x1d, 0x40f00001},
+               {0x21, 0x03211020}),
+       SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC282_STANDARD_PINS,
+               {0x12, 0x99a30130},
+               {0x17, 0x40000000},
+               {0x19, 0x04a11020},
+               {0x1d, 0x40f00001},
+               {0x21, 0x0421101f}),
+       SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC282_STANDARD_PINS,
+               {0x12, 0x99a30130},
+               {0x17, 0x40000000},
+               {0x19, 0x03a11030},
+               {0x1d, 0x40f00001},
+               {0x21, 0x04211020}),
+       SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED,
+               ALC282_STANDARD_PINS,
+               {0x12, 0x90a60140},
+               {0x17, 0x40000000},
+               {0x19, 0x04a11030},
+               {0x1d, 0x40f00001},
+               {0x21, 0x04211020}),
        SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC282_STANDARD_PINS,
                {0x12, 0x90a60130},
-               {0x14, 0x90170110},
                {0x17, 0x40020008},
-               {0x18, 0x411111f0},
                {0x19, 0x411111f0},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
                {0x1d, 0x40e00001},
-               {0x1e, 0x411111f0},
                {0x21, 0x0321101f}),
        SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
                {0x12, 0x90a60160},
@@ -5284,42 +5060,97 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
                {0x1d, 0x40700001},
                {0x1e, 0x411111f0},
                {0x21, 0x02211030}),
+       SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC282_STANDARD_PINS,
+               {0x12, 0x90a60130},
+               {0x17, 0x40020008},
+               {0x19, 0x03a11020},
+               {0x1d, 0x40e00001},
+               {0x21, 0x0321101f}),
+       SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC290_STANDARD_PINS,
+               {0x14, 0x411111f0},
+               {0x15, 0x04211040},
+               {0x18, 0x90170112},
+               {0x1a, 0x04a11020},
+               {0x1d, 0x4075812d}),
+       SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC290_STANDARD_PINS,
+               {0x14, 0x411111f0},
+               {0x15, 0x04211040},
+               {0x18, 0x90170110},
+               {0x1a, 0x04a11020},
+               {0x1d, 0x4075812d}),
+       SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC290_STANDARD_PINS,
+               {0x14, 0x411111f0},
+               {0x15, 0x0421101f},
+               {0x18, 0x411111f0},
+               {0x1a, 0x04a11020},
+               {0x1d, 0x4075812d}),
+       SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC290_STANDARD_PINS,
+               {0x14, 0x411111f0},
+               {0x15, 0x04211020},
+               {0x18, 0x411111f0},
+               {0x1a, 0x04a11040},
+               {0x1d, 0x4076a12d}),
+       SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC290_STANDARD_PINS,
+               {0x14, 0x90170110},
+               {0x15, 0x04211020},
+               {0x18, 0x411111f0},
+               {0x1a, 0x04a11040},
+               {0x1d, 0x4076a12d}),
+       SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC290_STANDARD_PINS,
+               {0x14, 0x90170110},
+               {0x15, 0x04211020},
+               {0x18, 0x411111f0},
+               {0x1a, 0x04a11020},
+               {0x1d, 0x4076a12d}),
+       SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+               ALC290_STANDARD_PINS,
+               {0x14, 0x90170110},
+               {0x15, 0x0421101f},
+               {0x18, 0x411111f0},
+               {0x1a, 0x04a11020},
+               {0x1d, 0x4075812d}),
+       SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+               ALC292_STANDARD_PINS,
+               {0x12, 0x90a60140},
+               {0x13, 0x411111f0},
+               {0x16, 0x01014020},
+               {0x18, 0x411111f0},
+               {0x19, 0x01a19030}),
+       SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+               ALC292_STANDARD_PINS,
+               {0x12, 0x90a60140},
+               {0x13, 0x411111f0},
+               {0x16, 0x01014020},
+               {0x18, 0x02a19031},
+               {0x19, 0x01a1903e}),
        SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+               ALC292_STANDARD_PINS,
                {0x12, 0x90a60140},
                {0x13, 0x411111f0},
-               {0x14, 0x90170110},
-               {0x15, 0x0221401f},
                {0x16, 0x411111f0},
                {0x18, 0x411111f0},
-               {0x19, 0x411111f0},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
-               {0x1d, 0x40700001},
-               {0x1e, 0x411111f0}),
+               {0x19, 0x411111f0}),
        SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC292_STANDARD_PINS,
                {0x12, 0x40000000},
                {0x13, 0x90a60140},
-               {0x14, 0x90170110},
-               {0x15, 0x0221401f},
                {0x16, 0x21014020},
                {0x18, 0x411111f0},
-               {0x19, 0x21a19030},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
-               {0x1d, 0x40700001},
-               {0x1e, 0x411111f0}),
+               {0x19, 0x21a19030}),
        SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC292_STANDARD_PINS,
                {0x12, 0x40000000},
                {0x13, 0x90a60140},
-               {0x14, 0x90170110},
-               {0x15, 0x0221401f},
                {0x16, 0x411111f0},
                {0x18, 0x411111f0},
-               {0x19, 0x411111f0},
-               {0x1a, 0x411111f0},
-               {0x1b, 0x411111f0},
-               {0x1d, 0x40700001},
-               {0x1e, 0x411111f0}),
+               {0x19, 0x411111f0}),
        {}
 };
 
@@ -5342,10 +5173,8 @@ static void alc269_fill_coef(struct hda_codec *codec)
        }
 
        if ((alc_get_coef0(codec) & 0x00ff) == 0x017) {
-               val = alc_read_coef_idx(codec, 0x04);
                /* Power up output pin */
-               if (val != -1)
-                       alc_write_coef_idx(codec, 0x04, val | (1<<11));
+               alc_update_coef_idx(codec, 0x04, 0, 1<<11);
        }
 
        if ((alc_get_coef0(codec) & 0x00ff) == 0x018) {
@@ -5361,13 +5190,11 @@ static void alc269_fill_coef(struct hda_codec *codec)
                }
        }
 
-       val = alc_read_coef_idx(codec, 0xd); /* Class D */
-       if (val != -1)
-               alc_write_coef_idx(codec, 0xd, val | (1<<14));
+       /* Class D */
+       alc_update_coef_idx(codec, 0xd, 0, 1<<14);
 
-       val = alc_read_coef_idx(codec, 0x4); /* HP */
-       if (val != -1)
-               alc_write_coef_idx(codec, 0x4, val | (1<<11));
+       /* HP */
+       alc_update_coef_idx(codec, 0x4, 0, 1<<11);
 }
 
 /*
@@ -6012,7 +5839,7 @@ static const struct hda_fixup alc662_fixups[] = {
        },
        [ALC662_FIXUP_INV_DMIC] = {
                .type = HDA_FIXUP_FUNC,
-               .v.func = alc_fixup_inv_dmic_0x12,
+               .v.func = alc_fixup_inv_dmic,
        },
        [ALC668_FIXUP_DELL_XPS13] = {
                .type = HDA_FIXUP_FUNC,
@@ -6247,16 +6074,14 @@ static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
 
 static void alc662_fill_coef(struct hda_codec *codec)
 {
-       int val, coef;
+       int coef;
 
        coef = alc_get_coef0(codec);
 
        switch (codec->vendor_id) {
        case 0x10ec0662:
-               if ((coef & 0x00f0) == 0x0030) {
-                       val = alc_read_coef_idx(codec, 0x4); /* EAPD Ctrl */
-                       alc_write_coef_idx(codec, 0x4, val & ~(1<<10));
-               }
+               if ((coef & 0x00f0) == 0x0030)
+                       alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
                break;
        case 0x10ec0272:
        case 0x10ec0273:
@@ -6265,8 +6090,7 @@ static void alc662_fill_coef(struct hda_codec *codec)
        case 0x10ec0670:
        case 0x10ec0671:
        case 0x10ec0672:
-               val = alc_read_coef_idx(codec, 0xd); /* EAPD Ctrl */
-               alc_write_coef_idx(codec, 0xd, val | (1<<14));
+               alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
                break;
        }
 }
index 98cd1908c0393856e8bfeec7622874079d60c3c1..4f6413e01c133567a2c01ccee10543d6fc82a864 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/module.h>
 #include <sound/core.h>
 #include <sound/jack.h>
-#include <sound/tlv.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_auto_parser.h"
 #include "hda_jack.h"
 #include "hda_generic.h"
 
-enum {
-       STAC_VREF_EVENT = 8,
-       STAC_PWR_EVENT,
-};
-
 enum {
        STAC_REF,
        STAC_9200_OQO,
@@ -487,7 +481,7 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
 
 /* update power bit per jack plug/unplug */
 static void jack_update_power(struct hda_codec *codec,
-                             struct hda_jack_tbl *jack)
+                             struct hda_jack_callback *jack)
 {
        struct sigmatel_spec *spec = codec->spec;
        int i;
@@ -495,9 +489,9 @@ static void jack_update_power(struct hda_codec *codec,
        if (!spec->num_pwrs)
                return;
 
-       if (jack && jack->nid) {
-               stac_toggle_power_map(codec, jack->nid,
-                                     snd_hda_jack_detect(codec, jack->nid),
+       if (jack && jack->tbl->nid) {
+               stac_toggle_power_map(codec, jack->tbl->nid,
+                                     snd_hda_jack_detect(codec, jack->tbl->nid),
                                      true);
                return;
        }
@@ -505,42 +499,19 @@ static void jack_update_power(struct hda_codec *codec,
        /* update all jacks */
        for (i = 0; i < spec->num_pwrs; i++) {
                hda_nid_t nid = spec->pwr_nids[i];
-               jack = snd_hda_jack_tbl_get(codec, nid);
-               if (!jack || !jack->action)
+               if (!snd_hda_jack_tbl_get(codec, nid))
                        continue;
-               if (jack->action == STAC_PWR_EVENT ||
-                   jack->action <= HDA_GEN_LAST_EVENT)
-                       stac_toggle_power_map(codec, nid,
-                                             snd_hda_jack_detect(codec, nid),
-                                             false);
+               stac_toggle_power_map(codec, nid,
+                                     snd_hda_jack_detect(codec, nid),
+                                     false);
        }
 
        snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_IDT_SET_POWER_MAP,
                            spec->power_map_bits);
 }
 
-static void stac_hp_automute(struct hda_codec *codec,
-                                struct hda_jack_tbl *jack)
-{
-       snd_hda_gen_hp_automute(codec, jack);
-       jack_update_power(codec, jack);
-}
-
-static void stac_line_automute(struct hda_codec *codec,
-                                  struct hda_jack_tbl *jack)
-{
-       snd_hda_gen_line_automute(codec, jack);
-       jack_update_power(codec, jack);
-}
-
-static void stac_mic_autoswitch(struct hda_codec *codec,
-                               struct hda_jack_tbl *jack)
-{
-       snd_hda_gen_mic_autoswitch(codec, jack);
-       jack_update_power(codec, jack);
-}
-
-static void stac_vref_event(struct hda_codec *codec, struct hda_jack_tbl *event)
+static void stac_vref_event(struct hda_codec *codec,
+                           struct hda_jack_callback *event)
 {
        unsigned int data;
 
@@ -563,13 +534,10 @@ static void stac_init_power_map(struct hda_codec *codec)
                hda_nid_t nid = spec->pwr_nids[i];
                unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
                def_conf = get_defcfg_connect(def_conf);
-               if (snd_hda_jack_tbl_get(codec, nid))
-                       continue;
                if (def_conf == AC_JACK_PORT_COMPLEX &&
                    spec->vref_mute_led_nid != nid &&
                    is_jack_detectable(codec, nid)) {
                        snd_hda_jack_detect_enable_callback(codec, nid,
-                                                           STAC_PWR_EVENT,
                                                            jack_update_power);
                } else {
                        if (def_conf == AC_JACK_PORT_NONE)
@@ -3020,7 +2988,7 @@ static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec,
                                      const struct hda_fixup *fix, int action)
 {
        struct sigmatel_spec *spec = codec->spec;
-       struct hda_jack_tbl *jack;
+       struct hda_jack_callback *jack;
 
        if (action != HDA_FIXUP_ACT_PRE_PROBE)
                return;
@@ -3028,11 +2996,9 @@ static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec,
        /* Enable VREF power saving on GPIO1 detect */
        snd_hda_codec_write_cache(codec, codec->afg, 0,
                                  AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
-       snd_hda_jack_detect_enable_callback(codec, codec->afg,
-                                           STAC_VREF_EVENT,
-                                           stac_vref_event);
-       jack = snd_hda_jack_tbl_get(codec, codec->afg);
-       if (jack)
+       jack = snd_hda_jack_detect_enable_callback(codec, codec->afg,
+                                                  stac_vref_event);
+       if (!IS_ERR(jack))
                jack->private_data = 0x02;
 
        spec->gpio_mask |= 0x02;
@@ -4044,7 +4010,7 @@ static void stac9205_fixup_dell_m43(struct hda_codec *codec,
                                    const struct hda_fixup *fix, int action)
 {
        struct sigmatel_spec *spec = codec->spec;
-       struct hda_jack_tbl *jack;
+       struct hda_jack_callback *jack;
 
        if (action == HDA_FIXUP_ACT_PRE_PROBE) {
                snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs);
@@ -4052,11 +4018,9 @@ static void stac9205_fixup_dell_m43(struct hda_codec *codec,
                /* Enable unsol response for GPIO4/Dock HP connection */
                snd_hda_codec_write_cache(codec, codec->afg, 0,
                        AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
-               snd_hda_jack_detect_enable_callback(codec, codec->afg,
-                                                   STAC_VREF_EVENT,
-                                                   stac_vref_event);
-               jack = snd_hda_jack_tbl_get(codec, codec->afg);
-               if (jack)
+               jack = snd_hda_jack_detect_enable_callback(codec, codec->afg,
+                                                          stac_vref_event);
+               if (!IS_ERR(jack))
                        jack->private_data = 0x01;
 
                spec->gpio_dir = 0x0b;
@@ -4219,17 +4183,11 @@ static int stac_parse_auto_config(struct hda_codec *codec)
        spec->gen.pcm_capture_hook = stac_capture_pcm_hook;
 
        spec->gen.automute_hook = stac_update_outputs;
-       spec->gen.hp_automute_hook = stac_hp_automute;
-       spec->gen.line_automute_hook = stac_line_automute;
-       spec->gen.mic_autoswitch_hook = stac_mic_autoswitch;
 
        err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
        if (err < 0)
                return err;
 
-       /* minimum value is actually mute */
-       spec->gen.vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
-
        /* setup analog beep controls */
        if (spec->anabeep_nid > 0) {
                err = stac_auto_create_beep_ctls(codec,
@@ -4276,16 +4234,8 @@ static int stac_parse_auto_config(struct hda_codec *codec)
                        return err;
        }
 
-       return 0;
-}
-
-static int stac_build_controls(struct hda_codec *codec)
-{
-       int err = snd_hda_gen_build_controls(codec);
-
-       if (err < 0)
-               return err;
        stac_init_power_map(codec);
+
        return 0;
 }
 
@@ -4399,7 +4349,7 @@ static int stac_suspend(struct hda_codec *codec)
 #endif /* CONFIG_PM */
 
 static const struct hda_codec_ops stac_patch_ops = {
-       .build_controls = stac_build_controls,
+       .build_controls = snd_hda_gen_build_controls,
        .build_pcms = snd_hda_gen_build_pcms,
        .init = stac_init,
        .free = stac_free,
@@ -4420,6 +4370,7 @@ static int alloc_stac_spec(struct hda_codec *codec)
        snd_hda_gen_spec_init(&spec->gen);
        codec->spec = spec;
        codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */
+       spec->gen.dac_min_mute = true;
        return 0;
 }
 
index 778166259b3e8e72fe039d08fe5e1671d5c6fd94..6c206b6c8d65d389119e5f7c630f45e52767bd61 100644 (file)
@@ -118,7 +118,6 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
                                  struct hda_codec *codec,
                                  struct snd_pcm_substream *substream,
                                  int action);
-static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl);
 
 static struct via_spec *via_new_spec(struct hda_codec *codec)
 {
@@ -575,25 +574,12 @@ static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
        {} /* terminator */
 };
 
-static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
+static void via_jack_powerstate_event(struct hda_codec *codec,
+                                     struct hda_jack_callback *tbl)
 {
        set_widgets_power_state(codec);
-       snd_hda_gen_hp_automute(codec, tbl);
 }
 
-static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
-{
-       set_widgets_power_state(codec);
-       snd_hda_gen_line_automute(codec, tbl);
-}
-
-static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
-{
-       set_widgets_power_state(codec);
-}
-
-#define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1)
-
 static void via_set_jack_unsol_events(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
@@ -601,25 +587,17 @@ static void via_set_jack_unsol_events(struct hda_codec *codec)
        hda_nid_t pin;
        int i;
 
-       spec->gen.hp_automute_hook = via_hp_automute;
-       if (cfg->speaker_pins[0])
-               spec->gen.line_automute_hook = via_line_automute;
-
        for (i = 0; i < cfg->line_outs; i++) {
                pin = cfg->line_out_pins[i];
-               if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
-                   is_jack_detectable(codec, pin))
+               if (pin && is_jack_detectable(codec, pin))
                        snd_hda_jack_detect_enable_callback(codec, pin,
-                                                           VIA_JACK_EVENT,
                                                            via_jack_powerstate_event);
        }
 
        for (i = 0; i < cfg->num_inputs; i++) {
                pin = cfg->line_out_pins[i];
-               if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
-                   is_jack_detectable(codec, pin))
+               if (pin && is_jack_detectable(codec, pin))
                        snd_hda_jack_detect_enable_callback(codec, pin,
-                                                           VIA_JACK_EVENT,
                                                            via_jack_powerstate_event);
        }
 }
index 87f7fc41d4f2f03b772d5c9c8ce9472fa250de2f..206ed2cbcef9024a13f7a859bebfe53a8050f7f8 100644 (file)
@@ -2528,7 +2528,7 @@ static int snd_ice1712_free(struct snd_ice1712 *ice)
        if (!ice->port)
                goto __hw_end;
        /* mask all interrupts */
-       outb(0xc0, ICEMT(ice, IRQ));
+       outb(ICE1712_MULTI_CAPTURE | ICE1712_MULTI_PLAYBACK, ICEMT(ice, IRQ));
        outb(0xff, ICEREG(ice, IRQMASK));
        /* --- */
 __hw_end:
index a671f0865f71ceccb28e912018774ab2b433da83..601315a1f58fbef5b50c757844dd376bc22c74c8 100644 (file)
@@ -279,7 +279,6 @@ static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
 {
        struct lx6464es *chip = snd_pcm_substream_chip(substream);
        snd_pcm_uframes_t pos;
-       unsigned long flags;
        int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
 
        struct lx_stream *lx_stream = is_capture ? &chip->capture_stream :
@@ -287,9 +286,9 @@ static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
 
        dev_dbg(chip->card->dev, "->lx_pcm_stream_pointer\n");
 
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        pos = lx_stream->frame_pos * substream->runtime->period_size;
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
 
        dev_dbg(chip->card->dev, "stream_pointer at %ld\n", pos);
        return pos;
@@ -485,8 +484,8 @@ static void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream)
 
 }
 
-static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip,
-                                              struct lx_stream *lx_stream)
+static void lx_trigger_dispatch_stream(struct lx6464es *chip,
+                                      struct lx_stream *lx_stream)
 {
        switch (lx_stream->status) {
        case LX_STREAM_STATUS_SCHEDULE_RUN:
@@ -502,24 +501,12 @@ static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip,
        }
 }
 
-static void lx_trigger_tasklet(unsigned long data)
-{
-       struct lx6464es *chip = (struct lx6464es *)data;
-       unsigned long flags;
-
-       dev_dbg(chip->card->dev, "->lx_trigger_tasklet\n");
-
-       spin_lock_irqsave(&chip->lock, flags);
-       lx_trigger_tasklet_dispatch_stream(chip, &chip->capture_stream);
-       lx_trigger_tasklet_dispatch_stream(chip, &chip->playback_stream);
-       spin_unlock_irqrestore(&chip->lock, flags);
-}
-
 static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
                                   struct lx_stream *lx_stream, int cmd)
 {
        int err = 0;
 
+       mutex_lock(&chip->lock);
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
                lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN;
@@ -533,9 +520,12 @@ static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
                err = -EINVAL;
                goto exit;
        }
-       tasklet_schedule(&chip->trigger_tasklet);
+
+       lx_trigger_dispatch_stream(chip, &chip->capture_stream);
+       lx_trigger_dispatch_stream(chip, &chip->playback_stream);
 
 exit:
+       mutex_unlock(&chip->lock);
        return err;
 }
 
@@ -861,6 +851,7 @@ static int lx_pcm_create(struct lx6464es *chip)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture);
 
        pcm->info_flags = 0;
+       pcm->nonatomic = true;
        strcpy(pcm->name, card_name);
 
        err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
@@ -1009,15 +1000,9 @@ static int snd_lx6464es_create(struct snd_card *card,
        chip->irq = -1;
 
        /* initialize synchronization structs */
-       spin_lock_init(&chip->lock);
-       spin_lock_init(&chip->msg_lock);
+       mutex_init(&chip->lock);
+       mutex_init(&chip->msg_lock);
        mutex_init(&chip->setup_mutex);
-       tasklet_init(&chip->trigger_tasklet, lx_trigger_tasklet,
-                    (unsigned long)chip);
-       tasklet_init(&chip->tasklet_capture, lx_tasklet_capture,
-                    (unsigned long)chip);
-       tasklet_init(&chip->tasklet_playback, lx_tasklet_playback,
-                    (unsigned long)chip);
 
        /* request resources */
        err = pci_request_regions(pci, card_name);
@@ -1032,8 +1017,8 @@ static int snd_lx6464es_create(struct snd_card *card,
        /* dsp port */
        chip->port_dsp_bar = pci_ioremap_bar(pci, 2);
 
-       err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED,
-                         KBUILD_MODNAME, chip);
+       err = request_threaded_irq(pci->irq, lx_interrupt, lx_threaded_irq,
+                                  IRQF_SHARED, KBUILD_MODNAME, chip);
        if (err) {
                dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
                goto request_irq_failed;
index 6792eda9c9a56c0c41f4d8077f83edb19fc94965..1bec187d772f8201a6e8cf8bf6d65986f999f287 100644 (file)
@@ -71,14 +71,10 @@ struct lx6464es {
 
        u8                      mac_address[6];
 
-       spinlock_t              lock;        /* interrupt spinlock */
+       struct mutex            lock;        /* interrupt lock */
        struct mutex            setup_mutex; /* mutex used in hw_params, open
                                              * and close */
 
-       struct tasklet_struct   trigger_tasklet; /* trigger tasklet */
-       struct tasklet_struct   tasklet_capture;
-       struct tasklet_struct   tasklet_playback;
-
        /* ports */
        unsigned long           port_plx;          /* io port (size=256) */
        void __iomem           *port_plx_remapped; /* remapped plx port */
@@ -87,8 +83,9 @@ struct lx6464es {
                                                    * size=8K) */
 
        /* messaging */
-       spinlock_t              msg_lock;          /* message spinlock */
+       struct mutex            msg_lock;          /* message lock */
        struct lx_rmh           rmh;
+       u32                     irqsrc;
 
        /* configuration */
        uint                    freq_ratio : 2;
index e8f38e5df10abcae863756ac0134c0e5d2c1f1c1..f3d62020ef66c95119461a8836a650f30071f39d 100644 (file)
@@ -332,27 +332,25 @@ polling_successful:
 int lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version)
 {
        u16 ret;
-       unsigned long flags;
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
 
        lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
        ret = lx_message_send_atomic(chip, &chip->rmh);
 
        *rdsp_version = chip->rmh.stat[1];
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return ret;
 }
 
 int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
 {
        u16 ret = 0;
-       unsigned long flags;
        u32 freq_raw = 0;
        u32 freq = 0;
        u32 frequency = 0;
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
 
        lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
        ret = lx_message_send_atomic(chip, &chip->rmh);
@@ -370,7 +368,7 @@ int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
                        frequency = 48000;
        }
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
 
        *rfreq = frequency * chip->freq_ratio;
 
@@ -398,25 +396,23 @@ int lx_dsp_get_mac(struct lx6464es *chip)
 
 int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran)
 {
-       unsigned long flags;
        int ret;
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
 
        lx_message_init(&chip->rmh, CMD_02_SET_GRANULARITY);
        chip->rmh.cmd[0] |= gran;
 
        ret = lx_message_send_atomic(chip, &chip->rmh);
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return ret;
 }
 
 int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
 {
-       unsigned long flags;
        int ret;
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
 
        lx_message_init(&chip->rmh, CMD_04_GET_EVENT);
        chip->rmh.stat_len = 9; /* we don't necessarily need the full length */
@@ -426,7 +422,7 @@ int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
        if (!ret)
                memcpy(data, chip->rmh.stat, chip->rmh.stat_len * sizeof(u32));
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return ret;
 }
 
@@ -440,18 +436,16 @@ int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
                     int channels)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_06_ALLOCATE_PIPE);
 
        chip->rmh.cmd[0] |= pipe_cmd;
        chip->rmh.cmd[0] |= channels;
 
        err = lx_message_send_atomic(chip, &chip->rmh);
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
 
        if (err != 0)
                dev_err(chip->card->dev, "could not allocate pipe\n");
@@ -462,17 +456,15 @@ int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
 int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_07_RELEASE_PIPE);
 
        chip->rmh.cmd[0] |= pipe_cmd;
 
        err = lx_message_send_atomic(chip, &chip->rmh);
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
 
        return err;
 }
@@ -481,8 +473,6 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
                  u32 *r_needed, u32 *r_freed, u32 *size_array)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
 #ifdef CONFIG_SND_DEBUG
@@ -493,7 +483,7 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
        *r_needed = 0;
        *r_freed = 0;
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_08_ASK_BUFFERS);
 
        chip->rmh.cmd[0] |= pipe_cmd;
@@ -527,7 +517,7 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
                }
        }
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -535,36 +525,32 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
 int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_09_STOP_PIPE);
 
        chip->rmh.cmd[0] |= pipe_cmd;
 
        err = lx_message_send_atomic(chip, &chip->rmh);
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
 static int lx_pipe_toggle_state(struct lx6464es *chip, u32 pipe, int is_capture)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_0B_TOGGLE_PIPE_STATE);
 
        chip->rmh.cmd[0] |= pipe_cmd;
 
        err = lx_message_send_atomic(chip, &chip->rmh);
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -600,11 +586,9 @@ int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
                         u64 *rsample_count)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
 
        chip->rmh.cmd[0] |= pipe_cmd;
@@ -621,18 +605,16 @@ int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
                        + chip->rmh.stat[1]; /* lo part */
        }
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
 int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
 
        chip->rmh.cmd[0] |= pipe_cmd;
@@ -644,7 +626,7 @@ int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate)
        else
                *rstate = (chip->rmh.stat[0] >> PSTATE_OFFSET) & 0x0F;
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -686,18 +668,16 @@ int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
                               int is_capture, enum stream_state_t state)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_13_SET_STREAM_STATE);
 
        chip->rmh.cmd[0] |= pipe_cmd;
        chip->rmh.cmd[0] |= state;
 
        err = lx_message_send_atomic(chip, &chip->rmh);
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
 
        return err;
 }
@@ -706,17 +686,14 @@ int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
                         u32 pipe, int is_capture)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
-
        u32 channels = runtime->channels;
 
        if (runtime->channels != channels)
                dev_err(chip->card->dev, "channel count mismatch: %d vs %d",
                           runtime->channels, channels);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_0C_DEF_STREAM);
 
        chip->rmh.cmd[0] |= pipe_cmd;
@@ -732,7 +709,7 @@ int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
        chip->rmh.cmd[0] |= channels-1;
 
        err = lx_message_send_atomic(chip, &chip->rmh);
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
 
        return err;
 }
@@ -741,11 +718,9 @@ int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
                    int *rstate)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
 
        chip->rmh.cmd[0] |= pipe_cmd;
@@ -754,7 +729,7 @@ int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
 
        *rstate = (chip->rmh.stat[0] & SF_START) ? START_STATE : PAUSE_STATE;
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -762,11 +737,9 @@ int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
                              u64 *r_bytepos)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
 
        chip->rmh.cmd[0] |= pipe_cmd;
@@ -777,7 +750,7 @@ int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
                      << 32)         /* hi part */
                + chip->rmh.stat[1]; /* lo part */
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -787,11 +760,9 @@ int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
                   u32 *r_buffer_index)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_0F_UPDATE_BUFFER);
 
        chip->rmh.cmd[0] |= pipe_cmd;
@@ -828,7 +799,7 @@ int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
                        "lx_buffer_give EB_CMD_REFUSED\n");
 
  done:
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -836,11 +807,9 @@ int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
                   u32 *r_buffer_size)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
 
        chip->rmh.cmd[0] |= pipe_cmd;
@@ -852,7 +821,7 @@ int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
        if (err == 0)
                *r_buffer_size = chip->rmh.stat[0]  & MASK_DATA_SIZE;
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -860,11 +829,9 @@ int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
                     u32 buffer_index)
 {
        int err;
-       unsigned long flags;
-
        u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
 
        chip->rmh.cmd[0] |= pipe_cmd;
@@ -872,7 +839,7 @@ int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
 
        err = lx_message_send_atomic(chip, &chip->rmh);
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -885,12 +852,10 @@ int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
 int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute)
 {
        int err;
-       unsigned long flags;
-
        /* bit set to 1: channel muted */
        u64 mute_mask = unmute ? 0 : 0xFFFFFFFFFFFFFFFFLLU;
 
-       spin_lock_irqsave(&chip->msg_lock, flags);
+       mutex_lock(&chip->msg_lock);
        lx_message_init(&chip->rmh, CMD_0D_SET_MUTE);
 
        chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, 0);
@@ -904,7 +869,7 @@ int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute)
 
        err = lx_message_send_atomic(chip, &chip->rmh);
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -931,10 +896,9 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
                   u32 *r_levels)
 {
        int err = 0;
-       unsigned long flags;
        int i;
-       spin_lock_irqsave(&chip->msg_lock, flags);
 
+       mutex_lock(&chip->msg_lock);
        for (i = 0; i < channels; i += 4) {
                u32 s0, s1, s2, s3;
 
@@ -959,7 +923,7 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
                r_levels += 4;
        }
 
-       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       mutex_unlock(&chip->msg_lock);
        return err;
 }
 
@@ -1075,7 +1039,6 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
        struct snd_pcm_substream *substream = lx_stream->stream;
        const unsigned int is_capture = lx_stream->is_capture;
        int err;
-       unsigned long flags;
 
        const u32 channels = substream->runtime->channels;
        const u32 bytes_per_frame = channels * 3;
@@ -1095,7 +1058,7 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
 
        dev_dbg(chip->card->dev, "->lx_interrupt_request_new_buffer\n");
 
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
 
        err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
        dev_dbg(chip->card->dev,
@@ -1109,85 +1072,28 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
                    buffer_index, (unsigned long)buf, period_bytes);
 
        lx_stream->frame_pos = next_pos;
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
 
        return err;
 }
 
-void lx_tasklet_playback(unsigned long data)
-{
-       struct lx6464es *chip = (struct lx6464es *)data;
-       struct lx_stream *lx_stream = &chip->playback_stream;
-       int err;
-
-       dev_dbg(chip->card->dev, "->lx_tasklet_playback\n");
-
-       err = lx_interrupt_request_new_buffer(chip, lx_stream);
-       if (err < 0)
-               dev_err(chip->card->dev,
-                          "cannot request new buffer for playback\n");
-
-       snd_pcm_period_elapsed(lx_stream->stream);
-}
-
-void lx_tasklet_capture(unsigned long data)
-{
-       struct lx6464es *chip = (struct lx6464es *)data;
-       struct lx_stream *lx_stream = &chip->capture_stream;
-       int err;
-
-       dev_dbg(chip->card->dev, "->lx_tasklet_capture\n");
-       err = lx_interrupt_request_new_buffer(chip, lx_stream);
-       if (err < 0)
-               dev_err(chip->card->dev,
-                          "cannot request new buffer for capture\n");
-
-       snd_pcm_period_elapsed(lx_stream->stream);
-}
-
-
-
-static int lx_interrupt_handle_audio_transfer(struct lx6464es *chip,
-                                             u64 notified_in_pipe_mask,
-                                             u64 notified_out_pipe_mask)
-{
-       int err = 0;
-
-       if (notified_in_pipe_mask) {
-               dev_dbg(chip->card->dev,
-                       "requesting audio transfer for capture\n");
-               tasklet_hi_schedule(&chip->tasklet_capture);
-       }
-
-       if (notified_out_pipe_mask) {
-               dev_dbg(chip->card->dev,
-                       "requesting audio transfer for playback\n");
-               tasklet_hi_schedule(&chip->tasklet_playback);
-       }
-
-       return err;
-}
-
-
 irqreturn_t lx_interrupt(int irq, void *dev_id)
 {
        struct lx6464es *chip = dev_id;
        int async_pending, async_escmd;
        u32 irqsrc;
-
-       spin_lock(&chip->lock);
+       bool wake_thread = false;
 
        dev_dbg(chip->card->dev,
                "**************************************************\n");
 
        if (!lx_interrupt_ack(chip, &irqsrc, &async_pending, &async_escmd)) {
-               spin_unlock(&chip->lock);
                dev_dbg(chip->card->dev, "IRQ_NONE\n");
                return IRQ_NONE; /* this device did not cause the interrupt */
        }
 
        if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
-               goto exit;
+               return IRQ_HANDLED;
 
        if (irqsrc & MASK_SYS_STATUS_EOBI)
                dev_dbg(chip->card->dev, "interrupt: EOBI\n");
@@ -1202,27 +1108,8 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
                dev_dbg(chip->card->dev, "interrupt: ORUN\n");
 
        if (async_pending) {
-               u64 notified_in_pipe_mask = 0;
-               u64 notified_out_pipe_mask = 0;
-               int freq_changed;
-               int err;
-
-               /* handle async events */
-               err = lx_interrupt_handle_async_events(chip, irqsrc,
-                                                      &freq_changed,
-                                                      &notified_in_pipe_mask,
-                                                      &notified_out_pipe_mask);
-               if (err)
-                       dev_err(chip->card->dev,
-                                  "error handling async events\n");
-
-               err = lx_interrupt_handle_audio_transfer(chip,
-                                                        notified_in_pipe_mask,
-                                                        notified_out_pipe_mask
-                       );
-               if (err)
-                       dev_err(chip->card->dev,
-                                  "error during audio transfer\n");
+               wake_thread = true;
+               chip->irqsrc = irqsrc;
        }
 
        if (async_escmd) {
@@ -1235,9 +1122,50 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
                dev_dbg(chip->card->dev, "interrupt requests escmd handling\n");
        }
 
-exit:
-       spin_unlock(&chip->lock);
-       return IRQ_HANDLED;     /* this device caused the interrupt */
+       return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+irqreturn_t lx_threaded_irq(int irq, void *dev_id)
+{
+       struct lx6464es *chip = dev_id;
+       u64 notified_in_pipe_mask = 0;
+       u64 notified_out_pipe_mask = 0;
+       int freq_changed;
+       int err;
+
+       /* handle async events */
+       err = lx_interrupt_handle_async_events(chip, chip->irqsrc,
+                                              &freq_changed,
+                                              &notified_in_pipe_mask,
+                                              &notified_out_pipe_mask);
+       if (err)
+               dev_err(chip->card->dev, "error handling async events\n");
+
+       if (notified_in_pipe_mask) {
+               struct lx_stream *lx_stream = &chip->capture_stream;
+
+               dev_dbg(chip->card->dev,
+                       "requesting audio transfer for capture\n");
+               err = lx_interrupt_request_new_buffer(chip, lx_stream);
+               if (err < 0)
+                       dev_err(chip->card->dev,
+                               "cannot request new buffer for capture\n");
+               snd_pcm_period_elapsed(lx_stream->stream);
+       }
+
+       if (notified_out_pipe_mask) {
+               struct lx_stream *lx_stream = &chip->playback_stream;
+
+               dev_dbg(chip->card->dev,
+                       "requesting audio transfer for playback\n");
+               err = lx_interrupt_request_new_buffer(chip, lx_stream);
+               if (err < 0)
+                       dev_err(chip->card->dev,
+                               "cannot request new buffer for playback\n");
+               snd_pcm_period_elapsed(lx_stream->stream);
+       }
+
+       return IRQ_HANDLED;
 }
 
 
index 5ec5e04da1a5a02f2008b070c76e24fe3964765e..0cc140ca98e3d845298b8f616c55328ddd8e1174 100644 (file)
@@ -181,12 +181,10 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
 
 /* interrupt handling */
 irqreturn_t lx_interrupt(int irq, void *dev_id);
+irqreturn_t lx_threaded_irq(int irq, void *dev_id);
 void lx_irq_enable(struct lx6464es *chip);
 void lx_irq_disable(struct lx6464es *chip);
 
-void lx_tasklet_capture(unsigned long data);
-void lx_tasklet_playback(unsigned long data);
-
 
 /* Stream Format Header Defines (for LIN and IEEE754) */
 #define HEADER_FMT_BASE                HEADER_FMT_BASE_LIN
index 75fc342cff2a842cb296788262dd674c9f51f8fb..1faf47e81570f889c5800165402a6c4cbea745d4 100644 (file)
@@ -986,6 +986,7 @@ static int snd_mixart_pcm_analog(struct snd_mixart *chip)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
 
        pcm->info_flags = 0;
+       pcm->nonatomic = true;
        strcpy(pcm->name, name);
 
        preallocate_buffers(chip, pcm);
@@ -1018,6 +1019,7 @@ static int snd_mixart_pcm_digital(struct snd_mixart *chip)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
 
        pcm->info_flags = 0;
+       pcm->nonatomic = true;
        strcpy(pcm->name, name);
 
        preallocate_buffers(chip, pcm);
@@ -1303,8 +1305,9 @@ static int snd_mixart_probe(struct pci_dev *pci,
                }
        }
 
-       if (request_irq(pci->irq, snd_mixart_interrupt, IRQF_SHARED,
-                       KBUILD_MODNAME, mgr)) {
+       if (request_threaded_irq(pci->irq, snd_mixart_interrupt,
+                                snd_mixart_threaded_irq, IRQF_SHARED,
+                                KBUILD_MODNAME, mgr)) {
                dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
                snd_mixart_free(mgr);
                return -EBUSY;
@@ -1314,24 +1317,18 @@ static int snd_mixart_probe(struct pci_dev *pci,
        sprintf(mgr->shortname, "Digigram miXart");
        sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq);
 
-       /* ISR spinlock  */
-       spin_lock_init(&mgr->lock);
-
        /* init mailbox  */
        mgr->msg_fifo_readptr = 0;
        mgr->msg_fifo_writeptr = 0;
 
-       spin_lock_init(&mgr->msg_lock);
-       mutex_init(&mgr->msg_mutex);
+       mutex_init(&mgr->lock);
+       mutex_init(&mgr->msg_lock);
        init_waitqueue_head(&mgr->msg_sleep);
        atomic_set(&mgr->msg_processed, 0);
 
        /* init setup mutex*/
        mutex_init(&mgr->setup_mutex);
 
-       /* init message taslket */
-       tasklet_init(&mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr);
-
        /* card assignment */
        mgr->num_cards = MIXART_MAX_CARDS; /* 4  FIXME: configurable? */
        for (i = 0; i < mgr->num_cards; i++) {
index 561634d5c0076aee392e022aa53d7745b251e4dc..0cc17e0ea34a4d19b3558ff18b2120b84e659490 100644 (file)
@@ -78,22 +78,18 @@ struct mixart_mgr {
        char shortname[32];         /* short name of this soundcard */
        char longname[80];          /* name of this soundcard */
 
-       /* message tasklet */
-       struct tasklet_struct msg_taskq;
-
        /* one and only blocking message or notification may be pending  */
        u32 pending_event;
        wait_queue_head_t msg_sleep;
 
-       /* messages stored for tasklet */
+       /* messages fifo */
        u32 msg_fifo[MSG_FIFO_SIZE];
        int msg_fifo_readptr;
        int msg_fifo_writeptr;
        atomic_t msg_processed;       /* number of messages to be processed in takslet */
 
-       spinlock_t lock;              /* interrupt spinlock */
-       spinlock_t msg_lock;          /* mailbox spinlock */
-       struct mutex msg_mutex;   /* mutex for blocking_requests */
+       struct mutex lock;              /* interrupt lock */
+       struct mutex msg_lock;          /* mailbox lock */
 
        struct mutex setup_mutex; /* mutex used in hw_params, open and close */
 
index 84f67450924e55fec906cb546151ad32ae9e7d28..fe80313674d9ce7e30ba9cc1b2278dfc521d9089 100644 (file)
@@ -76,7 +76,6 @@ static int retrieve_msg_frame(struct mixart_mgr *mgr, u32 *msg_frame)
 static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
                   u32 msg_frame_address )
 {
-       unsigned long flags;
        u32  headptr;
        u32  size;
        int  err;
@@ -84,7 +83,7 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
        unsigned int i;
 #endif
 
-       spin_lock_irqsave(&mgr->msg_lock, flags);
+       mutex_lock(&mgr->msg_lock);
        err = 0;
 
        /* copy message descriptor from miXart to driver */
@@ -133,7 +132,7 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,
        writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
 
  _clean_exit:
-       spin_unlock_irqrestore(&mgr->msg_lock, flags);
+       mutex_unlock(&mgr->msg_lock);
 
        return err;
 }
@@ -243,28 +242,24 @@ int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int
        wait_queue_t wait;
        long timeout;
 
-       mutex_lock(&mgr->msg_mutex);
-
        init_waitqueue_entry(&wait, current);
 
-       spin_lock_irq(&mgr->msg_lock);
+       mutex_lock(&mgr->msg_lock);
        /* send the message */
        err = send_msg(mgr, request, max_resp_size, 1, &msg_frame);  /* send and mark the answer pending */
        if (err) {
-               spin_unlock_irq(&mgr->msg_lock);
-               mutex_unlock(&mgr->msg_mutex);
+               mutex_unlock(&mgr->msg_lock);
                return err;
        }
 
        set_current_state(TASK_UNINTERRUPTIBLE);
        add_wait_queue(&mgr->msg_sleep, &wait);
-       spin_unlock_irq(&mgr->msg_lock);
+       mutex_unlock(&mgr->msg_lock);
        timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
        remove_wait_queue(&mgr->msg_sleep, &wait);
 
        if (! timeout) {
                /* error - no ack */
-               mutex_unlock(&mgr->msg_mutex);
                dev_err(&mgr->pci->dev,
                        "error: no response on msg %x\n", msg_frame);
                return -EIO;
@@ -281,7 +276,6 @@ int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int
        if( request->message_id != resp.message_id )
                dev_err(&mgr->pci->dev, "RESPONSE ERROR!\n");
 
-       mutex_unlock(&mgr->msg_mutex);
        return err;
 }
 
@@ -300,34 +294,29 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,
        if (snd_BUG_ON(notif_event & MSG_CANCEL_NOTIFY_MASK))
                return -EINVAL;
 
-       mutex_lock(&mgr->msg_mutex);
-
        init_waitqueue_entry(&wait, current);
 
-       spin_lock_irq(&mgr->msg_lock);
+       mutex_lock(&mgr->msg_lock);
        /* send the message */
        err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, &notif_event);  /* send and mark the notification event pending */
        if(err) {
-               spin_unlock_irq(&mgr->msg_lock);
-               mutex_unlock(&mgr->msg_mutex);
+               mutex_unlock(&mgr->msg_lock);
                return err;
        }
 
        set_current_state(TASK_UNINTERRUPTIBLE);
        add_wait_queue(&mgr->msg_sleep, &wait);
-       spin_unlock_irq(&mgr->msg_lock);
+       mutex_unlock(&mgr->msg_lock);
        timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
        remove_wait_queue(&mgr->msg_sleep, &wait);
 
        if (! timeout) {
                /* error - no ack */
-               mutex_unlock(&mgr->msg_mutex);
                dev_err(&mgr->pci->dev,
                        "error: notification %x not received\n", notif_event);
                return -EIO;
        }
 
-       mutex_unlock(&mgr->msg_mutex);
        return 0;
 }
 
@@ -335,13 +324,12 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,
 int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request)
 {
        u32 message_frame;
-       unsigned long flags;
        int err;
 
        /* just send the message (do not mark it as a pending one) */
-       spin_lock_irqsave(&mgr->msg_lock, flags);
+       mutex_lock(&mgr->msg_lock);
        err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame);
-       spin_unlock_irqrestore(&mgr->msg_lock, flags);
+       mutex_unlock(&mgr->msg_lock);
 
        /* the answer will be handled by snd_struct mixart_msgasklet()  */
        atomic_inc(&mgr->msg_processed);
@@ -350,19 +338,16 @@ int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *requ
 }
 
 
-/* common buffer of tasklet and interrupt to send/receive messages */
+/* common buffer of interrupt to send/receive messages */
 static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4];
 
 
-void snd_mixart_msg_tasklet(unsigned long arg)
+static void snd_mixart_process_msg(struct mixart_mgr *mgr)
 {
-       struct mixart_mgr *mgr = ( struct mixart_mgr*)(arg);
        struct mixart_msg resp;
        u32 msg, addr, type;
        int err;
 
-       spin_lock(&mgr->lock);
-
        while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) {
                msg = mgr->msg_fifo[mgr->msg_fifo_readptr];
                mgr->msg_fifo_readptr++;
@@ -381,7 +366,7 @@ void snd_mixart_msg_tasklet(unsigned long arg)
                        err = get_msg(mgr, &resp, addr);
                        if( err < 0 ) {
                                dev_err(&mgr->pci->dev,
-                                       "tasklet: error(%d) reading mf %x\n",
+                                       "error(%d) reading mf %x\n",
                                        err, msg);
                                break;
                        }
@@ -393,12 +378,12 @@ void snd_mixart_msg_tasklet(unsigned long arg)
                        case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET:
                                if(mixart_msg_data[0])
                                        dev_err(&mgr->pci->dev,
-                                               "tasklet : error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n",
+                                               "error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n",
                                                mixart_msg_data[0]);
                                break;
                        default:
                                dev_dbg(&mgr->pci->dev,
-                                       "tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n",
+                                       "received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n",
                                           msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size);
                                break;
                        }
@@ -409,7 +394,7 @@ void snd_mixart_msg_tasklet(unsigned long arg)
                        /* get_msg() necessary */
                default:
                        dev_err(&mgr->pci->dev,
-                               "tasklet doesn't know what to do with message %x\n",
+                               "doesn't know what to do with message %x\n",
                                msg);
                } /* switch type */
 
@@ -417,26 +402,17 @@ void snd_mixart_msg_tasklet(unsigned long arg)
                atomic_dec(&mgr->msg_processed);
 
        } /* while there is a msg in fifo */
-
-       spin_unlock(&mgr->lock);
 }
 
 
 irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
 {
        struct mixart_mgr *mgr = dev_id;
-       int err;
-       struct mixart_msg resp;
-
-       u32 msg;
        u32 it_reg;
 
-       spin_lock(&mgr->lock);
-
        it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET));
        if( !(it_reg & MIXART_OIDI) ) {
                /* this device did not cause the interrupt */
-               spin_unlock(&mgr->lock);
                return IRQ_NONE;
        }
 
@@ -450,6 +426,17 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
        /* clear interrupt */
        writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) );
 
+       return IRQ_WAKE_THREAD;
+}
+
+irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id)
+{
+       struct mixart_mgr *mgr = dev_id;
+       int err;
+       struct mixart_msg resp;
+       u32 msg;
+
+       mutex_lock(&mgr->lock);
        /* process interrupt */
        while (retrieve_msg_frame(mgr, &msg)) {
 
@@ -518,9 +505,9 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
                                                stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed );
 
                                                if(elapsed) {
-                                                       spin_unlock(&mgr->lock);
+                                                       mutex_unlock(&mgr->lock);
                                                        snd_pcm_period_elapsed(stream->substream);
-                                                       spin_lock(&mgr->lock);
+                                                       mutex_lock(&mgr->lock);
                                                }
                                        }
                                }
@@ -556,7 +543,7 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
                        /* no break, continue ! */
                case MSG_TYPE_ANSWER:
                        /* answer or notification to a message we are waiting for*/
-                       spin_lock(&mgr->msg_lock);
+                       mutex_lock(&mgr->msg_lock);
                        if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) {
                                wake_up(&mgr->msg_sleep);
                                mgr->pending_event = 0;
@@ -566,9 +553,9 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
                                mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
                                mgr->msg_fifo_writeptr++;
                                mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
-                               tasklet_schedule(&mgr->msg_taskq);
+                               snd_mixart_process_msg(mgr);
                        }
-                       spin_unlock(&mgr->msg_lock);
+                       mutex_unlock(&mgr->msg_lock);
                        break;
                case MSG_TYPE_REQUEST:
                default:
@@ -582,7 +569,7 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
        /* allow interrupt again */
        writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
 
-       spin_unlock(&mgr->lock);
+       mutex_unlock(&mgr->lock);
 
        return IRQ_HANDLED;
 }
index c919b734756ff3a9b387572d0de08f2a0a937199..d1722e575409b09ab62e5a258d7fed424de13347 100644 (file)
@@ -564,7 +564,7 @@ int  snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr, struct mixart_msg *r
 int  snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request);
 
 irqreturn_t snd_mixart_interrupt(int irq, void *dev_id);
-void snd_mixart_msg_tasklet(unsigned long arg);
+irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id);
 
 void snd_mixart_reset_board(struct mixart_mgr *mgr);
 
index cc0bcd9f335075b341dba57144fca8a44311adb6..02828240ba15740600a65b2eaf07b3f663c3463b 100644 (file)
@@ -29,6 +29,9 @@
 /* the multichannel DMA channel has a 24-bit counter */
 #define BUFFER_BYTES_MAX_MULTICH       ((1 << 24) * 4)
 
+#define FIFO_BYTES                     256
+#define FIFO_BYTES_MULTICH             1024
+
 #define PERIOD_BYTES_MIN               64
 
 #define DEFAULT_BUFFER_BYTES           (BUFFER_BYTES_MAX / 2)
@@ -60,6 +63,7 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {
        .period_bytes_max = BUFFER_BYTES_MAX,
        .periods_min = 1,
        .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+       .fifo_size = FIFO_BYTES,
 };
 static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
        .info = SNDRV_PCM_INFO_MMAP |
@@ -87,6 +91,7 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
        .period_bytes_max = BUFFER_BYTES_MAX_MULTICH,
        .periods_min = 1,
        .periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN,
+       .fifo_size = FIFO_BYTES_MULTICH,
 };
 static const struct snd_pcm_hardware oxygen_ac97_hardware = {
        .info = SNDRV_PCM_INFO_MMAP |
@@ -106,6 +111,7 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = {
        .period_bytes_max = BUFFER_BYTES_MAX,
        .periods_min = 1,
        .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+       .fifo_size = FIFO_BYTES,
 };
 
 static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
@@ -141,6 +147,10 @@ static int oxygen_open(struct snd_pcm_substream *substream,
                runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 |
                                       SNDRV_PCM_RATE_64000);
                runtime->hw.rate_min = 44100;
+               /* fall through */
+       case PCM_A:
+       case PCM_B:
+               runtime->hw.fifo_size = 0;
                break;
        case PCM_MULTICH:
                runtime->hw.channels_max = chip->model.dac_channels_pcm;
index 7b317a28a19ca3cfb43725947ad54db2a27d2d8d..83de6fb01021e6053f15b9e2ad5cdb15bddcb88c 100644 (file)
@@ -52,6 +52,7 @@ static const struct pci_device_id xonar_ids[] = {
        { OXYGEN_PCI_SUBID(0x1043, 0x835d) },
        { OXYGEN_PCI_SUBID(0x1043, 0x835e) },
        { OXYGEN_PCI_SUBID(0x1043, 0x838e) },
+       { OXYGEN_PCI_SUBID(0x1043, 0x8428) },
        { OXYGEN_PCI_SUBID(0x1043, 0x8522) },
        { OXYGEN_PCI_SUBID(0x1043, 0x85f4) },
        { OXYGEN_PCI_SUBID_BROKEN_EEPROM },
index e0260593166936a70c15f5e12cd0829e519fbbb1..24109d37ca09783e09b243044506c852d4cee94d 100644 (file)
 #define GPIO_ST_MAGIC          0x0040
 #define GPIO_ST_HP             0x0080
 
+#define GPIO_XENSE_OUTPUT_ENABLE       (0x0001 | 0x0010 | 0x0020)
+#define GPIO_XENSE_SPEAKERS            0x0080
+
 #define I2C_DEVICE_PCM1796(i)  (0x98 + ((i) << 1))     /* 10011, ii, /W=0 */
 #define I2C_DEVICE_CS2000      0x9c                    /* 100111, 0, /W=0 */
 
@@ -419,6 +422,7 @@ static void xonar_st_init_common(struct oxygen *chip)
 
        data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
        data->dacs = chip->model.dac_channels_mixer / 2;
+       data->h6 = chip->model.dac_channels_mixer > 2;
        data->hp_gain_offset = 2*-18;
 
        pcm1796_init(chip);
@@ -499,6 +503,51 @@ static void xonar_stx_init(struct oxygen *chip)
        xonar_st_init_common(chip);
 }
 
+static void xonar_xense_init(struct oxygen *chip)
+{
+       struct xonar_pcm179x *data = chip->model_data;
+
+       data->generic.ext_power_reg = OXYGEN_GPI_DATA;
+       data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+       data->generic.ext_power_bit = GPI_EXT_POWER;
+       xonar_init_ext_power(chip);
+
+       data->generic.anti_pop_delay = 100;
+       data->has_cs2000 = 1;
+       data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
+
+       oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+               OXYGEN_RATE_48000 |
+               OXYGEN_I2S_FORMAT_I2S |
+               OXYGEN_I2S_MCLK(MCLK_512) |
+               OXYGEN_I2S_BITS_16 |
+               OXYGEN_I2S_MASTER |
+               OXYGEN_I2S_BCLK_64);
+
+       xonar_st_init_i2c(chip);
+       cs2000_registers_init(chip);
+
+       data->generic.output_enable_bit = GPIO_XENSE_OUTPUT_ENABLE;
+       data->dacs = 1;
+       data->hp_gain_offset = 2*-18;
+
+       pcm1796_init(chip);
+
+       oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+               GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR |
+               GPIO_ST_MAGIC | GPIO_XENSE_SPEAKERS);
+       oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+               GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR |
+               GPIO_XENSE_SPEAKERS);
+
+       xonar_init_cs53x1(chip);
+       xonar_enable_output(chip);
+
+       snd_component_add(chip->card, "PCM1796");
+       snd_component_add(chip->card, "CS5381");
+       snd_component_add(chip->card, "CS2000");
+}
+
 static void xonar_d2_cleanup(struct oxygen *chip)
 {
        xonar_disable_output(chip);
@@ -795,11 +844,11 @@ static int st_output_switch_put(struct snd_kcontrol *ctl,
 static int st_hp_volume_offset_info(struct snd_kcontrol *ctl,
                                    struct snd_ctl_elem_info *info)
 {
-       static const char *const names[3] = {
-               "< 64 ohms", "64-300 ohms", "300-600 ohms"
+       static const char *const names[4] = {
+               "< 32 ohms", "32-64 ohms", "64-300 ohms", "300-600 ohms"
        };
 
-       return snd_ctl_enum_info(info, 1, 3, names);
+       return snd_ctl_enum_info(info, 1, 4, names);
 }
 
 static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
@@ -809,12 +858,14 @@ static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
        struct xonar_pcm179x *data = chip->model_data;
 
        mutex_lock(&chip->mutex);
-       if (data->hp_gain_offset < 2*-6)
+       if (data->hp_gain_offset < 2*-12)
                value->value.enumerated.item[0] = 0;
-       else if (data->hp_gain_offset < 0)
+       else if (data->hp_gain_offset < 2*-6)
                value->value.enumerated.item[0] = 1;
-       else
+       else if (data->hp_gain_offset < 0)
                value->value.enumerated.item[0] = 2;
+       else
+               value->value.enumerated.item[0] = 3;
        mutex_unlock(&chip->mutex);
        return 0;
 }
@@ -823,13 +874,13 @@ static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
 static int st_hp_volume_offset_put(struct snd_kcontrol *ctl,
                                   struct snd_ctl_elem_value *value)
 {
-       static const s8 offsets[] = { 2*-18, 2*-6, 0 };
+       static const s8 offsets[] = { 2*-18, 2*-12, 2*-6, 0 };
        struct oxygen *chip = ctl->private_data;
        struct xonar_pcm179x *data = chip->model_data;
        s8 offset;
        int changed;
 
-       if (value->value.enumerated.item[0] > 2)
+       if (value->value.enumerated.item[0] > 3)
                return -EINVAL;
        offset = offsets[value->value.enumerated.item[0]];
        mutex_lock(&chip->mutex);
@@ -859,6 +910,67 @@ static const struct snd_kcontrol_new st_controls[] = {
        },
 };
 
+static int xense_output_switch_get(struct snd_kcontrol *ctl,
+                                  struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       u16 gpio;
+
+       gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+       if (gpio & GPIO_XENSE_SPEAKERS)
+               value->value.enumerated.item[0] = 0;
+       else if (!(gpio & GPIO_XENSE_SPEAKERS) && (gpio & GPIO_ST_HP_REAR))
+               value->value.enumerated.item[0] = 1;
+       else
+               value->value.enumerated.item[0] = 2;
+       return 0;
+}
+
+static int xense_output_switch_put(struct snd_kcontrol *ctl,
+                                  struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_pcm179x *data = chip->model_data;
+       u16 gpio_old, gpio;
+
+       mutex_lock(&chip->mutex);
+       gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+       gpio = gpio_old;
+       switch (value->value.enumerated.item[0]) {
+       case 0:
+               gpio |= GPIO_XENSE_SPEAKERS | GPIO_ST_HP_REAR;
+               break;
+       case 1:
+               gpio = (gpio | GPIO_ST_HP_REAR) & ~GPIO_XENSE_SPEAKERS;
+               break;
+       case 2:
+               gpio &= ~(GPIO_XENSE_SPEAKERS | GPIO_ST_HP_REAR);
+               break;
+       }
+       oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio);
+       data->hp_active = !(gpio & GPIO_XENSE_SPEAKERS);
+       update_pcm1796_volume(chip);
+       mutex_unlock(&chip->mutex);
+       return gpio != gpio_old;
+}
+
+static const struct snd_kcontrol_new xense_controls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Output",
+               .info = st_output_switch_info,
+               .get = xense_output_switch_get,
+               .put = xense_output_switch_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Headphones Impedance Playback Enum",
+               .info = st_hp_volume_offset_info,
+               .get = st_hp_volume_offset_get,
+               .put = st_hp_volume_offset_put,
+       },
+};
+
 static void xonar_line_mic_ac97_switch(struct oxygen *chip,
                                       unsigned int reg, unsigned int mute)
 {
@@ -946,6 +1058,23 @@ static int xonar_st_mixer_init(struct oxygen *chip)
        return 0;
 }
 
+static int xonar_xense_mixer_init(struct oxygen *chip)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < ARRAY_SIZE(xense_controls); ++i) {
+               err = snd_ctl_add(chip->card,
+               snd_ctl_new1(&xense_controls[i], chip));
+               if (err < 0)
+                       return err;
+       }
+       err = add_pcm1796_controls(chip);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
 static void dump_pcm1796_registers(struct oxygen *chip,
                                   struct snd_info_buffer *buffer)
 {
@@ -1140,12 +1269,29 @@ int get_xonar_pcm179x_model(struct oxygen *chip,
                break;
        case 0x85f4:
                chip->model = model_xonar_st;
-               /* TODO: daughterboard support */
-               chip->model.shortname = "Xonar STX II";
+               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+               switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+               default:
+                       chip->model.shortname = "Xonar STX II";
+                       break;
+               case GPIO_DB_H6:
+                       chip->model.shortname = "Xonar STX II+H6";
+                       chip->model.dac_channels_pcm = 8;
+                       chip->model.dac_channels_mixer = 8;
+                       chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
+                       break;
+               }
                chip->model.init = xonar_stx_init;
                chip->model.resume = xonar_stx_resume;
                chip->model.set_dac_params = set_pcm1796_params;
                break;
+       case 0x8428:
+               chip->model = model_xonar_st;
+               chip->model.shortname = "Xonar Xense";
+               chip->model.chip = "AV100";
+               chip->model.init = xonar_xense_init;
+               chip->model.mixer_init = xonar_xense_mixer_init;
+               break;
        default:
                return -EINVAL;
        }
index 68a37a7906c121d3e5ebafab2943e198e7cab940..b854fc5e01f51171ff316cb6a320a7a2762e79c4 100644 (file)
@@ -702,13 +702,11 @@ static inline int pcxhr_stream_scheduled_get_pipe(struct pcxhr_stream *stream,
        return 0;
 }
 
-static void pcxhr_trigger_tasklet(unsigned long arg)
+static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr)
 {
-       unsigned long flags;
        int i, j, err;
        struct pcxhr_pipe *pipe;
        struct snd_pcxhr *chip;
-       struct pcxhr_mgr *mgr = (struct pcxhr_mgr*)(arg);
        int capture_mask = 0;
        int playback_mask = 0;
 
@@ -736,11 +734,11 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
        }
        if (capture_mask == 0 && playback_mask == 0) {
                mutex_unlock(&mgr->setup_mutex);
-               dev_err(&mgr->pci->dev, "pcxhr_trigger_tasklet : no pipes\n");
+               dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : no pipes\n");
                return;
        }
 
-       dev_dbg(&mgr->pci->dev, "pcxhr_trigger_tasklet : "
+       dev_dbg(&mgr->pci->dev, "pcxhr_start_linked_stream : "
                    "playback_mask=%x capture_mask=%x\n",
                    playback_mask, capture_mask);
 
@@ -748,7 +746,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
        err = pcxhr_set_pipe_state(mgr,  playback_mask, capture_mask, 0);
        if (err) {
                mutex_unlock(&mgr->setup_mutex);
-               dev_err(&mgr->pci->dev, "pcxhr_trigger_tasklet : "
+               dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : "
                           "error stop pipes (P%x C%x)\n",
                           playback_mask, capture_mask);
                return;
@@ -793,7 +791,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
        err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
        if (err) {
                mutex_unlock(&mgr->setup_mutex);
-               dev_err(&mgr->pci->dev, "pcxhr_trigger_tasklet : "
+               dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : "
                           "error start pipes (P%x C%x)\n",
                           playback_mask, capture_mask);
                return;
@@ -802,7 +800,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
        /* put the streams into the running state now
         * (increment pointer by interrupt)
         */
-       spin_lock_irqsave(&mgr->lock, flags);
+       mutex_lock(&mgr->lock);
        for ( i =0; i < mgr->num_cards; i++) {
                struct pcxhr_stream *stream;
                chip = mgr->chip[i];
@@ -820,13 +818,13 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
                        }
                }
        }
-       spin_unlock_irqrestore(&mgr->lock, flags);
+       mutex_unlock(&mgr->lock);
 
        mutex_unlock(&mgr->setup_mutex);
 
 #ifdef CONFIG_SND_DEBUG_VERBOSE
        do_gettimeofday(&my_tv2);
-       dev_dbg(&mgr->pci->dev, "***TRIGGER TASKLET*** TIME = %ld (err = %x)\n",
+       dev_dbg(&mgr->pci->dev, "***TRIGGER START*** TIME = %ld (err = %x)\n",
                    (long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
 #endif
 }
@@ -853,7 +851,7 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
                                        PCXHR_STREAM_STATUS_SCHEDULE_RUN;
                                snd_pcm_trigger_done(s, subs);
                        }
-                       tasklet_schedule(&chip->mgr->trigger_taskq);
+                       pcxhr_start_linked_stream(chip->mgr);
                } else {
                        stream = subs->runtime->private_data;
                        snd_printdd("Only one Substream %c %d\n",
@@ -1127,20 +1125,19 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
 
 static snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs)
 {
-       unsigned long flags;
        u_int32_t timer_period_frag;
        int timer_buf_periods;
        struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
        struct snd_pcm_runtime *runtime = subs->runtime;
        struct pcxhr_stream *stream  = runtime->private_data;
 
-       spin_lock_irqsave(&chip->mgr->lock, flags);
+       mutex_lock(&chip->mgr->lock);
 
        /* get the period fragment and the nb of periods in the buffer */
        timer_period_frag = stream->timer_period_frag;
        timer_buf_periods = stream->timer_buf_periods;
 
-       spin_unlock_irqrestore(&chip->mgr->lock, flags);
+       mutex_unlock(&chip->mgr->lock);
 
        return (snd_pcm_uframes_t)((timer_buf_periods * runtime->period_size) +
                                   timer_period_frag);
@@ -1181,6 +1178,7 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip)
                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcxhr_ops);
 
        pcm->info_flags = 0;
+       pcm->nonatomic = true;
        strcpy(pcm->name, name);
 
        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
@@ -1588,8 +1586,9 @@ static int pcxhr_probe(struct pci_dev *pci,
        mgr->pci = pci;
        mgr->irq = -1;
 
-       if (request_irq(pci->irq, pcxhr_interrupt, IRQF_SHARED,
-                       KBUILD_MODNAME, mgr)) {
+       if (request_threaded_irq(pci->irq, pcxhr_interrupt,
+                                pcxhr_threaded_irq, IRQF_SHARED,
+                                KBUILD_MODNAME, mgr)) {
                dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
                pcxhr_free(mgr);
                return -EBUSY;
@@ -1601,19 +1600,13 @@ static int pcxhr_probe(struct pci_dev *pci,
                mgr->shortname,
                mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
 
-       /* ISR spinlock  */
-       spin_lock_init(&mgr->lock);
-       spin_lock_init(&mgr->msg_lock);
+       /* ISR lock  */
+       mutex_init(&mgr->lock);
+       mutex_init(&mgr->msg_lock);
 
        /* init setup mutex*/
        mutex_init(&mgr->setup_mutex);
 
-       /* init taslket */
-       tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet,
-                    (unsigned long) mgr);
-       tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet,
-                    (unsigned long) mgr);
-
        mgr->prmh = kmalloc(sizeof(*mgr->prmh) + 
                            sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
                                           PCXHR_SIZE_MAX_STATUS),
index a4c602c451738bb541b2c1ddf20195d429c99000..9e39e509a3ef9375db81b853b8e61983a4de9c9c 100644 (file)
@@ -78,14 +78,10 @@ struct pcxhr_mgr {
        char shortname[32];             /* short name of this soundcard */
        char longname[96];              /* name of this soundcard */
 
-       /* message tasklet */
-       struct tasklet_struct msg_taskq;
        struct pcxhr_rmh *prmh;
-       /* trigger tasklet */
-       struct tasklet_struct trigger_taskq;
 
-       spinlock_t lock;                /* interrupt spinlock */
-       spinlock_t msg_lock;            /* message spinlock */
+       struct mutex lock;              /* interrupt lock */
+       struct mutex msg_lock;          /* message lock */
 
        struct mutex setup_mutex;       /* mutex used in hw_params, open and close */
        struct mutex mixer_mutex;       /* mutex for mixer */
index df9371918601e7cfc23874a0324bd3f287bbc6ec..a584acb61c00f815dce3009f30b794e65f481af3 100644 (file)
@@ -767,11 +767,11 @@ void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh *rmh, int capture,
  */
 int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
 {
-       unsigned long flags;
        int err;
-       spin_lock_irqsave(&mgr->msg_lock, flags);
+
+       mutex_lock(&mgr->msg_lock);
        err = pcxhr_send_msg_nolock(mgr, rmh);
-       spin_unlock_irqrestore(&mgr->msg_lock, flags);
+       mutex_unlock(&mgr->msg_lock);
        return err;
 }
 
@@ -971,17 +971,16 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
                                unsigned int value, int *changed)
 {
        struct pcxhr_rmh rmh;
-       unsigned long flags;
        int err;
 
-       spin_lock_irqsave(&mgr->msg_lock, flags);
+       mutex_lock(&mgr->msg_lock);
        if ((mgr->io_num_reg_cont & mask) == value) {
                dev_dbg(&mgr->pci->dev,
                        "IO_NUM_REG_CONT mask %x already is set to %x\n",
                            mask, value);
                if (changed)
                        *changed = 0;
-               spin_unlock_irqrestore(&mgr->msg_lock, flags);
+               mutex_unlock(&mgr->msg_lock);
                return 0;       /* already programmed */
        }
        pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
@@ -996,7 +995,7 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
                if (changed)
                        *changed = 1;
        }
-       spin_unlock_irqrestore(&mgr->msg_lock, flags);
+       mutex_unlock(&mgr->msg_lock);
        return err;
 }
 
@@ -1043,22 +1042,21 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
 }
 
 
-void pcxhr_msg_tasklet(unsigned long arg)
+static void pcxhr_msg_thread(struct pcxhr_mgr *mgr)
 {
-       struct pcxhr_mgr *mgr = (struct pcxhr_mgr *)(arg);
        struct pcxhr_rmh *prmh = mgr->prmh;
        int err;
        int i, j;
 
        if (mgr->src_it_dsp & PCXHR_IRQ_FREQ_CHANGE)
                dev_dbg(&mgr->pci->dev,
-                       "TASKLET : PCXHR_IRQ_FREQ_CHANGE event occurred\n");
+                       "PCXHR_IRQ_FREQ_CHANGE event occurred\n");
        if (mgr->src_it_dsp & PCXHR_IRQ_TIME_CODE)
                dev_dbg(&mgr->pci->dev,
-                       "TASKLET : PCXHR_IRQ_TIME_CODE event occurred\n");
+                       "PCXHR_IRQ_TIME_CODE event occurred\n");
        if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY)
                dev_dbg(&mgr->pci->dev,
-                       "TASKLET : PCXHR_IRQ_NOTIFY event occurred\n");
+                       "PCXHR_IRQ_NOTIFY event occurred\n");
        if (mgr->src_it_dsp & (PCXHR_IRQ_FREQ_CHANGE | PCXHR_IRQ_TIME_CODE)) {
                /* clear events FREQ_CHANGE and TIME_CODE */
                pcxhr_init_rmh(prmh, CMD_TEST_IT);
@@ -1068,7 +1066,7 @@ void pcxhr_msg_tasklet(unsigned long arg)
        }
        if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) {
                dev_dbg(&mgr->pci->dev,
-                       "TASKLET : PCXHR_IRQ_ASYNC event occurred\n");
+                       "PCXHR_IRQ_ASYNC event occurred\n");
 
                pcxhr_init_rmh(prmh, CMD_ASYNC);
                prmh->cmd[0] |= 1;      /* add SEL_ASYNC_EVENTS */
@@ -1076,7 +1074,7 @@ void pcxhr_msg_tasklet(unsigned long arg)
                prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS;
                err = pcxhr_send_msg(mgr, prmh);
                if (err)
-                       dev_err(&mgr->pci->dev, "ERROR pcxhr_msg_tasklet=%x;\n",
+                       dev_err(&mgr->pci->dev, "ERROR pcxhr_msg_thread=%x;\n",
                                   err);
                i = 1;
                while (i < prmh->stat_len) {
@@ -1220,9 +1218,9 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
                }
 
                if (elapsed) {
-                       spin_unlock(&mgr->lock);
+                       mutex_unlock(&mgr->lock);
                        snd_pcm_period_elapsed(stream->substream);
-                       spin_lock(&mgr->lock);
+                       mutex_lock(&mgr->lock);
                }
        }
 }
@@ -1231,14 +1229,10 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
 {
        struct pcxhr_mgr *mgr = dev_id;
        unsigned int reg;
-       int i, j;
-       struct snd_pcxhr *chip;
-
-       spin_lock(&mgr->lock);
+       bool wake_thread = false;
 
        reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS);
        if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) {
-               spin_unlock(&mgr->lock);
                /* this device did not cause the interrupt */
                return IRQ_NONE;
        }
@@ -1250,6 +1244,44 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
        /* timer irq occurred */
        if (reg & PCXHR_IRQ_TIMER) {
                int timer_toggle = reg & PCXHR_IRQ_TIMER;
+               if (timer_toggle == mgr->timer_toggle) {
+                       dev_dbg(&mgr->pci->dev, "ERROR TIMER TOGGLE\n");
+                       mgr->dsp_time_err++;
+               }
+
+               mgr->timer_toggle = timer_toggle;
+               mgr->src_it_dsp = reg;
+               wake_thread = true;
+       }
+
+       /* other irq's handled in the thread */
+       if (reg & PCXHR_IRQ_MASK) {
+               if (reg & PCXHR_IRQ_ASYNC) {
+                       /* as we didn't request any async notifications,
+                        * some kind of xrun error will probably occurred
+                        */
+                       /* better resynchronize all streams next interrupt : */
+                       mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
+               }
+               mgr->src_it_dsp = reg;
+               wake_thread = true;
+       }
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       if (reg & PCXHR_FATAL_DSP_ERR)
+               dev_dbg(&mgr->pci->dev, "FATAL DSP ERROR : %x\n", reg);
+#endif
+
+       return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id)
+{
+       struct pcxhr_mgr *mgr = dev_id;
+       int i, j;
+       struct snd_pcxhr *chip;
+
+       mutex_lock(&mgr->lock);
+       if (mgr->src_it_dsp & PCXHR_IRQ_TIMER) {
                /* is a 24 bit counter */
                int dsp_time_new =
                        PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK;
@@ -1290,13 +1322,6 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
 #endif
                mgr->dsp_time_last = dsp_time_new;
 
-               if (timer_toggle == mgr->timer_toggle) {
-                       dev_dbg(&mgr->pci->dev, "ERROR TIMER TOGGLE\n");
-                       mgr->dsp_time_err++;
-               }
-               mgr->timer_toggle = timer_toggle;
-
-               reg &= ~PCXHR_IRQ_TIMER;
                for (i = 0; i < mgr->num_cards; i++) {
                        chip = mgr->chip[i];
                        for (j = 0; j < chip->nb_streams_capt; j++)
@@ -1312,22 +1337,7 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
                                                dsp_time_diff);
                }
        }
-       /* other irq's handled in the tasklet */
-       if (reg & PCXHR_IRQ_MASK) {
-               if (reg & PCXHR_IRQ_ASYNC) {
-                       /* as we didn't request any async notifications,
-                        * some kind of xrun error will probably occurred
-                        */
-                       /* better resynchronize all streams next interrupt : */
-                       mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
-               }
-               mgr->src_it_dsp = reg;
-               tasklet_schedule(&mgr->msg_taskq);
-       }
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       if (reg & PCXHR_FATAL_DSP_ERR)
-               dev_dbg(&mgr->pci->dev, "FATAL DSP ERROR : %x\n", reg);
-#endif
-       spin_unlock(&mgr->lock);
-       return IRQ_HANDLED;     /* this device caused the interrupt */
+
+       pcxhr_msg_thread(mgr);
+       return IRQ_HANDLED;
 }
index a81ab6b811e727a8f18820fed1cd3c5c1f2f3f2f..dc267e4c1074f816ad8b6f27ab700d0403d2cb76 100644 (file)
@@ -200,6 +200,6 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
 
 /* interrupt handling */
 irqreturn_t pcxhr_interrupt(int irq, void *dev_id);
-void pcxhr_msg_tasklet(unsigned long arg);
+irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id);
 
 #endif /* __SOUND_PCXHR_CORE_H */
index 3dc4732142ee5784dd04112761ca0776ea63c941..c5a25e39e3a8ea8f231d94e15ca8fea4cecc6dcf 100644 (file)
@@ -168,8 +168,9 @@ static int snd_vx222_create(struct snd_card *card, struct pci_dev *pci,
        for (i = 0; i < 2; i++)
                vx->port[i] = pci_resource_start(pci, i + 1);
 
-       if (request_irq(pci->irq, snd_vx_irq_handler, IRQF_SHARED,
-                       KBUILD_MODNAME, chip)) {
+       if (request_threaded_irq(pci->irq, snd_vx_irq_handler,
+                                snd_vx_threaded_irq_handler, IRQF_SHARED,
+                                KBUILD_MODNAME, chip)) {
                dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
                snd_vx222_free(chip);
                return -EBUSY;
index 56bda124cd4acfda66a3865761c2ffa1208009f7..07f4b33db3afd8d2ccb150549b54b762b5ad1dcb 100644 (file)
@@ -61,6 +61,7 @@ static void snd_pdacf_detach(struct pcmcia_device *p_dev);
 
 static void pdacf_release(struct pcmcia_device *link)
 {
+       free_irq(link->irq, link->priv);
        pcmcia_disable_device(link);
 }
 
@@ -220,11 +221,13 @@ static int pdacf_config(struct pcmcia_device *link)
 
        ret = pcmcia_request_io(link);
        if (ret)
-               goto failed;
+               goto failed_preirq;
 
-       ret = pcmcia_request_irq(link, pdacf_interrupt);
+       ret = request_threaded_irq(link->irq, pdacf_interrupt,
+                                  pdacf_threaded_irq,
+                                  IRQF_SHARED, link->devname, link->priv);
        if (ret)
-               goto failed;
+               goto failed_preirq;
 
        ret = pcmcia_enable_device(link);
        if (ret)
@@ -236,7 +239,9 @@ static int pdacf_config(struct pcmcia_device *link)
 
        return 0;
 
-failed:
+ failed:
+       free_irq(link->irq, link->priv);
+failed_preirq:
        pcmcia_disable_device(link);
        return -ENODEV;
 }
index ea41e57d71794974559318d7aea7a8c46dfca5c9..e9a7d3a784f76963b8d4d591e8c5f6e0c32e445f 100644 (file)
@@ -88,10 +88,9 @@ struct snd_pdacf {
        unsigned long port;
        int irq;
 
-       spinlock_t reg_lock;
+       struct mutex reg_lock;
        unsigned short regmap[8];
        unsigned short suspend_reg_scr;
-       struct tasklet_struct tq;
 
        spinlock_t ak4117_lock;
        struct ak4117 *ak4117;
@@ -136,7 +135,7 @@ int snd_pdacf_resume(struct snd_pdacf *chip);
 #endif
 int snd_pdacf_pcm_new(struct snd_pdacf *chip);
 irqreturn_t pdacf_interrupt(int irq, void *dev);
-void pdacf_tasklet(unsigned long private_data);
+irqreturn_t pdacf_threaded_irq(int irq, void *dev);
 void pdacf_reinit(struct snd_pdacf *chip, int resume);
 
 #endif /* __PDAUDIOCF_H */
index ea0adfb984ad7f37626b9ecff2833dc939dca61f..d724ab0653cfcd9eee87d87299653f860583bf3c 100644 (file)
@@ -162,9 +162,8 @@ struct snd_pdacf *snd_pdacf_create(struct snd_card *card)
        if (chip == NULL)
                return NULL;
        chip->card = card;
-       spin_lock_init(&chip->reg_lock);
+       mutex_init(&chip->reg_lock);
        spin_lock_init(&chip->ak4117_lock);
-       tasklet_init(&chip->tq, pdacf_tasklet, (unsigned long)chip);
        card->private_data = chip;
 
        pdacf_proc_init(chip);
@@ -174,19 +173,18 @@ struct snd_pdacf *snd_pdacf_create(struct snd_card *card)
 static void snd_pdacf_ak4117_change(struct ak4117 *ak4117, unsigned char c0, unsigned char c1)
 {
        struct snd_pdacf *chip = ak4117->change_callback_private;
-       unsigned long flags;
        u16 val;
 
        if (!(c0 & AK4117_UNLCK))
                return;
-       spin_lock_irqsave(&chip->reg_lock, flags);
+       mutex_lock(&chip->reg_lock);
        val = chip->regmap[PDAUDIOCF_REG_SCR>>1];
        if (ak4117->rcs0 & AK4117_UNLCK)
                val |= PDAUDIOCF_BLUE_LED_OFF;
        else
                val &= ~PDAUDIOCF_BLUE_LED_OFF;
        pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val);
-       spin_unlock_irqrestore(&chip->reg_lock, flags);
+       mutex_unlock(&chip->reg_lock);
 }
 
 int snd_pdacf_ak4117_create(struct snd_pdacf *chip)
index dcd32201bc8c5cf4dfec40b0deba7edfaafe91ea..ecf0fbd9179467ed05a819dd99512862f842d100 100644 (file)
@@ -30,6 +30,7 @@ irqreturn_t pdacf_interrupt(int irq, void *dev)
 {
        struct snd_pdacf *chip = dev;
        unsigned short stat;
+       bool wake_thread = false;
 
        if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|
                                  PDAUDIOCF_STAT_IS_CONFIGURED|
@@ -41,13 +42,13 @@ irqreturn_t pdacf_interrupt(int irq, void *dev)
                if (stat & PDAUDIOCF_IRQOVR)    /* should never happen */
                        snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n");
                if (chip->pcm_substream)
-                       tasklet_schedule(&chip->tq);
+                       wake_thread = true;
                if (!(stat & PDAUDIOCF_IRQAKM))
                        stat |= PDAUDIOCF_IRQAKM;       /* check rate */
        }
        if (get_irq_regs() != NULL)
                snd_ak4117_check_rate_and_errors(chip->ak4117, 0);
-       return IRQ_HANDLED;
+       return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
 }
 
 static inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
@@ -256,16 +257,16 @@ static void pdacf_transfer(struct snd_pdacf *chip, unsigned int size, unsigned i
        }
 }
 
-void pdacf_tasklet(unsigned long private_data)
+irqreturn_t pdacf_threaded_irq(int irq, void *dev)
 {
-       struct snd_pdacf *chip = (struct snd_pdacf *) private_data;
+       struct snd_pdacf *chip = dev;
        int size, off, cont, rdp, wdp;
 
        if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED)
-               return;
+               return IRQ_HANDLED;
        
        if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream))
-               return;
+               return IRQ_HANDLED;
 
        rdp = inw(chip->port + PDAUDIOCF_REG_RDP);
        wdp = inw(chip->port + PDAUDIOCF_REG_WDP);
@@ -311,15 +312,15 @@ void pdacf_tasklet(unsigned long private_data)
                size -= cont;
        }
 #endif
-       spin_lock(&chip->reg_lock);
+       mutex_lock(&chip->reg_lock);
        while (chip->pcm_tdone >= chip->pcm_period) {
                chip->pcm_hwptr += chip->pcm_period;
                chip->pcm_hwptr %= chip->pcm_size;
                chip->pcm_tdone -= chip->pcm_period;
-               spin_unlock(&chip->reg_lock);
+               mutex_unlock(&chip->reg_lock);
                snd_pcm_period_elapsed(chip->pcm_substream);
-               spin_lock(&chip->reg_lock);
+               mutex_lock(&chip->reg_lock);
        }
-       spin_unlock(&chip->reg_lock);
-       /* printk(KERN_DEBUG "TASKLET: end\n"); */
+       mutex_unlock(&chip->reg_lock);
+       return IRQ_HANDLED;
 }
index 43f995a3f96033c081499dd41002541279252b95..b48aa0a78c19b42583d7d25e58dcdb94cb0cb61f 100644 (file)
@@ -77,7 +77,7 @@ static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
        default:
                return -EINVAL;
        }
-       spin_lock(&chip->reg_lock);
+       mutex_lock(&chip->reg_lock);
        chip->pcm_running += inc;
        tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
        if (chip->pcm_running) {
@@ -91,7 +91,7 @@ static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
        tmp |= val;
        pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp);
       __end:
-       spin_unlock(&chip->reg_lock);
+       mutex_unlock(&chip->reg_lock);
        snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE);
        return ret;
 }
@@ -296,6 +296,7 @@ int snd_pdacf_pcm_new(struct snd_pdacf *chip)
 
        pcm->private_data = chip;
        pcm->info_flags = 0;
+       pcm->nonatomic = true;
        strcpy(pcm->name, chip->card->shortname);
        chip->pcm = pcm;
        
index fe33e122e372b9e039c0c6746f3ffdbe50cdea2c..281972913c32140939c087067f4340d6882e6626 100644 (file)
@@ -468,12 +468,11 @@ static void vxp_write_codec_reg(struct vx_core *chip, int codec, unsigned int da
 void vx_set_mic_boost(struct vx_core *chip, int boost)
 {
        struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip;
-       unsigned long flags;
 
        if (chip->chip_status & VX_STAT_IS_STALE)
                return;
 
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) {
                if (boost) {
                        /* boost: 38 dB */
@@ -486,7 +485,7 @@ void vx_set_mic_boost(struct vx_core *chip, int boost)
                 }
                vx_outb(chip, CDSP, pchip->regCDSP);
        }
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
 }
 
 /*
@@ -511,17 +510,16 @@ static int vx_compute_mic_level(int level)
 void vx_set_mic_level(struct vx_core *chip, int level)
 {
        struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip;
-       unsigned long flags;
 
        if (chip->chip_status & VX_STAT_IS_STALE)
                return;
 
-       spin_lock_irqsave(&chip->lock, flags);
+       mutex_lock(&chip->lock);
        if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) {
                level = vx_compute_mic_level(level);
                vx_outb(chip, MICRO, level);
        }
-       spin_unlock_irqrestore(&chip->lock, flags);
+       mutex_unlock(&chip->lock);
 }
 
 
index 786e7e139c9eeae713a57ab334ea08bd1c6ce6f1..92ec11456e3a9a55daff3f0386e8c0f65f50d662 100644 (file)
@@ -62,6 +62,7 @@ static unsigned int card_alloc;
  */
 static void vxpocket_release(struct pcmcia_device *link)
 {
+       free_irq(link->irq, link->priv);
        pcmcia_disable_device(link);
 }
 
@@ -227,11 +228,13 @@ static int vxpocket_config(struct pcmcia_device *link)
 
        ret = pcmcia_request_io(link);
        if (ret)
-               goto failed;
+               goto failed_preirq;
 
-       ret = pcmcia_request_irq(link, snd_vx_irq_handler);
+       ret = request_threaded_irq(link->irq, snd_vx_irq_handler,
+                                  snd_vx_threaded_irq_handler,
+                                  IRQF_SHARED, link->devname, link->priv);
        if (ret)
-               goto failed;
+               goto failed_preirq;
 
        ret = pcmcia_enable_device(link);
        if (ret)
@@ -245,7 +248,9 @@ static int vxpocket_config(struct pcmcia_device *link)
 
        return 0;
 
-failed:
+ failed:
+       free_irq(link->irq, link->priv);
+failed_preirq:
        pcmcia_disable_device(link);
        return -ENODEV;
 }
index 922006dd0583cf03698bc39d2033fd8b3d48112b..e88a6b67f7812c68997f7342341c2befda194bd2 100644 (file)
@@ -146,7 +146,7 @@ struct pm860x_priv {
        struct pm860x_det       det;
 
        int                     irq[4];
-       unsigned char           name[4][MAX_NAME_LEN];
+       unsigned char           name[4][MAX_NAME_LEN+1];
 };
 
 /* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */
@@ -1337,8 +1337,6 @@ static int pm860x_probe(struct snd_soc_codec *codec)
                }
        }
 
-       pm860x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 
 out:
@@ -1354,7 +1352,6 @@ static int pm860x_remove(struct snd_soc_codec *codec)
 
        for (i = 3; i >= 0; i--)
                free_irq(pm860x->irq[i], pm860x);
-       pm860x_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
index 8838838e25ed89a9525e80b86cd92575701a75d9..a68d1731a8fde35fe199f61fc702a9259ad25a81 100644 (file)
@@ -43,6 +43,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_ALC5623 if I2C
        select SND_SOC_ALC5632 if I2C
        select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
+       select SND_SOC_CS35L32 if I2C
        select SND_SOC_CS42L51_I2C if I2C
        select SND_SOC_CS42L52 if I2C && INPUT
        select SND_SOC_CS42L56 if I2C && INPUT
@@ -56,7 +57,10 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_DA7213 if I2C
        select SND_SOC_DA732X if I2C
        select SND_SOC_DA9055 if I2C
+       select SND_SOC_DMIC
        select SND_SOC_BT_SCO
+       select SND_SOC_ES8328_SPI if SPI_MASTER
+       select SND_SOC_ES8328_I2C if I2C
        select SND_SOC_ISABELLE if I2C
        select SND_SOC_JZ4740_CODEC
        select SND_SOC_LM4857 if I2C
@@ -90,6 +94,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_SSM2518 if I2C
        select SND_SOC_SSM2602_SPI if SPI_MASTER
        select SND_SOC_SSM2602_I2C if I2C
+       select SND_SOC_SSM4567 if I2C
        select SND_SOC_STA32X if I2C
        select SND_SOC_STA350 if I2C
        select SND_SOC_STA529 if I2C
@@ -323,6 +328,10 @@ config SND_SOC_ALC5632
 config SND_SOC_CQ0093VC
        tristate
 
+config SND_SOC_CS35L32
+       tristate "Cirrus Logic CS35L32 CODEC"
+       depends on I2C
+
 config SND_SOC_CS42L51
        tristate
 
@@ -405,6 +414,17 @@ config SND_SOC_DMIC
 config SND_SOC_HDMI_CODEC
        tristate "HDMI stub CODEC"
 
+config SND_SOC_ES8328
+       tristate "Everest Semi ES8328 CODEC"
+
+config SND_SOC_ES8328_I2C
+       tristate
+       select SND_SOC_ES8328
+
+config SND_SOC_ES8328_SPI
+       tristate
+       select SND_SOC_ES8328
+
 config SND_SOC_ISABELLE
         tristate
 
@@ -464,6 +484,7 @@ config SND_SOC_RL6231
 
 config SND_SOC_RT286
        tristate
+       depends on I2C
 
 config SND_SOC_RT5631
        tristate
@@ -520,12 +541,20 @@ config SND_SOC_SSM2602
        tristate
 
 config SND_SOC_SSM2602_SPI
+       tristate "Analog Devices SSM2602 CODEC - SPI"
+       depends on SPI_MASTER
        select SND_SOC_SSM2602
-       tristate
+       select REGMAP_SPI
 
 config SND_SOC_SSM2602_I2C
+       tristate "Analog Devices SSM2602 CODEC - I2C"
+       depends on I2C
        select SND_SOC_SSM2602
-       tristate
+       select REGMAP_I2C
+
+config SND_SOC_SSM4567
+       tristate "Analog Devices ssm4567 amplifier driver support"
+       depends on I2C
 
 config SND_SOC_STA32X
        tristate
@@ -712,7 +741,8 @@ config SND_SOC_WM8974
        tristate
 
 config SND_SOC_WM8978
-       tristate
+       tristate "Wolfson Microelectronics WM8978 codec"
+       depends on I2C
 
 config SND_SOC_WM8983
        tristate
index 20afe0f0c5be4f724c6a24bcaef084ef49d0d00b..5dce451661e47813bb0b95b8b0121ae2545a2dab 100644 (file)
@@ -32,6 +32,7 @@ snd-soc-ak4671-objs := ak4671.o
 snd-soc-ak5386-objs := ak5386.o
 snd-soc-arizona-objs := arizona.o
 snd-soc-cq93vc-objs := cq93vc.o
+snd-soc-cs35l32-objs := cs35l32.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
 snd-soc-cs42l52-objs := cs42l52.o
@@ -49,6 +50,9 @@ snd-soc-da732x-objs := da732x.o
 snd-soc-da9055-objs := da9055.o
 snd-soc-bt-sco-objs := bt-sco.o
 snd-soc-dmic-objs := dmic.o
+snd-soc-es8328-objs := es8328.o
+snd-soc-es8328-i2c-objs := es8328-i2c.o
+snd-soc-es8328-spi-objs := es8328-spi.o
 snd-soc-isabelle-objs := isabelle.o
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-l3-objs := l3.o
@@ -91,6 +95,7 @@ snd-soc-ssm2518-objs := ssm2518.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-ssm2602-spi-objs := ssm2602-spi.o
 snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o
+snd-soc-ssm4567-objs := ssm4567.o
 snd-soc-sta32x-objs := sta32x.o
 snd-soc-sta350-objs := sta350.o
 snd-soc-sta529-objs := sta529.o
@@ -203,6 +208,7 @@ obj-$(CONFIG_SND_SOC_ALC5623)    += snd-soc-alc5623.o
 obj-$(CONFIG_SND_SOC_ALC5632)  += snd-soc-alc5632.o
 obj-$(CONFIG_SND_SOC_ARIZONA)  += snd-soc-arizona.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
+obj-$(CONFIG_SND_SOC_CS35L32)  += snd-soc-cs35l32.o
 obj-$(CONFIG_SND_SOC_CS42L51)  += snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L51_I2C)      += snd-soc-cs42l51-i2c.o
 obj-$(CONFIG_SND_SOC_CS42L52)  += snd-soc-cs42l52.o
@@ -220,6 +226,9 @@ obj-$(CONFIG_SND_SOC_DA732X)        += snd-soc-da732x.o
 obj-$(CONFIG_SND_SOC_DA9055)   += snd-soc-da9055.o
 obj-$(CONFIG_SND_SOC_BT_SCO)   += snd-soc-bt-sco.o
 obj-$(CONFIG_SND_SOC_DMIC)     += snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_ES8328)   += snd-soc-es8328.o
+obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
+obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
 obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
@@ -258,6 +267,7 @@ obj-$(CONFIG_SND_SOC_SSM2518)       += snd-soc-ssm2518.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_SSM2602_SPI)      += snd-soc-ssm2602-spi.o
 obj-$(CONFIG_SND_SOC_SSM2602_I2C)      += snd-soc-ssm2602-i2c.o
+obj-$(CONFIG_SND_SOC_SSM4567)  += snd-soc-ssm4567.o
 obj-$(CONFIG_SND_SOC_STA32X)   += snd-soc-sta32x.o
 obj-$(CONFIG_SND_SOC_STA350)   += snd-soc-sta350.o
 obj-$(CONFIG_SND_SOC_STA529)   += snd-soc-sta529.o
index 1fb4402bf72d3047c38fdca362700d4c65739dd7..fd43827bb856b522d6acd4e2c95e2b2eea287f25 100644 (file)
@@ -56,8 +56,7 @@
 #define GPIO31_DIR_OUTPUT                      0x40
 
 /* Macrocell register definitions */
-#define AB8500_CTRL3_REG                       0x0200
-#define AB8500_GPIO_DIR4_REG                   0x1013
+#define AB8500_GPIO_DIR4_REG                   0x13 /* Bank AB8500_MISC */
 
 /* Nr of FIR/IIR-coeff banks in ANC-block */
 #define AB8500_NR_OF_ANC_COEFF_BANKS           2
@@ -126,6 +125,8 @@ struct ab8500_codec_drvdata_dbg {
 
 /* Private data for AB8500 device-driver */
 struct ab8500_codec_drvdata {
+       struct regmap *regmap;
+
        /* Sidetone */
        long *sid_fir_values;
        enum sid_state sid_status;
@@ -166,49 +167,35 @@ static inline const char *amic_type_str(enum amic_type type)
  */
 
 /* Read a register from the audio-bank of AB8500 */
-static unsigned int ab8500_codec_read_reg(struct snd_soc_codec *codec,
-                                       unsigned int reg)
+static int ab8500_codec_read_reg(void *context, unsigned int reg,
+                                unsigned int *value)
 {
+       struct device *dev = context;
        int status;
-       unsigned int value = 0;
 
        u8 value8;
-       status = abx500_get_register_interruptible(codec->dev, AB8500_AUDIO,
-                                               reg, &value8);
-       if (status < 0) {
-               dev_err(codec->dev,
-                       "%s: ERROR: Register (0x%02x:0x%02x) read failed (%d).\n",
-                       __func__, (u8)AB8500_AUDIO, (u8)reg, status);
-       } else {
-               dev_dbg(codec->dev,
-                       "%s: Read 0x%02x from register 0x%02x:0x%02x\n",
-                       __func__, value8, (u8)AB8500_AUDIO, (u8)reg);
-               value = (unsigned int)value8;
-       }
+       status = abx500_get_register_interruptible(dev, AB8500_AUDIO,
+                                                  reg, &value8);
+       *value = (unsigned int)value8;
 
-       return value;
+       return status;
 }
 
 /* Write to a register in the audio-bank of AB8500 */
-static int ab8500_codec_write_reg(struct snd_soc_codec *codec,
-                               unsigned int reg, unsigned int value)
+static int ab8500_codec_write_reg(void *context, unsigned int reg,
+                                 unsigned int value)
 {
-       int status;
-
-       status = abx500_set_register_interruptible(codec->dev, AB8500_AUDIO,
-                                               reg, value);
-       if (status < 0)
-               dev_err(codec->dev,
-                       "%s: ERROR: Register (%02x:%02x) write failed (%d).\n",
-                       __func__, (u8)AB8500_AUDIO, (u8)reg, status);
-       else
-               dev_dbg(codec->dev,
-                       "%s: Wrote 0x%02x into register %02x:%02x\n",
-                       __func__, (u8)value, (u8)AB8500_AUDIO, (u8)reg);
+       struct device *dev = context;
 
-       return status;
+       return abx500_set_register_interruptible(dev, AB8500_AUDIO,
+                                                reg, value);
 }
 
+static const struct regmap_config ab8500_codec_regmap = {
+       .reg_read = ab8500_codec_read_reg,
+       .reg_write = ab8500_codec_write_reg,
+};
+
 /*
  * Controls - DAPM
  */
@@ -1968,16 +1955,16 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
        dev_dbg(codec->dev, "%s: Enter.\n", __func__);
 
        /* Set DMic-clocks to outputs */
-       status = abx500_get_register_interruptible(codec->dev, (u8)AB8500_MISC,
-                                               (u8)AB8500_GPIO_DIR4_REG,
+       status = abx500_get_register_interruptible(codec->dev, AB8500_MISC,
+                                               AB8500_GPIO_DIR4_REG,
                                                &value8);
        if (status < 0)
                return status;
        value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT |
                GPIO31_DIR_OUTPUT;
        status = abx500_set_register_interruptible(codec->dev,
-                                               (u8)AB8500_MISC,
-                                               (u8)AB8500_GPIO_DIR4_REG,
+                                               AB8500_MISC,
+                                               AB8500_GPIO_DIR4_REG,
                                                value);
        if (status < 0)
                return status;
@@ -2565,9 +2552,6 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
 
 static struct snd_soc_codec_driver ab8500_codec_driver = {
        .probe =                ab8500_codec_probe,
-       .read =                 ab8500_codec_read_reg,
-       .write =                ab8500_codec_write_reg,
-       .reg_word_size =        sizeof(u8),
        .controls =             ab8500_ctrls,
        .num_controls =         ARRAY_SIZE(ab8500_ctrls),
        .dapm_widgets =         ab8500_dapm_widgets,
@@ -2592,6 +2576,15 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
        drvdata->anc_status = ANC_UNCONFIGURED;
        dev_set_drvdata(&pdev->dev, drvdata);
 
+       drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev,
+                                          &ab8500_codec_regmap);
+       if (IS_ERR(drvdata->regmap)) {
+               status = PTR_ERR(drvdata->regmap);
+               dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+                       __func__, status);
+               return status;
+       }
+
        dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__);
        status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver,
                                ab8500_codec_dai,
index e889e1b84192bcc8d0baf354e1a66a8892c72e4f..bd9b1839c8b0e6843d858d93a15d9d440abdc86f 100644 (file)
@@ -69,19 +69,6 @@ static struct snd_soc_dai_driver ac97_dai = {
        .ops = &ac97_dai_ops,
 };
 
-static unsigned int ac97_read(struct snd_soc_codec *codec,
-       unsigned int reg)
-{
-       return soc_ac97_ops->read(codec->ac97, reg);
-}
-
-static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
-       unsigned int val)
-{
-       soc_ac97_ops->write(codec->ac97, reg, val);
-       return 0;
-}
-
 static int ac97_soc_probe(struct snd_soc_codec *codec)
 {
        struct snd_ac97_bus *ac97_bus;
@@ -122,8 +109,6 @@ static int ac97_soc_resume(struct snd_soc_codec *codec)
 #endif
 
 static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
-       .write =        ac97_write,
-       .read =         ac97_read,
        .probe =        ac97_soc_probe,
        .suspend =      ac97_soc_suspend,
        .resume =       ac97_soc_resume,
index 1ff7d4d027e932e9c20f8841bf09b89f210e6635..7c784ad3e8b2a2589d28f621f1fd49f0a938c9a1 100644 (file)
@@ -1448,29 +1448,10 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int adau1373_remove(struct snd_soc_codec *codec)
-{
-       adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int adau1373_suspend(struct snd_soc_codec *codec)
-{
-       struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
-       int ret;
-
-       ret = adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       regcache_cache_only(adau1373->regmap, true);
-
-       return ret;
-}
-
 static int adau1373_resume(struct snd_soc_codec *codec)
 {
        struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
 
-       regcache_cache_only(adau1373->regmap, false);
-       adau1373_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        regcache_sync(adau1373->regmap);
 
        return 0;
@@ -1501,8 +1482,6 @@ static const struct regmap_config adau1373_regmap_config = {
 
 static struct snd_soc_codec_driver adau1373_codec_driver = {
        .probe =        adau1373_probe,
-       .remove =       adau1373_remove,
-       .suspend =      adau1373_suspend,
        .resume =       adau1373_resume,
        .set_bias_level = adau1373_set_bias_level,
        .idle_bias_off = true,
index 848cab83955392b33f93c18dac3631f1f9fb0a97..5518ebd6947c5f28329fb474b4b313920f5e94ec 100644 (file)
@@ -714,9 +714,9 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
 
 static const struct snd_soc_codec_driver adau1761_codec_driver = {
        .probe = adau1761_codec_probe,
-       .suspend = adau17x1_suspend,
        .resume = adau17x1_resume,
        .set_bias_level = adau1761_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = adau1761_controls,
        .num_controls = ARRAY_SIZE(adau1761_controls),
index 045a61413840f013b79aa0bcec624b1b8b75d6f5..e9fc00fb13ddf65597513fe58652183776b71d99 100644 (file)
@@ -446,9 +446,9 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
 
 static const struct snd_soc_codec_driver adau1781_codec_driver = {
        .probe = adau1781_codec_probe,
-       .suspend = adau17x1_suspend,
        .resume = adau17x1_resume,
        .set_bias_level = adau1781_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = adau1781_controls,
        .num_controls = ARRAY_SIZE(adau1781_controls),
index 0b659704e60c0db2c425474857a5372e0702a22a..3e16c1c641156d428e1759956191f418a1edcd03 100644 (file)
@@ -815,13 +815,6 @@ int adau17x1_add_routes(struct snd_soc_codec *codec)
 }
 EXPORT_SYMBOL_GPL(adau17x1_add_routes);
 
-int adau17x1_suspend(struct snd_soc_codec *codec)
-{
-       codec->driver->set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(adau17x1_suspend);
-
 int adau17x1_resume(struct snd_soc_codec *codec)
 {
        struct adau *adau = snd_soc_codec_get_drvdata(codec);
@@ -829,7 +822,6 @@ int adau17x1_resume(struct snd_soc_codec *codec)
        if (adau->switch_mode)
                adau->switch_mode(codec->dev);
 
-       codec->driver->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        regcache_sync(adau->regmap);
 
        return 0;
index 3ffabaf4c7a846f7f9e46763981c3f92866cf0ec..e4a557fd7155c34b6c5c5da7a9f7337b2d14ecb7 100644 (file)
@@ -52,7 +52,6 @@ int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
        enum adau17x1_micbias_voltage micbias);
 bool adau17x1_readable_register(struct device *dev, unsigned int reg);
 bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
-int adau17x1_suspend(struct snd_soc_codec *codec);
 int adau17x1_resume(struct snd_soc_codec *codec);
 
 extern const struct snd_soc_dai_ops adau17x1_dai_ops;
index c43b93fdf0dfe466e3c72e97eec0c5da5da06ecb..ce3cdca9fc62d7efca27b9904a883decd0341a7f 100644 (file)
@@ -812,42 +812,23 @@ static int adav80x_probe(struct snd_soc_codec *codec)
        /* Disable DAC zero flag */
        regmap_write(adav80x->regmap, ADAV80X_DAC_CTRL3, 0x6);
 
-       return adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-}
-
-static int adav80x_suspend(struct snd_soc_codec *codec)
-{
-       struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
-       int ret;
-
-       ret = adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       regcache_cache_only(adav80x->regmap, true);
-
-       return ret;
+       return 0;
 }
 
 static int adav80x_resume(struct snd_soc_codec *codec)
 {
        struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 
-       regcache_cache_only(adav80x->regmap, false);
-       adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        regcache_sync(adav80x->regmap);
 
        return 0;
 }
 
-static int adav80x_remove(struct snd_soc_codec *codec)
-{
-       return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-}
-
 static struct snd_soc_codec_driver adav80x_codec_driver = {
        .probe = adav80x_probe,
-       .remove = adav80x_remove,
-       .suspend = adav80x_suspend,
        .resume = adav80x_resume,
        .set_bias_level = adav80x_set_bias_level,
+       .suspend_bias_off = true,
 
        .set_pll = adav80x_set_pll,
        .set_sysclk = adav80x_set_sysclk,
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
new file mode 100644 (file)
index 0000000..c125925
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * cs35l32.c -- CS35L32 ALSA SoC audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <dt-bindings/sound/cs35l32.h>
+
+#include "cs35l32.h"
+
+#define CS35L32_NUM_SUPPLIES 2
+static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
+       "VA",
+       "VP",
+};
+
+struct  cs35l32_private {
+       struct regmap *regmap;
+       struct snd_soc_codec *codec;
+       struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
+       struct cs35l32_platform_data pdata;
+       struct gpio_desc *reset_gpio;
+};
+
+static const struct reg_default cs35l32_reg_defaults[] = {
+
+       { 0x06, 0x04 }, /* Power Ctl 1 */
+       { 0x07, 0xE8 }, /* Power Ctl 2 */
+       { 0x08, 0x40 }, /* Clock Ctl */
+       { 0x09, 0x20 }, /* Low Battery Threshold */
+       { 0x0A, 0x00 }, /* Voltage Monitor [RO] */
+       { 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */
+       { 0x0C, 0x07 }, /* IMON Scaling */
+       { 0x0D, 0x03 }, /* Audio/LED Pwr Manager */
+       { 0x0F, 0x20 }, /* Serial Port Control */
+       { 0x10, 0x14 }, /* Class D Amp CTL */
+       { 0x11, 0x00 }, /* Protection Release CTL */
+       { 0x12, 0xFF }, /* Interrupt Mask 1 */
+       { 0x13, 0xFF }, /* Interrupt Mask 2 */
+       { 0x14, 0xFF }, /* Interrupt Mask 3 */
+       { 0x19, 0x00 }, /* LED Flash Mode Current */
+       { 0x1A, 0x00 }, /* LED Movie Mode Current */
+       { 0x1B, 0x20 }, /* LED Flash Timer */
+       { 0x1C, 0x00 }, /* LED Flash Inhibit Current */
+};
+
+static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L32_DEVID_AB:
+       case CS35L32_DEVID_CD:
+       case CS35L32_DEVID_E:
+       case CS35L32_FAB_ID:
+       case CS35L32_REV_ID:
+       case CS35L32_PWRCTL1:
+       case CS35L32_PWRCTL2:
+       case CS35L32_CLK_CTL:
+       case CS35L32_BATT_THRESHOLD:
+       case CS35L32_VMON:
+       case CS35L32_BST_CPCP_CTL:
+       case CS35L32_IMON_SCALING:
+       case CS35L32_AUDIO_LED_MNGR:
+       case CS35L32_ADSP_CTL:
+       case CS35L32_CLASSD_CTL:
+       case CS35L32_PROTECT_CTL:
+       case CS35L32_INT_MASK_1:
+       case CS35L32_INT_MASK_2:
+       case CS35L32_INT_MASK_3:
+       case CS35L32_INT_STATUS_1:
+       case CS35L32_INT_STATUS_2:
+       case CS35L32_INT_STATUS_3:
+       case CS35L32_LED_STATUS:
+       case CS35L32_FLASH_MODE:
+       case CS35L32_MOVIE_MODE:
+       case CS35L32_FLASH_TIMER:
+       case CS35L32_FLASH_INHIBIT:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L32_DEVID_AB:
+       case CS35L32_DEVID_CD:
+       case CS35L32_DEVID_E:
+       case CS35L32_FAB_ID:
+       case CS35L32_REV_ID:
+       case CS35L32_INT_STATUS_1:
+       case CS35L32_INT_STATUS_2:
+       case CS35L32_INT_STATUS_3:
+       case CS35L32_LED_STATUS:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool cs35l32_precious_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L32_INT_STATUS_1:
+       case CS35L32_INT_STATUS_2:
+       case CS35L32_INT_STATUS_3:
+       case CS35L32_LED_STATUS:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0);
+
+static const struct snd_kcontrol_new imon_ctl =
+       SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1);
+
+static const struct snd_kcontrol_new vmon_ctl =
+       SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1);
+
+static const struct snd_kcontrol_new vpmon_ctl =
+       SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1);
+
+static const struct snd_kcontrol_new cs35l32_snd_controls[] = {
+       SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL,
+                      3, 0x04, 1, classd_ctl_tlv),
+       SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0),
+       SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = {
+
+       SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0),
+       SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0),
+
+       SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1),
+
+       SND_SOC_DAPM_INPUT("VP"),
+       SND_SOC_DAPM_INPUT("ISENSE"),
+       SND_SOC_DAPM_INPUT("VSENSE"),
+
+       SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl),
+       SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl),
+       SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl),
+};
+
+static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
+
+       {"Speaker", NULL, "BOOST"},
+
+       {"VMON ADC", NULL, "VSENSE"},
+       {"IMON ADC", NULL, "ISENSE"},
+       {"VPMON ADC", NULL, "VP"},
+
+       {"SDOUT", "Switch", "VMON ADC"},
+       {"SDOUT",  "Switch", "IMON ADC"},
+       {"SDOUT", "Switch", "VPMON ADC"},
+
+       {"Capture", NULL, "SDOUT"},
+};
+
+static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
+                                   CS35L32_ADSP_MASTER_MASK,
+                               CS35L32_ADSP_MASTER_MASK);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
+                                   CS35L32_ADSP_MASTER_MASK, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       return snd_soc_update_bits(codec, CS35L32_PWRCTL2,
+                                       CS35L32_SDOUT_3ST, tristate << 3);
+}
+
+static const struct snd_soc_dai_ops cs35l32_ops = {
+       .set_fmt = cs35l32_set_dai_fmt,
+       .set_tristate = cs35l32_set_tristate,
+};
+
+static struct snd_soc_dai_driver cs35l32_dai[] = {
+       {
+               .name = "cs35l32-monitor",
+               .id = 0,
+               .capture = {
+                       .stream_name = "Capture",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = CS35L32_RATES,
+                       .formats = CS35L32_FORMATS,
+               },
+               .ops = &cs35l32_ops,
+               .symmetric_rates = 1,
+       }
+};
+
+static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
+                             int clk_id, int source, unsigned int freq, int dir)
+{
+       unsigned int val;
+
+       switch (freq) {
+       case 6000000:
+               val = CS35L32_MCLK_RATIO;
+               break;
+       case 12000000:
+               val = CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO;
+               break;
+       case 6144000:
+               val = 0;
+               break;
+       case 12288000:
+               val = CS35L32_MCLK_DIV2_MASK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return snd_soc_update_bits(codec, CS35L32_CLK_CTL,
+                       CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l32 = {
+       .set_sysclk = cs35l32_codec_set_sysclk,
+
+       .dapm_widgets = cs35l32_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets),
+       .dapm_routes = cs35l32_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map),
+
+       .controls = cs35l32_snd_controls,
+       .num_controls = ARRAY_SIZE(cs35l32_snd_controls),
+};
+
+/* Current and threshold powerup sequence Pg37 in datasheet */
+static const struct reg_default cs35l32_monitor_patch[] = {
+
+       { 0x00, 0x99 },
+       { 0x48, 0x17 },
+       { 0x49, 0x56 },
+       { 0x43, 0x01 },
+       { 0x3B, 0x62 },
+       { 0x3C, 0x80 },
+       { 0x00, 0x00 },
+};
+
+static struct regmap_config cs35l32_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = CS35L32_MAX_REGISTER,
+       .reg_defaults = cs35l32_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults),
+       .volatile_reg = cs35l32_volatile_register,
+       .readable_reg = cs35l32_readable_register,
+       .precious_reg = cs35l32_precious_register,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
+                                   struct cs35l32_platform_data *pdata)
+{
+       struct device_node *np = i2c_client->dev.of_node;
+       unsigned int val;
+
+       if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0)
+               pdata->sdout_share = val;
+
+       of_property_read_u32(np, "cirrus,boost-manager", &val);
+       switch (val) {
+       case CS35L32_BOOST_MGR_AUTO:
+       case CS35L32_BOOST_MGR_AUTO_AUDIO:
+       case CS35L32_BOOST_MGR_BYPASS:
+       case CS35L32_BOOST_MGR_FIXED:
+               pdata->boost_mng = val;
+               break;
+       default:
+               dev_err(&i2c_client->dev,
+                       "Wrong cirrus,boost-manager DT value %d\n", val);
+               pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS;
+       }
+
+       of_property_read_u32(np, "cirrus,sdout-datacfg", &val);
+       switch (val) {
+       case CS35L32_DATA_CFG_LR_VP:
+       case CS35L32_DATA_CFG_LR_STAT:
+       case CS35L32_DATA_CFG_LR:
+       case CS35L32_DATA_CFG_LR_VPSTAT:
+               pdata->sdout_datacfg = val;
+               break;
+       default:
+               dev_err(&i2c_client->dev,
+                       "Wrong cirrus,sdout-datacfg DT value %d\n", val);
+               pdata->sdout_datacfg = CS35L32_DATA_CFG_LR;
+       }
+
+       of_property_read_u32(np, "cirrus,battery-threshold", &val);
+       switch (val) {
+       case CS35L32_BATT_THRESH_3_1V:
+       case CS35L32_BATT_THRESH_3_2V:
+       case CS35L32_BATT_THRESH_3_3V:
+       case CS35L32_BATT_THRESH_3_4V:
+               pdata->batt_thresh = val;
+               break;
+       default:
+               dev_err(&i2c_client->dev,
+                       "Wrong cirrus,battery-threshold DT value %d\n", val);
+               pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V;
+       }
+
+       of_property_read_u32(np, "cirrus,battery-recovery", &val);
+       switch (val) {
+       case CS35L32_BATT_RECOV_3_1V:
+       case CS35L32_BATT_RECOV_3_2V:
+       case CS35L32_BATT_RECOV_3_3V:
+       case CS35L32_BATT_RECOV_3_4V:
+       case CS35L32_BATT_RECOV_3_5V:
+       case CS35L32_BATT_RECOV_3_6V:
+               pdata->batt_recov = val;
+               break;
+       default:
+               dev_err(&i2c_client->dev,
+                       "Wrong cirrus,battery-recovery DT value %d\n", val);
+               pdata->batt_recov = CS35L32_BATT_RECOV_3_4V;
+       }
+
+       return 0;
+}
+
+static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
+                                      const struct i2c_device_id *id)
+{
+       struct cs35l32_private *cs35l32;
+       struct cs35l32_platform_data *pdata =
+               dev_get_platdata(&i2c_client->dev);
+       int ret, i;
+       unsigned int devid = 0;
+       unsigned int reg;
+
+
+       cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l32_private),
+                              GFP_KERNEL);
+       if (!cs35l32) {
+               dev_err(&i2c_client->dev, "could not allocate codec\n");
+               return -ENOMEM;
+       }
+
+       i2c_set_clientdata(i2c_client, cs35l32);
+
+       cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap);
+       if (IS_ERR(cs35l32->regmap)) {
+               ret = PTR_ERR(cs35l32->regmap);
+               dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+               return ret;
+       }
+
+       if (pdata) {
+               cs35l32->pdata = *pdata;
+       } else {
+               pdata = devm_kzalloc(&i2c_client->dev,
+                                    sizeof(struct cs35l32_platform_data),
+                               GFP_KERNEL);
+               if (!pdata) {
+                       dev_err(&i2c_client->dev, "could not allocate pdata\n");
+                       return -ENOMEM;
+               }
+               if (i2c_client->dev.of_node) {
+                       ret = cs35l32_handle_of_data(i2c_client,
+                                                    &cs35l32->pdata);
+                       if (ret != 0)
+                               return ret;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++)
+               cs35l32->supplies[i].supply = cs35l32_supply_names[i];
+
+       ret = devm_regulator_bulk_get(&i2c_client->dev,
+                                     ARRAY_SIZE(cs35l32->supplies),
+                                     cs35l32->supplies);
+       if (ret != 0) {
+               dev_err(&i2c_client->dev,
+                       "Failed to request supplies: %d\n", ret);
+               return ret;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
+                                   cs35l32->supplies);
+       if (ret != 0) {
+               dev_err(&i2c_client->dev,
+                       "Failed to enable supplies: %d\n", ret);
+               return ret;
+       }
+
+       /* Reset the Device */
+       cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev,
+               "reset-gpios");
+       if (IS_ERR(cs35l32->reset_gpio)) {
+               ret = PTR_ERR(cs35l32->reset_gpio);
+               if (ret != -ENOENT && ret != -ENOSYS)
+                       return ret;
+
+               cs35l32->reset_gpio = NULL;
+       } else {
+               ret = gpiod_direction_output(cs35l32->reset_gpio, 0);
+               if (ret)
+                       return ret;
+               gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+       }
+
+       /* initialize codec */
+       ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, &reg);
+       devid = (reg & 0xFF) << 12;
+
+       ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_CD, &reg);
+       devid |= (reg & 0xFF) << 4;
+
+       ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, &reg);
+       devid |= (reg & 0xF0) >> 4;
+
+       if (devid != CS35L32_CHIP_ID) {
+               ret = -ENODEV;
+               dev_err(&i2c_client->dev,
+                       "CS35L32 Device ID (%X). Expected %X\n",
+                       devid, CS35L32_CHIP_ID);
+               return ret;
+       }
+
+       ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, &reg);
+       if (ret < 0) {
+               dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+               return ret;
+       }
+
+       ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch,
+                                   ARRAY_SIZE(cs35l32_monitor_patch));
+       if (ret < 0) {
+               dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
+               return ret;
+       }
+
+       dev_info(&i2c_client->dev,
+                "Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF);
+
+       /* Setup VBOOST Management */
+       if (cs35l32->pdata.boost_mng)
+               regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR,
+                                  CS35L32_BOOST_MASK,
+                               cs35l32->pdata.boost_mng);
+
+       /* Setup ADSP Format Config */
+       if (cs35l32->pdata.sdout_share)
+               regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
+                                   CS35L32_ADSP_SHARE_MASK,
+                               cs35l32->pdata.sdout_share << 3);
+
+       /* Setup ADSP Data Configuration */
+       if (cs35l32->pdata.sdout_datacfg)
+               regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
+                                  CS35L32_ADSP_DATACFG_MASK,
+                               cs35l32->pdata.sdout_datacfg << 4);
+
+       /* Setup Low Battery Recovery  */
+       if (cs35l32->pdata.batt_recov)
+               regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
+                                  CS35L32_BATT_REC_MASK,
+                               cs35l32->pdata.batt_recov << 1);
+
+       /* Setup Low Battery Threshold */
+       if (cs35l32->pdata.batt_thresh)
+               regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
+                                  CS35L32_BATT_THRESH_MASK,
+                               cs35l32->pdata.batt_thresh << 4);
+
+       /* Power down the AMP */
+       regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP,
+                           CS35L32_PDN_AMP);
+
+       /* Clear MCLK Error Bit since we don't have the clock yet */
+       ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
+
+       ret =  snd_soc_register_codec(&i2c_client->dev,
+                       &soc_codec_dev_cs35l32, cs35l32_dai,
+                       ARRAY_SIZE(cs35l32_dai));
+       if (ret < 0)
+               goto err_disable;
+
+       return 0;
+
+err_disable:
+       regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
+                              cs35l32->supplies);
+       return ret;
+}
+
+static int cs35l32_i2c_remove(struct i2c_client *i2c_client)
+{
+       struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client);
+
+       snd_soc_unregister_codec(&i2c_client->dev);
+
+       /* Hold down reset */
+       if (cs35l32->reset_gpio)
+               gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int cs35l32_runtime_suspend(struct device *dev)
+{
+       struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
+
+       regcache_cache_only(cs35l32->regmap, true);
+       regcache_mark_dirty(cs35l32->regmap);
+
+       /* Hold down reset */
+       if (cs35l32->reset_gpio)
+               gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+
+       /* remove power */
+       regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
+                              cs35l32->supplies);
+
+       return 0;
+}
+
+static int cs35l32_runtime_resume(struct device *dev)
+{
+       struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
+       int ret;
+
+       /* Enable power */
+       ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
+                                   cs35l32->supplies);
+       if (ret != 0) {
+               dev_err(dev, "Failed to enable supplies: %d\n",
+                       ret);
+               return ret;
+       }
+
+       if (cs35l32->reset_gpio)
+               gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+
+       regcache_cache_only(cs35l32->regmap, false);
+       regcache_sync(cs35l32->regmap);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs35l32_runtime_pm = {
+       SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume,
+                          NULL)
+};
+
+static const struct of_device_id cs35l32_of_match[] = {
+       { .compatible = "cirrus,cs35l32", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, cs35l32_of_match);
+
+
+static const struct i2c_device_id cs35l32_id[] = {
+       {"cs35l32", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l32_id);
+
+static struct i2c_driver cs35l32_i2c_driver = {
+       .driver = {
+                  .name = "cs35l32",
+                  .owner = THIS_MODULE,
+                  .pm = &cs35l32_runtime_pm,
+                  .of_match_table = cs35l32_of_match,
+                  },
+       .id_table = cs35l32_id,
+       .probe = cs35l32_i2c_probe,
+       .remove = cs35l32_i2c_remove,
+};
+
+module_i2c_driver(cs35l32_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L32 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l32.h b/sound/soc/codecs/cs35l32.h
new file mode 100644 (file)
index 0000000..31ab804
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * cs35l32.h -- CS35L32 ALSA SoC audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS35L32_H__
+#define __CS35L32_H__
+
+struct cs35l32_platform_data {
+       /* Low Battery Threshold */
+       unsigned int batt_thresh;
+       /* Low Battery Recovery */
+       unsigned int batt_recov;
+       /* LED Current Management*/
+       unsigned int led_mng;
+       /* Audio Gain w/ LED */
+       unsigned int audiogain_mng;
+       /* Boost Management */
+       unsigned int boost_mng;
+       /* Data CFG for DUAL device */
+       unsigned int sdout_datacfg;
+       /* SDOUT Sharing */
+       unsigned int sdout_share;
+};
+
+#define CS35L32_CHIP_ID                0x00035A32
+#define CS35L32_DEVID_AB       0x01    /* Device ID A & B [RO] */
+#define CS35L32_DEVID_CD       0x02    /* Device ID C & D [RO] */
+#define CS35L32_DEVID_E                0x03    /* Device ID E [RO] */
+#define CS35L32_FAB_ID         0x04    /* Fab ID [RO] */
+#define CS35L32_REV_ID         0x05    /* Revision ID [RO] */
+#define CS35L32_PWRCTL1                0x06    /* Power Ctl 1 */
+#define CS35L32_PWRCTL2                0x07    /* Power Ctl 2 */
+#define CS35L32_CLK_CTL                0x08    /* Clock Ctl */
+#define CS35L32_BATT_THRESHOLD 0x09    /* Low Battery Threshold */
+#define CS35L32_VMON           0x0A    /* Voltage Monitor [RO] */
+#define CS35L32_BST_CPCP_CTL   0x0B    /* Conv Peak Curr Protection CTL */
+#define CS35L32_IMON_SCALING   0x0C    /* IMON Scaling */
+#define CS35L32_AUDIO_LED_MNGR 0x0D    /* Audio/LED Pwr Manager */
+#define CS35L32_ADSP_CTL       0x0F    /* Serial Port Control */
+#define CS35L32_CLASSD_CTL     0x10    /* Class D Amp CTL */
+#define CS35L32_PROTECT_CTL    0x11    /* Protection Release CTL */
+#define CS35L32_INT_MASK_1     0x12    /* Interrupt Mask 1 */
+#define CS35L32_INT_MASK_2     0x13    /* Interrupt Mask 2 */
+#define CS35L32_INT_MASK_3     0x14    /* Interrupt Mask 3 */
+#define CS35L32_INT_STATUS_1   0x15    /* Interrupt Status 1 [RO] */
+#define CS35L32_INT_STATUS_2   0x16    /* Interrupt Status 2 [RO] */
+#define CS35L32_INT_STATUS_3   0x17    /* Interrupt Status 3 [RO] */
+#define CS35L32_LED_STATUS     0x18    /* LED Lighting Status [RO] */
+#define CS35L32_FLASH_MODE     0x19    /* LED Flash Mode Current */
+#define CS35L32_MOVIE_MODE     0x1A    /* LED Movie Mode Current */
+#define CS35L32_FLASH_TIMER    0x1B    /* LED Flash Timer */
+#define CS35L32_FLASH_INHIBIT  0x1C    /* LED Flash Inhibit Current */
+#define CS35L32_MAX_REGISTER   0x1C
+
+#define CS35L32_MCLK_DIV2      0x01
+#define CS35L32_MCLK_RATIO     0x01
+#define CS35L32_MCLKDIS                0x80
+#define CS35L32_PDN_ALL                0x01
+#define CS35L32_PDN_AMP                0x80
+#define CS35L32_PDN_BOOST      0x04
+#define CS35L32_PDN_IMON       0x40
+#define CS35L32_PDN_VMON       0x80
+#define CS35L32_PDN_VPMON      0x20
+#define CS35L32_PDN_ADSP       0x08
+
+#define CS35L32_MCLK_DIV2_MASK         0x40
+#define CS35L32_MCLK_RATIO_MASK                0x01
+#define CS35L32_MCLK_MASK              0x41
+#define CS35L32_ADSP_MASTER_MASK       0x40
+#define CS35L32_BOOST_MASK             0x03
+#define CS35L32_GAIN_MGR_MASK          0x08
+#define CS35L32_ADSP_SHARE_MASK                0x08
+#define CS35L32_ADSP_DATACFG_MASK      0x30
+#define CS35L32_SDOUT_3ST              0x80
+#define CS35L32_BATT_REC_MASK          0x0E
+#define CS35L32_BATT_THRESH_MASK       0x30
+
+#define CS35L32_RATES (SNDRV_PCM_RATE_48000)
+#define CS35L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE | \
+                       SNDRV_PCM_FMTBIT_S32_LE)
+
+
+#endif
index 69a85164357c58ab66ae761edfe0449e9f9f471b..4fdd47d700e325ac290fe118c9d3dd58d71b7ec9 100644 (file)
@@ -77,6 +77,7 @@ static bool cs4265_readable_register(struct device *dev, unsigned int reg)
        case CS4265_INT_MASK:
        case CS4265_STATUS_MODE_MSB:
        case CS4265_STATUS_MODE_LSB:
+       case CS4265_CHIP_ID:
                return true;
        default:
                return false;
index 969167d8b71e0fa8ff68a61f2014564e1a69aa5d..35fbef743fbe21883db9d2e645ea018a5c8c9080 100644 (file)
@@ -176,9 +176,9 @@ static bool cs42l52_volatile_register(struct device *dev, unsigned int reg)
        case CS42L52_BATT_LEVEL:
        case CS42L52_SPK_STATUS:
        case CS42L52_CHARGE_PUMP:
-               return 1;
+               return true;
        default:
-               return 0;
+               return false;
        }
 }
 
@@ -946,20 +946,6 @@ static struct snd_soc_dai_driver cs42l52_dai = {
                .ops = &cs42l52_ops,
 };
 
-static int cs42l52_suspend(struct snd_soc_codec *codec)
-{
-       cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int cs42l52_resume(struct snd_soc_codec *codec)
-{
-       cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
 static int beep_rates[] = {
        261, 522, 585, 667, 706, 774, 889, 1000,
        1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
@@ -1104,8 +1090,6 @@ static int cs42l52_probe(struct snd_soc_codec *codec)
 
        cs42l52_init_beep(codec);
 
-       cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        cs42l52->sysclk = CS42L52_DEFAULT_CLK;
        cs42l52->config.format = CS42L52_DEFAULT_FORMAT;
 
@@ -1115,7 +1099,6 @@ static int cs42l52_probe(struct snd_soc_codec *codec)
 static int cs42l52_remove(struct snd_soc_codec *codec)
 {
        cs42l52_free_beep(codec);
-       cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        return 0;
 }
@@ -1123,9 +1106,8 @@ static int cs42l52_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_cs42l52 = {
        .probe = cs42l52_probe,
        .remove = cs42l52_remove,
-       .suspend = cs42l52_suspend,
-       .resume = cs42l52_resume,
        .set_bias_level = cs42l52_set_bias_level,
+       .suspend_bias_off = true,
 
        .dapm_widgets = cs42l52_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets),
index c766a5a9ce8088441df5d363c91244c49d270798..2ddc7ac10ad7a311ccda24f7add9613891b068c9 100644 (file)
@@ -171,9 +171,9 @@ static bool cs42l56_volatile_register(struct device *dev, unsigned int reg)
 {
        switch (reg) {
        case CS42L56_INT_STATUS:
-               return 1;
+               return true;
        default:
-               return 0;
+               return false;
        }
 }
 
@@ -1016,20 +1016,6 @@ static struct snd_soc_dai_driver cs42l56_dai = {
                .ops = &cs42l56_ops,
 };
 
-static int cs42l56_suspend(struct snd_soc_codec *codec)
-{
-       cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int cs42l56_resume(struct snd_soc_codec *codec)
-{
-       cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
 static int beep_freq[] = {
        261, 522, 585, 667, 706, 774, 889, 1000,
        1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
@@ -1168,18 +1154,12 @@ static int cs42l56_probe(struct snd_soc_codec *codec)
 {
        cs42l56_init_beep(codec);
 
-       cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 }
 
 static int cs42l56_remove(struct snd_soc_codec *codec)
 {
-       struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
-
        cs42l56_free_beep(codec);
-       cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       regulator_bulk_free(ARRAY_SIZE(cs42l56->supplies), cs42l56->supplies);
 
        return 0;
 }
@@ -1187,9 +1167,8 @@ static int cs42l56_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_cs42l56 = {
        .probe = cs42l56_probe,
        .remove = cs42l56_remove,
-       .suspend = cs42l56_suspend,
-       .resume = cs42l56_resume,
        .set_bias_level = cs42l56_set_bias_level,
+       .suspend_bias_off = true,
 
        .dapm_widgets = cs42l56_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets),
index 0e7b9eb2ba61669021064d76c827f759dd50c644..2f8b94683e83054febd1642719e5ecb61df99725 100644 (file)
@@ -1330,25 +1330,10 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
         }
 };
 
-static int cs42l73_suspend(struct snd_soc_codec *codec)
-{
-       cs42l73_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int cs42l73_resume(struct snd_soc_codec *codec)
-{
-       cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int cs42l73_probe(struct snd_soc_codec *codec)
 {
        struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
 
-       cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        /* Set Charge Pump Frequency */
        if (cs42l73->pdata.chgfreq)
                snd_soc_update_bits(codec, CS42L73_CPFCHC,
@@ -1362,18 +1347,10 @@ static int cs42l73_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int cs42l73_remove(struct snd_soc_codec *codec)
-{
-       cs42l73_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_cs42l73 = {
        .probe = cs42l73_probe,
-       .remove = cs42l73_remove,
-       .suspend = cs42l73_suspend,
-       .resume = cs42l73_resume,
        .set_bias_level = cs42l73_set_bias_level,
+       .suspend_bias_off = true,
 
        .dapm_widgets = cs42l73_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets),
index 2fae31cb006745587e0fb1a51491dd10e0facea5..61b2f9a2eef1b87d8daea13ab83d90779a883fc5 100644 (file)
@@ -35,7 +35,6 @@
 
 struct da732x_priv {
        struct regmap *regmap;
-       struct snd_soc_codec *codec;
 
        unsigned int sysclk;
        bool pll_en;
@@ -217,7 +216,7 @@ static void da732x_set_charge_pump(struct snd_soc_codec *codec, int state)
                snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA723X_CP_DIS);
                break;
        default:
-               pr_err(KERN_ERR "Wrong charge pump state\n");
+               pr_err("Wrong charge pump state\n");
                break;
        }
 }
@@ -1508,31 +1507,7 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int da732x_probe(struct snd_soc_codec *codec)
-{
-       struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       da732x->codec = codec;
-
-       dapm->idle_bias_off = false;
-
-       da732x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int da732x_remove(struct snd_soc_codec *codec)
-{
-
-       da732x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_da732x = {
-       .probe                  = da732x_probe,
-       .remove                 = da732x_remove,
        .set_bias_level         = da732x_set_bias_level,
        .controls               = da732x_snd_controls,
        .num_controls           = ARRAY_SIZE(da732x_snd_controls),
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
new file mode 100644 (file)
index 0000000..aae410d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * es8328-i2c.c  --  ES8328 ALSA SoC I2C Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "es8328.h"
+
+static const struct i2c_device_id es8328_id[] = {
+       { "everest,es8328", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, es8328_id);
+
+static const struct of_device_id es8328_of_match[] = {
+       { .compatible = "everest,es8328", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, es8328_of_match);
+
+static int es8328_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       return es8328_probe(&i2c->dev,
+                       devm_regmap_init_i2c(i2c, &es8328_regmap_config));
+}
+
+static int es8328_i2c_remove(struct i2c_client *i2c)
+{
+       snd_soc_unregister_codec(&i2c->dev);
+       return 0;
+}
+
+static struct i2c_driver es8328_i2c_driver = {
+       .driver = {
+               .name           = "es8328",
+               .of_match_table = es8328_of_match,
+       },
+       .probe    = es8328_i2c_probe,
+       .remove   = es8328_i2c_remove,
+       .id_table = es8328_id,
+};
+
+module_i2c_driver(es8328_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328-spi.c b/sound/soc/codecs/es8328-spi.c
new file mode 100644 (file)
index 0000000..8fbd935
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * es8328.c  --  ES8328 ALSA SoC SPI Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+#include "es8328.h"
+
+static const struct of_device_id es8328_of_match[] = {
+       { .compatible = "everest,es8328", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, es8328_of_match);
+
+static int es8328_spi_probe(struct spi_device *spi)
+{
+       return es8328_probe(&spi->dev,
+                       devm_regmap_init_spi(spi, &es8328_regmap_config));
+}
+
+static int es8328_spi_remove(struct spi_device *spi)
+{
+       snd_soc_unregister_codec(&spi->dev);
+       return 0;
+}
+
+static struct spi_driver es8328_spi_driver = {
+       .driver = {
+               .name           = "es8328",
+               .of_match_table = es8328_of_match,
+       },
+       .probe  = es8328_spi_probe,
+       .remove = es8328_spi_remove,
+};
+
+module_spi_driver(es8328_spi_driver);
+MODULE_DESCRIPTION("ASoC ES8328 audio CODEC SPI driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
new file mode 100644 (file)
index 0000000..f273251
--- /dev/null
@@ -0,0 +1,756 @@
+/*
+ * es8328.c  --  ES8328 ALSA SoC Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "es8328.h"
+
+#define ES8328_SYSCLK_RATE_1X 11289600
+#define ES8328_SYSCLK_RATE_2X 22579200
+
+/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
+static struct {
+       int rate;
+       u8 ratio;
+} mclk_ratios[] = {
+       { 8000, 9 },
+       {11025, 7 },
+       {22050, 4 },
+       {44100, 2 },
+};
+
+/* regulator supplies for sgtl5000, VDDD is an optional external supply */
+enum sgtl5000_regulator_supplies {
+       DVDD,
+       AVDD,
+       PVDD,
+       HPVDD,
+       ES8328_SUPPLY_NUM
+};
+
+/* vddd is optional supply */
+static const char * const supply_names[ES8328_SUPPLY_NUM] = {
+       "DVDD",
+       "AVDD",
+       "PVDD",
+       "HPVDD",
+};
+
+#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
+               SNDRV_PCM_RATE_22050 | \
+               SNDRV_PCM_RATE_11025)
+#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+struct es8328_priv {
+       struct regmap *regmap;
+       struct clk *clk;
+       int playback_fs;
+       bool deemph;
+       struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
+};
+
+/*
+ * ES8328 Controls
+ */
+
+static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert",
+                                         "L + R Invert"};
+static SOC_ENUM_SINGLE_DECL(adcpol,
+                           ES8328_ADCCONTROL6, 6, adcpol_txt);
+
+static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
+
+static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
+
+static int es8328_set_deemph(struct snd_soc_codec *codec)
+{
+       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+       int val, i, best;
+
+       /*
+        * If we're using deemphasis select the nearest available sample
+        * rate.
+        */
+       if (es8328->deemph) {
+               best = 1;
+               for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
+                       if (abs(deemph_settings[i] - es8328->playback_fs) <
+                           abs(deemph_settings[best] - es8328->playback_fs))
+                               best = i;
+               }
+
+               val = best << 1;
+       } else {
+               val = 0;
+       }
+
+       dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+       return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val);
+}
+
+static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.enumerated.item[0] = es8328->deemph;
+       return 0;
+}
+
+static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+       int deemph = ucontrol->value.enumerated.item[0];
+       int ret;
+
+       if (deemph > 1)
+               return -EINVAL;
+
+       ret = es8328_set_deemph(codec);
+       if (ret < 0)
+               return ret;
+
+       es8328->deemph = deemph;
+
+       return 0;
+}
+
+
+
+static const struct snd_kcontrol_new es8328_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("Capture Digital Volume",
+               ES8328_ADCCONTROL8, ES8328_ADCCONTROL9,
+                0, 0xc0, 1, dac_adc_tlv),
+       SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 6, 1, 0),
+
+       SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+                   es8328_get_deemph, es8328_put_deemph),
+
+       SOC_ENUM("Capture Polarity", adcpol),
+
+       SOC_SINGLE_TLV("Left Mixer Left Bypass Volume",
+                       ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv),
+       SOC_SINGLE_TLV("Left Mixer Right Bypass Volume",
+                       ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv),
+       SOC_SINGLE_TLV("Right Mixer Left Bypass Volume",
+                       ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv),
+       SOC_SINGLE_TLV("Right Mixer Right Bypass Volume",
+                       ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv),
+
+       SOC_DOUBLE_R_TLV("PCM Volume",
+                       ES8328_LDACVOL, ES8328_RDACVOL,
+                       0, ES8328_DACVOL_MAX, 1, dac_adc_tlv),
+
+       SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
+                       ES8328_LOUT1VOL, ES8328_ROUT1VOL,
+                       0, ES8328_OUT1VOL_MAX, 0, play_tlv),
+
+       SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
+                       ES8328_LOUT2VOL, ES8328_ROUT2VOL,
+                       0, ES8328_OUT2VOL_MAX, 0, play_tlv),
+
+       SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1,
+                       4, 0, 8, 0, mic_tlv),
+};
+
+/*
+ * DAPM Controls
+ */
+
+static const char * const es8328_line_texts[] = {
+       "Line 1", "Line 2", "PGA", "Differential"};
+
+static const struct soc_enum es8328_lline_enum =
+       SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3,
+                             ARRAY_SIZE(es8328_line_texts),
+                             es8328_line_texts);
+static const struct snd_kcontrol_new es8328_left_line_controls =
+       SOC_DAPM_ENUM("Route", es8328_lline_enum);
+
+static const struct soc_enum es8328_rline_enum =
+       SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0,
+                             ARRAY_SIZE(es8328_line_texts),
+                             es8328_line_texts);
+static const struct snd_kcontrol_new es8328_right_line_controls =
+       SOC_DAPM_ENUM("Route", es8328_lline_enum);
+
+/* Left Mixer */
+static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0),
+       SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0),
+       SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0),
+       SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0),
+       SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0),
+       SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0),
+};
+
+static const char * const es8328_pga_sel[] = {
+       "Line 1", "Line 2", "Line 3", "Differential"};
+
+/* Left PGA Mux */
+static const struct soc_enum es8328_lpga_enum =
+       SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6,
+                             ARRAY_SIZE(es8328_pga_sel),
+                             es8328_pga_sel);
+static const struct snd_kcontrol_new es8328_left_pga_controls =
+       SOC_DAPM_ENUM("Route", es8328_lpga_enum);
+
+/* Right PGA Mux */
+static const struct soc_enum es8328_rpga_enum =
+       SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4,
+                             ARRAY_SIZE(es8328_pga_sel),
+                             es8328_pga_sel);
+static const struct snd_kcontrol_new es8328_right_pga_controls =
+       SOC_DAPM_ENUM("Route", es8328_rpga_enum);
+
+/* Differential Mux */
+static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"};
+static SOC_ENUM_SINGLE_DECL(diffmux,
+                           ES8328_ADCCONTROL3, 7, es8328_diff_sel);
+static const struct snd_kcontrol_new es8328_diffmux_controls =
+       SOC_DAPM_ENUM("Route", diffmux);
+
+/* Mono ADC Mux */
+static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)",
+       "Mono (Right)", "Digital Mono"};
+static SOC_ENUM_SINGLE_DECL(monomux,
+                           ES8328_ADCCONTROL3, 3, es8328_mono_mux);
+static const struct snd_kcontrol_new es8328_monomux_controls =
+       SOC_DAPM_ENUM("Route", monomux);
+
+static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
+       SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+               &es8328_diffmux_controls),
+       SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+               &es8328_monomux_controls),
+       SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+               &es8328_monomux_controls),
+
+       SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER,
+                       ES8328_ADCPOWER_AINL_OFF, 1,
+                       &es8328_left_pga_controls),
+       SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER,
+                       ES8328_ADCPOWER_AINR_OFF, 1,
+                       &es8328_right_pga_controls),
+
+       SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+               &es8328_left_line_controls),
+       SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+               &es8328_right_line_controls),
+
+       SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER,
+                       ES8328_ADCPOWER_ADCR_OFF, 1),
+       SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER,
+                       ES8328_ADCPOWER_ADCL_OFF, 1),
+
+       SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER,
+                       ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER,
+                       ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER,
+                       ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER,
+                       ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER,
+                       ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER,
+                       ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER,
+                       ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER,
+                       ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER,
+                       ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER,
+                       ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
+
+       SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER,
+                       ES8328_DACPOWER_RDAC_OFF, 1),
+       SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER,
+                       ES8328_DACPOWER_LDAC_OFF, 1),
+
+       SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+               &es8328_left_mixer_controls[0],
+               ARRAY_SIZE(es8328_left_mixer_controls)),
+       SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+               &es8328_right_mixer_controls[0],
+               ARRAY_SIZE(es8328_right_mixer_controls)),
+
+       SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER,
+                       ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER,
+                       ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER,
+                       ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER,
+                       ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0),
+
+       SND_SOC_DAPM_OUTPUT("LOUT1"),
+       SND_SOC_DAPM_OUTPUT("ROUT1"),
+       SND_SOC_DAPM_OUTPUT("LOUT2"),
+       SND_SOC_DAPM_OUTPUT("ROUT2"),
+
+       SND_SOC_DAPM_INPUT("LINPUT1"),
+       SND_SOC_DAPM_INPUT("LINPUT2"),
+       SND_SOC_DAPM_INPUT("RINPUT1"),
+       SND_SOC_DAPM_INPUT("RINPUT2"),
+};
+
+static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
+
+       { "Left Line Mux", "Line 1", "LINPUT1" },
+       { "Left Line Mux", "Line 2", "LINPUT2" },
+       { "Left Line Mux", "PGA", "Left PGA Mux" },
+       { "Left Line Mux", "Differential", "Differential Mux" },
+
+       { "Right Line Mux", "Line 1", "RINPUT1" },
+       { "Right Line Mux", "Line 2", "RINPUT2" },
+       { "Right Line Mux", "PGA", "Right PGA Mux" },
+       { "Right Line Mux", "Differential", "Differential Mux" },
+
+       { "Left PGA Mux", "Line 1", "LINPUT1" },
+       { "Left PGA Mux", "Line 2", "LINPUT2" },
+       { "Left PGA Mux", "Differential", "Differential Mux" },
+
+       { "Right PGA Mux", "Line 1", "RINPUT1" },
+       { "Right PGA Mux", "Line 2", "RINPUT2" },
+       { "Right PGA Mux", "Differential", "Differential Mux" },
+
+       { "Differential Mux", "Line 1", "LINPUT1" },
+       { "Differential Mux", "Line 1", "RINPUT1" },
+       { "Differential Mux", "Line 2", "LINPUT2" },
+       { "Differential Mux", "Line 2", "RINPUT2" },
+
+       { "Left ADC Mux", "Stereo", "Left PGA Mux" },
+       { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
+       { "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
+
+       { "Right ADC Mux", "Stereo", "Right PGA Mux" },
+       { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
+       { "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
+
+       { "Left ADC", NULL, "Left ADC Mux" },
+       { "Right ADC", NULL, "Right ADC Mux" },
+
+       { "ADC DIG", NULL, "ADC STM" },
+       { "ADC DIG", NULL, "ADC Vref" },
+       { "ADC DIG", NULL, "ADC DLL" },
+
+       { "Left ADC", NULL, "ADC DIG" },
+       { "Right ADC", NULL, "ADC DIG" },
+
+       { "Mic Bias", NULL, "Mic Bias Gen" },
+
+       { "Left Line Mux", "Line 1", "LINPUT1" },
+       { "Left Line Mux", "Line 2", "LINPUT2" },
+       { "Left Line Mux", "PGA", "Left PGA Mux" },
+       { "Left Line Mux", "Differential", "Differential Mux" },
+
+       { "Right Line Mux", "Line 1", "RINPUT1" },
+       { "Right Line Mux", "Line 2", "RINPUT2" },
+       { "Right Line Mux", "PGA", "Right PGA Mux" },
+       { "Right Line Mux", "Differential", "Differential Mux" },
+
+       { "Left Out 1", NULL, "Left DAC" },
+       { "Right Out 1", NULL, "Right DAC" },
+       { "Left Out 2", NULL, "Left DAC" },
+       { "Right Out 2", NULL, "Right DAC" },
+
+       { "Left Mixer", "Playback Switch", "Left DAC" },
+       { "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
+       { "Left Mixer", "Right Playback Switch", "Right DAC" },
+       { "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+       { "Right Mixer", "Left Playback Switch", "Left DAC" },
+       { "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
+       { "Right Mixer", "Playback Switch", "Right DAC" },
+       { "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+       { "DAC DIG", NULL, "DAC STM" },
+       { "DAC DIG", NULL, "DAC Vref" },
+       { "DAC DIG", NULL, "DAC DLL" },
+
+       { "Left DAC", NULL, "DAC DIG" },
+       { "Right DAC", NULL, "DAC DIG" },
+
+       { "Left Out 1", NULL, "Left Mixer" },
+       { "LOUT1", NULL, "Left Out 1" },
+       { "Right Out 1", NULL, "Right Mixer" },
+       { "ROUT1", NULL, "Right Out 1" },
+
+       { "Left Out 2", NULL, "Left Mixer" },
+       { "LOUT2", NULL, "Left Out 2" },
+       { "Right Out 2", NULL, "Right Mixer" },
+       { "ROUT2", NULL, "Right Out 2" },
+};
+
+static int es8328_mute(struct snd_soc_dai *dai, int mute)
+{
+       return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3,
+                       ES8328_DACCONTROL3_DACMUTE,
+                       mute ? ES8328_DACCONTROL3_DACMUTE : 0);
+}
+
+static int es8328_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+       int clk_rate;
+       int i;
+       int reg;
+       u8 ratio;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               reg = ES8328_DACCONTROL2;
+       else
+               reg = ES8328_ADCCONTROL5;
+
+       clk_rate = clk_get_rate(es8328->clk);
+
+       if ((clk_rate != ES8328_SYSCLK_RATE_1X) &&
+               (clk_rate != ES8328_SYSCLK_RATE_2X)) {
+               dev_err(codec->dev,
+                       "%s: clock is running at %d Hz, not %d or %d Hz\n",
+                        __func__, clk_rate,
+                        ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
+               return -EINVAL;
+       }
+
+       /* find master mode MCLK to sampling frequency ratio */
+       ratio = mclk_ratios[0].rate;
+       for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
+               if (params_rate(params) <= mclk_ratios[i].rate)
+                       ratio = mclk_ratios[i].ratio;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               es8328->playback_fs = params_rate(params);
+               es8328_set_deemph(codec);
+       }
+
+       return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
+}
+
+static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+       int clk_rate;
+       u8 mode = ES8328_DACCONTROL1_DACWL_16;
+
+       /* set master/slave audio interface */
+       if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
+               return -EINVAL;
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
+               return -EINVAL;
+
+       snd_soc_write(codec, ES8328_DACCONTROL1, mode);
+       snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
+
+       /* Master serial port mode, with BCLK generated automatically */
+       clk_rate = clk_get_rate(es8328->clk);
+       if (clk_rate == ES8328_SYSCLK_RATE_1X)
+               snd_soc_write(codec, ES8328_MASTERMODE,
+                               ES8328_MASTERMODE_MSC);
+       else
+               snd_soc_write(codec, ES8328_MASTERMODE,
+                               ES8328_MASTERMODE_MCLKDIV2 |
+                               ES8328_MASTERMODE_MSC);
+
+       return 0;
+}
+
+static int es8328_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* VREF, VMID=2x50k, digital enabled */
+               snd_soc_write(codec, ES8328_CHIPPOWER, 0);
+               snd_soc_update_bits(codec, ES8328_CONTROL1,
+                               ES8328_CONTROL1_VMIDSEL_MASK |
+                               ES8328_CONTROL1_ENREF,
+                               ES8328_CONTROL1_VMIDSEL_50k |
+                               ES8328_CONTROL1_ENREF);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       snd_soc_update_bits(codec, ES8328_CONTROL1,
+                                       ES8328_CONTROL1_VMIDSEL_MASK |
+                                       ES8328_CONTROL1_ENREF,
+                                       ES8328_CONTROL1_VMIDSEL_5k |
+                                       ES8328_CONTROL1_ENREF);
+
+                       /* Charge caps */
+                       msleep(100);
+               }
+
+               snd_soc_write(codec, ES8328_CONTROL2,
+                               ES8328_CONTROL2_OVERCURRENT_ON |
+                               ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+
+               /* VREF, VMID=2*500k, digital stopped */
+               snd_soc_update_bits(codec, ES8328_CONTROL1,
+                               ES8328_CONTROL1_VMIDSEL_MASK |
+                               ES8328_CONTROL1_ENREF,
+                               ES8328_CONTROL1_VMIDSEL_500k |
+                               ES8328_CONTROL1_ENREF);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, ES8328_CONTROL1,
+                               ES8328_CONTROL1_VMIDSEL_MASK |
+                               ES8328_CONTROL1_ENREF,
+                               0);
+               break;
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static const struct snd_soc_dai_ops es8328_dai_ops = {
+       .hw_params      = es8328_hw_params,
+       .digital_mute   = es8328_mute,
+       .set_fmt        = es8328_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver es8328_dai = {
+       .name = "es8328-hifi-analog",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = ES8328_RATES,
+               .formats = ES8328_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = ES8328_RATES,
+               .formats = ES8328_FORMATS,
+       },
+       .ops = &es8328_dai_ops,
+};
+
+static int es8328_suspend(struct snd_soc_codec *codec)
+{
+       struct es8328_priv *es8328;
+       int ret;
+
+       es8328 = snd_soc_codec_get_drvdata(codec);
+
+       clk_disable_unprepare(es8328->clk);
+
+       ret = regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
+                       es8328->supplies);
+       if (ret) {
+               dev_err(codec->dev, "unable to disable regulators\n");
+               return ret;
+       }
+       return 0;
+}
+
+static int es8328_resume(struct snd_soc_codec *codec)
+{
+       struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+       struct es8328_priv *es8328;
+       int ret;
+
+       es8328 = snd_soc_codec_get_drvdata(codec);
+
+       ret = clk_prepare_enable(es8328->clk);
+       if (ret) {
+               dev_err(codec->dev, "unable to enable clock\n");
+               return ret;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
+                                       es8328->supplies);
+       if (ret) {
+               dev_err(codec->dev, "unable to enable regulators\n");
+               return ret;
+       }
+
+       regcache_mark_dirty(regmap);
+       ret = regcache_sync(regmap);
+       if (ret) {
+               dev_err(codec->dev, "unable to sync regcache\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int es8328_codec_probe(struct snd_soc_codec *codec)
+{
+       struct es8328_priv *es8328;
+       int ret;
+
+       es8328 = snd_soc_codec_get_drvdata(codec);
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
+                                       es8328->supplies);
+       if (ret) {
+               dev_err(codec->dev, "unable to enable regulators\n");
+               return ret;
+       }
+
+       /* Setup clocks */
+       es8328->clk = devm_clk_get(codec->dev, NULL);
+       if (IS_ERR(es8328->clk)) {
+               dev_err(codec->dev, "codec clock missing or invalid\n");
+               ret = PTR_ERR(es8328->clk);
+               goto clk_fail;
+       }
+
+       ret = clk_prepare_enable(es8328->clk);
+       if (ret) {
+               dev_err(codec->dev, "unable to prepare codec clk\n");
+               goto clk_fail;
+       }
+
+       return 0;
+
+clk_fail:
+       regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
+                              es8328->supplies);
+       return ret;
+}
+
+static int es8328_remove(struct snd_soc_codec *codec)
+{
+       struct es8328_priv *es8328;
+
+       es8328 = snd_soc_codec_get_drvdata(codec);
+
+       if (es8328->clk)
+               clk_disable_unprepare(es8328->clk);
+
+       regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
+                              es8328->supplies);
+
+       return 0;
+}
+
+const struct regmap_config es8328_regmap_config = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = ES8328_REG_MAX,
+       .cache_type     = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(es8328_regmap_config);
+
+static struct snd_soc_codec_driver es8328_codec_driver = {
+       .probe            = es8328_codec_probe,
+       .suspend          = es8328_suspend,
+       .resume           = es8328_resume,
+       .remove           = es8328_remove,
+       .set_bias_level   = es8328_set_bias_level,
+       .suspend_bias_off = true,
+
+       .controls         = es8328_snd_controls,
+       .num_controls     = ARRAY_SIZE(es8328_snd_controls),
+       .dapm_widgets     = es8328_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
+       .dapm_routes      = es8328_dapm_routes,
+       .num_dapm_routes  = ARRAY_SIZE(es8328_dapm_routes),
+};
+
+int es8328_probe(struct device *dev, struct regmap *regmap)
+{
+       struct es8328_priv *es8328;
+       int ret;
+       int i;
+
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL);
+       if (es8328 == NULL)
+               return -ENOMEM;
+
+       es8328->regmap = regmap;
+
+       for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++)
+               es8328->supplies[i].supply = supply_names[i];
+
+       ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8328->supplies),
+                               es8328->supplies);
+       if (ret) {
+               dev_err(dev, "unable to get regulators\n");
+               return ret;
+       }
+
+       dev_set_drvdata(dev, es8328);
+
+       return snd_soc_register_codec(dev,
+                       &es8328_codec_driver, &es8328_dai, 1);
+}
+EXPORT_SYMBOL_GPL(es8328_probe);
+
+MODULE_DESCRIPTION("ASoC ES8328 driver");
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h
new file mode 100644 (file)
index 0000000..cb36afe
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * es8328.h  --  ES8328 ALSA SoC Audio driver
+ */
+
+#ifndef _ES8328_H
+#define _ES8328_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct regmap_config es8328_regmap_config;
+int es8328_probe(struct device *dev, struct regmap *regmap);
+
+#define ES8328_DACLVOL 46
+#define ES8328_DACRVOL 47
+#define ES8328_DACCTL 28
+#define ES8328_RATEMASK (0x1f << 0)
+
+#define ES8328_CONTROL1                0x00
+#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0)
+#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
+#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
+#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
+#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0)
+#define ES8328_CONTROL1_ENREF (1 << 2)
+#define ES8328_CONTROL1_SEQEN (1 << 3)
+#define ES8328_CONTROL1_SAMEFS (1 << 4)
+#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5)
+#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5)
+#define ES8328_CONTROL1_LRCM (1 << 6)
+#define ES8328_CONTROL1_SCP_RESET (1 << 7)
+
+#define ES8328_CONTROL2                0x01
+#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0)
+#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1)
+#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2)
+#define ES8328_CONTROL2_ANALOG_OFF (1 << 3)
+#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
+#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
+#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6)
+#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
+
+#define ES8328_CHIPPOWER       0x02
+#define ES8328_CHIPPOWER_DACVREF_OFF 0
+#define ES8328_CHIPPOWER_ADCVREF_OFF 1
+#define ES8328_CHIPPOWER_DACDLL_OFF 2
+#define ES8328_CHIPPOWER_ADCDLL_OFF 3
+#define ES8328_CHIPPOWER_DACSTM_RESET 4
+#define ES8328_CHIPPOWER_ADCSTM_RESET 5
+#define ES8328_CHIPPOWER_DACDIG_OFF 6
+#define ES8328_CHIPPOWER_ADCDIG_OFF 7
+
+#define ES8328_ADCPOWER                0x03
+#define ES8328_ADCPOWER_INT1_LOWPOWER 0
+#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1
+#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2
+#define ES8328_ADCPOWER_MIC_BIAS_OFF 3
+#define ES8328_ADCPOWER_ADCR_OFF 4
+#define ES8328_ADCPOWER_ADCL_OFF 5
+#define ES8328_ADCPOWER_AINR_OFF 6
+#define ES8328_ADCPOWER_AINL_OFF 7
+
+#define ES8328_DACPOWER                0x04
+#define ES8328_DACPOWER_OUT3_ON 0
+#define ES8328_DACPOWER_MONO_ON 1
+#define ES8328_DACPOWER_ROUT2_ON 2
+#define ES8328_DACPOWER_LOUT2_ON 3
+#define ES8328_DACPOWER_ROUT1_ON 4
+#define ES8328_DACPOWER_LOUT1_ON 5
+#define ES8328_DACPOWER_RDAC_OFF 6
+#define ES8328_DACPOWER_LDAC_OFF 7
+
+#define ES8328_CHIPLOPOW1      0x05
+#define ES8328_CHIPLOPOW2      0x06
+#define ES8328_ANAVOLMANAG     0x07
+
+#define ES8328_MASTERMODE      0x08
+#define ES8328_MASTERMODE_BCLKDIV (0 << 0)
+#define ES8328_MASTERMODE_BCLK_INV (1 << 5)
+#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6)
+#define ES8328_MASTERMODE_MSC (1 << 7)
+
+#define ES8328_ADCCONTROL1     0x09
+#define ES8328_ADCCONTROL2     0x0a
+#define ES8328_ADCCONTROL3     0x0b
+#define ES8328_ADCCONTROL4     0x0c
+#define ES8328_ADCCONTROL5     0x0d
+#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
+
+#define ES8328_ADCCONTROL6     0x0e
+
+#define ES8328_ADCCONTROL7     0x0f
+#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2)
+#define ES8328_ADCCONTROL7_ADC_LER (1 << 3)
+#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
+#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
+
+#define ES8328_ADCCONTROL8     0x10
+#define ES8328_ADCCONTROL9     0x11
+#define ES8328_ADCCONTROL10    0x12
+#define ES8328_ADCCONTROL11    0x13
+#define ES8328_ADCCONTROL12    0x14
+#define ES8328_ADCCONTROL13    0x15
+#define ES8328_ADCCONTROL14    0x16
+
+#define ES8328_DACCONTROL1     0x17
+#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
+#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
+#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
+#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
+#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
+#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6)
+#define ES8328_DACCONTROL1_LRSWAP (1 << 7)
+
+#define ES8328_DACCONTROL2     0x18
+#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0)
+#define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5)
+
+#define ES8328_DACCONTROL3     0x19
+#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2)
+#define ES8328_DACCONTROL3_DACMUTE (1 << 2)
+#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3)
+#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4)
+#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5)
+#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6)
+
+#define ES8328_LDACVOL 0x1a
+#define ES8328_LDACVOL_MASK (0 << 0)
+#define ES8328_LDACVOL_MAX (0xc0)
+
+#define ES8328_RDACVOL 0x1b
+#define ES8328_RDACVOL_MASK (0 << 0)
+#define ES8328_RDACVOL_MAX (0xc0)
+
+#define ES8328_DACVOL_MAX (0xc0)
+
+#define ES8328_DACCONTROL4     0x1a
+#define ES8328_DACCONTROL5     0x1b
+
+#define ES8328_DACCONTROL6     0x1c
+#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
+#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
+#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
+#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6)
+
+#define ES8328_DACCONTROL7     0x1d
+#define ES8328_DACCONTROL7_VPP_SCALE_3p5       (0 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_4p0       (1 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_3p0       (2 << 0)
+#define ES8328_DACCONTROL7_VPP_SCALE_2p5       (3 << 0)
+#define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */
+#define ES8328_DACCONTROL7_MONO                (1 << 5)
+#define ES8328_DACCONTROL7_ZEROR       (1 << 6)
+#define ES8328_DACCONTROL7_ZEROL       (1 << 7)
+
+/* Shelving filter */
+#define ES8328_DACCONTROL8     0x1e
+#define ES8328_DACCONTROL9     0x1f
+#define ES8328_DACCONTROL10    0x20
+#define ES8328_DACCONTROL11    0x21
+#define ES8328_DACCONTROL12    0x22
+#define ES8328_DACCONTROL13    0x23
+#define ES8328_DACCONTROL14    0x24
+#define ES8328_DACCONTROL15    0x25
+
+#define ES8328_DACCONTROL16    0x26
+#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3)
+
+#define ES8328_DACCONTROL17    0x27
+#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL17_LI2LO (1 << 6)
+#define ES8328_DACCONTROL17_LD2LO (1 << 7)
+
+#define ES8328_DACCONTROL18    0x28
+#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL18_RI2LO (1 << 6)
+#define ES8328_DACCONTROL18_RD2LO (1 << 7)
+
+#define ES8328_DACCONTROL19    0x29
+#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL19_LI2RO (1 << 6)
+#define ES8328_DACCONTROL19_LD2RO (1 << 7)
+
+#define ES8328_DACCONTROL20    0x2a
+#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL20_RI2RO (1 << 6)
+#define ES8328_DACCONTROL20_RD2RO (1 << 7)
+
+#define ES8328_DACCONTROL21    0x2b
+#define ES8328_DACCONTROL21_LI2MOVOL (7 << 3)
+#define ES8328_DACCONTROL21_LI2MO (1 << 6)
+#define ES8328_DACCONTROL21_LD2MO (1 << 7)
+
+#define ES8328_DACCONTROL22    0x2c
+#define ES8328_DACCONTROL22_RI2MOVOL (7 << 3)
+#define ES8328_DACCONTROL22_RI2MO (1 << 6)
+#define ES8328_DACCONTROL22_RD2MO (1 << 7)
+
+#define ES8328_DACCONTROL23    0x2d
+#define ES8328_DACCONTROL23_MOUTINV            (1 << 1)
+#define ES8328_DACCONTROL23_HPSWPOL            (1 << 2)
+#define ES8328_DACCONTROL23_HPSWEN             (1 << 3)
+#define ES8328_DACCONTROL23_VROI_1p5k          (0 << 4)
+#define ES8328_DACCONTROL23_VROI_40k           (1 << 4)
+#define ES8328_DACCONTROL23_OUT3_VREF          (0 << 5)
+#define ES8328_DACCONTROL23_OUT3_ROUT1         (1 << 5)
+#define ES8328_DACCONTROL23_OUT3_MONOOUT       (2 << 5)
+#define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER   (3 << 5)
+#define ES8328_DACCONTROL23_ROUT2INV           (1 << 7)
+
+/* LOUT1 Amplifier */
+#define ES8328_LOUT1VOL 0x2e
+#define ES8328_LOUT1VOL_MASK (0 << 5)
+#define ES8328_LOUT1VOL_MAX (0x24)
+
+/* ROUT1 Amplifier */
+#define ES8328_ROUT1VOL 0x2f
+#define ES8328_ROUT1VOL_MASK (0 << 5)
+#define ES8328_ROUT1VOL_MAX (0x24)
+
+#define ES8328_OUT1VOL_MAX (0x24)
+
+/* LOUT2 Amplifier */
+#define ES8328_LOUT2VOL 0x30
+#define ES8328_LOUT2VOL_MASK (0 << 5)
+#define ES8328_LOUT2VOL_MAX (0x24)
+
+/* ROUT2 Amplifier */
+#define ES8328_ROUT2VOL 0x31
+#define ES8328_ROUT2VOL_MASK (0 << 5)
+#define ES8328_ROUT2VOL_MAX (0x24)
+
+#define ES8328_OUT2VOL_MAX (0x24)
+
+/* Mono Out Amplifier */
+#define ES8328_MONOOUTVOL 0x32
+#define ES8328_MONOOUTVOL_MASK (0 << 5)
+#define ES8328_MONOOUTVOL_MAX (0x24)
+
+#define ES8328_DACCONTROL29    0x33
+#define ES8328_DACCONTROL30    0x34
+
+#define ES8328_SYSCLK          0
+
+#define ES8328_REG_MAX         0x35
+
+#define ES8328_PLL1            0
+#define ES8328_PLL2            1
+
+/* clock inputs */
+#define ES8328_MCLK            0
+#define ES8328_PCMCLK          1
+
+/* clock divider id's */
+#define ES8328_PCMDIV          0
+#define ES8328_BCLKDIV         1
+#define ES8328_VXCLKDIV                2
+
+/* PCM clock dividers */
+#define ES8328_PCM_DIV_1       (0 << 6)
+#define ES8328_PCM_DIV_3       (2 << 6)
+#define ES8328_PCM_DIV_5_5     (3 << 6)
+#define ES8328_PCM_DIV_2       (4 << 6)
+#define ES8328_PCM_DIV_4       (5 << 6)
+#define ES8328_PCM_DIV_6       (6 << 6)
+#define ES8328_PCM_DIV_8       (7 << 6)
+
+/* BCLK clock dividers */
+#define ES8328_BCLK_DIV_1      (0 << 7)
+#define ES8328_BCLK_DIV_2      (1 << 7)
+#define ES8328_BCLK_DIV_4      (2 << 7)
+#define ES8328_BCLK_DIV_8      (3 << 7)
+
+/* VXCLK clock dividers */
+#define ES8328_VXCLK_DIV_1     (0 << 6)
+#define ES8328_VXCLK_DIV_2     (1 << 6)
+#define ES8328_VXCLK_DIV_4     (2 << 6)
+#define ES8328_VXCLK_DIV_8     (3 << 6)
+#define ES8328_VXCLK_DIV_16    (4 << 6)
+
+#define ES8328_DAI_HIFI                0
+#define ES8328_DAI_VOICE       1
+
+#define ES8328_1536FS          1536
+#define ES8328_1024FS          1024
+#define ES8328_768FS           768
+#define ES8328_512FS           512
+#define ES8328_384FS           384
+#define ES8328_256FS           256
+#define ES8328_128FS           128
+
+#endif
index bcebd1a9ce313bce582284f17009ff82708d82cb..df7c01cf7072c20f282ef2476c6d1da8f459585a 100644 (file)
@@ -293,41 +293,13 @@ static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
        regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1,
                        JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
 
-       jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 }
 
-static int jz4740_codec_dev_remove(struct snd_soc_codec *codec)
-{
-       jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int jz4740_codec_suspend(struct snd_soc_codec *codec)
-{
-       return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
-}
-
-static int jz4740_codec_resume(struct snd_soc_codec *codec)
-{
-       return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-}
-
-#else
-#define jz4740_codec_suspend NULL
-#define jz4740_codec_resume NULL
-#endif
-
 static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
        .probe = jz4740_codec_dev_probe,
-       .remove = jz4740_codec_dev_remove,
-       .suspend = jz4740_codec_suspend,
-       .resume = jz4740_codec_resume,
        .set_bias_level = jz4740_codec_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = jz4740_codec_controls,
        .num_controls = ARRAY_SIZE(jz4740_codec_controls),
index 275b3f72f3f42db71bd94b7ca2b35f146b134d8a..c1ae5764983fa73fd86f67c360d15876c284477c 100644 (file)
@@ -1395,18 +1395,6 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
        },
 };
 
-static int lm49453_suspend(struct snd_soc_codec *codec)
-{
-       lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int lm49453_resume(struct snd_soc_codec *codec)
-{
-       lm49453_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 /* power down chip */
 static int lm49453_remove(struct snd_soc_codec *codec)
 {
@@ -1416,8 +1404,6 @@ static int lm49453_remove(struct snd_soc_codec *codec)
 
 static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
        .remove = lm49453_remove,
-       .suspend = lm49453_suspend,
-       .resume = lm49453_resume,
        .set_bias_level = lm49453_set_bias_level,
        .controls = lm49453_snd_controls,
        .num_controls = ARRAY_SIZE(lm49453_snd_controls),
index 4a063fa88526821b279bd7c49cdbb14cfef994c8..d519294f57c79c289784f9a71bb43d7bf51dc7e9 100644 (file)
@@ -1311,8 +1311,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
        {"MIC1 Input", NULL, "MIC1"},
        {"MIC2 Input", NULL, "MIC2"},
 
-       {"DMICL", NULL, "DMICL_ENA"},
-       {"DMICR", NULL, "DMICR_ENA"},
        {"DMICL", NULL, "AHPF"},
        {"DMICR", NULL, "AHPF"},
 
@@ -1370,6 +1368,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
        {"DMIC Mux", "ADC", "ADCR"},
        {"DMIC Mux", "DMIC", "DMICL"},
        {"DMIC Mux", "DMIC", "DMICR"},
+       {"DMIC Mux", "DMIC", "DMICL_ENA"},
+       {"DMIC Mux", "DMIC", "DMICR_ENA"},
 
        {"LBENL Mux", "Normal", "DMIC Mux"},
        {"LBENL Mux", "Loopback", "LTENL Mux"},
@@ -1972,6 +1972,102 @@ static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
        return 0;
 }
 
+static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (!max98090->master && dai->active == 1)
+                       queue_delayed_work(system_power_efficient_wq,
+                                          &max98090->pll_det_enable_work,
+                                          msecs_to_jiffies(10));
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (!max98090->master && dai->active == 1)
+                       schedule_work(&max98090->pll_det_disable_work);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static void max98090_pll_det_enable_work(struct work_struct *work)
+{
+       struct max98090_priv *max98090 =
+               container_of(work, struct max98090_priv,
+                            pll_det_enable_work.work);
+       struct snd_soc_codec *codec = max98090->codec;
+       unsigned int status, mask;
+
+       /*
+        * Clear status register in order to clear possibly already occurred
+        * PLL unlock. If PLL hasn't still locked, the status will be set
+        * again and PLL unlock interrupt will occur.
+        * Note this will clear all status bits
+        */
+       regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &status);
+
+       /*
+        * Queue jack work in case jack state has just changed but handler
+        * hasn't run yet
+        */
+       regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
+       status &= mask;
+       if (status & M98090_JDET_MASK)
+               queue_delayed_work(system_power_efficient_wq,
+                                  &max98090->jack_work,
+                                  msecs_to_jiffies(100));
+
+       /* Enable PLL unlock interrupt */
+       snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+                           M98090_IULK_MASK,
+                           1 << M98090_IULK_SHIFT);
+}
+
+static void max98090_pll_det_disable_work(struct work_struct *work)
+{
+       struct max98090_priv *max98090 =
+               container_of(work, struct max98090_priv, pll_det_disable_work);
+       struct snd_soc_codec *codec = max98090->codec;
+
+       cancel_delayed_work_sync(&max98090->pll_det_enable_work);
+
+       /* Disable PLL unlock interrupt */
+       snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+                           M98090_IULK_MASK, 0);
+}
+
+static void max98090_pll_work(struct work_struct *work)
+{
+       struct max98090_priv *max98090 =
+               container_of(work, struct max98090_priv, pll_work);
+       struct snd_soc_codec *codec = max98090->codec;
+
+       if (!snd_soc_codec_is_active(codec))
+               return;
+
+       dev_info(codec->dev, "PLL unlocked\n");
+
+       /* Toggle shutdown OFF then ON */
+       snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+                           M98090_SHDNN_MASK, 0);
+       msleep(10);
+       snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+                           M98090_SHDNN_MASK, M98090_SHDNN_MASK);
+
+       /* Give PLL time to lock */
+       msleep(10);
+}
+
 static void max98090_jack_work(struct work_struct *work)
 {
        struct max98090_priv *max98090 = container_of(work,
@@ -2063,12 +2159,16 @@ static void max98090_jack_work(struct work_struct *work)
 
 static irqreturn_t max98090_interrupt(int irq, void *data)
 {
-       struct snd_soc_codec *codec = data;
-       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       struct max98090_priv *max98090 = data;
+       struct snd_soc_codec *codec = max98090->codec;
        int ret;
        unsigned int mask;
        unsigned int active;
 
+       /* Treat interrupt before codec is initialized as spurious */
+       if (codec == NULL)
+               return IRQ_NONE;
+
        dev_dbg(codec->dev, "***** max98090_interrupt *****\n");
 
        ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
@@ -2103,8 +2203,10 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
        if (active & M98090_SLD_MASK)
                dev_dbg(codec->dev, "M98090_SLD_MASK\n");
 
-       if (active & M98090_ULK_MASK)
-               dev_err(codec->dev, "M98090_ULK_MASK\n");
+       if (active & M98090_ULK_MASK) {
+               dev_dbg(codec->dev, "M98090_ULK_MASK\n");
+               schedule_work(&max98090->pll_work);
+       }
 
        if (active & M98090_JDET_MASK) {
                dev_dbg(codec->dev, "M98090_JDET_MASK\n");
@@ -2177,6 +2279,7 @@ static struct snd_soc_dai_ops max98090_dai_ops = {
        .set_tdm_slot = max98090_set_tdm_slot,
        .hw_params = max98090_dai_hw_params,
        .digital_mute = max98090_dai_digital_mute,
+       .trigger = max98090_dai_trigger,
 };
 
 static struct snd_soc_dai_driver max98090_dai[] = {
@@ -2230,7 +2333,6 @@ static int max98090_probe(struct snd_soc_codec *codec)
        max98090->lin_state = 0;
        max98090->pa1en = 0;
        max98090->pa2en = 0;
-       max98090->extmic_mux = 0;
 
        ret = snd_soc_read(codec, M98090_REG_REVISION_ID);
        if (ret < 0) {
@@ -2258,22 +2360,16 @@ static int max98090_probe(struct snd_soc_codec *codec)
        max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
 
        INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work);
+       INIT_DELAYED_WORK(&max98090->pll_det_enable_work,
+                         max98090_pll_det_enable_work);
+       INIT_WORK(&max98090->pll_det_disable_work,
+                 max98090_pll_det_disable_work);
+       INIT_WORK(&max98090->pll_work, max98090_pll_work);
 
        /* Enable jack detection */
        snd_soc_write(codec, M98090_REG_JACK_DETECT,
                M98090_JDETEN_MASK | M98090_JDEB_25MS);
 
-       /* Register for interrupts */
-       dev_dbg(codec->dev, "irq = %d\n", max98090->irq);
-
-       ret = devm_request_threaded_irq(codec->dev, max98090->irq, NULL,
-               max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-               "max98090_interrupt", codec);
-       if (ret < 0) {
-               dev_err(codec->dev, "request_irq failed: %d\n",
-                       ret);
-       }
-
        /*
         * Clear any old interrupts.
         * An old interrupt ocurring prior to installing the ISR
@@ -2310,6 +2406,10 @@ static int max98090_remove(struct snd_soc_codec *codec)
        struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
 
        cancel_delayed_work_sync(&max98090->jack_work);
+       cancel_delayed_work_sync(&max98090->pll_det_enable_work);
+       cancel_work_sync(&max98090->pll_det_disable_work);
+       cancel_work_sync(&max98090->pll_work);
+       max98090->codec = NULL;
 
        return 0;
 }
@@ -2362,7 +2462,6 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
        max98090->devtype = driver_data;
        i2c_set_clientdata(i2c, max98090);
        max98090->pdata = i2c->dev.platform_data;
-       max98090->irq = i2c->irq;
 
        max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
        if (IS_ERR(max98090->regmap)) {
@@ -2371,6 +2470,15 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
                goto err_enable;
        }
 
+       ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+               max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+               "max98090_interrupt", max98090);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "request_irq failed: %d\n",
+                       ret);
+               return ret;
+       }
+
        ret = snd_soc_register_codec(&i2c->dev,
                        &soc_codec_dev_max98090, max98090_dai,
                        ARRAY_SIZE(max98090_dai));
index cf1b6062ba8c6de043fa2ffb46834119e858f936..a5f6bada06daf2d886e0717f5c13c3bcd0cfdb1f 100644 (file)
 #ifndef _MAX98090_H
 #define _MAX98090_H
 
-#include <linux/version.h>
-
-/* One can override the Linux version here with an explicit version number */
-#define M98090_LINUX_VERSION LINUX_VERSION_CODE
-
 /*
  * MAX98090 Register Definitions
  */
 #define M98090_REVID_WIDTH             8
 #define M98090_REVID_NUM               (1<<M98090_REVID_WIDTH)
 
-#define M98090_BYTE1(w) ((w >> 8) & 0xff)
-#define M98090_BYTE0(w) (w & 0xff)
-
 /* Silicon revision number */
 #define M98090_REVA                    0x40
 #define M98091_REVA                    0x50
@@ -1529,9 +1521,11 @@ struct max98090_priv {
        unsigned int bclk;
        unsigned int lrclk;
        struct max98090_cdata dai[1];
-       int irq;
        int jack_state;
        struct delayed_work jack_work;
+       struct delayed_work pll_det_enable_work;
+       struct work_struct pll_det_disable_work;
+       struct work_struct pll_work;
        struct snd_soc_jack *jack;
        unsigned int dai_fmt;
        int tdm_slots;
@@ -1539,7 +1533,6 @@ struct max98090_priv {
        u8 lin_state;
        unsigned int pa1en;
        unsigned int pa2en;
-       unsigned int extmic_mux;
        unsigned int sidetone;
        bool master;
 };
index 388f90a597fa0ea31a608351ee99233bc2d3fc68..71f775aad7c715f18b3ebdd16b385bb5501de5eb 100644 (file)
@@ -765,12 +765,18 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
                        return -ENOSYS;
 
                ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
-               if (ret)
-                       goto out;
+               if (ret) {
+                       of_node_put(np);
+                       return ret;
+               }
 
                ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
-               if (ret)
-                       goto out;
+               if (ret) {
+                       of_node_put(np);
+                       return ret;
+               }
+
+               of_node_put(np);
        }
 
        dev_set_drvdata(&pdev->dev, priv);
@@ -783,8 +789,6 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
                ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
                        mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
 
-out:
-       of_node_put(np);
        return ret;
 }
 
index e661e8420e3dd25b95179c7413adfea8637cd9e2..711f55039522f473a5070cb1454a03132423f5d6 100644 (file)
@@ -565,41 +565,19 @@ static struct snd_soc_dai_driver ml26124_dai = {
        .symmetric_rates = 1,
 };
 
-#ifdef CONFIG_PM
-static int ml26124_suspend(struct snd_soc_codec *codec)
-{
-       ml26124_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int ml26124_resume(struct snd_soc_codec *codec)
-{
-       ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define ml26124_suspend NULL
-#define ml26124_resume NULL
-#endif
-
 static int ml26124_probe(struct snd_soc_codec *codec)
 {
        /* Software Reset */
        snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);
        snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0);
 
-       ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
        .probe =        ml26124_probe,
-       .suspend =      ml26124_suspend,
-       .resume =       ml26124_resume,
        .set_bias_level = ml26124_set_bias_level,
+       .suspend_bias_off = true,
        .dapm_widgets = ml26124_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
        .dapm_routes = ml26124_intercon,
index b86b426f159d136c07cbc4491c8241db0c5a2001..4aa555cbcca81ce60b880d6159b1fb91777e9920 100644 (file)
@@ -269,6 +269,7 @@ static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
        return 0;
 }
 
+#ifdef CONFIG_PM
 static void rt286_index_sync(struct snd_soc_codec *codec)
 {
        struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
@@ -279,6 +280,7 @@ static void rt286_index_sync(struct snd_soc_codec *codec)
                                  rt286->index_cache[i].def);
        }
 }
+#endif
 
 static int rt286_support_power_controls[] = {
        RT286_DAC_OUT1,
index f1ec6e6bd08aced4be7c38d868ccba94a655c5f7..c3f2decd643c9ba7eb57eec8a644091bc440376d 100644 (file)
@@ -1906,6 +1906,32 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
+int rt5640_dmic_enable(struct snd_soc_codec *codec,
+                      bool dmic1_data_pin, bool dmic2_data_pin)
+{
+       struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+       regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+               RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL);
+
+       if (dmic1_data_pin) {
+               regmap_update_bits(rt5640->regmap, RT5640_DMIC,
+                       RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3);
+               regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+                       RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA);
+       }
+
+       if (dmic2_data_pin) {
+               regmap_update_bits(rt5640->regmap, RT5640_DMIC,
+                       RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4);
+               regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+                       RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
+
 static int rt5640_probe(struct snd_soc_codec *codec)
 {
        struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
@@ -1945,6 +1971,10 @@ static int rt5640_probe(struct snd_soc_codec *codec)
                return -ENODEV;
        }
 
+       if (rt5640->pdata.dmic_en)
+               rt5640_dmic_enable(codec, rt5640->pdata.dmic1_data_pin,
+                                         rt5640->pdata.dmic2_data_pin);
+
        return 0;
 }
 
@@ -2195,25 +2225,6 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
                regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
                                        RT5640_IN_DF2, RT5640_IN_DF2);
 
-       if (rt5640->pdata.dmic_en) {
-               regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
-                       RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL);
-
-               if (rt5640->pdata.dmic1_data_pin) {
-                       regmap_update_bits(rt5640->regmap, RT5640_DMIC,
-                               RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3);
-                       regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
-                               RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA);
-               }
-
-               if (rt5640->pdata.dmic2_data_pin) {
-                       regmap_update_bits(rt5640->regmap, RT5640_DMIC,
-                               RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4);
-                       regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
-                               RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA);
-               }
-       }
-
        rt5640->hp_mute = 1;
 
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
index 58ebe96b86dae452165fc679ce424017e9088833..3deb8babeabb691d37a314bb3ab19c84d5c9cd1f 100644 (file)
@@ -2097,4 +2097,7 @@ struct rt5640_priv {
        bool hp_mute;
 };
 
+int rt5640_dmic_enable(struct snd_soc_codec *codec,
+                      bool dmic1_data_pin, bool dmic2_data_pin);
+
 #endif
index a7762d0a623e86705e32570d7e36958df8be638e..3fb83bf09768347f1bcd469d2be9be4b56ea62e2 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/gpio.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -2103,6 +2104,77 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
+static int rt5645_jack_detect(struct snd_soc_codec *codec,
+       struct snd_soc_jack *jack)
+{
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+       int gpio_state, jack_type = 0;
+       unsigned int val;
+
+       gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
+
+       dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio,
+               gpio_state);
+
+       if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) ||
+               (!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) {
+               snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1");
+               snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2");
+               snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
+               snd_soc_dapm_force_enable_pin(&codec->dapm, "Mic Det Power");
+               snd_soc_dapm_sync(&codec->dapm);
+
+               snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006);
+               snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0);
+
+               snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
+                       RT5645_CBJ_MN_JD, 0);
+               snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
+                       RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD);
+
+               msleep(400);
+               val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7;
+               dev_dbg(codec->dev, "val = %d\n", val);
+
+               if (val == 1 || val == 2)
+                       jack_type = SND_JACK_HEADSET;
+               else
+                       jack_type = SND_JACK_HEADPHONE;
+
+               snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
+               snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
+               snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
+               snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
+               snd_soc_dapm_sync(&codec->dapm);
+       }
+
+       snd_soc_jack_report(rt5645->jack, jack_type, SND_JACK_HEADSET);
+
+       return 0;
+}
+
+int rt5645_set_jack_detect(struct snd_soc_codec *codec,
+       struct snd_soc_jack *jack)
+{
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+       rt5645->jack = jack;
+
+       rt5645_jack_detect(codec, rt5645->jack);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
+
+static irqreturn_t rt5645_irq(int irq, void *data)
+{
+       struct rt5645_priv *rt5645 = data;
+
+       rt5645_jack_detect(rt5645->codec, rt5645->jack);
+
+       return IRQ_HANDLED;
+}
+
 static int rt5645_probe(struct snd_soc_codec *codec)
 {
        struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
@@ -2250,6 +2322,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
        if (rt5645 == NULL)
                return -ENOMEM;
 
+       rt5645->i2c = i2c;
        i2c_set_clientdata(i2c, rt5645);
 
        if (pdata)
@@ -2345,12 +2418,38 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 
        }
 
+       if (rt5645->i2c->irq) {
+               ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
+                       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+                       | IRQF_ONESHOT, "rt5645", rt5645);
+               if (ret)
+                       dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+       }
+
+       if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
+               ret = gpio_request(rt5645->pdata.hp_det_gpio, "rt5645");
+               if (ret)
+                       dev_err(&i2c->dev, "Fail gpio_request hp_det_gpio\n");
+
+               ret = gpio_direction_input(rt5645->pdata.hp_det_gpio);
+               if (ret)
+                       dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
+       }
+
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
                                      rt5645_dai, ARRAY_SIZE(rt5645_dai));
 }
 
 static int rt5645_i2c_remove(struct i2c_client *i2c)
 {
+       struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c);
+
+       if (i2c->irq)
+               free_irq(i2c->irq, rt5645);
+
+       if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
+               gpio_free(rt5645->pdata.hp_det_gpio);
+
        snd_soc_unregister_codec(&i2c->dev);
 
        return 0;
index 355b7e9eefab0071b885f410ddd1a822ca6519f4..50c62c5668ea61220fda047ea35b97542630ed0c 100644 (file)
@@ -2166,6 +2166,8 @@ struct rt5645_priv {
        struct snd_soc_codec *codec;
        struct rt5645_platform_data pdata;
        struct regmap *regmap;
+       struct i2c_client *i2c;
+       struct snd_soc_jack *jack;
 
        int sysclk;
        int sysclk_src;
@@ -2178,4 +2180,7 @@ struct rt5645_priv {
        int pll_out;
 };
 
+int rt5645_set_jack_detect(struct snd_soc_codec *codec,
+       struct snd_soc_jack *jack);
+
 #endif /* __RT5645_H__ */
index 5337c448b5e365de204a95e3a945291dd228844b..16aa4d99a71304ea20e1c108a9e90f1c61709e45 100644 (file)
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
+#include <linux/of_gpio.h>
 #include <linux/regmap.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/gpio.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -540,6 +542,7 @@ static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
 static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
 static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
 static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0);
 
 /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
 static unsigned int bst_tlv[] = {
@@ -604,6 +607,10 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
                RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 127, 0,
                adc_vol_tlv),
 
+       /* Sidetone Control */
+       SOC_SINGLE_TLV("Sidetone Volume", RT5677_SIDETONE_CTRL,
+               RT5677_ST_VOL_SFT, 31, 0, st_vol_tlv),
+
        /* ADC Boost Volume Control */
        SOC_DOUBLE_TLV("STO1 ADC Boost Volume", RT5677_STO1_2_ADC_BST,
                RT5677_STO1_ADC_L_BST_SFT, RT5677_STO1_ADC_R_BST_SFT, 3, 0,
@@ -1700,14 +1707,19 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 
        SND_SOC_DAPM_INPUT("Haptic Generator"),
 
-       SND_SOC_DAPM_PGA("DMIC1", RT5677_DMIC_CTRL1, RT5677_DMIC_1_EN_SFT, 0,
-               NULL, 0),
-       SND_SOC_DAPM_PGA("DMIC2", RT5677_DMIC_CTRL1, RT5677_DMIC_2_EN_SFT, 0,
-               NULL, 0),
-       SND_SOC_DAPM_PGA("DMIC3", RT5677_DMIC_CTRL1, RT5677_DMIC_3_EN_SFT, 0,
-               NULL, 0),
-       SND_SOC_DAPM_PGA("DMIC4", RT5677_DMIC_CTRL2, RT5677_DMIC_4_EN_SFT, 0,
-               NULL, 0),
+       SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DMIC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DMIC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("DMIC1 power", RT5677_DMIC_CTRL1,
+               RT5677_DMIC_1_EN_SFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMIC2 power", RT5677_DMIC_CTRL1,
+               RT5677_DMIC_2_EN_SFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMIC3 power", RT5677_DMIC_CTRL1,
+               RT5677_DMIC_3_EN_SFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMIC4 power", RT5677_DMIC_CTRL2,
+               RT5677_DMIC_4_EN_SFT, 0, NULL, 0),
 
        SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
                set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
@@ -1987,6 +1999,9 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        /* Sidetone Mux */
        SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_sidetone_mux),
+       SND_SOC_DAPM_SUPPLY("Sidetone Power", RT5677_SIDETONE_CTRL,
+               RT5677_ST_EN_SFT, 0, NULL, 0),
+
        /* VAD Mux*/
        SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_vad_src_mux),
@@ -2130,6 +2145,13 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "DMIC L4", NULL, "DMIC CLK" },
        { "DMIC R4", NULL, "DMIC CLK" },
 
+       { "DMIC L1", NULL, "DMIC1 power" },
+       { "DMIC R1", NULL, "DMIC1 power" },
+       { "DMIC L3", NULL, "DMIC3 power" },
+       { "DMIC R3", NULL, "DMIC3 power" },
+       { "DMIC L4", NULL, "DMIC4 power" },
+       { "DMIC R4", NULL, "DMIC4 power" },
+
        { "BST1", NULL, "IN1P" },
        { "BST1", NULL, "IN1N" },
        { "BST2", NULL, "IN2P" },
@@ -2691,6 +2713,7 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "Sidetone Mux", "DMIC4 L", "DMIC L4" },
        { "Sidetone Mux", "ADC1", "ADC 1" },
        { "Sidetone Mux", "ADC2", "ADC 2" },
+       { "Sidetone Mux", NULL, "Sidetone Power" },
 
        { "Stereo DAC MIXL", "ST L Switch", "Sidetone Mux" },
        { "Stereo DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" },
@@ -2793,6 +2816,16 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "PDM2R", NULL, "PDM2 R Mux" },
 };
 
+static const struct snd_soc_dapm_route rt5677_dmic2_clk_1[] = {
+       { "DMIC L2", NULL, "DMIC1 power" },
+       { "DMIC R2", NULL, "DMIC1 power" },
+};
+
+static const struct snd_soc_dapm_route rt5677_dmic2_clk_2[] = {
+       { "DMIC L2", NULL, "DMIC2 power" },
+       { "DMIC R2", NULL, "DMIC2 power" },
+};
+
 static int rt5677_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
@@ -3084,6 +3117,59 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
        return 0;
 }
 
+static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+                       unsigned int rx_mask, int slots, int slot_width)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       unsigned int val = 0;
+
+       if (rx_mask || tx_mask)
+               val |= (1 << 12);
+
+       switch (slots) {
+       case 4:
+               val |= (1 << 10);
+               break;
+       case 6:
+               val |= (2 << 10);
+               break;
+       case 8:
+               val |= (3 << 10);
+               break;
+       case 2:
+       default:
+               break;
+       }
+
+       switch (slot_width) {
+       case 20:
+               val |= (1 << 8);
+               break;
+       case 24:
+               val |= (2 << 8);
+               break;
+       case 32:
+               val |= (3 << 8);
+               break;
+       case 16:
+       default:
+               break;
+       }
+
+       switch (dai->id) {
+       case RT5677_AIF1:
+               snd_soc_update_bits(codec, RT5677_TDM1_CTRL1, 0x1f00, val);
+               break;
+       case RT5677_AIF2:
+               snd_soc_update_bits(codec, RT5677_TDM2_CTRL1, 0x1f00, val);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 static int rt5677_set_bias_level(struct snd_soc_codec *codec,
                        enum snd_soc_bias_level level)
 {
@@ -3138,12 +3224,148 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
+#ifdef CONFIG_GPIOLIB
+static inline struct rt5677_priv *gpio_to_rt5677(struct gpio_chip *chip)
+{
+       return container_of(chip, struct rt5677_priv, gpio_chip);
+}
+
+static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+
+       switch (offset) {
+       case RT5677_GPIO1 ... RT5677_GPIO5:
+               regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
+                       0x1 << (offset * 3 + 1), !!value << (offset * 3 + 1));
+               break;
+
+       case RT5677_GPIO6:
+               regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
+                       RT5677_GPIO6_OUT_MASK, !!value << RT5677_GPIO6_OUT_SFT);
+               break;
+
+       default:
+               break;
+       }
+}
+
+static int rt5677_gpio_direction_out(struct gpio_chip *chip,
+                                    unsigned offset, int value)
+{
+       struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+
+       switch (offset) {
+       case RT5677_GPIO1 ... RT5677_GPIO5:
+               regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
+                       0x3 << (offset * 3 + 1),
+                       (0x2 | !!value) << (offset * 3 + 1));
+               break;
+
+       case RT5677_GPIO6:
+               regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
+                       RT5677_GPIO6_DIR_MASK | RT5677_GPIO6_OUT_MASK,
+                       RT5677_GPIO6_DIR_OUT | !!value << RT5677_GPIO6_OUT_SFT);
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+       int value, ret;
+
+       ret = regmap_read(rt5677->regmap, RT5677_GPIO_ST, &value);
+       if (ret < 0)
+               return ret;
+
+       return (value & (0x1 << offset)) >> offset;
+}
+
+static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+       struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+
+       switch (offset) {
+       case RT5677_GPIO1 ... RT5677_GPIO5:
+               regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
+                       0x1 << (offset * 3 + 2), 0x0);
+               break;
+
+       case RT5677_GPIO6:
+               regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
+                       RT5677_GPIO6_DIR_MASK, RT5677_GPIO6_DIR_IN);
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static struct gpio_chip rt5677_template_chip = {
+       .label                  = "rt5677",
+       .owner                  = THIS_MODULE,
+       .direction_output       = rt5677_gpio_direction_out,
+       .set                    = rt5677_gpio_set,
+       .direction_input        = rt5677_gpio_direction_in,
+       .get                    = rt5677_gpio_get,
+       .can_sleep              = 1,
+};
+
+static void rt5677_init_gpio(struct i2c_client *i2c)
+{
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+       int ret;
+
+       rt5677->gpio_chip = rt5677_template_chip;
+       rt5677->gpio_chip.ngpio = RT5677_GPIO_NUM;
+       rt5677->gpio_chip.dev = &i2c->dev;
+       rt5677->gpio_chip.base = -1;
+
+       ret = gpiochip_add(&rt5677->gpio_chip);
+       if (ret != 0)
+               dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void rt5677_free_gpio(struct i2c_client *i2c)
+{
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+       gpiochip_remove(&rt5677->gpio_chip);
+}
+#else
+static void rt5677_init_gpio(struct i2c_client *i2c)
+{
+}
+
+static void rt5677_free_gpio(struct i2c_client *i2c)
+{
+}
+#endif
+
 static int rt5677_probe(struct snd_soc_codec *codec)
 {
        struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
 
        rt5677->codec = codec;
 
+       if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
+               snd_soc_dapm_add_routes(&codec->dapm,
+                       rt5677_dmic2_clk_2,
+                       ARRAY_SIZE(rt5677_dmic2_clk_2));
+       } else { /*use dmic1 clock by default*/
+               snd_soc_dapm_add_routes(&codec->dapm,
+                       rt5677_dmic2_clk_1,
+                       ARRAY_SIZE(rt5677_dmic2_clk_1));
+       }
+
        rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
@@ -3157,6 +3379,8 @@ static int rt5677_remove(struct snd_soc_codec *codec)
        struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
 
        regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+       if (gpio_is_valid(rt5677->pow_ldo2))
+               gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
 
        return 0;
 }
@@ -3168,6 +3392,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
 
        regcache_cache_only(rt5677->regmap, true);
        regcache_mark_dirty(rt5677->regmap);
+       if (gpio_is_valid(rt5677->pow_ldo2))
+               gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
 
        return 0;
 }
@@ -3176,6 +3402,10 @@ static int rt5677_resume(struct snd_soc_codec *codec)
 {
        struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
 
+       if (gpio_is_valid(rt5677->pow_ldo2)) {
+               gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
+               msleep(10);
+       }
        regcache_cache_only(rt5677->regmap, false);
        regcache_sync(rt5677->regmap);
 
@@ -3195,6 +3425,7 @@ static struct snd_soc_dai_ops rt5677_aif_dai_ops = {
        .set_fmt = rt5677_set_dai_fmt,
        .set_sysclk = rt5677_set_dai_sysclk,
        .set_pll = rt5677_set_dai_pll,
+       .set_tdm_slot = rt5677_set_tdm_slot,
 };
 
 static struct snd_soc_dai_driver rt5677_dai[] = {
@@ -3333,6 +3564,35 @@ static const struct i2c_device_id rt5677_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
 
+static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
+{
+       rt5677->pdata.in1_diff = of_property_read_bool(np,
+                                       "realtek,in1-differential");
+       rt5677->pdata.in2_diff = of_property_read_bool(np,
+                                       "realtek,in2-differential");
+       rt5677->pdata.lout1_diff = of_property_read_bool(np,
+                                       "realtek,lout1-differential");
+       rt5677->pdata.lout2_diff = of_property_read_bool(np,
+                                       "realtek,lout2-differential");
+       rt5677->pdata.lout3_diff = of_property_read_bool(np,
+                                       "realtek,lout3-differential");
+
+       rt5677->pow_ldo2 = of_get_named_gpio(np,
+                                       "realtek,pow-ldo2-gpio", 0);
+
+       /*
+        * POW_LDO2 is optional (it may be statically tied on the board).
+        * -ENOENT means that the property doesn't exist, i.e. there is no
+        * GPIO, so is not an error. Any other error code means the property
+        * exists, but could not be parsed.
+        */
+       if (!gpio_is_valid(rt5677->pow_ldo2) &&
+                       (rt5677->pow_ldo2 != -ENOENT))
+               return rt5677->pow_ldo2;
+
+       return 0;
+}
+
 static int rt5677_i2c_probe(struct i2c_client *i2c,
                    const struct i2c_device_id *id)
 {
@@ -3351,6 +3611,33 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
        if (pdata)
                rt5677->pdata = *pdata;
 
+       if (i2c->dev.of_node) {
+               ret = rt5677_parse_dt(rt5677, i2c->dev.of_node);
+               if (ret) {
+                       dev_err(&i2c->dev, "Failed to parse device tree: %d\n",
+                               ret);
+                       return ret;
+               }
+       } else {
+               rt5677->pow_ldo2 = -EINVAL;
+       }
+
+       if (gpio_is_valid(rt5677->pow_ldo2)) {
+               ret = devm_gpio_request_one(&i2c->dev, rt5677->pow_ldo2,
+                                           GPIOF_OUT_INIT_HIGH,
+                                           "RT5677 POW_LDO2");
+               if (ret < 0) {
+                       dev_err(&i2c->dev, "Failed to request POW_LDO2 %d: %d\n",
+                               rt5677->pow_ldo2, ret);
+                       return ret;
+               }
+               /* Wait a while until I2C bus becomes available. The datasheet
+                * does not specify the exact we should wait but startup
+                * sequence mentiones at least a few milliseconds.
+                */
+               msleep(10);
+       }
+
        rt5677->regmap = devm_regmap_init_i2c(i2c, &rt5677_regmap);
        if (IS_ERR(rt5677->regmap)) {
                ret = PTR_ERR(rt5677->regmap);
@@ -3381,6 +3668,29 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
                regmap_update_bits(rt5677->regmap, RT5677_IN1,
                                        RT5677_IN_DF2, RT5677_IN_DF2);
 
+       if (rt5677->pdata.lout1_diff)
+               regmap_update_bits(rt5677->regmap, RT5677_LOUT1,
+                                       RT5677_LOUT1_L_DF, RT5677_LOUT1_L_DF);
+
+       if (rt5677->pdata.lout2_diff)
+               regmap_update_bits(rt5677->regmap, RT5677_LOUT1,
+                                       RT5677_LOUT2_L_DF, RT5677_LOUT2_L_DF);
+
+       if (rt5677->pdata.lout3_diff)
+               regmap_update_bits(rt5677->regmap, RT5677_LOUT1,
+                                       RT5677_LOUT3_L_DF, RT5677_LOUT3_L_DF);
+
+       if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
+               regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2,
+                                       RT5677_GPIO5_FUNC_MASK,
+                                       RT5677_GPIO5_FUNC_DMIC);
+               regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
+                                       RT5677_GPIO5_DIR_MASK,
+                                       RT5677_GPIO5_DIR_OUT);
+       }
+
+       rt5677_init_gpio(i2c);
+
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
                                      rt5677_dai, ARRAY_SIZE(rt5677_dai));
 }
@@ -3388,6 +3698,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
 static int rt5677_i2c_remove(struct i2c_client *i2c)
 {
        snd_soc_unregister_codec(&i2c->dev);
+       rt5677_free_gpio(i2c);
 
        return 0;
 }
index 863393e620960c7a621b6b431741cb7a1a50ecbf..d4eb6d5e6746f949c2a601613e85595c34f01c20 100644 (file)
 #define RT5677_ST_SEL_SFT                      9
 #define RT5677_ST_EN                           (0x1 << 6)
 #define RT5677_ST_EN_SFT                       6
+#define RT5677_ST_GAIN                         (0x1 << 5)
+#define RT5677_ST_GAIN_SFT                     5
+#define RT5677_ST_VOL_MASK                     (0x1f << 0)
+#define RT5677_ST_VOL_SFT                      0
 
 /* Analog DAC1/2/3 Source Control (0x15) */
 #define RT5677_ANA_DAC3_SRC_SEL_MASK           (0x3 << 4)
 #define RT5677_PLL1_PD_SFT                     8
 #define RT5677_PLL1_PD_1                       (0x0 << 8)
 #define RT5677_PLL1_PD_2                       (0x1 << 8)
-#define RT5671_DAC_OSR_MASK                    (0x3 << 6)
-#define RT5671_DAC_OSR_SFT                     6
-#define RT5671_DAC_OSR_128                     (0x0 << 6)
-#define RT5671_DAC_OSR_64                      (0x1 << 6)
-#define RT5671_DAC_OSR_32                      (0x2 << 6)
-#define RT5671_ADC_OSR_MASK                    (0x3 << 4)
-#define RT5671_ADC_OSR_SFT                     4
-#define RT5671_ADC_OSR_128                     (0x0 << 4)
-#define RT5671_ADC_OSR_64                      (0x1 << 4)
-#define RT5671_ADC_OSR_32                      (0x2 << 4)
+#define RT5677_DAC_OSR_MASK                    (0x3 << 6)
+#define RT5677_DAC_OSR_SFT                     6
+#define RT5677_DAC_OSR_128                     (0x0 << 6)
+#define RT5677_DAC_OSR_64                      (0x1 << 6)
+#define RT5677_DAC_OSR_32                      (0x2 << 6)
+#define RT5677_ADC_OSR_MASK                    (0x3 << 4)
+#define RT5677_ADC_OSR_SFT                     4
+#define RT5677_ADC_OSR_128                     (0x0 << 4)
+#define RT5677_ADC_OSR_64                      (0x1 << 4)
+#define RT5677_ADC_OSR_32                      (0x2 << 4)
 
 /* Global Clock Control 2 (0x81) */
 #define RT5677_PLL2_PR_SRC_MASK                        (0x1 << 15)
 #define RT5677_PLL2_SRC_BCLK4                  (0x4 << 12)
 #define RT5677_PLL2_SRC_RCCLK                  (0x5 << 12)
 #define RT5677_PLL2_SRC_SLIM                   (0x6 << 12)
-#define RT5671_DSP_ASRC_O_SRC                  (0x3 << 10)
-#define RT5671_DSP_ASRC_O_SRC_SFT              10
-#define RT5671_DSP_ASRC_O_MCLK                 (0x0 << 10)
-#define RT5671_DSP_ASRC_O_PLL1                 (0x1 << 10)
-#define RT5671_DSP_ASRC_O_SLIM                 (0x2 << 10)
-#define RT5671_DSP_ASRC_O_RCCLK                        (0x3 << 10)
-#define RT5671_DSP_ASRC_I_SRC                  (0x3 << 8)
-#define RT5671_DSP_ASRC_I_SRC_SFT              8
-#define RT5671_DSP_ASRC_I_MCLK                 (0x0 << 8)
-#define RT5671_DSP_ASRC_I_PLL1                 (0x1 << 8)
-#define RT5671_DSP_ASRC_I_SLIM                 (0x2 << 8)
-#define RT5671_DSP_ASRC_I_RCCLK                        (0x3 << 8)
+#define RT5677_DSP_ASRC_O_SRC                  (0x3 << 10)
+#define RT5677_DSP_ASRC_O_SRC_SFT              10
+#define RT5677_DSP_ASRC_O_MCLK                 (0x0 << 10)
+#define RT5677_DSP_ASRC_O_PLL1                 (0x1 << 10)
+#define RT5677_DSP_ASRC_O_SLIM                 (0x2 << 10)
+#define RT5677_DSP_ASRC_O_RCCLK                        (0x3 << 10)
+#define RT5677_DSP_ASRC_I_SRC                  (0x3 << 8)
+#define RT5677_DSP_ASRC_I_SRC_SFT              8
+#define RT5677_DSP_ASRC_I_MCLK                 (0x0 << 8)
+#define RT5677_DSP_ASRC_I_PLL1                 (0x1 << 8)
+#define RT5677_DSP_ASRC_I_SLIM                 (0x2 << 8)
+#define RT5677_DSP_ASRC_I_RCCLK                        (0x3 << 8)
 #define RT5677_DSP_CLK_SRC_MASK                        (0x1 << 7)
 #define RT5677_DSP_CLK_SRC_SFT                 7
 #define RT5677_DSP_CLK_SRC_PLL2                        (0x0 << 7)
 #define RT5677_SEL_SRC_IB01                    (0x1 << 0)
 #define RT5677_SEL_SRC_IB01_SFT                        0
 
+/* GPIO status (0xbf) */
+#define RT5677_GPIO6_STATUS_MASK               (0x1 << 5)
+#define RT5677_GPIO6_STATUS_SFT                        5
+#define RT5677_GPIO5_STATUS_MASK               (0x1 << 4)
+#define RT5677_GPIO5_STATUS_SFT                        4
+#define RT5677_GPIO4_STATUS_MASK               (0x1 << 3)
+#define RT5677_GPIO4_STATUS_SFT                        3
+#define RT5677_GPIO3_STATUS_MASK               (0x1 << 2)
+#define RT5677_GPIO3_STATUS_SFT                        2
+#define RT5677_GPIO2_STATUS_MASK               (0x1 << 1)
+#define RT5677_GPIO2_STATUS_SFT                        1
+#define RT5677_GPIO1_STATUS_MASK               (0x1 << 0)
+#define RT5677_GPIO1_STATUS_SFT                        0
+
+/* GPIO Control 1 (0xc0) */
+#define RT5677_GPIO1_PIN_MASK                  (0x1 << 15)
+#define RT5677_GPIO1_PIN_SFT                   15
+#define RT5677_GPIO1_PIN_GPIO1                 (0x0 << 15)
+#define RT5677_GPIO1_PIN_IRQ                   (0x1 << 15)
+#define RT5677_IPTV_MODE_MASK                  (0x1 << 14)
+#define RT5677_IPTV_MODE_SFT                   14
+#define RT5677_IPTV_MODE_GPIO                  (0x0 << 14)
+#define RT5677_IPTV_MODE_IPTV                  (0x1 << 14)
+#define RT5677_FUNC_MODE_MASK                  (0x1 << 13)
+#define RT5677_FUNC_MODE_SFT                   13
+#define RT5677_FUNC_MODE_DMIC_GPIO             (0x0 << 13)
+#define RT5677_FUNC_MODE_JTAG                  (0x1 << 13)
+
+/* GPIO Control 2 (0xc1) */
+#define RT5677_GPIO5_DIR_MASK                  (0x1 << 14)
+#define RT5677_GPIO5_DIR_SFT                   14
+#define RT5677_GPIO5_DIR_IN                    (0x0 << 14)
+#define RT5677_GPIO5_DIR_OUT                   (0x1 << 14)
+#define RT5677_GPIO5_OUT_MASK                  (0x1 << 13)
+#define RT5677_GPIO5_OUT_SFT                   13
+#define RT5677_GPIO5_OUT_LO                    (0x0 << 13)
+#define RT5677_GPIO5_OUT_HI                    (0x1 << 13)
+#define RT5677_GPIO5_P_MASK                    (0x1 << 12)
+#define RT5677_GPIO5_P_SFT                     12
+#define RT5677_GPIO5_P_NOR                     (0x0 << 12)
+#define RT5677_GPIO5_P_INV                     (0x1 << 12)
+#define RT5677_GPIO4_DIR_MASK                  (0x1 << 11)
+#define RT5677_GPIO4_DIR_SFT                   11
+#define RT5677_GPIO4_DIR_IN                    (0x0 << 11)
+#define RT5677_GPIO4_DIR_OUT                   (0x1 << 11)
+#define RT5677_GPIO4_OUT_MASK                  (0x1 << 10)
+#define RT5677_GPIO4_OUT_SFT                   10
+#define RT5677_GPIO4_OUT_LO                    (0x0 << 10)
+#define RT5677_GPIO4_OUT_HI                    (0x1 << 10)
+#define RT5677_GPIO4_P_MASK                    (0x1 << 9)
+#define RT5677_GPIO4_P_SFT                     9
+#define RT5677_GPIO4_P_NOR                     (0x0 << 9)
+#define RT5677_GPIO4_P_INV                     (0x1 << 9)
+#define RT5677_GPIO3_DIR_MASK                  (0x1 << 8)
+#define RT5677_GPIO3_DIR_SFT                   8
+#define RT5677_GPIO3_DIR_IN                    (0x0 << 8)
+#define RT5677_GPIO3_DIR_OUT                   (0x1 << 8)
+#define RT5677_GPIO3_OUT_MASK                  (0x1 << 7)
+#define RT5677_GPIO3_OUT_SFT                   7
+#define RT5677_GPIO3_OUT_LO                    (0x0 << 7)
+#define RT5677_GPIO3_OUT_HI                    (0x1 << 7)
+#define RT5677_GPIO3_P_MASK                    (0x1 << 6)
+#define RT5677_GPIO3_P_SFT                     6
+#define RT5677_GPIO3_P_NOR                     (0x0 << 6)
+#define RT5677_GPIO3_P_INV                     (0x1 << 6)
+#define RT5677_GPIO2_DIR_MASK                  (0x1 << 5)
+#define RT5677_GPIO2_DIR_SFT                   5
+#define RT5677_GPIO2_DIR_IN                    (0x0 << 5)
+#define RT5677_GPIO2_DIR_OUT                   (0x1 << 5)
+#define RT5677_GPIO2_OUT_MASK                  (0x1 << 4)
+#define RT5677_GPIO2_OUT_SFT                   4
+#define RT5677_GPIO2_OUT_LO                    (0x0 << 4)
+#define RT5677_GPIO2_OUT_HI                    (0x1 << 4)
+#define RT5677_GPIO2_P_MASK                    (0x1 << 3)
+#define RT5677_GPIO2_P_SFT                     3
+#define RT5677_GPIO2_P_NOR                     (0x0 << 3)
+#define RT5677_GPIO2_P_INV                     (0x1 << 3)
+#define RT5677_GPIO1_DIR_MASK                  (0x1 << 2)
+#define RT5677_GPIO1_DIR_SFT                   2
+#define RT5677_GPIO1_DIR_IN                    (0x0 << 2)
+#define RT5677_GPIO1_DIR_OUT                   (0x1 << 2)
+#define RT5677_GPIO1_OUT_MASK                  (0x1 << 1)
+#define RT5677_GPIO1_OUT_SFT                   1
+#define RT5677_GPIO1_OUT_LO                    (0x0 << 1)
+#define RT5677_GPIO1_OUT_HI                    (0x1 << 1)
+#define RT5677_GPIO1_P_MASK                    (0x1 << 0)
+#define RT5677_GPIO1_P_SFT                     0
+#define RT5677_GPIO1_P_NOR                     (0x0 << 0)
+#define RT5677_GPIO1_P_INV                     (0x1 << 0)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5677_GPIO6_DIR_MASK                  (0x1 << 2)
+#define RT5677_GPIO6_DIR_SFT                   2
+#define RT5677_GPIO6_DIR_IN                    (0x0 << 2)
+#define RT5677_GPIO6_DIR_OUT                   (0x1 << 2)
+#define RT5677_GPIO6_OUT_MASK                  (0x1 << 1)
+#define RT5677_GPIO6_OUT_SFT                   1
+#define RT5677_GPIO6_OUT_LO                    (0x0 << 1)
+#define RT5677_GPIO6_OUT_HI                    (0x1 << 1)
+#define RT5677_GPIO6_P_MASK                    (0x1 << 0)
+#define RT5677_GPIO6_P_SFT                     0
+#define RT5677_GPIO6_P_NOR                     (0x0 << 0)
+#define RT5677_GPIO6_P_INV                     (0x1 << 0)
+
 /* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */
 #define RT5677_DSP_IB_01_H                     (0x1 << 15)
 #define RT5677_DSP_IB_01_H_SFT                 15
 #define RT5677_DSP_IB_9_L                      (0x1 << 1)
 #define RT5677_DSP_IB_9_L_SFT                  1
 
+/* General Control2 (0xfc)*/
+#define RT5677_GPIO5_FUNC_MASK                 (0x1 << 9)
+#define RT5677_GPIO5_FUNC_GPIO                 (0x0 << 9)
+#define RT5677_GPIO5_FUNC_DMIC                 (0x1 << 9)
+
 /* System Clock Source */
 enum {
        RT5677_SCLK_S_MCLK,
@@ -1418,6 +1531,16 @@ enum {
        RT5677_AIFS,
 };
 
+enum {
+       RT5677_GPIO1,
+       RT5677_GPIO2,
+       RT5677_GPIO3,
+       RT5677_GPIO4,
+       RT5677_GPIO5,
+       RT5677_GPIO6,
+       RT5677_GPIO_NUM,
+};
+
 struct rt5677_priv {
        struct snd_soc_codec *codec;
        struct rt5677_platform_data pdata;
@@ -1431,6 +1554,10 @@ struct rt5677_priv {
        int pll_src;
        int pll_in;
        int pll_out;
+       int pow_ldo2; /* POW_LDO2 pin */
+#ifdef CONFIG_GPIOLIB
+       struct gpio_chip gpio_chip;
+#endif
 };
 
 #endif /* __RT5677_H__ */
index e997d271728d4943c1ffa63d59f7930ef2b59e65..6bb77d76561b8964303275955d1e1beb579ba859 100644 (file)
@@ -626,6 +626,9 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
                } else {
                        dev_err(codec->dev,
                                "PLL not supported in slave mode\n");
+                       dev_err(codec->dev, "%d ratio is not supported. "
+                               "SYS_MCLK needs to be 256, 384 or 512 * fs\n",
+                               sgtl5000->sysclk / sys_fs);
                        return -EINVAL;
                }
        }
@@ -1073,26 +1076,6 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg)
        }
 }
 
-#ifdef CONFIG_SUSPEND
-static int sgtl5000_suspend(struct snd_soc_codec *codec)
-{
-       sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int sgtl5000_resume(struct snd_soc_codec *codec)
-{
-       /* Bring the codec back up to standby to enable regulators */
-       sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define sgtl5000_suspend NULL
-#define sgtl5000_resume  NULL
-#endif /* CONFIG_SUSPEND */
-
 /*
  * sgtl5000 has 3 internal power supplies:
  * 1. VAG, normally set to vdda/2
@@ -1352,11 +1335,6 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
         */
        snd_soc_write(codec, SGTL5000_DAP_CTRL, 0);
 
-       /* leading to standby state */
-       ret = sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       if (ret)
-               goto err;
-
        return 0;
 
 err:
@@ -1373,8 +1351,6 @@ static int sgtl5000_remove(struct snd_soc_codec *codec)
 {
        struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
 
-       sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
                                                sgtl5000->supplies);
        regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
@@ -1387,9 +1363,8 @@ static int sgtl5000_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver sgtl5000_driver = {
        .probe = sgtl5000_probe,
        .remove = sgtl5000_remove,
-       .suspend = sgtl5000_suspend,
-       .resume = sgtl5000_resume,
        .set_bias_level = sgtl5000_set_bias_level,
+       .suspend_bias_off = true,
        .controls = sgtl5000_snd_controls,
        .num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
        .dapm_widgets = sgtl5000_dapm_widgets,
@@ -1442,6 +1417,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
 {
        struct sgtl5000_priv *sgtl5000;
        int ret, reg, rev;
+       unsigned int mclk;
 
        sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv),
                                                                GFP_KERNEL);
@@ -1465,6 +1441,14 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
                return ret;
        }
 
+       /* SGTL5000 SYS_MCLK should be between 8 and 27 MHz */
+       mclk = clk_get_rate(sgtl5000->mclk);
+       if (mclk < 8000000 || mclk > 27000000) {
+               dev_err(&client->dev, "Invalid SYS_CLK frequency: %u.%03uMHz\n",
+                       mclk / 1000000, mclk / 1000 % 1000);
+               return -EINVAL;
+       }
+
        ret = clk_prepare_enable(sgtl5000->mclk);
        if (ret)
                return ret;
index e8680bea5f8634ed9f2643f4d830bfe8d8b27e69..67ea55adb307d373dd4f3d91291b930b35d68a7a 100644 (file)
@@ -646,17 +646,6 @@ static struct snd_soc_dai_driver ssm2518_dai = {
        .ops = &ssm2518_dai_ops,
 };
 
-static int ssm2518_probe(struct snd_soc_codec *codec)
-{
-       return ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
-}
-
-static int ssm2518_remove(struct snd_soc_codec *codec)
-{
-       ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
        int source, unsigned int freq, int dir)
 {
@@ -727,8 +716,6 @@ static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
 }
 
 static struct snd_soc_codec_driver ssm2518_codec_driver = {
-       .probe = ssm2518_probe,
-       .remove = ssm2518_remove,
        .set_bias_level = ssm2518_set_bias_level,
        .set_sysclk = ssm2518_set_sysclk,
        .idle_bias_off = true,
index abd63d5371733d36cf3e31dfe6ed4a2e79692bfc..0d9779d6bfda7fced8b472d283a04ad9d91c7f75 100644 (file)
@@ -41,10 +41,19 @@ static const struct i2c_device_id ssm2602_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
 
+static const struct of_device_id ssm2602_of_match[] = {
+       { .compatible = "adi,ssm2602", },
+       { .compatible = "adi,ssm2603", },
+       { .compatible = "adi,ssm2604", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ssm2602_of_match);
+
 static struct i2c_driver ssm2602_i2c_driver = {
        .driver = {
                .name = "ssm2602",
                .owner = THIS_MODULE,
+               .of_match_table = ssm2602_of_match,
        },
        .probe = ssm2602_i2c_probe,
        .remove = ssm2602_i2c_remove,
index 2bf55e24a7bbfce316cc90a3431beca554bd7b8a..b5df14fbe3adabd9f0604b0af536caa4df6182e6 100644 (file)
@@ -26,10 +26,17 @@ static int ssm2602_spi_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct of_device_id ssm2602_of_match[] = {
+       { .compatible = "adi,ssm2602", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ssm2602_of_match);
+
 static struct spi_driver ssm2602_spi_driver = {
        .driver = {
                .name   = "ssm2602",
                .owner  = THIS_MODULE,
+               .of_match_table = ssm2602_of_match,
        },
        .probe          = ssm2602_spi_probe,
        .remove         = ssm2602_spi_remove,
index 4021cd4357409e9ee95080ae9e61bb5752774272..314eaece1b7d6ac50bfb2d8af18ef1247bd3194c 100644 (file)
@@ -192,7 +192,7 @@ static const struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
 };
 
 static const unsigned int ssm2602_rates_11289600[] = {
-       8000, 44100, 88200,
+       8000, 11025, 22050, 44100, 88200,
 };
 
 static const struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = {
@@ -237,6 +237,16 @@ static const struct ssm2602_coeff ssm2602_coeff_table[] = {
        {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
        {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
 
+       /* 11.025k */
+       {11289600, 11025, SSM2602_COEFF_SRATE(0xc, 0x0, 0x0)},
+       {16934400, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x0)},
+       {12000000, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x1)},
+
+       /* 22.05k */
+       {11289600, 22050, SSM2602_COEFF_SRATE(0xd, 0x0, 0x0)},
+       {16934400, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x0)},
+       {12000000, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x1)},
+
        /* 44.1k */
        {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
        {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
@@ -467,7 +477,8 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
                SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
                SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
                SNDRV_PCM_RATE_96000)
@@ -502,18 +513,11 @@ static struct snd_soc_dai_driver ssm2602_dai = {
        .symmetric_samplebits = 1,
 };
 
-static int ssm2602_suspend(struct snd_soc_codec *codec)
-{
-       ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int ssm2602_resume(struct snd_soc_codec *codec)
 {
        struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
 
        regcache_sync(ssm2602->regmap);
-       ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
        return 0;
 }
@@ -586,27 +590,14 @@ static int ssm260x_codec_probe(struct snd_soc_codec *codec)
                break;
        }
 
-       if (ret)
-               return ret;
-
-       ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-/* remove everything here */
-static int ssm2602_remove(struct snd_soc_codec *codec)
-{
-       ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
+       return ret;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
        .probe =        ssm260x_codec_probe,
-       .remove =       ssm2602_remove,
-       .suspend =      ssm2602_suspend,
        .resume =       ssm2602_resume,
        .set_bias_level = ssm2602_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = ssm260x_snd_controls,
        .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
new file mode 100644 (file)
index 0000000..4b5c17f
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * SSM4567 amplifier audio driver
+ *
+ * Copyright 2014 Google Chromium project.
+ *  Author: Anatol Pomozov <anatol@chromium.org>
+ *
+ * Based on code copyright/by:
+ *   Copyright 2013 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#define SSM4567_REG_POWER_CTRL         0x00
+#define SSM4567_REG_AMP_SNS_CTRL               0x01
+#define SSM4567_REG_DAC_CTRL           0x02
+#define SSM4567_REG_DAC_VOLUME         0x03
+#define SSM4567_REG_SAI_CTRL_1         0x04
+#define SSM4567_REG_SAI_CTRL_2         0x05
+#define SSM4567_REG_SAI_PLACEMENT_1            0x06
+#define SSM4567_REG_SAI_PLACEMENT_2            0x07
+#define SSM4567_REG_SAI_PLACEMENT_3            0x08
+#define SSM4567_REG_SAI_PLACEMENT_4            0x09
+#define SSM4567_REG_SAI_PLACEMENT_5            0x0a
+#define SSM4567_REG_SAI_PLACEMENT_6            0x0b
+#define SSM4567_REG_BATTERY_V_OUT              0x0c
+#define SSM4567_REG_LIMITER_CTRL_1             0x0d
+#define SSM4567_REG_LIMITER_CTRL_2             0x0e
+#define SSM4567_REG_LIMITER_CTRL_3             0x0f
+#define SSM4567_REG_STATUS_1           0x10
+#define SSM4567_REG_STATUS_2           0x11
+#define SSM4567_REG_FAULT_CTRL         0x12
+#define SSM4567_REG_PDM_CTRL           0x13
+#define SSM4567_REG_MCLK_RATIO         0x14
+#define SSM4567_REG_BOOST_CTRL_1               0x15
+#define SSM4567_REG_BOOST_CTRL_2               0x16
+#define SSM4567_REG_SOFT_RESET         0xff
+
+/* POWER_CTRL */
+#define SSM4567_POWER_APWDN_EN         BIT(7)
+#define SSM4567_POWER_BSNS_PWDN                BIT(6)
+#define SSM4567_POWER_VSNS_PWDN                BIT(5)
+#define SSM4567_POWER_ISNS_PWDN                BIT(4)
+#define SSM4567_POWER_BOOST_PWDN               BIT(3)
+#define SSM4567_POWER_AMP_PWDN         BIT(2)
+#define SSM4567_POWER_VBAT_ONLY                BIT(1)
+#define SSM4567_POWER_SPWDN                    BIT(0)
+
+/* DAC_CTRL */
+#define SSM4567_DAC_HV                 BIT(7)
+#define SSM4567_DAC_MUTE               BIT(6)
+#define SSM4567_DAC_HPF                        BIT(5)
+#define SSM4567_DAC_LPM                        BIT(4)
+#define SSM4567_DAC_FS_MASK    0x7
+#define SSM4567_DAC_FS_8000_12000      0x0
+#define SSM4567_DAC_FS_16000_24000     0x1
+#define SSM4567_DAC_FS_32000_48000     0x2
+#define SSM4567_DAC_FS_64000_96000     0x3
+#define SSM4567_DAC_FS_128000_192000   0x4
+
+struct ssm4567 {
+       struct regmap *regmap;
+};
+
+static const struct reg_default ssm4567_reg_defaults[] = {
+       { SSM4567_REG_POWER_CTRL,       0x81 },
+       { SSM4567_REG_AMP_SNS_CTRL, 0x09 },
+       { SSM4567_REG_DAC_CTRL, 0x32 },
+       { SSM4567_REG_DAC_VOLUME, 0x40 },
+       { SSM4567_REG_SAI_CTRL_1, 0x00 },
+       { SSM4567_REG_SAI_CTRL_2, 0x08 },
+       { SSM4567_REG_SAI_PLACEMENT_1, 0x01 },
+       { SSM4567_REG_SAI_PLACEMENT_2, 0x20 },
+       { SSM4567_REG_SAI_PLACEMENT_3, 0x32 },
+       { SSM4567_REG_SAI_PLACEMENT_4, 0x07 },
+       { SSM4567_REG_SAI_PLACEMENT_5, 0x07 },
+       { SSM4567_REG_SAI_PLACEMENT_6, 0x07 },
+       { SSM4567_REG_BATTERY_V_OUT, 0x00 },
+       { SSM4567_REG_LIMITER_CTRL_1, 0xa4 },
+       { SSM4567_REG_LIMITER_CTRL_2, 0x73 },
+       { SSM4567_REG_LIMITER_CTRL_3, 0x00 },
+       { SSM4567_REG_STATUS_1, 0x00 },
+       { SSM4567_REG_STATUS_2, 0x00 },
+       { SSM4567_REG_FAULT_CTRL, 0x30 },
+       { SSM4567_REG_PDM_CTRL, 0x40 },
+       { SSM4567_REG_MCLK_RATIO, 0x11 },
+       { SSM4567_REG_BOOST_CTRL_1, 0x03 },
+       { SSM4567_REG_BOOST_CTRL_2, 0x00 },
+       { SSM4567_REG_SOFT_RESET, 0x00 },
+};
+
+
+static bool ssm4567_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SSM4567_REG_POWER_CTRL ... SSM4567_REG_BOOST_CTRL_2:
+               return true;
+       default:
+               return false;
+       }
+
+}
+
+static bool ssm4567_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SSM4567_REG_POWER_CTRL ... SSM4567_REG_SAI_PLACEMENT_6:
+       case SSM4567_REG_LIMITER_CTRL_1 ... SSM4567_REG_LIMITER_CTRL_3:
+       case SSM4567_REG_FAULT_CTRL ... SSM4567_REG_BOOST_CTRL_2:
+       /* The datasheet states that soft reset register is read-only,
+        * but logically it is write-only. */
+       case SSM4567_REG_SOFT_RESET:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool ssm4567_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SSM4567_REG_BATTERY_V_OUT:
+       case SSM4567_REG_STATUS_1 ... SSM4567_REG_STATUS_2:
+       case SSM4567_REG_SOFT_RESET:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(ssm4567_vol_tlv, -7125, 2400);
+
+static const struct snd_kcontrol_new ssm4567_snd_controls[] = {
+       SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0,
+               0xff, 1, ssm4567_vol_tlv),
+       SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1),
+
+       SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route ssm4567_routes[] = {
+       { "OUT", NULL, "DAC" },
+};
+
+static int ssm4567_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
+       unsigned int rate = params_rate(params);
+       unsigned int dacfs;
+
+       if (rate >= 8000 && rate <= 12000)
+               dacfs = SSM4567_DAC_FS_8000_12000;
+       else if (rate >= 16000 && rate <= 24000)
+               dacfs = SSM4567_DAC_FS_16000_24000;
+       else if (rate >= 32000 && rate <= 48000)
+               dacfs = SSM4567_DAC_FS_32000_48000;
+       else if (rate >= 64000 && rate <= 96000)
+               dacfs = SSM4567_DAC_FS_64000_96000;
+       else if (rate >= 128000 && rate <= 192000)
+               dacfs = SSM4567_DAC_FS_128000_192000;
+       else
+               return -EINVAL;
+
+       return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
+                               SSM4567_DAC_FS_MASK, dacfs);
+}
+
+static int ssm4567_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(dai->codec);
+       unsigned int val;
+
+       val = mute ? SSM4567_DAC_MUTE : 0;
+       return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
+                       SSM4567_DAC_MUTE, val);
+}
+
+static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
+{
+       int ret = 0;
+
+       if (!enable) {
+               ret = regmap_update_bits(ssm4567->regmap,
+                       SSM4567_REG_POWER_CTRL,
+                       SSM4567_POWER_SPWDN, SSM4567_POWER_SPWDN);
+               regcache_mark_dirty(ssm4567->regmap);
+       }
+
+       regcache_cache_only(ssm4567->regmap, !enable);
+
+       if (enable) {
+               ret = regmap_update_bits(ssm4567->regmap,
+                       SSM4567_REG_POWER_CTRL,
+                       SSM4567_POWER_SPWDN, 0x00);
+               regcache_sync(ssm4567->regmap);
+       }
+
+       return ret;
+}
+
+static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
+       enum snd_soc_bias_level level)
+{
+       struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+                       ret = ssm4567_set_power(ssm4567, true);
+               break;
+       case SND_SOC_BIAS_OFF:
+               ret = ssm4567_set_power(ssm4567, false);
+               break;
+       }
+
+       if (ret)
+               return ret;
+
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops ssm4567_dai_ops = {
+       .hw_params      = ssm4567_hw_params,
+       .digital_mute   = ssm4567_mute,
+};
+
+static struct snd_soc_dai_driver ssm4567_dai = {
+       .name = "ssm4567-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SNDRV_PCM_RATE_8000_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+                       SNDRV_PCM_FMTBIT_S32,
+       },
+       .ops = &ssm4567_dai_ops,
+};
+
+static struct snd_soc_codec_driver ssm4567_codec_driver = {
+       .set_bias_level = ssm4567_set_bias_level,
+       .idle_bias_off = true,
+
+       .controls = ssm4567_snd_controls,
+       .num_controls = ARRAY_SIZE(ssm4567_snd_controls),
+       .dapm_widgets = ssm4567_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ssm4567_dapm_widgets),
+       .dapm_routes = ssm4567_routes,
+       .num_dapm_routes = ARRAY_SIZE(ssm4567_routes),
+};
+
+static const struct regmap_config ssm4567_regmap_config = {
+       .val_bits = 8,
+       .reg_bits = 8,
+
+       .max_register = SSM4567_REG_SOFT_RESET,
+       .readable_reg = ssm4567_readable_reg,
+       .writeable_reg = ssm4567_writeable_reg,
+       .volatile_reg = ssm4567_volatile_reg,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = ssm4567_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults),
+};
+
+static int ssm4567_i2c_probe(struct i2c_client *i2c,
+       const struct i2c_device_id *id)
+{
+       struct ssm4567 *ssm4567;
+       int ret;
+
+       ssm4567 = devm_kzalloc(&i2c->dev, sizeof(*ssm4567), GFP_KERNEL);
+       if (ssm4567 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, ssm4567);
+
+       ssm4567->regmap = devm_regmap_init_i2c(i2c, &ssm4567_regmap_config);
+       if (IS_ERR(ssm4567->regmap))
+               return PTR_ERR(ssm4567->regmap);
+
+       ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET, 0x00);
+       if (ret)
+               return ret;
+
+       ret = ssm4567_set_power(ssm4567, false);
+       if (ret)
+               return ret;
+
+       return snd_soc_register_codec(&i2c->dev, &ssm4567_codec_driver,
+                       &ssm4567_dai, 1);
+}
+
+static int ssm4567_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id ssm4567_i2c_ids[] = {
+       { "ssm4567", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
+
+static struct i2c_driver ssm4567_driver = {
+       .driver = {
+               .name = "ssm4567",
+               .owner = THIS_MODULE,
+       },
+       .probe = ssm4567_i2c_probe,
+       .remove = ssm4567_i2c_remove,
+       .id_table = ssm4567_i2c_ids,
+};
+module_i2c_driver(ssm4567_driver);
+
+MODULE_DESCRIPTION("ASoC SSM4567 driver");
+MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
+MODULE_LICENSE("GPL");
index 23b32960ff1db62f2c5738f402bed0d3c0f116b5..f039dc82597198f9f50686f98d480024176d5bf7 100644 (file)
@@ -78,6 +78,44 @@ struct tas2552_data {
        unsigned int mclk;
 };
 
+/* Input mux controls */
+static const char *tas2552_input_texts[] = {
+       "Digital", "Analog"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7,
+                           tas2552_input_texts);
+
+static const struct snd_kcontrol_new tas2552_input_mux_control[] = {
+       SOC_DAPM_ENUM("Input selection", tas2552_input_mux_enum)
+};
+
+static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
+{
+       SND_SOC_DAPM_INPUT("IN"),
+
+       /* MUX Controls */
+       SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0,
+                               tas2552_input_mux_control),
+
+       SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
+
+       SND_SOC_DAPM_OUTPUT("OUT")
+};
+
+static const struct snd_soc_dapm_route tas2552_audio_map[] = {
+       {"DAC", NULL, "DAC IN"},
+       {"Input selection", "Digital", "DAC"},
+       {"Input selection", "Analog", "IN"},
+       {"ClassD", NULL, "Input selection"},
+       {"OUT", NULL, "ClassD"},
+       {"ClassD", NULL, "PLL"},
+};
+
+#ifdef CONFIG_PM_RUNTIME
 static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
 {
        u8 cfg1_reg;
@@ -90,6 +128,7 @@ static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
        snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1,
                                                 TAS2552_SWS_MASK, cfg1_reg);
 }
+#endif
 
 static int tas2552_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params,
@@ -101,10 +140,6 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
        int d;
        u8 p, j;
 
-       /* Turn on Class D amplifier */
-       snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN_MASK,
-                                               TAS2552_CLASSD_EN);
-
        if (!tas2552->mclk)
                return -EINVAL;
 
@@ -147,9 +182,6 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
 
        }
 
-       snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
-                                               TAS2552_PLL_ENABLE);
-
        return 0;
 }
 
@@ -269,19 +301,10 @@ static const struct dev_pm_ops tas2552_pm = {
                           NULL)
 };
 
-static void tas2552_shutdown(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *dai)
-{
-       struct snd_soc_codec *codec = dai->codec;
-
-       snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
-}
-
 static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
        .hw_params      = tas2552_hw_params,
        .set_sysclk     = tas2552_set_dai_sysclk,
        .set_fmt        = tas2552_set_dai_fmt,
-       .shutdown       = tas2552_shutdown,
        .digital_mute = tas2552_mute,
 };
 
@@ -294,7 +317,7 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
        {
                .name = "tas2552-amplifier",
                .playback = {
-                       .stream_name = "Speaker",
+                       .stream_name = "Playback",
                        .channels_min = 2,
                        .channels_max = 2,
                        .rates = SNDRV_PCM_RATE_8000_192000,
@@ -312,6 +335,7 @@ static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24);
 static const struct snd_kcontrol_new tas2552_snd_controls[] = {
        SOC_SINGLE_TLV("Speaker Driver Playback Volume",
                         TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv),
+       SOC_DAPM_SINGLE("Playback AMP", SND_SOC_NOPM, 0, 1, 0),
 };
 
 static const struct reg_default tas2552_init_regs[] = {
@@ -321,6 +345,7 @@ static const struct reg_default tas2552_init_regs[] = {
 static int tas2552_codec_probe(struct snd_soc_codec *codec)
 {
        struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
        int ret;
 
        tas2552->codec = codec;
@@ -362,9 +387,14 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
                goto patch_fail;
        }
 
-       snd_soc_write(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN |
-                                 TAS2552_BOOST_EN | TAS2552_APT_EN |
-                                 TAS2552_LIM_EN);
+       snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
+                                 TAS2552_APT_EN | TAS2552_LIM_EN);
+
+       snd_soc_dapm_new_controls(dapm, tas2552_dapm_widgets,
+                               ARRAY_SIZE(tas2552_dapm_widgets));
+       snd_soc_dapm_add_routes(dapm, tas2552_audio_map,
+                               ARRAY_SIZE(tas2552_audio_map));
+
        return 0;
 
 patch_fail:
index aea9e1ff9126592b91c70d29195725b0b2e183bd..145fe5b253d40f3e4eb9b43db64bbd97e41f8775 100644 (file)
@@ -167,13 +167,13 @@ struct aic31xx_priv {
        struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
        struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
        unsigned int sysclk;
+       u8 p_div;
        int rate_div_line;
 };
 
 struct aic31xx_rate_divs {
-       u32 mclk;
+       u32 mclk_p;
        u32 rate;
-       u8 p_val;
        u8 pll_j;
        u16 pll_d;
        u16 dosr;
@@ -186,62 +186,51 @@ struct aic31xx_rate_divs {
 
 /* ADC dividers can be disabled by cofiguring them to 0 */
 static const struct aic31xx_rate_divs aic31xx_divs[] = {
-       /* mclk      rate  pll: p  j     d     dosr ndac mdac  aors nadc madc */
+       /* mclk/p    rate  pll: j     d        dosr ndac mdac  aors nadc madc */
        /* 8k rate */
-       {12000000,   8000,      1, 8, 1920,     128,  48,  2,   128,  48,  2},
-       {12000000,   8000,      1, 8, 1920,     128,  32,  3,   128,  32,  3},
-       {24000000,   8000,      2, 8, 1920,     128,  48,  2,   128,  48,  2},
-       {25000000,   8000,      2, 7, 8643,     128,  48,  2,   128,  48,  2},
+       {12000000,   8000,      8, 1920,        128,  48,  2,   128,  48,  2},
+       {12000000,   8000,      8, 1920,        128,  32,  3,   128,  32,  3},
+       {12500000,   8000,      7, 8643,        128,  48,  2,   128,  48,  2},
        /* 11.025k rate */
-       {12000000,  11025,      1, 7, 5264,     128,  32,  2,   128,  32,  2},
-       {12000000,  11025,      1, 8, 4672,     128,  24,  3,   128,  24,  3},
-       {24000000,  11025,      2, 7, 5264,     128,  32,  2,   128,  32,  2},
-       {25000000,  11025,      2, 7, 2253,     128,  32,  2,   128,  32,  2},
+       {12000000,  11025,      7, 5264,        128,  32,  2,   128,  32,  2},
+       {12000000,  11025,      8, 4672,        128,  24,  3,   128,  24,  3},
+       {12500000,  11025,      7, 2253,        128,  32,  2,   128,  32,  2},
        /* 16k rate */
-       {12000000,  16000,      1, 8, 1920,     128,  24,  2,   128,  24,  2},
-       {12000000,  16000,      1, 8, 1920,     128,  16,  3,   128,  16,  3},
-       {24000000,  16000,      2, 8, 1920,     128,  24,  2,   128,  24,  2},
-       {25000000,  16000,      2, 7, 8643,     128,  24,  2,   128,  24,  2},
+       {12000000,  16000,      8, 1920,        128,  24,  2,   128,  24,  2},
+       {12000000,  16000,      8, 1920,        128,  16,  3,   128,  16,  3},
+       {12500000,  16000,      7, 8643,        128,  24,  2,   128,  24,  2},
        /* 22.05k rate */
-       {12000000,  22050,      1, 7, 5264,     128,  16,  2,   128,  16,  2},
-       {12000000,  22050,      1, 8, 4672,     128,  12,  3,   128,  12,  3},
-       {24000000,  22050,      2, 7, 5264,     128,  16,  2,   128,  16,  2},
-       {25000000,  22050,      2, 7, 2253,     128,  16,  2,   128,  16,  2},
+       {12000000,  22050,      7, 5264,        128,  16,  2,   128,  16,  2},
+       {12000000,  22050,      8, 4672,        128,  12,  3,   128,  12,  3},
+       {12500000,  22050,      7, 2253,        128,  16,  2,   128,  16,  2},
        /* 32k rate */
-       {12000000,  32000,      1, 8, 1920,     128,  12,  2,   128,  12,  2},
-       {12000000,  32000,      1, 8, 1920,     128,   8,  3,   128,   8,  3},
-       {24000000,  32000,      2, 8, 1920,     128,  12,  2,   128,  12,  2},
-       {25000000,  32000,      2, 7, 8643,     128,  12,  2,   128,  12,  2},
+       {12000000,  32000,      8, 1920,        128,  12,  2,   128,  12,  2},
+       {12000000,  32000,      8, 1920,        128,   8,  3,   128,   8,  3},
+       {12500000,  32000,      7, 8643,        128,  12,  2,   128,  12,  2},
        /* 44.1k rate */
-       {12000000,  44100,      1, 7, 5264,     128,   8,  2,   128,   8,  2},
-       {12000000,  44100,      1, 8, 4672,     128,   6,  3,   128,   6,  3},
-       {24000000,  44100,      2, 7, 5264,     128,   8,  2,   128,   8,  2},
-       {25000000,  44100,      2, 7, 2253,     128,   8,  2,   128,   8,  2},
+       {12000000,  44100,      7, 5264,        128,   8,  2,   128,   8,  2},
+       {12000000,  44100,      8, 4672,        128,   6,  3,   128,   6,  3},
+       {12500000,  44100,      7, 2253,        128,   8,  2,   128,   8,  2},
        /* 48k rate */
-       {12000000,  48000,      1, 8, 1920,     128,   8,  2,   128,   8,  2},
-       {12000000,  48000,      1, 7, 6800,      96,   5,  4,    96,   5,  4},
-       {24000000,  48000,      2, 8, 1920,     128,   8,  2,   128,   8,  2},
-       {25000000,  48000,      2, 7, 8643,     128,   8,  2,   128,   8,  2},
+       {12000000,  48000,      8, 1920,        128,   8,  2,   128,   8,  2},
+       {12000000,  48000,      7, 6800,         96,   5,  4,    96,   5,  4},
+       {12500000,  48000,      7, 8643,        128,   8,  2,   128,   8,  2},
        /* 88.2k rate */
-       {12000000,  88200,      1, 7, 5264,      64,   8,  2,    64,   8,  2},
-       {12000000,  88200,      1, 8, 4672,      64,   6,  3,    64,   6,  3},
-       {24000000,  88200,      2, 7, 5264,      64,   8,  2,    64,   8,  2},
-       {25000000,  88200,      2, 7, 2253,      64,   8,  2,    64,   8,  2},
+       {12000000,  88200,      7, 5264,         64,   8,  2,    64,   8,  2},
+       {12000000,  88200,      8, 4672,         64,   6,  3,    64,   6,  3},
+       {12500000,  88200,      7, 2253,         64,   8,  2,    64,   8,  2},
        /* 96k rate */
-       {12000000,  96000,      1, 8, 1920,      64,   8,  2,    64,   8,  2},
-       {12000000,  96000,      1, 7, 6800,      48,   5,  4,    48,   5,  4},
-       {24000000,  96000,      2, 8, 1920,      64,   8,  2,    64,   8,  2},
-       {25000000,  96000,      2, 7, 8643,      64,   8,  2,    64,   8,  2},
+       {12000000,  96000,      8, 1920,         64,   8,  2,    64,   8,  2},
+       {12000000,  96000,      7, 6800,         48,   5,  4,    48,   5,  4},
+       {12500000,  96000,      7, 8643,         64,   8,  2,    64,   8,  2},
        /* 176.4k rate */
-       {12000000, 176400,      1, 7, 5264,      32,   8,  2,    32,   8,  2},
-       {12000000, 176400,      1, 8, 4672,      32,   6,  3,    32,   6,  3},
-       {24000000, 176400,      2, 7, 5264,      32,   8,  2,    32,   8,  2},
-       {25000000, 176400,      2, 7, 2253,      32,   8,  2,    32,   8,  2},
+       {12000000, 176400,      7, 5264,         32,   8,  2,    32,   8,  2},
+       {12000000, 176400,      8, 4672,         32,   6,  3,    32,   6,  3},
+       {12500000, 176400,      7, 2253,         32,   8,  2,    32,   8,  2},
        /* 192k rate */
-       {12000000, 192000,      1, 8, 1920,      32,   8,  2,    32,   8,  2},
-       {12000000, 192000,      1, 7, 6800,      24,   5,  4,    24,   5,  4},
-       {24000000, 192000,      2, 8, 1920,      32,   8,  2,    32,   8,  2},
-       {25000000, 192000,      2, 7, 8643,      32,   8,  2,    32,   8,  2},
+       {12000000, 192000,      8, 1920,         32,   8,  2,    32,   8,  2},
+       {12000000, 192000,      7, 6800,         24,   5,  4,    24,   5,  4},
+       {12500000, 192000,      7, 8643,         32,   8,  2,    32,   8,  2},
 };
 
 static const char * const ldac_in_text[] = {
@@ -692,6 +681,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
 {
        struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
        int bclk_score = snd_soc_params_to_frame_size(params);
+       int mclk_p = aic31xx->sysclk / aic31xx->p_div;
        int bclk_n = 0;
        int match = -1;
        int i;
@@ -704,7 +694,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
 
        for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) {
                if (aic31xx_divs[i].rate == params_rate(params) &&
-                   aic31xx_divs[i].mclk == aic31xx->sysclk) {
+                   aic31xx_divs[i].mclk_p == mclk_p) {
                        int s = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) %
                                snd_soc_params_to_frame_size(params);
                        int bn = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) /
@@ -738,7 +728,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
 
        /* PLL configuration */
        snd_soc_update_bits(codec, AIC31XX_PLLPR, AIC31XX_PLL_MASK,
-                           (aic31xx_divs[i].p_val << 4) | 0x01);
+                           (aic31xx->p_div << 4) | 0x01);
        snd_soc_write(codec, AIC31XX_PLLJ, aic31xx_divs[i].pll_j);
 
        snd_soc_write(codec, AIC31XX_PLLDMSB,
@@ -772,7 +762,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec,
        dev_dbg(codec->dev,
                "pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n",
                aic31xx_divs[i].pll_j, aic31xx_divs[i].pll_d,
-               aic31xx_divs[i].p_val, aic31xx_divs[i].dosr,
+               aic31xx->p_div, aic31xx_divs[i].dosr,
                aic31xx_divs[i].ndac, aic31xx_divs[i].mdac,
                aic31xx_divs[i].aosr, aic31xx_divs[i].nadc,
                aic31xx_divs[i].madc, bclk_n);
@@ -840,7 +830,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        u8 iface_reg1 = 0;
-       u8 iface_reg3 = 0;
+       u8 iface_reg2 = 0;
        u8 dsp_a_val = 0;
 
        dev_dbg(codec->dev, "## %s: fmt = 0x%x\n", __func__, fmt);
@@ -865,7 +855,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
                /* NOTE: BCLKINV bit value 1 equas NB and 0 equals IB */
                switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
                case SND_SOC_DAIFMT_NB_NF:
-                       iface_reg3 |= AIC31XX_BCLKINV_MASK;
+                       iface_reg2 |= AIC31XX_BCLKINV_MASK;
                        break;
                case SND_SOC_DAIFMT_IB_NF:
                        break;
@@ -897,7 +887,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
                            dsp_a_val);
        snd_soc_update_bits(codec, AIC31XX_IFACE2,
                            AIC31XX_BCLKINV_MASK,
-                           iface_reg3);
+                           iface_reg2);
 
        return 0;
 }
@@ -912,7 +902,16 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
        dev_dbg(codec->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n",
                __func__, clk_id, freq, dir);
 
-       for (i = 0; aic31xx_divs[i].mclk != freq; i++) {
+       for (i = 1; freq/i > 20000000 && i < 8; i++)
+               ;
+       if (freq/i > 20000000) {
+               dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n",
+                       __func__, freq);
+                       return -EINVAL;
+       }
+       aic31xx->p_div = i;
+
+       for (i = 0; aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) {
                if (i == ARRAY_SIZE(aic31xx_divs)) {
                        dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
                                __func__, freq);
index 52ed57c69dfa031b10a37d57c38dd3928c7d088c..fe16c34607bb143fd28f8469c38bd27e17d0f489 100644 (file)
@@ -18,7 +18,8 @@
 #define AIC31XX_RATES  SNDRV_PCM_RATE_8000_192000
 
 #define AIC31XX_FORMATS        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
-                        | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+                        | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE \
+                        | SNDRV_PCM_FMTBIT_S32_LE)
 
 
 #define AIC31XX_STEREO_CLASS_D_BIT     0x1
index 64f179ee98345f841319599c255188e14fc37767..f7c2a575a892a2c89f196409207d2db7a48f652d 100644 (file)
@@ -1121,6 +1121,7 @@ static int aic3x_regulator_event(struct notifier_block *nb,
 static int aic3x_set_power(struct snd_soc_codec *codec, int power)
 {
        struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+       unsigned int pll_c, pll_d;
        int ret;
 
        if (power) {
@@ -1138,6 +1139,18 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
                /* Sync reg_cache with the hardware */
                regcache_cache_only(aic3x->regmap, false);
                regcache_sync(aic3x->regmap);
+
+               /* Rewrite paired PLL D registers in case cached sync skipped
+                * writing one of them and thus caused other one also not
+                * being written
+                */
+               pll_c = snd_soc_read(codec, AIC3X_PLL_PROGC_REG);
+               pll_d = snd_soc_read(codec, AIC3X_PLL_PROGD_REG);
+               if (pll_c == aic3x_reg[AIC3X_PLL_PROGC_REG].def ||
+                       pll_d == aic3x_reg[AIC3X_PLL_PROGD_REG].def) {
+                       snd_soc_write(codec, AIC3X_PLL_PROGC_REG, pll_c);
+                       snd_soc_write(codec, AIC3X_PLL_PROGD_REG, pll_d);
+               }
        } else {
                /*
                 * Do soft reset to this codec instance in order to clear
@@ -1222,20 +1235,6 @@ static struct snd_soc_dai_driver aic3x_dai = {
        .symmetric_rates = 1,
 };
 
-static int aic3x_suspend(struct snd_soc_codec *codec)
-{
-       aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int aic3x_resume(struct snd_soc_codec *codec)
-{
-       aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
 static void aic3x_mono_init(struct snd_soc_codec *codec)
 {
        /* DAC to Mono Line Out default volume and route to Output mixer */
@@ -1429,8 +1428,6 @@ static struct snd_soc_codec_driver soc_codec_dev_aic3x = {
        .idle_bias_off = true,
        .probe = aic3x_probe,
        .remove = aic3x_remove,
-       .suspend = aic3x_suspend,
-       .resume = aic3x_resume,
        .controls = aic3x_snd_controls,
        .num_controls = ARRAY_SIZE(aic3x_snd_controls),
        .dapm_widgets = aic3x_dapm_widgets,
index 7bb0d36d4c5485b703d2e7cd0adede1e732446b0..a01ad629ed6159800d57447c8efe80797af79625 100644 (file)
@@ -2319,11 +2319,8 @@ static void wm5100_init_gpio(struct i2c_client *i2c)
 static void wm5100_free_gpio(struct i2c_client *i2c)
 {
        struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
-       int ret;
 
-       ret = gpiochip_remove(&wm5100->gpio_chip);
-       if (ret != 0)
-               dev_err(&i2c->dev, "Failed to remove GPIOs: %d\n", ret);
+       gpiochip_remove(&wm5100->gpio_chip);
 }
 #else
 static void wm5100_init_gpio(struct i2c_client *i2c)
index 3dfdcc4197fa96cce56a13e50291b9075e44d4a9..628ec774cf2296c56c7851fe773cacca3568b53f 100644 (file)
@@ -212,7 +212,7 @@ static void wm8350_pga_work(struct work_struct *work)
 {
        struct snd_soc_dapm_context *dapm =
            container_of(work, struct snd_soc_dapm_context, delayed_work.work);
-       struct snd_soc_codec *codec = dapm->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
        struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
        struct wm8350_output *out1 = &wm8350_data->out1,
            *out2 = &wm8350_data->out2;
index a237f1627f614d07a40c61f50d7a561e7a87b17f..31bb4801a00564158fd695dd7aa3438d916c6b97 100644 (file)
@@ -413,7 +413,6 @@ static int wm8741_resume(struct snd_soc_codec *codec)
        return 0;
 }
 #else
-#define wm8741_suspend NULL
 #define wm8741_resume NULL
 #endif
 
index e54e097f4fcbb738870a6c51e9c0970f21e18ade..21ca3a94fc96eadabbc42cfcfdbf8b7eeefb5472 100644 (file)
@@ -1433,7 +1433,7 @@ static void wm8753_work(struct work_struct *work)
        struct snd_soc_dapm_context *dapm =
                container_of(work, struct snd_soc_dapm_context,
                             delayed_work.work);
-       struct snd_soc_codec *codec = dapm->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
        wm8753_set_bias_level(codec, dapm->bias_level);
 }
 
index 0ea01dfcb6e166a2e9e51e2fbb9cec9f4e4edcd1..3addc5fe5cb23883001d3a28662f94cf59e92fa2 100644 (file)
@@ -518,23 +518,6 @@ static int wm8804_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8804_suspend(struct snd_soc_codec *codec)
-{
-       wm8804_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8804_resume(struct snd_soc_codec *codec)
-{
-       wm8804_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define wm8804_suspend NULL
-#define wm8804_resume NULL
-#endif
-
 static int wm8804_remove(struct snd_soc_codec *codec)
 {
        struct wm8804_priv *wm8804;
@@ -671,8 +654,6 @@ static struct snd_soc_dai_driver wm8804_dai = {
 static struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
        .probe = wm8804_probe,
        .remove = wm8804_remove,
-       .suspend = wm8804_suspend,
-       .resume = wm8804_resume,
        .set_bias_level = wm8804_set_bias_level,
        .idle_bias_off = true,
 
index aa0984864e761f52d677d458fbee6969588f5de8..c038b3e04398069b050b526ab2b6baca5cdfd2a9 100644 (file)
@@ -1877,11 +1877,7 @@ static void wm8903_init_gpio(struct wm8903_priv *wm8903)
 
 static void wm8903_free_gpio(struct wm8903_priv *wm8903)
 {
-       int ret;
-
-       ret = gpiochip_remove(&wm8903->gpio_chip);
-       if (ret != 0)
-               dev_err(wm8903->dev, "Failed to remove GPIOs: %d\n", ret);
+       gpiochip_remove(&wm8903->gpio_chip);
 }
 #else
 static void wm8903_init_gpio(struct wm8903_priv *wm8903)
index 1098ae32f1f9373a7600b4fea928501f02fd16b5..9077411e62ce5d3837ee4df65b1db6e48eda77dd 100644 (file)
@@ -3398,11 +3398,8 @@ static void wm8962_init_gpio(struct snd_soc_codec *codec)
 static void wm8962_free_gpio(struct snd_soc_codec *codec)
 {
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
-       int ret;
 
-       ret = gpiochip_remove(&wm8962->gpio_chip);
-       if (ret != 0)
-               dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
+       gpiochip_remove(&wm8962->gpio_chip);
 }
 #else
 static void wm8962_init_gpio(struct snd_soc_codec *codec)
index 0499cd4cfb71266e478fa5269abe8469b42e5d01..39ddb9b8834ccbf19a996605019eb8e74b4dddcf 100644 (file)
@@ -615,7 +615,7 @@ static void wm8971_work(struct work_struct *work)
        struct snd_soc_dapm_context *dapm =
                container_of(work, struct snd_soc_dapm_context,
                             delayed_work.work);
-       struct snd_soc_codec *codec = dapm->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
        wm8971_set_bias_level(codec, codec->dapm.bias_level);
 }
 
index 6cc0566dc29a42653c96b571f07c0a02397cbb7e..1fcb9f3f309762d2793b59ccd094b26a55f84111 100644 (file)
@@ -4082,17 +4082,23 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 
        switch (control->type) {
        case WM8994:
-               if (wm8994->micdet_irq) {
+               if (wm8994->micdet_irq)
                        ret = request_threaded_irq(wm8994->micdet_irq, NULL,
                                                   wm8994_mic_irq,
                                                   IRQF_TRIGGER_RISING,
                                                   "Mic1 detect",
                                                   wm8994);
-                       if (ret != 0)
-                               dev_warn(codec->dev,
-                                        "Failed to request Mic1 detect IRQ: %d\n",
-                                        ret);
-               }
+                else
+                       ret = wm8994_request_irq(wm8994->wm8994,
+                                       WM8994_IRQ_MIC1_DET,
+                                       wm8994_mic_irq, "Mic 1 detect",
+                                       wm8994);
+
+               if (ret != 0)
+                       dev_warn(codec->dev,
+                                "Failed to request Mic1 detect IRQ: %d\n",
+                                ret);
+
 
                ret = wm8994_request_irq(wm8994->wm8994,
                                         WM8994_IRQ_MIC1_SHRT,
index cae4ac5a573061614007f211ec523d96779e5b9d..1288edeb8c7dea9530620345028fc990ac55cb6c 100644 (file)
@@ -1998,23 +1998,6 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8995_suspend(struct snd_soc_codec *codec)
-{
-       wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8995_resume(struct snd_soc_codec *codec)
-{
-       wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define wm8995_suspend NULL
-#define wm8995_resume NULL
-#endif
-
 static int wm8995_remove(struct snd_soc_codec *codec)
 {
        struct wm8995_priv *wm8995;
@@ -2220,8 +2203,6 @@ static struct snd_soc_dai_driver wm8995_dai[] = {
 static struct snd_soc_codec_driver soc_codec_dev_wm8995 = {
        .probe = wm8995_probe,
        .remove = wm8995_remove,
-       .suspend = wm8995_suspend,
-       .resume = wm8995_resume,
        .set_bias_level = wm8995_set_bias_level,
        .idle_bias_off = true,
 };
index f16ff4f569237828ffa3c140e41815c616da1a8f..b1dcc11c1b23bd0134940d2f9571d14b9e1dac92 100644 (file)
@@ -2216,11 +2216,7 @@ static void wm8996_init_gpio(struct wm8996_priv *wm8996)
 
 static void wm8996_free_gpio(struct wm8996_priv *wm8996)
 {
-       int ret;
-
-       ret = gpiochip_remove(&wm8996->gpio_chip);
-       if (ret != 0)
-               dev_err(wm8996->dev, "Failed to remove GPIOs: %d\n", ret);
+       gpiochip_remove(&wm8996->gpio_chip);
 }
 #else
 static void wm8996_init_gpio(struct wm8996_priv *wm8996)
index d69510c53239575bc1b5bad24557f415fcd3d538..8e948c63f3d96ec6dab93a2891d41f6eee0912da 100644 (file)
@@ -63,7 +63,8 @@ config SND_DM365_AIC3X_CODEC
          Say Y if you want to add support for AIC3101 audio codec
 
 config SND_DM365_VOICE_CODEC
-       bool "Voice Codec - CQ93VC"
+       tristate "Voice Codec - CQ93VC"
+       depends on SND_DAVINCI_SOC
        select MFD_DAVINCI_VOICECODEC
        select SND_DAVINCI_SOC_VCIF
        select SND_SOC_CQ0093VC
index 68347b55f6e17ecd2aa900c875d22c45e4ab0bbe..0eed9b1b24e1a5410eb4ddcc90908e919f559f65 100644 (file)
 
 #define MCASP_MAX_AFIFO_DEPTH  64
 
+static u32 context_regs[] = {
+       DAVINCI_MCASP_TXFMCTL_REG,
+       DAVINCI_MCASP_RXFMCTL_REG,
+       DAVINCI_MCASP_TXFMT_REG,
+       DAVINCI_MCASP_RXFMT_REG,
+       DAVINCI_MCASP_ACLKXCTL_REG,
+       DAVINCI_MCASP_ACLKRCTL_REG,
+       DAVINCI_MCASP_AHCLKXCTL_REG,
+       DAVINCI_MCASP_AHCLKRCTL_REG,
+       DAVINCI_MCASP_PDIR_REG,
+       DAVINCI_MCASP_RXMASK_REG,
+       DAVINCI_MCASP_TXMASK_REG,
+       DAVINCI_MCASP_RXTDM_REG,
+       DAVINCI_MCASP_TXTDM_REG,
+};
+
 struct davinci_mcasp_context {
-       u32     txfmtctl;
-       u32     rxfmtctl;
-       u32     txfmt;
-       u32     rxfmt;
-       u32     aclkxctl;
-       u32     aclkrctl;
-       u32     pdir;
+       u32     config_regs[ARRAY_SIZE(context_regs)];
+       u32     afifo_regs[2]; /* for read/write fifo control registers */
+       u32     *xrsr_regs; /* for serializer configuration */
 };
 
 struct davinci_mcasp {
@@ -874,14 +886,24 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai)
 {
        struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
        struct davinci_mcasp_context *context = &mcasp->context;
+       u32 reg;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(context_regs); i++)
+               context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]);
+
+       if (mcasp->txnumevt) {
+               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+               context->afifo_regs[0] = mcasp_get_reg(mcasp, reg);
+       }
+       if (mcasp->rxnumevt) {
+               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+               context->afifo_regs[1] = mcasp_get_reg(mcasp, reg);
+       }
 
-       context->txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG);
-       context->rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
-       context->txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG);
-       context->rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG);
-       context->aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
-       context->aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG);
-       context->pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
+       for (i = 0; i < mcasp->num_serializer; i++)
+               context->xrsr_regs[i] = mcasp_get_reg(mcasp,
+                                               DAVINCI_MCASP_XRSRCTL_REG(i));
 
        return 0;
 }
@@ -890,14 +912,24 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai)
 {
        struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
        struct davinci_mcasp_context *context = &mcasp->context;
+       u32 reg;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(context_regs); i++)
+               mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]);
+
+       if (mcasp->txnumevt) {
+               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+               mcasp_set_reg(mcasp, reg, context->afifo_regs[0]);
+       }
+       if (mcasp->rxnumevt) {
+               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+               mcasp_set_reg(mcasp, reg, context->afifo_regs[1]);
+       }
 
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, context->txfmtctl);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, context->rxfmtctl);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, context->txfmt);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, context->rxfmt);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, context->aclkxctl);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, context->aclkrctl);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, context->pdir);
+       for (i = 0; i < mcasp->num_serializer; i++)
+               mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+                             context->xrsr_regs[i]);
 
        return 0;
 }
@@ -1216,6 +1248,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        mcasp->op_mode = pdata->op_mode;
        mcasp->tdm_slots = pdata->tdm_slots;
        mcasp->num_serializer = pdata->num_serializer;
+#ifdef CONFIG_PM_SLEEP
+       mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev,
+                                       sizeof(u32) * mcasp->num_serializer,
+                                       GFP_KERNEL);
+#endif
        mcasp->serial_dir = pdata->serial_dir;
        mcasp->version = pdata->version;
        mcasp->txnumevt = pdata->txnumevt;
index 605e643133db3ebe695a8cb16fdb16c8ed7ddd7e..59e588abe54b629d986d3951e64e4ccb61eabdb4 100644 (file)
@@ -25,6 +25,8 @@
 #include <sound/dmaengine_pcm.h>
 #include <linux/edma.h>
 
+#include "edma-pcm.h"
+
 static const struct snd_pcm_hardware edma_pcm_hardware = {
        .info                   = SNDRV_PCM_INFO_MMAP |
                                  SNDRV_PCM_INFO_MMAP_VALID |
index f3012b645b51fcfdcdbc5c2e60b8f1d5db11724f..081e406b3713aae2509206e9a779325eb7ec263e 100644 (file)
@@ -240,6 +240,18 @@ config SND_SOC_IMX_WM8962
          Say Y if you want to add support for SoC audio on an i.MX board with
          a wm8962 codec.
 
+config SND_SOC_IMX_ES8328
+       tristate "SoC Audio support for i.MX boards with the ES8328 codec"
+       depends on OF && (I2C || SPI)
+       select SND_SOC_ES8328_I2C if I2C
+       select SND_SOC_ES8328_SPI if SPI_MASTER
+       select SND_SOC_IMX_PCM_DMA
+       select SND_SOC_IMX_AUDMUX
+       select SND_SOC_FSL_SSI
+       help
+         Say Y if you want to add support for the ES8328 audio codec connected
+         via SSI/I2S over either SPI or I2C.
+
 config SND_SOC_IMX_SGTL5000
        tristate "SoC Audio support for i.MX boards with sgtl5000"
        depends on OF && I2C
@@ -268,6 +280,20 @@ config SND_SOC_IMX_MC13783
        select SND_SOC_MC13783
        select SND_SOC_IMX_PCM_DMA
 
+config SND_SOC_FSL_ASOC_CARD
+       tristate "Generic ASoC Sound Card with ASRC support"
+       depends on OF && I2C
+       select SND_SOC_IMX_AUDMUX
+       select SND_SOC_IMX_PCM_DMA
+       select SND_SOC_FSL_ESAI
+       select SND_SOC_FSL_SAI
+       select SND_SOC_FSL_SSI
+       help
+        ALSA SoC Audio support with ASRC feature for Freescale SoCs that have
+        ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888
+        and SGTL5000.
+        Say Y if you want to add support for Freescale Generic ASoC Sound Card.
+
 endif # SND_IMX_SOC
 
 endmenu
index 9ff59267eac9d1b822f1a50f514c30d09c62a12a..d28dc25c9375fd813e0f14d2c48a5d4d0e08576c 100644 (file)
@@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
 obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
 
 # Freescale SSI/DMA/SAI/SPDIF Support
+snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
 snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
 snd-soc-fsl-sai-objs := fsl_sai.o
 snd-soc-fsl-ssi-y := fsl_ssi.o
@@ -19,6 +20,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o
 snd-soc-fsl-esai-objs := fsl_esai.o
 snd-soc-fsl-utils-objs := fsl_utils.o
 snd-soc-fsl-dma-objs := fsl_dma.o
+obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
 obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
 obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
 obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
@@ -50,6 +52,7 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
 snd-soc-phycore-ac97-objs := phycore-ac97.o
 snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
 snd-soc-wm1133-ev1-objs := wm1133-ev1.o
+snd-soc-imx-es8328-objs := imx-es8328.o
 snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
 snd-soc-imx-wm8962-objs := imx-wm8962.o
 snd-soc-imx-spdif-objs := imx-spdif.o
@@ -59,6 +62,7 @@ obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
 obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
 obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
+obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
 obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
 obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
 obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
new file mode 100644 (file)
index 0000000..007c772
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * Freescale Generic ASoC Sound Card driver with ASRC
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "fsl_esai.h"
+#include "fsl_sai.h"
+#include "imx-audmux.h"
+
+#include "../codecs/sgtl5000.h"
+#include "../codecs/wm8962.h"
+
+#define RX 0
+#define TX 1
+
+/* Default DAI format without Master and Slave flag */
+#define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF)
+
+/**
+ * CODEC private data
+ *
+ * @mclk_freq: Clock rate of MCLK
+ * @mclk_id: MCLK (or main clock) id for set_sysclk()
+ * @fll_id: FLL (or secordary clock) id for set_sysclk()
+ * @pll_id: PLL id for set_pll()
+ */
+struct codec_priv {
+       unsigned long mclk_freq;
+       u32 mclk_id;
+       u32 fll_id;
+       u32 pll_id;
+};
+
+/**
+ * CPU private data
+ *
+ * @sysclk_freq[2]: SYSCLK rates for set_sysclk()
+ * @sysclk_dir[2]: SYSCLK directions for set_sysclk()
+ * @sysclk_id[2]: SYSCLK ids for set_sysclk()
+ *
+ * Note: [1] for tx and [0] for rx
+ */
+struct cpu_priv {
+       unsigned long sysclk_freq[2];
+       u32 sysclk_dir[2];
+       u32 sysclk_id[2];
+};
+
+/**
+ * Freescale Generic ASOC card private data
+ *
+ * @dai_link[3]: DAI link structure including normal one and DPCM link
+ * @pdev: platform device pointer
+ * @codec_priv: CODEC private data
+ * @cpu_priv: CPU private data
+ * @card: ASoC card structure
+ * @sample_rate: Current sample rate
+ * @sample_format: Current sample format
+ * @asrc_rate: ASRC sample rate used by Back-Ends
+ * @asrc_format: ASRC sample format used by Back-Ends
+ * @dai_fmt: DAI format between CPU and CODEC
+ * @name: Card name
+ */
+
+struct fsl_asoc_card_priv {
+       struct snd_soc_dai_link dai_link[3];
+       struct platform_device *pdev;
+       struct codec_priv codec_priv;
+       struct cpu_priv cpu_priv;
+       struct snd_soc_card card;
+       u32 sample_rate;
+       u32 sample_format;
+       u32 asrc_rate;
+       u32 asrc_format;
+       u32 dai_fmt;
+       char name[32];
+};
+
+/**
+ * This dapm route map exsits for DPCM link only.
+ * The other routes shall go through Device Tree.
+ */
+static const struct snd_soc_dapm_route audio_map[] = {
+       {"CPU-Playback",  NULL, "ASRC-Playback"},
+       {"Playback",  NULL, "CPU-Playback"},
+       {"ASRC-Capture",  NULL, "CPU-Capture"},
+       {"CPU-Capture",  NULL, "Capture"},
+};
+
+/* Add all possible widgets into here without being redundant */
+static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
+       SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+       SND_SOC_DAPM_LINE("Line In Jack", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_MIC("AMIC", NULL),
+       SND_SOC_DAPM_MIC("DMIC", NULL),
+};
+
+static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       struct cpu_priv *cpu_priv = &priv->cpu_priv;
+       struct device *dev = rtd->card->dev;
+       int ret;
+
+       priv->sample_rate = params_rate(params);
+       priv->sample_format = params_format(params);
+
+       if (priv->card.set_bias_level)
+               return 0;
+
+       /* Specific configurations of DAIs starts from here */
+       ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx],
+                                    cpu_priv->sysclk_freq[tx],
+                                    cpu_priv->sysclk_dir[tx]);
+       if (ret) {
+               dev_err(dev, "failed to set sysclk for cpu dai\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops fsl_asoc_card_ops = {
+       .hw_params = fsl_asoc_card_hw_params,
+};
+
+static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+                             struct snd_pcm_hw_params *params)
+{
+       struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct snd_interval *rate;
+       struct snd_mask *mask;
+
+       rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       rate->max = rate->min = priv->asrc_rate;
+
+       mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       snd_mask_none(mask);
+       snd_mask_set(mask, priv->asrc_format);
+
+       return 0;
+}
+
+static struct snd_soc_dai_link fsl_asoc_card_dai[] = {
+       /* Default ASoC DAI Link*/
+       {
+               .name = "HiFi",
+               .stream_name = "HiFi",
+               .ops = &fsl_asoc_card_ops,
+       },
+       /* DPCM Link between Front-End and Back-End (Optional) */
+       {
+               .name = "HiFi-ASRC-FE",
+               .stream_name = "HiFi-ASRC-FE",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .dynamic = 1,
+       },
+       {
+               .name = "HiFi-ASRC-BE",
+               .stream_name = "HiFi-ASRC-BE",
+               .platform_name = "snd-soc-dummy",
+               .be_hw_params_fixup = be_hw_params_fixup,
+               .ops = &fsl_asoc_card_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .no_pcm = 1,
+       },
+};
+
+static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
+                                       struct snd_soc_dapm_context *dapm,
+                                       enum snd_soc_bias_level level)
+{
+       struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
+       struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+       struct codec_priv *codec_priv = &priv->codec_priv;
+       struct device *dev = card->dev;
+       unsigned int pll_out;
+       int ret;
+
+       if (dapm->dev != codec_dai->dev)
+               return 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_PREPARE:
+               if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
+                       break;
+
+               if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
+                       pll_out = priv->sample_rate * 384;
+               else
+                       pll_out = priv->sample_rate * 256;
+
+               ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id,
+                                         codec_priv->mclk_id,
+                                         codec_priv->mclk_freq, pll_out);
+               if (ret) {
+                       dev_err(dev, "failed to start FLL: %d\n", ret);
+                       return ret;
+               }
+
+               ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id,
+                                            pll_out, SND_SOC_CLOCK_IN);
+               if (ret) {
+                       dev_err(dev, "failed to set SYSCLK: %d\n", ret);
+                       return ret;
+               }
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
+                       break;
+
+               ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
+                                            codec_priv->mclk_freq,
+                                            SND_SOC_CLOCK_IN);
+               if (ret) {
+                       dev_err(dev, "failed to switch away from FLL: %d\n", ret);
+                       return ret;
+               }
+
+               ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0);
+               if (ret) {
+                       dev_err(dev, "failed to stop FLL: %d\n", ret);
+                       return ret;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int fsl_asoc_card_audmux_init(struct device_node *np,
+                                    struct fsl_asoc_card_priv *priv)
+{
+       struct device *dev = &priv->pdev->dev;
+       u32 int_ptcr = 0, ext_ptcr = 0;
+       int int_port, ext_port;
+       int ret;
+
+       ret = of_property_read_u32(np, "mux-int-port", &int_port);
+       if (ret) {
+               dev_err(dev, "mux-int-port missing or invalid\n");
+               return ret;
+       }
+       ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
+       if (ret) {
+               dev_err(dev, "mux-ext-port missing or invalid\n");
+               return ret;
+       }
+
+       /*
+        * The port numbering in the hardware manual starts at 1, while
+        * the AUDMUX API expects it starts at 0.
+        */
+       int_port--;
+       ext_port--;
+
+       /*
+        * Use asynchronous mode (6 wires) for all cases.
+        * If only 4 wires are needed, just set SSI into
+        * synchronous mode and enable 4 PADs in IOMUX.
+        */
+       switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
+                          IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
+                          IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
+                          IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+                          IMX_AUDMUX_V2_PTCR_RFSDIR |
+                          IMX_AUDMUX_V2_PTCR_RCLKDIR |
+                          IMX_AUDMUX_V2_PTCR_TFSDIR |
+                          IMX_AUDMUX_V2_PTCR_TCLKDIR;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
+                          IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+                          IMX_AUDMUX_V2_PTCR_RCLKDIR |
+                          IMX_AUDMUX_V2_PTCR_TCLKDIR;
+               ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
+                          IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+                          IMX_AUDMUX_V2_PTCR_RFSDIR |
+                          IMX_AUDMUX_V2_PTCR_TFSDIR;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFM:
+               int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
+                          IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
+                          IMX_AUDMUX_V2_PTCR_RFSDIR |
+                          IMX_AUDMUX_V2_PTCR_TFSDIR;
+               ext_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
+                          IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
+                          IMX_AUDMUX_V2_PTCR_RCLKDIR |
+                          IMX_AUDMUX_V2_PTCR_TCLKDIR;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
+                          IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
+                          IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+                          IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
+                          IMX_AUDMUX_V2_PTCR_RFSDIR |
+                          IMX_AUDMUX_V2_PTCR_RCLKDIR |
+                          IMX_AUDMUX_V2_PTCR_TFSDIR |
+                          IMX_AUDMUX_V2_PTCR_TCLKDIR;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Asynchronous mode can not be set along with RCLKDIR */
+       ret = imx_audmux_v2_configure_port(int_port, 0,
+                                          IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
+       if (ret) {
+               dev_err(dev, "audmux internal port setup failed\n");
+               return ret;
+       }
+
+       ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
+                                          IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
+       if (ret) {
+               dev_err(dev, "audmux internal port setup failed\n");
+               return ret;
+       }
+
+       ret = imx_audmux_v2_configure_port(ext_port, 0,
+                                          IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
+       if (ret) {
+               dev_err(dev, "audmux external port setup failed\n");
+               return ret;
+       }
+
+       ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
+                                          IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
+       if (ret) {
+               dev_err(dev, "audmux external port setup failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
+{
+       struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
+       struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+       struct codec_priv *codec_priv = &priv->codec_priv;
+       struct device *dev = card->dev;
+       int ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
+                                    codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
+       if (ret) {
+               dev_err(dev, "failed to set sysclk in %s\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int fsl_asoc_card_probe(struct platform_device *pdev)
+{
+       struct device_node *cpu_np, *codec_np, *asrc_np;
+       struct device_node *np = pdev->dev.of_node;
+       struct platform_device *asrc_pdev = NULL;
+       struct platform_device *cpu_pdev;
+       struct fsl_asoc_card_priv *priv;
+       struct i2c_client *codec_dev;
+       struct clk *codec_clk;
+       u32 width;
+       int ret;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       cpu_np = of_parse_phandle(np, "audio-cpu", 0);
+       /* Give a chance to old DT binding */
+       if (!cpu_np)
+               cpu_np = of_parse_phandle(np, "ssi-controller", 0);
+       codec_np = of_parse_phandle(np, "audio-codec", 0);
+       if (!cpu_np || !codec_np) {
+               dev_err(&pdev->dev, "phandle missing or invalid\n");
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       cpu_pdev = of_find_device_by_node(cpu_np);
+       if (!cpu_pdev) {
+               dev_err(&pdev->dev, "failed to find CPU DAI device\n");
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       codec_dev = of_find_i2c_device_by_node(codec_np);
+       if (!codec_dev) {
+               dev_err(&pdev->dev, "failed to find codec platform device\n");
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       asrc_np = of_parse_phandle(np, "audio-asrc", 0);
+       if (asrc_np)
+               asrc_pdev = of_find_device_by_node(asrc_np);
+
+       /* Get the MCLK rate only, and leave it controlled by CODEC drivers */
+       codec_clk = clk_get(&codec_dev->dev, NULL);
+       if (!IS_ERR(codec_clk)) {
+               priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
+               clk_put(codec_clk);
+       }
+
+       /* Default sample rate and format, will be updated in hw_params() */
+       priv->sample_rate = 44100;
+       priv->sample_format = SNDRV_PCM_FORMAT_S16_LE;
+
+       /* Assign a default DAI format, and allow each card to overwrite it */
+       priv->dai_fmt = DAI_FMT_BASE;
+
+       /* Diversify the card configurations */
+       if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
+               priv->card.set_bias_level = NULL;
+               priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
+               priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
+               priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
+               priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
+               priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+       } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
+               priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
+               priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+       } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
+               priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
+               priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
+               priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
+               priv->codec_priv.pll_id = WM8962_FLL;
+               priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+       } else {
+               dev_err(&pdev->dev, "unknown Device Tree compatible\n");
+               return -EINVAL;
+       }
+
+       /* Common settings for corresponding Freescale CPU DAI driver */
+       if (strstr(cpu_np->name, "ssi")) {
+               /* Only SSI needs to configure AUDMUX */
+               ret = fsl_asoc_card_audmux_init(np, priv);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to init audmux\n");
+                       goto asrc_fail;
+               }
+       } else if (strstr(cpu_np->name, "esai")) {
+               priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL;
+               priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL;
+       } else if (strstr(cpu_np->name, "sai")) {
+               priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
+               priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
+       }
+
+       sprintf(priv->name, "%s-audio", codec_dev->name);
+
+       /* Initialize sound card */
+       priv->pdev = pdev;
+       priv->card.dev = &pdev->dev;
+       priv->card.name = priv->name;
+       priv->card.dai_link = priv->dai_link;
+       priv->card.dapm_routes = audio_map;
+       priv->card.late_probe = fsl_asoc_card_late_probe;
+       priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
+       priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
+       priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets);
+
+       memcpy(priv->dai_link, fsl_asoc_card_dai,
+              sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
+
+       /* Normal DAI Link */
+       priv->dai_link[0].cpu_of_node = cpu_np;
+       priv->dai_link[0].codec_of_node = codec_np;
+       priv->dai_link[0].codec_dai_name = codec_dev->name;
+       priv->dai_link[0].platform_of_node = cpu_np;
+       priv->dai_link[0].dai_fmt = priv->dai_fmt;
+       priv->card.num_links = 1;
+
+       if (asrc_pdev) {
+               /* DPCM DAI Links only if ASRC exsits */
+               priv->dai_link[1].cpu_of_node = asrc_np;
+               priv->dai_link[1].platform_of_node = asrc_np;
+               priv->dai_link[2].codec_dai_name = codec_dev->name;
+               priv->dai_link[2].codec_of_node = codec_np;
+               priv->dai_link[2].cpu_of_node = cpu_np;
+               priv->dai_link[2].dai_fmt = priv->dai_fmt;
+               priv->card.num_links = 3;
+
+               ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
+                                          &priv->asrc_rate);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get output rate\n");
+                       ret = -EINVAL;
+                       goto asrc_fail;
+               }
+
+               ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get output rate\n");
+                       ret = -EINVAL;
+                       goto asrc_fail;
+               }
+
+               if (width == 24)
+                       priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+               else
+                       priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+       }
+
+       /* Finish card registering */
+       platform_set_drvdata(pdev, priv);
+       snd_soc_card_set_drvdata(&priv->card, priv);
+
+       ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
+       if (ret)
+               dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+
+asrc_fail:
+       of_node_put(asrc_np);
+fail:
+       of_node_put(codec_np);
+       of_node_put(cpu_np);
+
+       return ret;
+}
+
+static const struct of_device_id fsl_asoc_card_dt_ids[] = {
+       { .compatible = "fsl,imx-audio-cs42888", },
+       { .compatible = "fsl,imx-audio-sgtl5000", },
+       { .compatible = "fsl,imx-audio-wm8962", },
+       {}
+};
+
+static struct platform_driver fsl_asoc_card_driver = {
+       .probe = fsl_asoc_card_probe,
+       .driver = {
+               .name = "fsl-asoc-card",
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = fsl_asoc_card_dt_ids,
+       },
+};
+module_platform_driver(fsl_asoc_card_driver);
+
+MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC");
+MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
+MODULE_ALIAS("platform:fsl-asoc-card");
+MODULE_LICENSE("GPL");
index 822110420b71cdf9df6922a43b213a61ac00b598..3b145313f93eecc0602cc72115d71769f07fdf26 100644 (file)
@@ -684,7 +684,7 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
        }
 }
 
-static struct regmap_config fsl_asrc_regmap_config = {
+static const struct regmap_config fsl_asrc_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
        .val_bits = 32,
@@ -802,10 +802,6 @@ static int fsl_asrc_probe(struct platform_device *pdev)
 
        asrc_priv->paddr = res->start;
 
-       /* Register regmap and let it prepare core clock */
-       if (of_property_read_bool(np, "big-endian"))
-               fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
-
        asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
                                                      &fsl_asrc_regmap_config);
        if (IS_ERR(asrc_priv->regmap)) {
index a3b29ed84963459baada6cc9d8ab742f696ee44e..8bcdfda09d7ad9c5fd724cc685e63f3ac7829f77 100644 (file)
@@ -37,6 +37,7 @@
  * @fsysclk: system clock source to derive HCK, SCK and FS
  * @fifo_depth: depth of tx/rx FIFO
  * @slot_width: width of each DAI slot
+ * @slots: number of slots
  * @hck_rate: clock rate of desired HCKx clock
  * @sck_rate: clock rate of desired SCKx clock
  * @hck_dir: the direction of HCKx pads
@@ -55,6 +56,7 @@ struct fsl_esai {
        struct clk *fsysclk;
        u32 fifo_depth;
        u32 slot_width;
+       u32 slots;
        u32 hck_rate[2];
        u32 sck_rate[2];
        bool hck_dir[2];
@@ -362,6 +364,7 @@ static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
                           ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(rx_mask));
 
        esai_priv->slot_width = slot_width;
+       esai_priv->slots = slots;
 
        return 0;
 }
@@ -509,10 +512,11 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
        bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        u32 width = snd_pcm_format_width(params_format(params));
        u32 channels = params_channels(params);
+       u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
        u32 bclk, mask, val;
        int ret;
 
-       bclk = params_rate(params) * esai_priv->slot_width * 2;
+       bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots;
 
        ret = fsl_esai_set_bclk(dai, tx, bclk);
        if (ret)
@@ -529,7 +533,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
        mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK |
              (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK);
        val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) |
-            (tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels));
+            (tx ? ESAI_xFCR_TE(pins) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(pins));
 
        regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
 
@@ -564,6 +568,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
        struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
        bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        u8 i, channels = substream->runtime->channels;
+       u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
@@ -578,7 +583,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
 
                regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
                                   tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
-                                  tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels));
+                                  tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins));
                break;
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_STOP:
@@ -705,7 +710,7 @@ static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
        }
 }
 
-static struct regmap_config fsl_esai_regmap_config = {
+static const struct regmap_config fsl_esai_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
        .val_bits = 32,
@@ -731,9 +736,6 @@ static int fsl_esai_probe(struct platform_device *pdev)
        esai_priv->pdev = pdev;
        strcpy(esai_priv->name, np->name);
 
-       if (of_property_read_bool(np, "big-endian"))
-               fsl_esai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
-
        /* Get the addresses and IRQ */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        regs = devm_ioremap_resource(&pdev->dev, res);
@@ -781,6 +783,9 @@ static int fsl_esai_probe(struct platform_device *pdev)
        /* Set a default slot size */
        esai_priv->slot_width = 32;
 
+       /* Set a default slot number */
+       esai_priv->slots = 2;
+
        /* Set a default master/slave state */
        esai_priv->slave_mode = true;
 
index 75e14033e8d8f0a7da6b1db6387d543c5a9a1acc..91a550f4a10dc7e0156efd8389fc3ba90bb15955 100644 (file)
 #define ESAI_xFCR_RE_WIDTH     4
 #define ESAI_xFCR_TE_MASK      (((1 << ESAI_xFCR_TE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT)
 #define ESAI_xFCR_RE_MASK      (((1 << ESAI_xFCR_RE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT)
-#define ESAI_xFCR_TE(x)        ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_TE_MASK)
-#define ESAI_xFCR_RE(x)        ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_RE_MASK)
+#define ESAI_xFCR_TE(x)        ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - x)) & ESAI_xFCR_TE_MASK)
+#define ESAI_xFCR_RE(x)        ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - x)) & ESAI_xFCR_RE_MASK)
 #define ESAI_xFCR_xFR_SHIFT    1
 #define ESAI_xFCR_xFR_MASK     (1 << ESAI_xFCR_xFR_SHIFT)
 #define ESAI_xFCR_xFR          (1 << ESAI_xFCR_xFR_SHIFT)
 #define ESAI_xCR_RE_WIDTH      4
 #define ESAI_xCR_TE_MASK       (((1 << ESAI_xCR_TE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT)
 #define ESAI_xCR_RE_MASK       (((1 << ESAI_xCR_RE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT)
-#define ESAI_xCR_TE(x)                 ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_TE_MASK)
-#define ESAI_xCR_RE(x)                 ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_RE_MASK)
+#define ESAI_xCR_TE(x)                 ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - x)) & ESAI_xCR_TE_MASK)
+#define ESAI_xCR_RE(x)                 ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - x)) & ESAI_xCR_RE_MASK)
 
 /*
  * Transmit Clock Control Register -- REG_ESAI_TCCR 0xD8
index faa049797897ed1e8996755437df2488c19c457b..7eeb1dd8ce27339e05305552aa7f3cc34240ca29 100644 (file)
@@ -175,7 +175,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
        bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
        u32 val_cr2 = 0, val_cr4 = 0;
 
-       if (!sai->big_endian_data)
+       if (!sai->is_lsb_first)
                val_cr4 |= FSL_SAI_CR4_MF;
 
        /* DAI mode */
@@ -304,7 +304,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
        val_cr5 |= FSL_SAI_CR5_WNW(word_width);
        val_cr5 |= FSL_SAI_CR5_W0W(word_width);
 
-       if (sai->big_endian_data)
+       if (sai->is_lsb_first)
                val_cr5 |= FSL_SAI_CR5_FBT(0);
        else
                val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
@@ -330,13 +330,13 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
        u32 xcsr, count = 100;
 
        /*
-        * The transmitter bit clock and frame sync are to be
-        * used by both the transmitter and receiver.
+        * Asynchronous mode: Clear SYNC for both Tx and Rx.
+        * Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
+        * Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
         */
-       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
-                          ~FSL_SAI_CR2_SYNC);
+       regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0);
        regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
-                          FSL_SAI_CR2_SYNC);
+                          sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
 
        /*
         * It is recommended that the transmitter is the last enabled
@@ -437,8 +437,13 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
 
-       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0);
-       regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0);
+       /* Software Reset for both Tx and Rx */
+       regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+       regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+       /* Clear SR bit to finish the reset */
+       regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+       regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+
        regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
                           FSL_SAI_MAXBURST_TX * 2);
        regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
@@ -539,7 +544,7 @@ static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
        }
 }
 
-static struct regmap_config fsl_sai_regmap_config = {
+static const struct regmap_config fsl_sai_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
        .val_bits = 32,
@@ -568,11 +573,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
        if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
                sai->sai_on_imx = true;
 
-       sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
-       if (sai->big_endian_regs)
-               fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
-
-       sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
+       sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        base = devm_ioremap_resource(&pdev->dev, res);
@@ -621,6 +622,33 @@ static int fsl_sai_probe(struct platform_device *pdev)
                return ret;
        }
 
+       /* Sync Tx with Rx as default by following old DT binding */
+       sai->synchronous[RX] = true;
+       sai->synchronous[TX] = false;
+       fsl_sai_dai.symmetric_rates = 1;
+       fsl_sai_dai.symmetric_channels = 1;
+       fsl_sai_dai.symmetric_samplebits = 1;
+
+       if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) &&
+           of_find_property(np, "fsl,sai-asynchronous", NULL)) {
+               /* error out if both synchronous and asynchronous are present */
+               dev_err(&pdev->dev, "invalid binding for synchronous mode\n");
+               return -EINVAL;
+       }
+
+       if (of_find_property(np, "fsl,sai-synchronous-rx", NULL)) {
+               /* Sync Rx with Tx */
+               sai->synchronous[RX] = false;
+               sai->synchronous[TX] = true;
+       } else if (of_find_property(np, "fsl,sai-asynchronous", NULL)) {
+               /* Discard all settings for asynchronous mode */
+               sai->synchronous[RX] = false;
+               sai->synchronous[TX] = false;
+               fsl_sai_dai.symmetric_rates = 0;
+               fsl_sai_dai.symmetric_channels = 0;
+               fsl_sai_dai.symmetric_samplebits = 0;
+       }
+
        sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
        sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
        sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
index 0e6c9f595d7550957eabc534174b7a14b2056422..34667209b607d435d3abd9ac10bbfe6e8cf9a64c 100644 (file)
@@ -48,6 +48,7 @@
 /* SAI Transmit/Recieve Control Register */
 #define FSL_SAI_CSR_TERE       BIT(31)
 #define FSL_SAI_CSR_FR         BIT(25)
+#define FSL_SAI_CSR_SR         BIT(24)
 #define FSL_SAI_CSR_xF_SHIFT   16
 #define FSL_SAI_CSR_xF_W_SHIFT 18
 #define FSL_SAI_CSR_xF_MASK    (0x1f << FSL_SAI_CSR_xF_SHIFT)
@@ -131,13 +132,16 @@ struct fsl_sai {
        struct clk *bus_clk;
        struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
 
-       bool big_endian_regs;
-       bool big_endian_data;
+       bool is_lsb_first;
        bool is_dsp_mode;
        bool sai_on_imx;
+       bool synchronous[2];
 
        struct snd_dmaengine_dai_dma_data dma_params_rx;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
 };
 
+#define TX 1
+#define RX 0
+
 #endif /* __FSL_SAI_H */
index 70acfe4a9bd5a969e5418daf098a5e40115191bb..9b791621294cf6d2f1c1059df8d3de9ce2634639 100644 (file)
@@ -15,7 +15,6 @@
 
 #include <linux/bitrev.h>
 #include <linux/clk.h>
-#include <linux/clk-private.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
@@ -1040,7 +1039,7 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
        }
 }
 
-static struct regmap_config fsl_spdif_regmap_config = {
+static const struct regmap_config fsl_spdif_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
        .val_bits = 32,
@@ -1184,9 +1183,6 @@ static int fsl_spdif_probe(struct platform_device *pdev)
        memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
        spdif_priv->cpu_dai_drv.name = spdif_priv->name;
 
-       if (of_property_read_bool(np, "big-endian"))
-               fsl_spdif_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
-
        /* Get the addresses and IRQ */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        regs = devm_ioremap_resource(&pdev->dev, res);
index de6ab06f58a54dbd32b871273a530db6e8d9fbed..e6955170dc42c8b805d6aa654a5eadebc437b27e 100644 (file)
@@ -169,6 +169,7 @@ struct fsl_ssi_private {
        u8 i2s_mode;
        bool use_dma;
        bool use_dual_fifo;
+       bool has_ipg_clk_name;
        unsigned int fifo_depth;
        struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
 
@@ -259,6 +260,11 @@ static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private)
                SND_SOC_DAIFMT_CBS_CFS;
 }
 
+static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi_private *ssi_private)
+{
+       return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
+               SND_SOC_DAIFMT_CBM_CFS;
+}
 /**
  * fsl_ssi_isr: SSI interrupt handler
  *
@@ -525,6 +531,11 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct fsl_ssi_private *ssi_private =
                snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       int ret;
+
+       ret = clk_prepare_enable(ssi_private->clk);
+       if (ret)
+               return ret;
 
        /* When using dual fifo mode, it is safer to ensure an even period
         * size. If appearing to an odd number while DMA always starts its
@@ -538,6 +549,21 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
        return 0;
 }
 
+/**
+ * fsl_ssi_shutdown: shutdown the SSI
+ *
+ */
+static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_ssi_private *ssi_private =
+               snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+       clk_disable_unprepare(ssi_private->clk);
+
+}
+
 /**
  * fsl_ssi_set_bclk - configure Digital Audio Interface bit clock
  *
@@ -705,6 +731,23 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
                }
        }
 
+       if (!fsl_ssi_is_ac97(ssi_private)) {
+               u8 i2smode;
+               /*
+                * Switch to normal net mode in order to have a frame sync
+                * signal every 32 bits instead of 16 bits
+                */
+               if (fsl_ssi_is_i2s_cbm_cfs(ssi_private) && sample_size == 16)
+                       i2smode = CCSR_SSI_SCR_I2S_MODE_NORMAL |
+                               CCSR_SSI_SCR_NET;
+               else
+                       i2smode = ssi_private->i2s_mode;
+
+               regmap_update_bits(regs, CCSR_SSI_SCR,
+                               CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
+                               channels == 1 ? 0 : i2smode);
+       }
+
        /*
         * FIXME: The documentation says that SxCCR[WL] should not be
         * modified while the SSI is enabled.  The only time this can
@@ -724,11 +767,6 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
                regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_WL_MASK,
                                wl);
 
-       if (!fsl_ssi_is_ac97(ssi_private))
-               regmap_update_bits(regs, CCSR_SSI_SCR,
-                               CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
-                               channels == 1 ? 0 : ssi_private->i2s_mode);
-
        return 0;
 }
 
@@ -781,6 +819,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBM_CFS:
                case SND_SOC_DAIFMT_CBS_CFS:
                        ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER;
                        regmap_update_bits(regs, CCSR_SSI_STCCR,
@@ -854,6 +893,11 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
        case SND_SOC_DAIFMT_CBM_CFM:
                scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
                break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               strcr &= ~CCSR_SSI_STCR_TXDIR;
+               strcr |= CCSR_SSI_STCR_TFDIR;
+               scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
+               break;
        default:
                return -EINVAL;
        }
@@ -1021,6 +1065,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
 
 static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
        .startup        = fsl_ssi_startup,
+       .shutdown       = fsl_ssi_shutdown,
        .hw_params      = fsl_ssi_hw_params,
        .hw_free        = fsl_ssi_hw_free,
        .set_fmt        = fsl_ssi_set_dai_fmt,
@@ -1146,17 +1191,22 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
        u32 dmas[4];
        int ret;
 
-       ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
+       if (ssi_private->has_ipg_clk_name)
+               ssi_private->clk = devm_clk_get(&pdev->dev, "ipg");
+       else
+               ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(ssi_private->clk)) {
                ret = PTR_ERR(ssi_private->clk);
                dev_err(&pdev->dev, "could not get clock: %d\n", ret);
                return ret;
        }
 
-       ret = clk_prepare_enable(ssi_private->clk);
-       if (ret) {
-               dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
-               return ret;
+       if (!ssi_private->has_ipg_clk_name) {
+               ret = clk_prepare_enable(ssi_private->clk);
+               if (ret) {
+                       dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
+                       return ret;
+               }
        }
 
        /* For those SLAVE implementations, we ingore non-baudclk cases
@@ -1214,8 +1264,9 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
        return 0;
 
 error_pcm:
-       clk_disable_unprepare(ssi_private->clk);
 
+       if (!ssi_private->has_ipg_clk_name)
+               clk_disable_unprepare(ssi_private->clk);
        return ret;
 }
 
@@ -1224,7 +1275,8 @@ static void fsl_ssi_imx_clean(struct platform_device *pdev,
 {
        if (!ssi_private->use_dma)
                imx_pcm_fiq_exit(pdev);
-       clk_disable_unprepare(ssi_private->clk);
+       if (!ssi_private->has_ipg_clk_name)
+               clk_disable_unprepare(ssi_private->clk);
 }
 
 static int fsl_ssi_probe(struct platform_device *pdev)
@@ -1263,9 +1315,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        if (sprop) {
                if (!strcmp(sprop, "ac97-slave"))
                        ssi_private->dai_fmt = SND_SOC_DAIFMT_AC97;
-               else if (!strcmp(sprop, "i2s-slave"))
-                       ssi_private->dai_fmt = SND_SOC_DAIFMT_I2S |
-                               SND_SOC_DAIFMT_CBM_CFM;
        }
 
        ssi_private->use_dma = !of_property_read_bool(np,
@@ -1299,8 +1348,16 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem,
+       ret = of_property_match_string(np, "clock-names", "ipg");
+       if (ret < 0) {
+               ssi_private->has_ipg_clk_name = false;
+               ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem,
                        &fsl_ssi_regconfig);
+       } else {
+               ssi_private->has_ipg_clk_name = true;
+               ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev,
+                       "ipg", iomem, &fsl_ssi_regconfig);
+       }
        if (IS_ERR(ssi_private->regs)) {
                dev_err(&pdev->dev, "Failed to init register map\n");
                return PTR_ERR(ssi_private->regs);
diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c
new file mode 100644 (file)
index 0000000..f8cf10e
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "imx-audmux.h"
+
+#define DAI_NAME_SIZE  32
+#define MUX_PORT_MAX   7
+
+struct imx_es8328_data {
+       struct device *dev;
+       struct snd_soc_dai_link dai;
+       struct snd_soc_card card;
+       char codec_dai_name[DAI_NAME_SIZE];
+       char platform_name[DAI_NAME_SIZE];
+       int jack_gpio;
+};
+
+static struct snd_soc_jack_gpio headset_jack_gpios[] = {
+       {
+               .gpio = -1,
+               .name = "headset-gpio",
+               .report = SND_JACK_HEADSET,
+               .invert = 0,
+               .debounce_time = 200,
+       },
+};
+
+static struct snd_soc_jack headset_jack;
+
+static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct imx_es8328_data *data = container_of(rtd->card,
+                                       struct imx_es8328_data, card);
+       int ret = 0;
+
+       /* Headphone jack detection */
+       if (gpio_is_valid(data->jack_gpio)) {
+               ret = snd_soc_jack_new(rtd->codec, "Headphone",
+                                      SND_JACK_HEADPHONE | SND_JACK_BTN_0,
+                                      &headset_jack);
+               if (ret)
+                       return ret;
+
+               headset_jack_gpios[0].gpio = data->jack_gpio;
+               ret = snd_soc_jack_add_gpios(&headset_jack,
+                                            ARRAY_SIZE(headset_jack_gpios),
+                                            headset_jack_gpios);
+       }
+
+       return ret;
+}
+
+static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = {
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+       SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0),
+};
+
+static int imx_es8328_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *ssi_np = NULL, *codec_np = NULL;
+       struct platform_device *ssi_pdev;
+       struct imx_es8328_data *data;
+       u32 int_port, ext_port;
+       int ret;
+       struct device *dev = &pdev->dev;
+
+       ret = of_property_read_u32(np, "mux-int-port", &int_port);
+       if (ret) {
+               dev_err(dev, "mux-int-port missing or invalid\n");
+               goto fail;
+       }
+       if (int_port > MUX_PORT_MAX || int_port == 0) {
+               dev_err(dev, "mux-int-port: hardware only has %d mux ports\n",
+                       MUX_PORT_MAX);
+               goto fail;
+       }
+
+       ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
+       if (ret) {
+               dev_err(dev, "mux-ext-port missing or invalid\n");
+               goto fail;
+       }
+       if (ext_port > MUX_PORT_MAX || ext_port == 0) {
+               dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n",
+                       MUX_PORT_MAX);
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       /*
+        * The port numbering in the hardware manual starts at 1, while
+        * the audmux API expects it starts at 0.
+        */
+       int_port--;
+       ext_port--;
+       ret = imx_audmux_v2_configure_port(int_port,
+                       IMX_AUDMUX_V2_PTCR_SYN |
+                       IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
+                       IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+                       IMX_AUDMUX_V2_PTCR_TFSDIR |
+                       IMX_AUDMUX_V2_PTCR_TCLKDIR,
+                       IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
+       if (ret) {
+               dev_err(dev, "audmux internal port setup failed\n");
+               return ret;
+       }
+       ret = imx_audmux_v2_configure_port(ext_port,
+                       IMX_AUDMUX_V2_PTCR_SYN,
+                       IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
+       if (ret) {
+               dev_err(dev, "audmux external port setup failed\n");
+               return ret;
+       }
+
+       ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
+       codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+       if (!ssi_np || !codec_np) {
+               dev_err(dev, "phandle missing or invalid\n");
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       ssi_pdev = of_find_device_by_node(ssi_np);
+       if (!ssi_pdev) {
+               dev_err(dev, "failed to find SSI platform device\n");
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       data->dev = dev;
+
+       data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
+
+       data->dai.name = "hifi";
+       data->dai.stream_name = "hifi";
+       data->dai.codec_dai_name = "es8328-hifi-analog";
+       data->dai.codec_of_node = codec_np;
+       data->dai.cpu_of_node = ssi_np;
+       data->dai.platform_of_node = ssi_np;
+       data->dai.init = &imx_es8328_dai_init;
+       data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                           SND_SOC_DAIFMT_CBM_CFM;
+
+       data->card.dev = dev;
+       data->card.dapm_widgets = imx_es8328_dapm_widgets;
+       data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets);
+       ret = snd_soc_of_parse_card_name(&data->card, "model");
+       if (ret) {
+               dev_err(dev, "Unable to parse card name\n");
+               goto fail;
+       }
+       ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
+       if (ret) {
+               dev_err(dev, "Unable to parse routing: %d\n", ret);
+               goto fail;
+       }
+       data->card.num_links = 1;
+       data->card.owner = THIS_MODULE;
+       data->card.dai_link = &data->dai;
+
+       ret = snd_soc_register_card(&data->card);
+       if (ret) {
+               dev_err(dev, "Unable to register: %d\n", ret);
+               goto fail;
+       }
+
+       platform_set_drvdata(pdev, data);
+fail:
+       of_node_put(ssi_np);
+       of_node_put(codec_np);
+
+       return ret;
+}
+
+static int imx_es8328_remove(struct platform_device *pdev)
+{
+       struct imx_es8328_data *data = platform_get_drvdata(pdev);
+
+       snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios),
+                               headset_jack_gpios);
+
+       snd_soc_unregister_card(&data->card);
+
+       return 0;
+}
+
+static const struct of_device_id imx_es8328_dt_ids[] = {
+       { .compatible = "fsl,imx-audio-es8328", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids);
+
+static struct platform_driver imx_es8328_driver = {
+       .driver = {
+               .name = "imx-es8328",
+               .of_match_table = imx_es8328_dt_ids,
+       },
+       .probe = imx_es8328_probe,
+       .remove = imx_es8328_remove,
+};
+module_platform_driver(imx_es8328_driver);
+
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-audio-es8328");
index cef7776b712cff43f37f81648a6fe2a59256ddd9..d1b7293c133eaa456f9d7761f273ee2887251121 100644 (file)
  */
 #include <linux/clk.h>
 #include <linux/device.h>
+#include <linux/gpio.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <linux/string.h>
+#include <sound/jack.h>
 #include <sound/simple_card.h>
 #include <sound/soc-dai.h>
 #include <sound/soc.h>
@@ -25,9 +28,15 @@ struct simple_card_data {
                struct asoc_simple_dai codec_dai;
        } *dai_props;
        unsigned int mclk_fs;
+       int gpio_hp_det;
+       int gpio_mic_det;
        struct snd_soc_dai_link dai_link[];     /* dynamically allocated */
 };
 
+#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
+#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
+#define simple_priv_to_props(priv, i) ((priv)->dai_props + i)
+
 static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
                                      struct snd_pcm_hw_params *params)
 {
@@ -50,6 +59,32 @@ static struct snd_soc_ops asoc_simple_card_ops = {
        .hw_params = asoc_simple_card_hw_params,
 };
 
+static struct snd_soc_jack simple_card_hp_jack;
+static struct snd_soc_jack_pin simple_card_hp_jack_pins[] = {
+       {
+               .pin = "Headphones",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+static struct snd_soc_jack_gpio simple_card_hp_jack_gpio = {
+       .name = "Headphone detection",
+       .report = SND_JACK_HEADPHONE,
+       .debounce_time = 150,
+};
+
+static struct snd_soc_jack simple_card_mic_jack;
+static struct snd_soc_jack_pin simple_card_mic_jack_pins[] = {
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+};
+static struct snd_soc_jack_gpio simple_card_mic_jack_gpio = {
+       .name = "Mic detection",
+       .report = SND_JACK_MICROPHONE,
+       .debounce_time = 150,
+};
+
 static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
                                       struct asoc_simple_dai *set)
 {
@@ -105,42 +140,70 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
        if (ret < 0)
                return ret;
 
+       if (gpio_is_valid(priv->gpio_hp_det)) {
+               snd_soc_jack_new(codec->codec, "Headphones", SND_JACK_HEADPHONE,
+                                &simple_card_hp_jack);
+               snd_soc_jack_add_pins(&simple_card_hp_jack,
+                                     ARRAY_SIZE(simple_card_hp_jack_pins),
+                                     simple_card_hp_jack_pins);
+
+               simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
+               snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
+                                      &simple_card_hp_jack_gpio);
+       }
+
+       if (gpio_is_valid(priv->gpio_mic_det)) {
+               snd_soc_jack_new(codec->codec, "Mic Jack", SND_JACK_MICROPHONE,
+                                &simple_card_mic_jack);
+               snd_soc_jack_add_pins(&simple_card_mic_jack,
+                                     ARRAY_SIZE(simple_card_mic_jack_pins),
+                                     simple_card_mic_jack_pins);
+               simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
+               snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
+                                      &simple_card_mic_jack_gpio);
+       }
        return 0;
 }
 
 static int
 asoc_simple_card_sub_parse_of(struct device_node *np,
                              struct asoc_simple_dai *dai,
-                             const struct device_node **p_node,
-                             const char **name)
+                             struct device_node **p_node,
+                             const char **name,
+                             int *args_count)
 {
-       struct device_node *node;
+       struct of_phandle_args args;
        struct clk *clk;
        u32 val;
        int ret;
 
        /*
-        * get node via "sound-dai = <&phandle port>"
+        * Get node via "sound-dai = <&phandle port>"
         * it will be used as xxx_of_node on soc_bind_dai_link()
         */
-       node = of_parse_phandle(np, "sound-dai", 0);
-       if (!node)
-               return -ENODEV;
-       *p_node = node;
+       ret = of_parse_phandle_with_args(np, "sound-dai",
+                                        "#sound-dai-cells", 0, &args);
+       if (ret)
+               return ret;
+
+       *p_node = args.np;
+
+       if (args_count)
+               *args_count = args.args_count;
 
-       /* get dai->name */
+       /* Get dai->name */
        ret = snd_soc_of_get_dai_name(np, name);
        if (ret < 0)
                return ret;
 
-       /* parse TDM slot */
+       /* Parse TDM slot */
        ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
        if (ret)
                return ret;
 
        /*
-        * dai->sysclk come from
-        *  "clocks = <&xxx>" (if system has common clock)
+        * Parse dai->sysclk come from "clocks = <&xxx>"
+        * (if system has common clock)
         *  or "system-clock-frequency = <xxx>"
         *  or device's module clock.
         */
@@ -155,7 +218,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
        } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
                dai->sysclk = val;
        } else {
-               clk = of_clk_get(node, 0);
+               clk = of_clk_get(args.np, 0);
                if (!IS_ERR(clk))
                        dai->sysclk = clk_get_rate(clk);
        }
@@ -163,12 +226,14 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
        return 0;
 }
 
-static int simple_card_dai_link_of(struct device_node *node,
-                                  struct device *dev,
-                                  struct snd_soc_dai_link *dai_link,
-                                  struct simple_dai_props *dai_props,
-                                  bool is_top_level_node)
+static int asoc_simple_card_dai_link_of(struct device_node *node,
+                                       struct simple_card_data *priv,
+                                       int idx,
+                                       bool is_top_level_node)
 {
+       struct device *dev = simple_priv_to_dev(priv);
+       struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
+       struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
        struct device_node *np = NULL;
        struct device_node *bitclkmaster = NULL;
        struct device_node *framemaster = NULL;
@@ -176,8 +241,9 @@ static int simple_card_dai_link_of(struct device_node *node,
        char *name;
        char prop[128];
        char *prefix = "";
-       int ret;
+       int ret, cpu_args;
 
+       /* For single DAI link & old style of DT node */
        if (is_top_level_node)
                prefix = "simple-audio-card,";
 
@@ -195,7 +261,8 @@ static int simple_card_dai_link_of(struct device_node *node,
 
        ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai,
                                            &dai_link->cpu_of_node,
-                                           &dai_link->cpu_dai_name);
+                                           &dai_link->cpu_dai_name,
+                                           &cpu_args);
        if (ret < 0)
                goto dai_link_of_err;
 
@@ -226,14 +293,16 @@ static int simple_card_dai_link_of(struct device_node *node,
 
        ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai,
                                            &dai_link->codec_of_node,
-                                           &dai_link->codec_dai_name);
+                                           &dai_link->codec_dai_name, NULL);
        if (ret < 0)
                goto dai_link_of_err;
 
        if (strlen(prefix) && !bitclkmaster && !framemaster) {
-               /* No dai-link level and master setting was not found from
-                  sound node level, revert back to legacy DT parsing and
-                  take the settings from codec node. */
+               /*
+                * No DAI link level and master setting was found
+                * from sound node level, revert back to legacy DT
+                * parsing and take the settings from codec node.
+                */
                dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n",
                        __func__);
                dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt =
@@ -262,10 +331,10 @@ static int simple_card_dai_link_of(struct device_node *node,
                goto dai_link_of_err;
        }
 
-       /* simple-card assumes platform == cpu */
+       /* Simple Card assumes platform == cpu */
        dai_link->platform_of_node = dai_link->cpu_of_node;
 
-       /* Link name is created from CPU/CODEC dai name */
+       /* DAI link name is created from CPU/CODEC dai name */
        name = devm_kzalloc(dev,
                            strlen(dai_link->cpu_dai_name)   +
                            strlen(dai_link->codec_dai_name) + 2,
@@ -274,6 +343,7 @@ static int simple_card_dai_link_of(struct device_node *node,
                                dai_link->codec_dai_name);
        dai_link->name = dai_link->stream_name = name;
        dai_link->ops = &asoc_simple_card_ops;
+       dai_link->init = asoc_simple_card_dai_init;
 
        dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
        dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
@@ -285,6 +355,18 @@ static int simple_card_dai_link_of(struct device_node *node,
                dai_props->codec_dai.fmt,
                dai_props->codec_dai.sysclk);
 
+       /*
+        * In soc_bind_dai_link() will check cpu name after
+        * of_node matching if dai_link has cpu_dai_name.
+        * but, it will never match if name was created by
+        * fmt_single_name() remove cpu_dai_name if cpu_args
+        * was 0. See:
+        *      fmt_single_name()
+        *      fmt_multiple_name()
+        */
+       if (!cpu_args)
+               dai_link->cpu_dai_name = NULL;
+
 dai_link_of_err:
        if (np)
                of_node_put(np);
@@ -296,19 +378,19 @@ dai_link_of_err:
 }
 
 static int asoc_simple_card_parse_of(struct device_node *node,
-                                    struct simple_card_data *priv,
-                                    struct device *dev,
-                                    int multi)
+                                    struct simple_card_data *priv)
 {
-       struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link;
-       struct simple_dai_props *dai_props = priv->dai_props;
+       struct device *dev = simple_priv_to_dev(priv);
        u32 val;
        int ret;
 
-       /* parsing the card name from DT */
+       if (!node)
+               return -EINVAL;
+
+       /* Parse the card name from DT */
        snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");
 
-       /* off-codec widgets */
+       /* The off-codec widgets */
        if (of_property_read_bool(node, "simple-audio-card,widgets")) {
                ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
                                        "simple-audio-card,widgets");
@@ -332,32 +414,45 @@ static int asoc_simple_card_parse_of(struct device_node *node,
        dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
                priv->snd_card.name : "");
 
-       if (multi) {
+       /* Single/Muti DAI link(s) & New style of DT node */
+       if (of_get_child_by_name(node, "simple-audio-card,dai-link")) {
                struct device_node *np = NULL;
-               int i;
-               for (i = 0; (np = of_get_next_child(node, np)); i++) {
+               int i = 0;
+
+               for_each_child_of_node(node, np) {
                        dev_dbg(dev, "\tlink %d:\n", i);
-                       ret = simple_card_dai_link_of(np, dev, dai_link + i,
-                                                     dai_props + i, false);
+                       ret = asoc_simple_card_dai_link_of(np, priv,
+                                                          i, false);
                        if (ret < 0) {
                                of_node_put(np);
                                return ret;
                        }
+                       i++;
                }
        } else {
-               ret = simple_card_dai_link_of(node, dev, dai_link, dai_props,
-                                             true);
+               /* For single DAI link & old style of DT node */
+               ret = asoc_simple_card_dai_link_of(node, priv, 0, true);
                if (ret < 0)
                        return ret;
        }
 
+       priv->gpio_hp_det = of_get_named_gpio(node,
+                               "simple-audio-card,hp-det-gpio", 0);
+       if (priv->gpio_hp_det == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
+       priv->gpio_mic_det = of_get_named_gpio(node,
+                               "simple-audio-card,mic-det-gpio", 0);
+       if (priv->gpio_mic_det == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
        if (!priv->snd_card.name)
                priv->snd_card.name = priv->snd_card.dai_link->name;
 
        return 0;
 }
 
-/* update the reference count of the devices nodes at end of probe */
+/* Decrease the reference count of the device nodes */
 static int asoc_simple_card_unref(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
@@ -384,34 +479,32 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
        struct snd_soc_dai_link *dai_link;
        struct device_node *np = pdev->dev.of_node;
        struct device *dev = &pdev->dev;
-       int num_links, multi, ret;
+       int num_links, ret;
 
-       /* get the number of DAI links */
-       if (np && of_get_child_by_name(np, "simple-audio-card,dai-link")) {
+       /* Get the number of DAI links */
+       if (np && of_get_child_by_name(np, "simple-audio-card,dai-link"))
                num_links = of_get_child_count(np);
-               multi = 1;
-       } else {
+       else
                num_links = 1;
-               multi = 0;
-       }
 
-       /* allocate the private data and the DAI link array */
+       /* Allocate the private data and the DAI link array */
        priv = devm_kzalloc(dev,
                        sizeof(*priv) + sizeof(*dai_link) * num_links,
                        GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
-       /*
-        * init snd_soc_card
-        */
+       /* Init snd_soc_card */
        priv->snd_card.owner = THIS_MODULE;
        priv->snd_card.dev = dev;
        dai_link = priv->dai_link;
        priv->snd_card.dai_link = dai_link;
        priv->snd_card.num_links = num_links;
 
-       /* get room for the other properties */
+       priv->gpio_hp_det = -ENOENT;
+       priv->gpio_mic_det = -ENOENT;
+
+       /* Get room for the other properties */
        priv->dai_props = devm_kzalloc(dev,
                        sizeof(*priv->dai_props) * num_links,
                        GFP_KERNEL);
@@ -420,25 +513,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
 
        if (np && of_device_is_available(np)) {
 
-               ret = asoc_simple_card_parse_of(np, priv, dev, multi);
+               ret = asoc_simple_card_parse_of(np, priv);
                if (ret < 0) {
                        if (ret != -EPROBE_DEFER)
                                dev_err(dev, "parse error %d\n", ret);
                        goto err;
                }
 
-               /*
-                * soc_bind_dai_link() will check cpu name
-                * after of_node matching if dai_link has cpu_dai_name.
-                * but, it will never match if name was created by fmt_single_name()
-                * remove cpu_dai_name to escape name matching.
-                * see
-                *      fmt_single_name()
-                *      fmt_multiple_name()
-                */
-               if (num_links == 1)
-                       dai_link->cpu_dai_name = NULL;
-
        } else {
                struct asoc_simple_card_info *cinfo;
 
@@ -464,6 +545,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
                dai_link->codec_name    = cinfo->codec;
                dai_link->cpu_dai_name  = cinfo->cpu_dai.name;
                dai_link->codec_dai_name = cinfo->codec_dai.name;
+               dai_link->init          = asoc_simple_card_dai_init;
                memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
                                        sizeof(priv->dai_props->cpu_dai));
                memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
@@ -473,11 +555,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
                priv->dai_props->codec_dai.fmt  |= cinfo->daifmt;
        }
 
-       /*
-        * init snd_soc_dai_link
-        */
-       dai_link->init = asoc_simple_card_dai_init;
-
        snd_soc_card_set_drvdata(&priv->snd_card, priv);
 
        ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
@@ -491,6 +568,16 @@ err:
 
 static int asoc_simple_card_remove(struct platform_device *pdev)
 {
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
+
+       if (gpio_is_valid(priv->gpio_hp_det))
+               snd_soc_jack_free_gpios(&simple_card_hp_jack, 1,
+                                       &simple_card_hp_jack_gpio);
+       if (gpio_is_valid(priv->gpio_mic_det))
+               snd_soc_jack_free_gpios(&simple_card_mic_jack, 1,
+                                       &simple_card_mic_jack_gpio);
+
        return asoc_simple_card_unref(pdev);
 }
 
index 7acbfc43a0c6df74404e456c05f2590867c0410b..f841786dad155c56904656d51114068e086654be 100644 (file)
@@ -2,7 +2,8 @@
 snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
 snd-soc-sst-acpi-objs := sst-acpi.o
 
-snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o sst-mfld-platform-compress.o
+snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
+       sst-mfld-platform-compress.o sst-atom-controls.o
 snd-soc-mfld-machine-objs := mfld_machine.o
 
 obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
index b8b8af571ef169307e8b0e1efb68b5435c97df66..d52681e7225e72737145463f5db7664f6c6037af 100644 (file)
@@ -139,6 +139,7 @@ static struct snd_soc_card byt_max98090_card = {
        .num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map),
        .controls = byt_max98090_controls,
        .num_controls = ARRAY_SIZE(byt_max98090_controls),
+       .fully_routed = true,
 };
 
 static int byt_max98090_probe(struct platform_device *pdev)
index 234a58de3c53b0f4e758fa04e93db5012fcfda0b..e03abdf21c1bc3f4dbff62f4cd46d80327dbadd1 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/platform_device.h>
 #include <linux/acpi.h>
 #include <linux/device.h>
+#include <linux/dmi.h>
 #include <linux/slab.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -36,8 +37,6 @@ static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
 static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
        {"Headset Mic", NULL, "MICBIAS1"},
        {"IN2P", NULL, "Headset Mic"},
-       {"IN2N", NULL, "Headset Mic"},
-       {"DMIC1", NULL, "Internal Mic"},
        {"Headphone", NULL, "HPOL"},
        {"Headphone", NULL, "HPOR"},
        {"Speaker", NULL, "SPOLP"},
@@ -46,6 +45,31 @@ static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
        {"Speaker", NULL, "SPORN"},
 };
 
+static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
+       {"DMIC1", NULL, "Internal Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
+       {"DMIC2", NULL, "Internal Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
+       {"Internal Mic", NULL, "MICBIAS1"},
+       {"IN1P", NULL, "Internal Mic"},
+};
+
+enum {
+       BYT_RT5640_DMIC1_MAP,
+       BYT_RT5640_DMIC2_MAP,
+       BYT_RT5640_IN1_MAP,
+};
+
+#define BYT_RT5640_MAP(quirk)  ((quirk) & 0xff)
+#define BYT_RT5640_DMIC_EN     BIT(16)
+
+static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
+                                       BYT_RT5640_DMIC_EN;
+
 static const struct snd_kcontrol_new byt_rt5640_controls[] = {
        SOC_DAPM_PIN_SWITCH("Headphone"),
        SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -77,12 +101,41 @@ static int byt_rt5640_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
+static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
+{
+       byt_rt5640_quirk = (unsigned long)id->driver_data;
+       return 1;
+}
+
+static const struct dmi_system_id byt_rt5640_quirk_table[] = {
+       {
+               .callback = byt_rt5640_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+               },
+               .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
+       },
+       {
+               .callback = byt_rt5640_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
+               },
+               .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
+                                                BYT_RT5640_DMIC_EN),
+       },
+       {}
+};
+
 static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
 {
        int ret;
        struct snd_soc_codec *codec = runtime->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
        struct snd_soc_card *card = runtime->card;
+       const struct snd_soc_dapm_route *custom_map;
+       int num_routes;
 
        card->dapm.idle_bias_off = true;
 
@@ -93,6 +146,31 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
                return ret;
        }
 
+       dmi_check_system(byt_rt5640_quirk_table);
+       switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
+       case BYT_RT5640_IN1_MAP:
+               custom_map = byt_rt5640_intmic_in1_map;
+               num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
+               break;
+       case BYT_RT5640_DMIC2_MAP:
+               custom_map = byt_rt5640_intmic_dmic2_map;
+               num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
+               break;
+       default:
+               custom_map = byt_rt5640_intmic_dmic1_map;
+               num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
+       }
+
+       ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
+       if (ret)
+               return ret;
+
+       if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
+               ret = rt5640_dmic_enable(codec, 0, 0);
+               if (ret)
+                       return ret;
+       }
+
        snd_soc_dapm_ignore_suspend(dapm, "HPOL");
        snd_soc_dapm_ignore_suspend(dapm, "HPOR");
 
@@ -131,6 +209,7 @@ static struct snd_soc_card byt_rt5640_card = {
        .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
        .dapm_routes = byt_rt5640_audio_map,
        .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
+       .fully_routed = true,
 };
 
 static int byt_rt5640_probe(struct platform_device *pdev)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c
new file mode 100644 (file)
index 0000000..7104a34
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ *  sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld
+ *
+ *  Copyright (C) 2013-14 Intel Corp
+ *  Author: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *     Vinod Koul <vinod.koul@intel.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; 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "sst-mfld-platform.h"
+#include "sst-atom-controls.h"
+
+static int sst_fill_byte_control(struct sst_data *drv,
+                                        u8 ipc_msg, u8 block,
+                                        u8 task_id, u8 pipe_id,
+                                        u16 len, void *cmd_data)
+{
+       struct snd_sst_bytes_v2 *byte_data = drv->byte_stream;
+
+       byte_data->type = SST_CMD_BYTES_SET;
+       byte_data->ipc_msg = ipc_msg;
+       byte_data->block = block;
+       byte_data->task_id = task_id;
+       byte_data->pipe_id = pipe_id;
+
+       if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) {
+               dev_err(&drv->pdev->dev, "command length too big (%u)", len);
+               return -EINVAL;
+       }
+       byte_data->len = len;
+       memcpy(byte_data->bytes, cmd_data, len);
+       print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET,
+                            byte_data, len + sizeof(*byte_data));
+       return 0;
+}
+
+static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv,
+                                u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
+                                void *cmd_data, u16 len)
+{
+       int ret = 0;
+
+       ret = sst_fill_byte_control(drv, ipc_msg,
+                               block, task_id, pipe_id, len, cmd_data);
+       if (ret < 0)
+               return ret;
+       return sst->ops->send_byte_stream(sst->dev, drv->byte_stream);
+}
+
+/**
+ * sst_fill_and_send_cmd - generate the IPC message and send it to the FW
+ * @ipc_msg:   type of IPC (CMD, SET_PARAMS, GET_PARAMS)
+ * @cmd_data:  the IPC payload
+ */
+static int sst_fill_and_send_cmd(struct sst_data *drv,
+                                u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
+                                void *cmd_data, u16 len)
+{
+       int ret;
+
+       mutex_lock(&drv->lock);
+       ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block,
+                                       task_id, pipe_id, cmd_data, len);
+       mutex_unlock(&drv->lock);
+
+       return ret;
+}
+
+static int sst_send_algo_cmd(struct sst_data *drv,
+                             struct sst_algo_control *bc)
+{
+       int len, ret = 0;
+       struct sst_cmd_set_params *cmd;
+
+       /*bc->max includes sizeof algos + length field*/
+       len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max;
+
+       cmd = kzalloc(len, GFP_KERNEL);
+       if (cmd == NULL)
+               return -ENOMEM;
+
+       SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id);
+       cmd->command_id = bc->cmd_id;
+       memcpy(cmd->params, bc->params, bc->max);
+
+       ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+                               SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len);
+       kfree(cmd);
+       return ret;
+}
+
+static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_info *uinfo)
+{
+       struct sst_algo_control *bc = (void *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = bc->max;
+
+       return 0;
+}
+
+static int sst_algo_control_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct sst_algo_control *bc = (void *)kcontrol->private_value;
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+
+       switch (bc->type) {
+       case SST_ALGO_PARAMS:
+               memcpy(ucontrol->value.bytes.data, bc->params, bc->max);
+               break;
+       default:
+               dev_err(component->dev, "Invalid Input- algo type:%d\n",
+                               bc->type);
+               return -EINVAL;
+
+       }
+       return 0;
+}
+
+static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       int ret = 0;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+       struct sst_algo_control *bc = (void *)kcontrol->private_value;
+
+       dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name);
+       mutex_lock(&drv->lock);
+       switch (bc->type) {
+       case SST_ALGO_PARAMS:
+               memcpy(bc->params, ucontrol->value.bytes.data, bc->max);
+               break;
+       default:
+               mutex_unlock(&drv->lock);
+               dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n",
+                               bc->type);
+               return -EINVAL;
+       }
+       /*if pipe is enabled, need to send the algo params from here*/
+       if (bc->w && bc->w->power)
+               ret = sst_send_algo_cmd(drv, bc);
+       mutex_unlock(&drv->lock);
+
+       return ret;
+}
+
+static const struct snd_kcontrol_new sst_algo_controls[] = {
+       SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
+                SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
+       SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24,
+               SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
+       SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 286, SST_MODULE_ID_MDRP,
+               SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),
+       SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24,
+               SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
+       SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24,
+               SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
+       SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 286, SST_MODULE_ID_MDRP,
+               SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),
+       SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT,
+               SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO),
+       SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 52, SST_MODULE_ID_FILT_DCR,
+               SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
+       SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 52, SST_MODULE_ID_FILT_DCR,
+               SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
+
+};
+
+static int sst_algo_control_init(struct device *dev)
+{
+       int i = 0;
+       struct sst_algo_control *bc;
+       /*allocate space to cache the algo parameters in the driver*/
+       for (i = 0; i < ARRAY_SIZE(sst_algo_controls); i++) {
+               bc = (struct sst_algo_control *)sst_algo_controls[i].private_value;
+               bc->params = devm_kzalloc(dev, bc->max, GFP_KERNEL);
+               if (bc->params == NULL)
+                       return -ENOMEM;
+       }
+       return 0;
+}
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+{
+       int ret = 0;
+       struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
+
+       drv->byte_stream = devm_kzalloc(platform->dev,
+                                       SST_MAX_BIN_BYTES, GFP_KERNEL);
+       if (!drv->byte_stream)
+               return -ENOMEM;
+
+       /*Initialize algo control params*/
+       ret = sst_algo_control_init(platform->dev);
+       if (ret)
+               return ret;
+       ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
+                       ARRAY_SIZE(sst_algo_controls));
+       return ret;
+}
index 14063ab8c7c5a8df912655319d0509247b3bf81a..a73e894b175c03836e4aa8d437a6fe3ee35de907 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ *  sst-atom-controls.h - Intel MID Platform driver header file
+ *
  *  Copyright (C) 2013-14 Intel Corp
  *  Author: Ramesh Babu <ramesh.babu.koul@intel.com>
  *     Omair M Abdullah <omair.m.abdullah@intel.com>
  *
  */
 
-#ifndef __SST_CONTROLS_V2_H__
-#define __SST_CONTROLS_V2_H__
+#ifndef __SST_ATOM_CONTROLS_H__
+#define __SST_ATOM_CONTROLS_H__
 
 enum {
        MERR_DPCM_AUDIO = 0,
        MERR_DPCM_COMPR,
 };
 
+/* define a bit for each mixer input */
+#define SST_MIX_IP(x)          (x)
+
+#define SST_IP_CODEC0          SST_MIX_IP(2)
+#define SST_IP_CODEC1          SST_MIX_IP(3)
+#define SST_IP_LOOP0           SST_MIX_IP(4)
+#define SST_IP_LOOP1           SST_MIX_IP(5)
+#define SST_IP_LOOP2           SST_MIX_IP(6)
+#define SST_IP_PROBE           SST_MIX_IP(7)
+#define SST_IP_VOIP            SST_MIX_IP(12)
+#define SST_IP_PCM0            SST_MIX_IP(13)
+#define SST_IP_PCM1            SST_MIX_IP(14)
+#define SST_IP_MEDIA0          SST_MIX_IP(17)
+#define SST_IP_MEDIA1          SST_MIX_IP(18)
+#define SST_IP_MEDIA2          SST_MIX_IP(19)
+#define SST_IP_MEDIA3          SST_MIX_IP(20)
+
+#define SST_IP_LAST            SST_IP_MEDIA3
+
+#define SST_SWM_INPUT_COUNT    (SST_IP_LAST + 1)
+#define SST_CMD_SWM_MAX_INPUTS 6
+
+#define SST_PATH_ID_SHIFT      8
+#define SST_DEFAULT_LOCATION_ID        0xFFFF
+#define SST_DEFAULT_CELL_NBR   0xFF
+#define SST_DEFAULT_MODULE_ID  0xFFFF
+
+/*
+ * Audio DSP Path Ids. Specified by the audio DSP FW
+ */
+enum sst_path_index {
+       SST_PATH_INDEX_CODEC_OUT0               = (0x02 << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_CODEC_OUT1               = (0x03 << SST_PATH_ID_SHIFT),
+
+       SST_PATH_INDEX_SPROT_LOOP_OUT           = (0x04 << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_MEDIA_LOOP1_OUT          = (0x05 << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_MEDIA_LOOP2_OUT          = (0x06 << SST_PATH_ID_SHIFT),
+
+       SST_PATH_INDEX_VOIP_OUT                 = (0x0C << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_PCM0_OUT                 = (0x0D << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_PCM1_OUT                 = (0x0E << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_PCM2_OUT                 = (0x0F << SST_PATH_ID_SHIFT),
+
+       SST_PATH_INDEX_MEDIA0_OUT               = (0x12 << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_MEDIA1_OUT               = (0x13 << SST_PATH_ID_SHIFT),
+
+
+       /* Start of input paths */
+       SST_PATH_INDEX_CODEC_IN0                = (0x82 << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_CODEC_IN1                = (0x83 << SST_PATH_ID_SHIFT),
+
+       SST_PATH_INDEX_SPROT_LOOP_IN            = (0x84 << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_MEDIA_LOOP1_IN           = (0x85 << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_MEDIA_LOOP2_IN           = (0x86 << SST_PATH_ID_SHIFT),
+
+       SST_PATH_INDEX_VOIP_IN                  = (0x8C << SST_PATH_ID_SHIFT),
+
+       SST_PATH_INDEX_PCM0_IN                  = (0x8D << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_PCM1_IN                  = (0x8E << SST_PATH_ID_SHIFT),
+
+       SST_PATH_INDEX_MEDIA0_IN                = (0x8F << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_MEDIA1_IN                = (0x90 << SST_PATH_ID_SHIFT),
+       SST_PATH_INDEX_MEDIA2_IN                = (0x91 << SST_PATH_ID_SHIFT),
+
+       SST_PATH_INDEX_MEDIA3_IN                = (0x9C << SST_PATH_ID_SHIFT),
+
+       SST_PATH_INDEX_RESERVED                 = (0xFF << SST_PATH_ID_SHIFT),
+};
+
+/*
+ * path IDs
+ */
+enum sst_swm_inputs {
+       SST_SWM_IN_CODEC0       = (SST_PATH_INDEX_CODEC_IN0       | SST_DEFAULT_CELL_NBR),
+       SST_SWM_IN_CODEC1       = (SST_PATH_INDEX_CODEC_IN1       | SST_DEFAULT_CELL_NBR),
+       SST_SWM_IN_SPROT_LOOP   = (SST_PATH_INDEX_SPROT_LOOP_IN   | SST_DEFAULT_CELL_NBR),
+       SST_SWM_IN_MEDIA_LOOP1  = (SST_PATH_INDEX_MEDIA_LOOP1_IN  | SST_DEFAULT_CELL_NBR),
+       SST_SWM_IN_MEDIA_LOOP2  = (SST_PATH_INDEX_MEDIA_LOOP2_IN  | SST_DEFAULT_CELL_NBR),
+       SST_SWM_IN_VOIP         = (SST_PATH_INDEX_VOIP_IN         | SST_DEFAULT_CELL_NBR),
+       SST_SWM_IN_PCM0         = (SST_PATH_INDEX_PCM0_IN         | SST_DEFAULT_CELL_NBR),
+       SST_SWM_IN_PCM1         = (SST_PATH_INDEX_PCM1_IN         | SST_DEFAULT_CELL_NBR),
+       SST_SWM_IN_MEDIA0       = (SST_PATH_INDEX_MEDIA0_IN       | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+       SST_SWM_IN_MEDIA1       = (SST_PATH_INDEX_MEDIA1_IN       | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+       SST_SWM_IN_MEDIA2       = (SST_PATH_INDEX_MEDIA2_IN       | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+       SST_SWM_IN_MEDIA3       = (SST_PATH_INDEX_MEDIA3_IN       | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+       SST_SWM_IN_END          = (SST_PATH_INDEX_RESERVED        | SST_DEFAULT_CELL_NBR)
+};
+
+/*
+ * path IDs
+ */
+enum sst_swm_outputs {
+       SST_SWM_OUT_CODEC0      = (SST_PATH_INDEX_CODEC_OUT0      | SST_DEFAULT_CELL_NBR),
+       SST_SWM_OUT_CODEC1      = (SST_PATH_INDEX_CODEC_OUT1      | SST_DEFAULT_CELL_NBR),
+       SST_SWM_OUT_SPROT_LOOP  = (SST_PATH_INDEX_SPROT_LOOP_OUT  | SST_DEFAULT_CELL_NBR),
+       SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR),
+       SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR),
+       SST_SWM_OUT_VOIP        = (SST_PATH_INDEX_VOIP_OUT        | SST_DEFAULT_CELL_NBR),
+       SST_SWM_OUT_PCM0        = (SST_PATH_INDEX_PCM0_OUT        | SST_DEFAULT_CELL_NBR),
+       SST_SWM_OUT_PCM1        = (SST_PATH_INDEX_PCM1_OUT        | SST_DEFAULT_CELL_NBR),
+       SST_SWM_OUT_PCM2        = (SST_PATH_INDEX_PCM2_OUT        | SST_DEFAULT_CELL_NBR),
+       SST_SWM_OUT_MEDIA0      = (SST_PATH_INDEX_MEDIA0_OUT      | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+       SST_SWM_OUT_MEDIA1      = (SST_PATH_INDEX_MEDIA1_OUT      | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+       SST_SWM_OUT_END         = (SST_PATH_INDEX_RESERVED        | SST_DEFAULT_CELL_NBR),
+};
+
+enum sst_ipc_msg {
+       SST_IPC_IA_CMD = 1,
+       SST_IPC_IA_SET_PARAMS,
+       SST_IPC_IA_GET_PARAMS,
+};
+
+enum sst_cmd_type {
+       SST_CMD_BYTES_SET = 1,
+       SST_CMD_BYTES_GET = 2,
+};
+
+enum sst_task {
+       SST_TASK_SBA = 1,
+       SST_TASK_MMX,
+};
+
+enum sst_type {
+       SST_TYPE_CMD = 1,
+       SST_TYPE_PARAMS,
+};
+
+enum sst_flag {
+       SST_FLAG_BLOCKED = 1,
+       SST_FLAG_NONBLOCK,
+};
+
+/*
+ * Enumeration for indexing the gain cells in VB_SET_GAIN DSP command
+ */
+enum sst_gain_index {
+       /* GAIN IDs for SB task start here */
+       SST_GAIN_INDEX_CODEC_OUT0,
+       SST_GAIN_INDEX_CODEC_OUT1,
+       SST_GAIN_INDEX_CODEC_IN0,
+       SST_GAIN_INDEX_CODEC_IN1,
+
+       SST_GAIN_INDEX_SPROT_LOOP_OUT,
+       SST_GAIN_INDEX_MEDIA_LOOP1_OUT,
+       SST_GAIN_INDEX_MEDIA_LOOP2_OUT,
+
+       SST_GAIN_INDEX_PCM0_IN_LEFT,
+       SST_GAIN_INDEX_PCM0_IN_RIGHT,
+
+       SST_GAIN_INDEX_PCM1_OUT_LEFT,
+       SST_GAIN_INDEX_PCM1_OUT_RIGHT,
+       SST_GAIN_INDEX_PCM1_IN_LEFT,
+       SST_GAIN_INDEX_PCM1_IN_RIGHT,
+       SST_GAIN_INDEX_PCM2_OUT_LEFT,
+
+       SST_GAIN_INDEX_PCM2_OUT_RIGHT,
+       SST_GAIN_INDEX_VOIP_OUT,
+       SST_GAIN_INDEX_VOIP_IN,
+
+       /* Gain IDs for MMX task start here */
+       SST_GAIN_INDEX_MEDIA0_IN_LEFT,
+       SST_GAIN_INDEX_MEDIA0_IN_RIGHT,
+       SST_GAIN_INDEX_MEDIA1_IN_LEFT,
+       SST_GAIN_INDEX_MEDIA1_IN_RIGHT,
+
+       SST_GAIN_INDEX_MEDIA2_IN_LEFT,
+       SST_GAIN_INDEX_MEDIA2_IN_RIGHT,
+
+       SST_GAIN_INDEX_GAIN_END
+};
+
+/*
+ * Audio DSP module IDs specified by FW spec
+ * TODO: Update with all modules
+ */
+enum sst_module_id {
+       SST_MODULE_ID_PCM                 = 0x0001,
+       SST_MODULE_ID_MP3                 = 0x0002,
+       SST_MODULE_ID_MP24                = 0x0003,
+       SST_MODULE_ID_AAC                 = 0x0004,
+       SST_MODULE_ID_AACP                = 0x0005,
+       SST_MODULE_ID_EAACP               = 0x0006,
+       SST_MODULE_ID_WMA9                = 0x0007,
+       SST_MODULE_ID_WMA10               = 0x0008,
+       SST_MODULE_ID_WMA10P              = 0x0009,
+       SST_MODULE_ID_RA                  = 0x000A,
+       SST_MODULE_ID_DDAC3               = 0x000B,
+       SST_MODULE_ID_TRUE_HD             = 0x000C,
+       SST_MODULE_ID_HD_PLUS             = 0x000D,
+
+       SST_MODULE_ID_SRC                 = 0x0064,
+       SST_MODULE_ID_DOWNMIX             = 0x0066,
+       SST_MODULE_ID_GAIN_CELL           = 0x0067,
+       SST_MODULE_ID_SPROT               = 0x006D,
+       SST_MODULE_ID_BASS_BOOST          = 0x006E,
+       SST_MODULE_ID_STEREO_WDNG         = 0x006F,
+       SST_MODULE_ID_AV_REMOVAL          = 0x0070,
+       SST_MODULE_ID_MIC_EQ              = 0x0071,
+       SST_MODULE_ID_SPL                 = 0x0072,
+       SST_MODULE_ID_ALGO_VTSV           = 0x0073,
+       SST_MODULE_ID_NR                  = 0x0076,
+       SST_MODULE_ID_BWX                 = 0x0077,
+       SST_MODULE_ID_DRP                 = 0x0078,
+       SST_MODULE_ID_MDRP                = 0x0079,
+
+       SST_MODULE_ID_ANA                 = 0x007A,
+       SST_MODULE_ID_AEC                 = 0x007B,
+       SST_MODULE_ID_NR_SNS              = 0x007C,
+       SST_MODULE_ID_SER                 = 0x007D,
+       SST_MODULE_ID_AGC                 = 0x007E,
+
+       SST_MODULE_ID_CNI                 = 0x007F,
+       SST_MODULE_ID_CONTEXT_ALGO_AWARE  = 0x0080,
+       SST_MODULE_ID_FIR_24              = 0x0081,
+       SST_MODULE_ID_IIR_24              = 0x0082,
+
+       SST_MODULE_ID_ASRC                = 0x0083,
+       SST_MODULE_ID_TONE_GEN            = 0x0084,
+       SST_MODULE_ID_BMF                 = 0x0086,
+       SST_MODULE_ID_EDL                 = 0x0087,
+       SST_MODULE_ID_GLC                 = 0x0088,
+
+       SST_MODULE_ID_FIR_16              = 0x0089,
+       SST_MODULE_ID_IIR_16              = 0x008A,
+       SST_MODULE_ID_DNR                 = 0x008B,
+
+       SST_MODULE_ID_VIRTUALIZER         = 0x008C,
+       SST_MODULE_ID_VISUALIZATION       = 0x008D,
+       SST_MODULE_ID_LOUDNESS_OPTIMIZER  = 0x008E,
+       SST_MODULE_ID_REVERBERATION       = 0x008F,
+
+       SST_MODULE_ID_CNI_TX              = 0x0090,
+       SST_MODULE_ID_REF_LINE            = 0x0091,
+       SST_MODULE_ID_VOLUME              = 0x0092,
+       SST_MODULE_ID_FILT_DCR            = 0x0094,
+       SST_MODULE_ID_SLV                 = 0x009A,
+       SST_MODULE_ID_NLF                 = 0x009B,
+       SST_MODULE_ID_TNR                 = 0x009C,
+       SST_MODULE_ID_WNR                 = 0x009D,
+
+       SST_MODULE_ID_LOG                 = 0xFF00,
+
+       SST_MODULE_ID_TASK                = 0xFFFF,
+};
+
+enum sst_cmd {
+       SBA_IDLE                = 14,
+       SBA_VB_SET_SPEECH_PATH  = 26,
+       MMX_SET_GAIN            = 33,
+       SBA_VB_SET_GAIN         = 33,
+       FBA_VB_RX_CNI           = 35,
+       MMX_SET_GAIN_TIMECONST  = 36,
+       SBA_VB_SET_TIMECONST    = 36,
+       SBA_VB_START            = 85,
+       SBA_SET_SWM             = 114,
+       SBA_SET_MDRP            = 116,
+       SBA_HW_SET_SSP          = 117,
+       SBA_SET_MEDIA_LOOP_MAP  = 118,
+       SBA_SET_MEDIA_PATH      = 119,
+       MMX_SET_MEDIA_PATH      = 119,
+       SBA_VB_LPRO             = 126,
+       SBA_VB_SET_FIR          = 128,
+       SBA_VB_SET_IIR          = 129,
+       SBA_SET_SSP_SLOT_MAP    = 130,
+};
+
+enum sst_dsp_switch {
+       SST_SWITCH_OFF = 0,
+       SST_SWITCH_ON = 3,
+};
+
+enum sst_path_switch {
+       SST_PATH_OFF = 0,
+       SST_PATH_ON = 1,
+};
+
+enum sst_swm_state {
+       SST_SWM_OFF = 0,
+       SST_SWM_ON = 3,
+};
+
+#define SST_FILL_LOCATION_IDS(dst, cell_idx, pipe_id)          do {    \
+               dst.location_id.p.cell_nbr_idx = (cell_idx);            \
+               dst.location_id.p.path_id = (pipe_id);                  \
+       } while (0)
+#define SST_FILL_LOCATION_ID(dst, loc_id)                              (\
+       dst.location_id.f = (loc_id))
+#define SST_FILL_MODULE_ID(dst, mod_id)                                        (\
+       dst.module_id = (mod_id))
+
+#define SST_FILL_DESTINATION1(dst, id)                         do {    \
+               SST_FILL_LOCATION_ID(dst, (id) & 0xFFFF);               \
+               SST_FILL_MODULE_ID(dst, ((id) & 0xFFFF0000) >> 16);     \
+       } while (0)
+#define SST_FILL_DESTINATION2(dst, loc_id, mod_id)             do {    \
+               SST_FILL_LOCATION_ID(dst, loc_id);                      \
+               SST_FILL_MODULE_ID(dst, mod_id);                        \
+       } while (0)
+#define SST_FILL_DESTINATION3(dst, cell_idx, path_id, mod_id)  do {    \
+               SST_FILL_LOCATION_IDS(dst, cell_idx, path_id);          \
+               SST_FILL_MODULE_ID(dst, mod_id);                        \
+       } while (0)
+
+#define SST_FILL_DESTINATION(level, dst, ...)                          \
+       SST_FILL_DESTINATION##level(dst, __VA_ARGS__)
+#define SST_FILL_DEFAULT_DESTINATION(dst)                              \
+       SST_FILL_DESTINATION(2, dst, SST_DEFAULT_LOCATION_ID, SST_DEFAULT_MODULE_ID)
+
+struct sst_destination_id {
+       union sst_location_id {
+               struct {
+                       u8 cell_nbr_idx;        /* module index */
+                       u8 path_id;             /* pipe_id */
+               } __packed      p;              /* part */
+               u16             f;              /* full */
+       } __packed location_id;
+       u16        module_id;
+} __packed;
+struct sst_dsp_header {
+       struct sst_destination_id dst;
+       u16 command_id;
+       u16 length;
+} __packed;
+
+/*
+ *
+ * Common Commands
+ *
+ */
+struct sst_cmd_generic {
+       struct sst_dsp_header header;
+} __packed;
+struct sst_cmd_set_params {
+       struct sst_destination_id dst;
+       u16 command_id;
+       char params[0];
+} __packed;
+#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
+       xpname " " xmname " " #xinstance " " xtype
+
+#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
+       xpname " " xmname " " #xinstance " " xtype " " xsubmodule
+enum sst_algo_kcontrol_type {
+       SST_ALGO_PARAMS,
+       SST_ALGO_BYPASS,
+};
+
+struct sst_algo_control {
+       enum sst_algo_kcontrol_type type;
+       int max;
+       u16 module_id;
+       u16 pipe_id;
+       u16 task_id;
+       u16 cmd_id;
+       bool bypass;
+       unsigned char *params;
+       struct snd_soc_dapm_widget *w;
+};
+
+/* size of the control = size of params + size of length field */
+#define SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, xmod, xtask, xcmd)                    \
+       (struct sst_algo_control){                                                      \
+               .max = xcount + sizeof(u16), .type = xtype, .module_id = xmod,                  \
+               .pipe_id = xpipe, .task_id = xtask, .cmd_id = xcmd,                     \
+       }
+
+#define SST_ALGO_KCONTROL(xname, xcount, xmod, xpipe,                                  \
+                         xtask, xcmd, xtype, xinfo, xget, xput)                        \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER,                                            \
+       .name =  xname,                                                                 \
+       .info = xinfo, .get = xget, .put = xput,                                        \
+       .private_value = (unsigned long)&                                               \
+                       SST_ALGO_CTL_VALUE(xcount, xtype, xpipe,                        \
+                                          xmod, xtask, xcmd),                          \
+}
+
+#define SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod,                          \
+                               xpipe, xinstance, xtask, xcmd)                          \
+       SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "params"),        \
+                         xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS,            \
+                         sst_algo_bytes_ctl_info,                                      \
+                         sst_algo_control_get, sst_algo_control_set)
+
+#define SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask)          \
+       SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "bypass"),        \
+                         0, xmod, xpipe, xtask, 0, SST_ALGO_BYPASS,                    \
+                         snd_soc_info_bool_ext,                                        \
+                         sst_algo_control_get, sst_algo_control_set)
+
+#define SST_ALGO_BYPASS_PARAMS(xpname, xmname, xcount, xmod, xpipe,                    \
+                               xinstance, xtask, xcmd)                                 \
+       SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask),          \
+       SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, xpipe, xinstance, xtask, xcmd)
+
+#define SST_COMBO_ALGO_KCONTROL_BYTES(xpname, xmname, xsubmod, xcount, xmod,           \
+                                     xpipe, xinstance, xtask, xcmd)                    \
+       SST_ALGO_KCONTROL(SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, "params",   \
+                                                xsubmod),                              \
+                         xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS,            \
+                         sst_algo_bytes_ctl_info,                                      \
+                         sst_algo_control_get, sst_algo_control_set)
+
+
+struct sst_enum {
+       bool tx;
+       unsigned short reg;
+       unsigned int max;
+       const char * const *texts;
+       struct snd_soc_dapm_widget *w;
+};
 
 #endif
index 61bf6da4bb02211f0c64593e1ba249c38b7701a1..33fc5c3abf558e52850e46c523702559e203b1bc 100644 (file)
@@ -138,11 +138,10 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value)
 static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       struct hsw_priv_data *pdata =
-               snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
@@ -176,11 +175,10 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
 static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       struct hsw_priv_data *pdata =
-               snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
@@ -208,8 +206,8 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
 static int hsw_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
@@ -233,8 +231,8 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol,
 static int hsw_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
        struct sst_hsw *hsw = pdata->hsw;
        unsigned int volume = 0;
 
@@ -778,20 +776,11 @@ static const struct snd_soc_dapm_route graph[] = {
 
 static int hsw_pcm_probe(struct snd_soc_platform *platform)
 {
+       struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
        struct sst_pdata *pdata = dev_get_platdata(platform->dev);
-       struct hsw_priv_data *priv_data;
-       struct device *dma_dev;
+       struct device *dma_dev = pdata->dma_dev;
        int i, ret = 0;
 
-       if (!pdata)
-               return -ENODEV;
-
-       dma_dev = pdata->dma_dev;
-
-       priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL);
-       priv_data->hsw = pdata->dsp;
-       snd_soc_platform_set_drvdata(platform, priv_data);
-
        /* allocate DSP buffer page tables */
        for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
 
@@ -848,27 +837,38 @@ static struct snd_soc_platform_driver hsw_soc_platform = {
        .ops            = &hsw_pcm_ops,
        .pcm_new        = hsw_pcm_new,
        .pcm_free       = hsw_pcm_free,
-       .controls       = hsw_volume_controls,
-       .num_controls   = ARRAY_SIZE(hsw_volume_controls),
-       .dapm_widgets   = widgets,
-       .num_dapm_widgets       = ARRAY_SIZE(widgets),
-       .dapm_routes    = graph,
-       .num_dapm_routes        = ARRAY_SIZE(graph),
 };
 
 static const struct snd_soc_component_driver hsw_dai_component = {
-       .name           = "haswell-dai",
+       .name = "haswell-dai",
+       .controls = hsw_volume_controls,
+       .num_controls = ARRAY_SIZE(hsw_volume_controls),
+       .dapm_widgets = widgets,
+       .num_dapm_widgets = ARRAY_SIZE(widgets),
+       .dapm_routes = graph,
+       .num_dapm_routes = ARRAY_SIZE(graph),
 };
 
 static int hsw_pcm_dev_probe(struct platform_device *pdev)
 {
        struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+       struct hsw_priv_data *priv_data;
        int ret;
 
+       if (!sst_pdata)
+               return -EINVAL;
+
+       priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv_data), GFP_KERNEL);
+       if (!priv_data)
+               return -ENOMEM;
+
        ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata);
        if (ret < 0)
                return -ENODEV;
 
+       priv_data->hsw = sst_pdata->dsp;
+       platform_set_drvdata(pdev, priv_data);
+
        ret = snd_soc_register_platform(&pdev->dev, &hsw_soc_platform);
        if (ret < 0)
                goto err_plat;
index 29c059ca19e8a6a4ac2b21de9a56fa3ea8cbbca2..59467775c9b8bd9486544a69054ceff364fb986f 100644 (file)
@@ -86,7 +86,7 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream)
        /*need to check*/
        str_id = stream->id;
        if (str_id)
-               ret_val = stream->compr_ops->close(str_id);
+               ret_val = stream->compr_ops->close(sst->dev, str_id);
        module_put(sst->dev->driver->owner);
        kfree(stream);
        pr_debug("%s: %d\n", __func__, ret_val);
@@ -158,7 +158,7 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
        cb.drain_cb_param = cstream;
        cb.drain_notify = sst_drain_notify;
 
-       retval = stream->compr_ops->open(&str_params, &cb);
+       retval = stream->compr_ops->open(sst->dev, &str_params, &cb);
        if (retval < 0) {
                pr_err("stream allocation failed %d\n", retval);
                return retval;
@@ -170,10 +170,30 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
 
 static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 {
-       struct sst_runtime_stream *stream =
-               cstream->runtime->private_data;
-
-       return stream->compr_ops->control(cmd, stream->id);
+       struct sst_runtime_stream *stream = cstream->runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               if (stream->compr_ops->stream_start)
+                       return stream->compr_ops->stream_start(sst->dev, stream->id);
+       case SNDRV_PCM_TRIGGER_STOP:
+               if (stream->compr_ops->stream_drop)
+                       return stream->compr_ops->stream_drop(sst->dev, stream->id);
+       case SND_COMPR_TRIGGER_DRAIN:
+               if (stream->compr_ops->stream_drain)
+                       return stream->compr_ops->stream_drain(sst->dev, stream->id);
+       case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+               if (stream->compr_ops->stream_partial_drain)
+                       return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (stream->compr_ops->stream_pause)
+                       return stream->compr_ops->stream_pause(sst->dev, stream->id);
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (stream->compr_ops->stream_pause_release)
+                       return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
+       default:
+               return -EINVAL;
+       }
 }
 
 static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
@@ -182,7 +202,7 @@ static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
        struct sst_runtime_stream *stream;
 
        stream  = cstream->runtime->private_data;
-       stream->compr_ops->tstamp(stream->id, tstamp);
+       stream->compr_ops->tstamp(sst->dev, stream->id, tstamp);
        tstamp->byte_offset = tstamp->copied_total %
                                 (u32)cstream->runtime->buffer_size;
        pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
@@ -195,7 +215,7 @@ static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
        struct sst_runtime_stream *stream;
 
        stream  = cstream->runtime->private_data;
-       stream->compr_ops->ack(stream->id, (unsigned long)bytes);
+       stream->compr_ops->ack(sst->dev, stream->id, (unsigned long)bytes);
        stream->bytes_written += bytes;
 
        return 0;
@@ -225,7 +245,7 @@ static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
        struct sst_runtime_stream *stream  =
                 cstream->runtime->private_data;
 
-       return stream->compr_ops->set_metadata(stream->id, metadata);
+       return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata);
 }
 
 struct snd_compr_ops sst_platform_compr_ops = {
index 706212a6a68c4c9d873da9d68cf057396ee38bfc..aa9b600dfc9bfd1468c280ff61b1b7c5f728f159 100644 (file)
@@ -43,12 +43,12 @@ int sst_register_dsp(struct sst_device *dev)
                return -ENODEV;
        mutex_lock(&sst_lock);
        if (sst) {
-               pr_err("we already have a device %s\n", sst->name);
+               dev_err(dev->dev, "we already have a device %s\n", sst->name);
                module_put(dev->dev->driver->owner);
                mutex_unlock(&sst_lock);
                return -EEXIST;
        }
-       pr_debug("registering device %s\n", dev->name);
+       dev_dbg(dev->dev, "registering device %s\n", dev->name);
        sst = dev;
        mutex_unlock(&sst_lock);
        return 0;
@@ -70,7 +70,7 @@ int sst_unregister_dsp(struct sst_device *dev)
        }
 
        module_put(sst->dev->driver->owner);
-       pr_debug("unreg %s\n", sst->name);
+       dev_dbg(dev->dev, "unreg %s\n", sst->name);
        sst = NULL;
        mutex_unlock(&sst_lock);
        return 0;
@@ -252,7 +252,7 @@ int sst_fill_stream_params(void *substream,
 }
 
 static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
-               struct snd_soc_platform *platform)
+               struct snd_soc_dai *dai)
 {
        struct sst_runtime_stream *stream =
                        substream->runtime->private_data;
@@ -260,7 +260,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
        struct snd_sst_params str_params = {0};
        struct snd_sst_alloc_params_ext alloc_params = {0};
        int ret_val = 0;
-       struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
+       struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
 
        /* set codec params and inform SST driver the same */
        sst_fill_pcm_params(substream, &param);
@@ -277,7 +277,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
 
        stream->stream_info.str_id = str_params.stream_id;
 
-       ret_val = stream->ops->open(&str_params);
+       ret_val = stream->ops->open(sst->dev, &str_params);
        if (ret_val <= 0)
                return ret_val;
 
@@ -306,22 +306,31 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
 {
        struct sst_runtime_stream *stream =
                        substream->runtime->private_data;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
        int ret_val;
 
-       pr_debug("setting buffer ptr param\n");
+       dev_dbg(rtd->dev, "setting buffer ptr param\n");
        sst_set_stream_status(stream, SST_PLATFORM_INIT);
        stream->stream_info.period_elapsed = sst_period_elapsed;
        stream->stream_info.arg = substream;
        stream->stream_info.buffer_ptr = 0;
        stream->stream_info.sfreq = substream->runtime->rate;
-       ret_val = stream->ops->device_control(
-                       SST_SND_STREAM_INIT, &stream->stream_info);
+       ret_val = stream->ops->stream_init(sst->dev, &stream->stream_info);
        if (ret_val)
-               pr_err("control_set ret error %d\n", ret_val);
+               dev_err(rtd->dev, "control_set ret error %d\n", ret_val);
        return ret_val;
 
 }
-/* end -- helper functions */
+
+static int power_up_sst(struct sst_runtime_stream *stream)
+{
+       return stream->ops->power(sst->dev, true);
+}
+
+static void power_down_sst(struct sst_runtime_stream *stream)
+{
+       stream->ops->power(sst->dev, false);
+}
 
 static int sst_media_open(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
@@ -339,7 +348,7 @@ static int sst_media_open(struct snd_pcm_substream *substream,
        mutex_lock(&sst_lock);
        if (!sst ||
            !try_module_get(sst->dev->driver->owner)) {
-               pr_err("no device available to run\n");
+               dev_err(dai->dev, "no device available to run\n");
                ret_val = -ENODEV;
                goto out_ops;
        }
@@ -352,6 +361,10 @@ static int sst_media_open(struct snd_pcm_substream *substream,
        /* allocate memory for SST API set */
        runtime->private_data = stream;
 
+       ret_val = power_up_sst(stream);
+       if (ret_val < 0)
+               return ret_val;
+
        /* Make sure, that the period size is always even */
        snd_pcm_hw_constraint_step(substream->runtime, 0,
                           SNDRV_PCM_HW_PARAM_PERIODS, 2);
@@ -371,26 +384,29 @@ static void sst_media_close(struct snd_pcm_substream *substream,
        int ret_val = 0, str_id;
 
        stream = substream->runtime->private_data;
+       power_down_sst(stream);
+
        str_id = stream->stream_info.str_id;
        if (str_id)
-               ret_val = stream->ops->close(str_id);
+               ret_val = stream->ops->close(sst->dev, str_id);
        module_put(sst->dev->driver->owner);
        kfree(stream);
 }
 
-static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform,
+static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai,
                                               struct snd_pcm_substream *substream)
 {
-       struct sst_data *sst = snd_soc_platform_get_drvdata(platform);
+       struct sst_data *sst = snd_soc_dai_get_drvdata(dai);
        struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
        struct sst_runtime_stream *stream =
                        substream->runtime->private_data;
        u32 str_id = stream->stream_info.str_id;
        unsigned int pipe_id;
+
        pipe_id = map[str_id].device_id;
 
-       pr_debug("%s: got pipe_id = %#x for str_id = %d\n",
-                __func__, pipe_id, str_id);
+       dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n",
+                       pipe_id, str_id);
        return pipe_id;
 }
 
@@ -403,12 +419,11 @@ static int sst_media_prepare(struct snd_pcm_substream *substream,
        stream = substream->runtime->private_data;
        str_id = stream->stream_info.str_id;
        if (stream->stream_info.str_id) {
-               ret_val = stream->ops->device_control(
-                               SST_SND_DROP, &str_id);
+               ret_val = stream->ops->stream_drop(sst->dev, str_id);
                return ret_val;
        }
 
-       ret_val = sst_platform_alloc_stream(substream, dai->platform);
+       ret_val = sst_platform_alloc_stream(substream, dai);
        if (ret_val <= 0)
                return ret_val;
        snprintf(substream->pcm->id, sizeof(substream->pcm->id),
@@ -461,37 +476,40 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
 {
        int ret_val = 0, str_id;
        struct sst_runtime_stream *stream;
-       int str_cmd, status;
+       int status;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
 
-       pr_debug("sst_platform_pcm_trigger called\n");
+       dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n");
+       if (substream->pcm->internal)
+               return 0;
        stream = substream->runtime->private_data;
        str_id = stream->stream_info.str_id;
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               pr_debug("sst: Trigger Start\n");
-               str_cmd = SST_SND_START;
+               dev_dbg(rtd->dev, "sst: Trigger Start\n");
                status = SST_PLATFORM_RUNNING;
                stream->stream_info.arg = substream;
+               ret_val = stream->ops->stream_start(sst->dev, str_id);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               pr_debug("sst: in stop\n");
-               str_cmd = SST_SND_DROP;
+               dev_dbg(rtd->dev, "sst: in stop\n");
                status = SST_PLATFORM_DROPPED;
+               ret_val = stream->ops->stream_drop(sst->dev, str_id);
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               pr_debug("sst: in pause\n");
-               str_cmd = SST_SND_PAUSE;
+               dev_dbg(rtd->dev, "sst: in pause\n");
                status = SST_PLATFORM_PAUSED;
+               ret_val = stream->ops->stream_pause(sst->dev, str_id);
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               pr_debug("sst: in pause release\n");
-               str_cmd = SST_SND_RESUME;
+               dev_dbg(rtd->dev, "sst: in pause release\n");
                status = SST_PLATFORM_RUNNING;
+               ret_val = stream->ops->stream_pause_release(sst->dev, str_id);
                break;
        default:
                return -EINVAL;
        }
-       ret_val = stream->ops->device_control(str_cmd, &str_id);
+
        if (!ret_val)
                sst_set_stream_status(stream, status);
 
@@ -505,16 +523,16 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer
        struct sst_runtime_stream *stream;
        int ret_val, status;
        struct pcm_stream_info *str_info;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
 
        stream = substream->runtime->private_data;
        status = sst_get_stream_status(stream);
        if (status == SST_PLATFORM_INIT)
                return 0;
        str_info = &stream->stream_info;
-       ret_val = stream->ops->device_control(
-                               SST_SND_BUFFER_POINTER, str_info);
+       ret_val = stream->ops->stream_read_tstamp(sst->dev, str_info);
        if (ret_val) {
-               pr_err("sst: error code = %d\n", ret_val);
+               dev_err(rtd->dev, "sst: error code = %d\n", ret_val);
                return ret_val;
        }
        substream->runtime->delay = str_info->pcm_delay;
@@ -530,7 +548,7 @@ static struct snd_pcm_ops sst_platform_ops = {
 
 static void sst_pcm_free(struct snd_pcm *pcm)
 {
-       pr_debug("sst_pcm_free called\n");
+       dev_dbg(pcm->dev, "sst_pcm_free called\n");
        snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
@@ -547,14 +565,20 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
                        snd_dma_continuous_data(GFP_DMA),
                        SST_MIN_BUFFER, SST_MAX_BUFFER);
                if (retval) {
-                       pr_err("dma buffer allocationf fail\n");
+                       dev_err(rtd->dev, "dma buffer allocationf fail\n");
                        return retval;
                }
        }
        return retval;
 }
 
-static struct snd_soc_platform_driver sst_soc_platform_drv = {
+static int sst_soc_probe(struct snd_soc_platform *platform)
+{
+       return sst_dsp_init_v2_dpcm(platform);
+}
+
+static struct snd_soc_platform_driver sst_soc_platform_drv  = {
+       .probe          = sst_soc_probe,
        .ops            = &sst_platform_ops,
        .compr_ops      = &sst_platform_compr_ops,
        .pcm_new        = sst_pcm_new,
@@ -574,13 +598,11 @@ static int sst_platform_probe(struct platform_device *pdev)
 
        drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
        if (drv == NULL) {
-               pr_err("kzalloc failed\n");
                return -ENOMEM;
        }
 
        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
        if (pdata == NULL) {
-               pr_err("kzalloc failed for pdata\n");
                return -ENOMEM;
        }
 
@@ -592,14 +614,14 @@ static int sst_platform_probe(struct platform_device *pdev)
 
        ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
        if (ret) {
-               pr_err("registering soc platform failed\n");
+               dev_err(&pdev->dev, "registering soc platform failed\n");
                return ret;
        }
 
        ret = snd_soc_register_component(&pdev->dev, &sst_component,
                                sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
        if (ret) {
-               pr_err("registering cpu dais failed\n");
+               dev_err(&pdev->dev, "registering cpu dais failed\n");
                snd_soc_unregister_platform(&pdev->dev);
        }
        return ret;
@@ -610,7 +632,7 @@ static int sst_platform_remove(struct platform_device *pdev)
 
        snd_soc_unregister_component(&pdev->dev);
        snd_soc_unregister_platform(&pdev->dev);
-       pr_debug("sst_platform_remove success\n");
+       dev_dbg(&pdev->dev, "sst_platform_remove success\n");
        return 0;
 }
 
index 6c6a42c08e2410b90a898d2e9d77730a4bf7721f..19f83ec51613a3ba46ddeab431dadd6ae7d12854 100644 (file)
@@ -54,20 +54,6 @@ enum sst_drv_status {
        SST_PLATFORM_DROPPED,
 };
 
-enum sst_controls {
-       SST_SND_ALLOC =                 0x00,
-       SST_SND_PAUSE =                 0x01,
-       SST_SND_RESUME =                0x02,
-       SST_SND_DROP =                  0x03,
-       SST_SND_FREE =                  0x04,
-       SST_SND_BUFFER_POINTER =        0x05,
-       SST_SND_STREAM_INIT =           0x06,
-       SST_SND_START    =              0x07,
-       SST_SET_BYTE_STREAM =           0x100A,
-       SST_GET_BYTE_STREAM =           0x100B,
-       SST_MAX_CONTROLS = SST_GET_BYTE_STREAM,
-};
-
 enum sst_stream_ops {
        STREAM_OPS_PLAYBACK = 0,
        STREAM_OPS_CAPTURE,
@@ -113,24 +99,37 @@ struct sst_compress_cb {
 
 struct compress_sst_ops {
        const char *name;
-       int (*open) (struct snd_sst_params *str_params,
-                       struct sst_compress_cb *cb);
-       int (*control) (unsigned int cmd, unsigned int str_id);
-       int (*tstamp) (unsigned int str_id, struct snd_compr_tstamp *tstamp);
-       int (*ack) (unsigned int str_id, unsigned long bytes);
-       int (*close) (unsigned int str_id);
-       int (*get_caps) (struct snd_compr_caps *caps);
-       int (*get_codec_caps) (struct snd_compr_codec_caps *codec);
-       int (*set_metadata) (unsigned int str_id,
+       int (*open)(struct device *dev,
+               struct snd_sst_params *str_params, struct sst_compress_cb *cb);
+       int (*stream_start)(struct device *dev, unsigned int str_id);
+       int (*stream_drop)(struct device *dev, unsigned int str_id);
+       int (*stream_drain)(struct device *dev, unsigned int str_id);
+       int (*stream_partial_drain)(struct device *dev, unsigned int str_id);
+       int (*stream_pause)(struct device *dev, unsigned int str_id);
+       int (*stream_pause_release)(struct device *dev, unsigned int str_id);
+
+       int (*tstamp)(struct device *dev, unsigned int str_id,
+                       struct snd_compr_tstamp *tstamp);
+       int (*ack)(struct device *dev, unsigned int str_id,
+                       unsigned long bytes);
+       int (*close)(struct device *dev, unsigned int str_id);
+       int (*get_caps)(struct snd_compr_caps *caps);
+       int (*get_codec_caps)(struct snd_compr_codec_caps *codec);
+       int (*set_metadata)(struct device *dev, unsigned int str_id,
                        struct snd_compr_metadata *mdata);
-
 };
 
 struct sst_ops {
-       int (*open) (struct snd_sst_params *str_param);
-       int (*device_control) (int cmd, void *arg);
-       int (*set_generic_params)(enum sst_controls cmd, void *arg);
-       int (*close) (unsigned int str_id);
+       int (*open)(struct device *dev, struct snd_sst_params *str_param);
+       int (*stream_init)(struct device *dev, struct pcm_stream_info *str_info);
+       int (*stream_start)(struct device *dev, int str_id);
+       int (*stream_drop)(struct device *dev, int str_id);
+       int (*stream_pause)(struct device *dev, int str_id);
+       int (*stream_pause_release)(struct device *dev, int str_id);
+       int (*stream_read_tstamp)(struct device *dev, struct pcm_stream_info *str_info);
+       int (*send_byte_stream)(struct device *dev, struct snd_sst_bytes_v2 *bytes);
+       int (*close)(struct device *dev, unsigned int str_id);
+       int (*power)(struct device *dev, bool state);
 };
 
 struct sst_runtime_stream {
@@ -152,6 +151,8 @@ struct sst_device {
 };
 
 struct sst_data;
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform);
 void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
 int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
                           struct snd_sst_params *str_params, bool is_compress);
@@ -166,6 +167,7 @@ struct sst_algo_int_control_v2 {
 struct sst_data {
        struct platform_device *pdev;
        struct sst_platform_data *pdata;
+       struct snd_sst_bytes_v2 *byte_stream;
        struct mutex lock;
 };
 int sst_register_dsp(struct sst_device *sst);
index 943922c79f784b1ad70f4f71628c71e6ebb87812..b10ae8074461e9403d6f276943c82cbf5c3531ce 100644 (file)
@@ -168,7 +168,7 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w,
 static int rx51_hp_event(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *k, int event)
 {
-       struct snd_soc_codec *codec = w->dapm->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
        if (SND_SOC_DAPM_EVENT_ON(event))
                tpa6130a2_stereo_enable(codec, 1);
index c196a466eef6ea2f3d45f69e92801a7c263120dd..78fc159559b00228ed055dfaa025bc58f3928e67 100644 (file)
@@ -2,11 +2,10 @@ config SND_SOC_ROCKCHIP
        tristate "ASoC support for Rockchip"
        depends on COMPILE_TEST || ARCH_ROCKCHIP
        select SND_SOC_GENERIC_DMAENGINE_PCM
-       select SND_ROCKCHIP_I2S
        help
          Say Y or M if you want to add support for codecs attached to
          the Rockchip SoCs' Audio interfaces. You will also need to
          select the audio interfaces to support below.
 
-config SND_ROCKCHIP_I2S
+config SND_SOC_ROCKCHIP_I2S
        tristate
index 1006418e1394402570d2668a20f189b57c4a62d5..b9219092b47fd917146ea4a25c195843ffb7c285 100644 (file)
@@ -1,4 +1,4 @@
 # ROCKCHIP Platform Support
 snd-soc-i2s-objs := rockchip_i2s.o
 
-obj-$(CONFIG_SND_ROCKCHIP_I2S) += snd-soc-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o
index fb9e05c9f47199c7bdcbd8413846899b074615ce..f373e37f83050a246c2d35fbed008db7cdd0b5a5 100644 (file)
@@ -108,8 +108,10 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
                        while (val) {
                                regmap_read(i2s->regmap, I2S_CLR, &val);
                                retry--;
-                               if (!retry)
+                               if (!retry) {
                                        dev_warn(i2s->dev, "fail to clear\n");
+                                       break;
+                               }
                        }
                }
        }
@@ -244,16 +246,6 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
        regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val);
        regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               dai->playback_dma_data = &i2s->playback_dma_data;
-               regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
-                                  I2S_DMACR_TDL(1) | I2S_DMACR_TDE_ENABLE);
-       } else {
-               dai->capture_dma_data = &i2s->capture_dma_data;
-               regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
-                                  I2S_DMACR_RDL(1) | I2S_DMACR_RDE_ENABLE);
-       }
-
        return 0;
 }
 
@@ -301,6 +293,16 @@ static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
        return ret;
 }
 
+static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+       struct rk_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
+
+       dai->capture_dma_data = &i2s->capture_dma_data;
+       dai->playback_dma_data = &i2s->playback_dma_data;
+
+       return 0;
+}
+
 static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
        .hw_params = rockchip_i2s_hw_params,
        .set_sysclk = rockchip_i2s_set_sysclk,
@@ -309,7 +311,9 @@ static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
 };
 
 static struct snd_soc_dai_driver rockchip_i2s_dai = {
+       .probe = rockchip_i2s_dai_probe,
        .playback = {
+               .stream_name = "Playback",
                .channels_min = 2,
                .channels_max = 8,
                .rates = SNDRV_PCM_RATE_8000_192000,
@@ -319,6 +323,7 @@ static struct snd_soc_dai_driver rockchip_i2s_dai = {
                            SNDRV_PCM_FMTBIT_S24_LE),
        },
        .capture = {
+               .stream_name = "Capture",
                .channels_min = 2,
                .channels_max = 2,
                .rates = SNDRV_PCM_RATE_8000_192000,
@@ -420,6 +425,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n");
                return PTR_ERR(i2s->hclk);
        }
+       ret = clk_prepare_enable(i2s->hclk);
+       if (ret) {
+               dev_err(i2s->dev, "hclock enable failed %d\n", ret);
+               return ret;
+       }
 
        i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk");
        if (IS_ERR(i2s->mclk)) {
index db6cefa18017c1db57568689661fc21bc80ec6ea..0e8dd985fcb315dab9bf293c8d7ee4dea0b9b34e 100644 (file)
@@ -351,7 +351,7 @@ static void idma_free(struct snd_pcm *pcm)
        if (!buf->area)
                return;
 
-       iounmap(buf->area);
+       iounmap((void __iomem *)buf->area);
 
        buf->area = NULL;
        buf->addr = 0;
@@ -369,7 +369,7 @@ static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream)
        buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS;
        buf->addr = idma.lp_tx_addr;
        buf->bytes = idma_hardware.buffer_bytes_max;
-       buf->area = (unsigned char *)ioremap(buf->addr, buf->bytes);
+       buf->area = (unsigned char * __force)ioremap(buf->addr, buf->bytes);
 
        return 0;
 }
index 278edf9e2a87e24067ced858a6d92d672f40abde..3c8f60423e825d401e659e459afed3f00642799a 100644 (file)
@@ -66,12 +66,12 @@ static struct snd_soc_card odroidx2 = {
        .late_probe             = odroidx2_late_probe,
 };
 
-struct odroidx2_drv_data odroidx2_drvdata = {
+static const struct odroidx2_drv_data odroidx2_drvdata = {
        .dapm_widgets           = odroidx2_dapm_widgets,
        .num_dapm_widgets       = ARRAY_SIZE(odroidx2_dapm_widgets),
 };
 
-struct odroidx2_drv_data odroidu3_drvdata = {
+static const struct odroidx2_drv_data odroidu3_drvdata = {
        .dapm_widgets           = odroidu3_dapm_widgets,
        .num_dapm_widgets       = ARRAY_SIZE(odroidu3_dapm_widgets),
 };
index 9902efcb8ea121a99a930816d727c11c1f614258..a05482651aaeaf5c1ff994de194d65a63857191d 100644 (file)
@@ -228,10 +228,12 @@ static struct snd_soc_dai_link speyside_dai[] = {
        },
 };
 
-static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm)
+static int speyside_wm9081_init(struct snd_soc_component *component)
 {
+       struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+
        /* At any time the WM9081 is active it will have this clock */
-       return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK, 0,
+       return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0,
                                        MCLK_AUDIO_RATE, 0);
 }
 
index c76344350e44421925702139e6acd00db8e560eb..66fddec9543d0ecc1bb414871d035bc244abc521 100644 (file)
@@ -1297,9 +1297,14 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
        struct snd_pcm_substream *substream = io->substream;
        struct dma_async_tx_descriptor *desc;
        int is_play = fsi_stream_is_play(fsi, io);
-       enum dma_data_direction dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+       enum dma_transfer_direction dir;
        int ret = -EIO;
 
+       if (is_play)
+               dir = DMA_MEM_TO_DEV;
+       else
+               dir = DMA_DEV_TO_MEM;
+
        desc = dmaengine_prep_dma_cyclic(io->chan,
                                         substream->runtime->dma_addr,
                                         snd_pcm_lib_buffer_bytes(substream),
index 19f78963e8b9b7fbd9a104736ed4b36a11b1bee8..1922ec57d10a1fd29c174295adb6de5446e8d7a2 100644 (file)
@@ -798,10 +798,8 @@ if (name##_node) {                                                 \
                        mod_parse(src);
                        mod_parse(dvc);
 
-                       if (playback)
-                               of_node_put(playback);
-                       if (capture)
-                               of_node_put(capture);
+                       of_node_put(playback);
+                       of_node_put(capture);
                }
 
                dai_i++;
index 488f9becb44f3a6653dd66a72e1881d8c2168694..32eb6da2d2bde2ac994aeda90ef673fbd6d6e426 100644 (file)
@@ -139,7 +139,7 @@ static int siu_pcm_wr_set(struct siu_port *port_info,
 
        desc->callback = siu_dma_tx_complete;
        desc->callback_param = siu_stream;
-       cookie = desc->tx_submit(desc);
+       cookie = dmaengine_submit(desc);
        if (cookie < 0) {
                dev_err(dev, "Failed to submit a dma transfer\n");
                return cookie;
@@ -189,7 +189,7 @@ static int siu_pcm_rd_set(struct siu_port *port_info,
 
        desc->callback = siu_dma_tx_complete;
        desc->callback_param = siu_stream;
-       cookie = desc->tx_submit(desc);
+       cookie = dmaengine_submit(desc);
        if (cookie < 0) {
                dev_err(dev, "Failed to submit dma descriptor\n");
                return cookie;
index 3a730374e259d2de380aadbb8acd9741e2c3ef93..186dc7f33a55f4dd409b52081945fc89d02ec246 100644 (file)
@@ -100,6 +100,16 @@ static int sirf_usp_pcm_set_dai_fmt(struct snd_soc_dai *dai,
                return -EINVAL;
        }
 
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               usp->daifmt_format |= (fmt & SND_SOC_DAIFMT_INV_MASK);
+               break;
+       default:
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -177,7 +187,7 @@ static int sirf_usp_pcm_hw_params(struct snd_pcm_substream *substream,
 
        shifter_len = data_len;
 
-       switch (usp->daifmt_format) {
+       switch (usp->daifmt_format & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL,
                        USP_I2S_SYNC_CHG, USP_I2S_SYNC_CHG);
@@ -193,6 +203,18 @@ static int sirf_usp_pcm_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
+       switch (usp->daifmt_format & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               regmap_update_bits(usp->regmap, USP_MODE1,
+                       USP_RXD_ACT_EDGE_FALLING | USP_TXD_ACT_EDGE_FALLING,
+                       USP_RXD_ACT_EDGE_FALLING);
+               break;
+       default:
+               return -EINVAL;
+       }
+
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                regmap_update_bits(usp->regmap, USP_TX_FRAME_CTRL,
                        USP_TXC_DATA_LEN_MASK | USP_TXC_FRAME_LEN_MASK
index d074aa91b023f8db8f7dace7f62db25a564517ce..4c8f8a23a0e9de132827fc041438fc1e07a4892c 100644 (file)
@@ -270,79 +270,54 @@ static const struct file_operations codec_reg_fops = {
        .llseek = default_llseek,
 };
 
-static struct dentry *soc_debugfs_create_dir(struct dentry *parent,
-       const char *fmt, ...)
+static void soc_init_component_debugfs(struct snd_soc_component *component)
 {
-       struct dentry *de;
-       va_list ap;
-       char *s;
+       if (component->debugfs_prefix) {
+               char *name;
 
-       va_start(ap, fmt);
-       s = kvasprintf(GFP_KERNEL, fmt, ap);
-       va_end(ap);
+               name = kasprintf(GFP_KERNEL, "%s:%s",
+                       component->debugfs_prefix, component->name);
+               if (name) {
+                       component->debugfs_root = debugfs_create_dir(name,
+                               component->card->debugfs_card_root);
+                       kfree(name);
+               }
+       } else {
+               component->debugfs_root = debugfs_create_dir(component->name,
+                               component->card->debugfs_card_root);
+       }
 
-       if (!s)
-               return NULL;
+       if (!component->debugfs_root) {
+               dev_warn(component->dev,
+                       "ASoC: Failed to create component debugfs directory\n");
+               return;
+       }
 
-       de = debugfs_create_dir(s, parent);
-       kfree(s);
+       snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component),
+               component->debugfs_root);
 
-       return de;
+       if (component->init_debugfs)
+               component->init_debugfs(component);
 }
 
-static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+static void soc_cleanup_component_debugfs(struct snd_soc_component *component)
 {
-       struct dentry *debugfs_card_root = codec->component.card->debugfs_card_root;
+       debugfs_remove_recursive(component->debugfs_root);
+}
 
-       codec->debugfs_codec_root = soc_debugfs_create_dir(debugfs_card_root,
-                                               "codec:%s",
-                                               codec->component.name);
-       if (!codec->debugfs_codec_root) {
-               dev_warn(codec->dev,
-                       "ASoC: Failed to create codec debugfs directory\n");
-               return;
-       }
+static void soc_init_codec_debugfs(struct snd_soc_component *component)
+{
+       struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
 
-       debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root,
+       debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root,
                            &codec->cache_sync);
-       debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root,
-                           &codec->cache_only);
 
        codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
-                                                codec->debugfs_codec_root,
+                                                codec->component.debugfs_root,
                                                 codec, &codec_reg_fops);
        if (!codec->debugfs_reg)
                dev_warn(codec->dev,
                        "ASoC: Failed to create codec register debugfs file\n");
-
-       snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root);
-}
-
-static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
-{
-       debugfs_remove_recursive(codec->debugfs_codec_root);
-}
-
-static void soc_init_platform_debugfs(struct snd_soc_platform *platform)
-{
-       struct dentry *debugfs_card_root = platform->component.card->debugfs_card_root;
-
-       platform->debugfs_platform_root = soc_debugfs_create_dir(debugfs_card_root,
-                                               "platform:%s",
-                                               platform->component.name);
-       if (!platform->debugfs_platform_root) {
-               dev_warn(platform->dev,
-                       "ASoC: Failed to create platform debugfs directory\n");
-               return;
-       }
-
-       snd_soc_dapm_debugfs_init(&platform->component.dapm,
-               platform->debugfs_platform_root);
-}
-
-static void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform)
-{
-       debugfs_remove_recursive(platform->debugfs_platform_root);
 }
 
 static ssize_t codec_list_read_file(struct file *file, char __user *user_buf,
@@ -474,19 +449,15 @@ static void soc_cleanup_card_debugfs(struct snd_soc_card *card)
 
 #else
 
-static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
-{
-}
+#define soc_init_codec_debugfs NULL
 
-static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+static inline void soc_init_component_debugfs(
+       struct snd_soc_component *component)
 {
 }
 
-static inline void soc_init_platform_debugfs(struct snd_soc_platform *platform)
-{
-}
-
-static inline void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform)
+static inline void soc_cleanup_component_debugfs(
+       struct snd_soc_component *component)
 {
 }
 
@@ -579,10 +550,8 @@ int snd_soc_suspend(struct device *dev)
        struct snd_soc_codec *codec;
        int i, j;
 
-       /* If the initialization of this soc device failed, there is no codec
-        * associated with it. Just bail out in this case.
-        */
-       if (list_empty(&card->codec_dev_list))
+       /* If the card is not initialized yet there is nothing to do */
+       if (!card->instantiated)
                return 0;
 
        /* Due to the resume being scheduled into a workqueue we could
@@ -668,7 +637,7 @@ int snd_soc_suspend(struct device *dev)
        list_for_each_entry(codec, &card->codec_dev_list, card_list) {
                /* If there are paths active then the CODEC will be held with
                 * bias _ON and should not be suspended. */
-               if (!codec->suspended && codec->driver->suspend) {
+               if (!codec->suspended) {
                        switch (codec->dapm.bias_level) {
                        case SND_SOC_BIAS_STANDBY:
                                /*
@@ -682,8 +651,10 @@ int snd_soc_suspend(struct device *dev)
                                                "ASoC: idle_bias_off CODEC on over suspend\n");
                                        break;
                                }
+
                        case SND_SOC_BIAS_OFF:
-                               codec->driver->suspend(codec);
+                               if (codec->driver->suspend)
+                                       codec->driver->suspend(codec);
                                codec->suspended = 1;
                                codec->cache_sync = 1;
                                if (codec->component.regmap)
@@ -757,11 +728,12 @@ static void soc_resume_deferred(struct work_struct *work)
                 * left with bias OFF or STANDBY and suspended so we must now
                 * resume.  Otherwise the suspend was suppressed.
                 */
-               if (codec->driver->resume && codec->suspended) {
+               if (codec->suspended) {
                        switch (codec->dapm.bias_level) {
                        case SND_SOC_BIAS_STANDBY:
                        case SND_SOC_BIAS_OFF:
-                               codec->driver->resume(codec);
+                               if (codec->driver->resume)
+                                       codec->driver->resume(codec);
                                codec->suspended = 0;
                                break;
                        default:
@@ -835,10 +807,8 @@ int snd_soc_resume(struct device *dev)
        struct snd_soc_card *card = dev_get_drvdata(dev);
        int i, ac97_control = 0;
 
-       /* If the initialization of this soc device failed, there is no codec
-        * associated with it. Just bail out in this case.
-        */
-       if (list_empty(&card->codec_dev_list))
+       /* If the card is not initialized yet there is nothing to do */
+       if (!card->instantiated)
                return 0;
 
        /* activate pins from sleep state */
@@ -887,35 +857,40 @@ EXPORT_SYMBOL_GPL(snd_soc_resume);
 static const struct snd_soc_dai_ops null_dai_ops = {
 };
 
-static struct snd_soc_codec *soc_find_codec(
-                                       const struct device_node *codec_of_node,
-                                       const char *codec_name)
+static struct snd_soc_component *soc_find_component(
+       const struct device_node *of_node, const char *name)
 {
-       struct snd_soc_codec *codec;
+       struct snd_soc_component *component;
 
-       list_for_each_entry(codec, &codec_list, list) {
-               if (codec_of_node) {
-                       if (codec->dev->of_node != codec_of_node)
-                               continue;
-               } else {
-                       if (strcmp(codec->component.name, codec_name))
-                               continue;
+       list_for_each_entry(component, &component_list, list) {
+               if (of_node) {
+                       if (component->dev->of_node == of_node)
+                               return component;
+               } else if (strcmp(component->name, name) == 0) {
+                       return component;
                }
-
-               return codec;
        }
 
        return NULL;
 }
 
-static struct snd_soc_dai *soc_find_codec_dai(struct snd_soc_codec *codec,
-                                             const char *codec_dai_name)
+static struct snd_soc_dai *snd_soc_find_dai(
+       const struct snd_soc_dai_link_component *dlc)
 {
-       struct snd_soc_dai *codec_dai;
+       struct snd_soc_component *component;
+       struct snd_soc_dai *dai;
 
-       list_for_each_entry(codec_dai, &codec->component.dai_list, list) {
-               if (!strcmp(codec_dai->name, codec_dai_name)) {
-                       return codec_dai;
+       /* Find CPU DAI from registered DAIs*/
+       list_for_each_entry(component, &component_list, list) {
+               if (dlc->of_node && component->dev->of_node != dlc->of_node)
+                       continue;
+               if (dlc->name && strcmp(dev_name(component->dev), dlc->name))
+                       continue;
+               list_for_each_entry(dai, &component->dai_list, list) {
+                       if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
+                               continue;
+
+                       return dai;
                }
        }
 
@@ -926,33 +901,19 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
 {
        struct snd_soc_dai_link *dai_link = &card->dai_link[num];
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
-       struct snd_soc_component *component;
        struct snd_soc_dai_link_component *codecs = dai_link->codecs;
+       struct snd_soc_dai_link_component cpu_dai_component;
        struct snd_soc_dai **codec_dais = rtd->codec_dais;
        struct snd_soc_platform *platform;
-       struct snd_soc_dai *cpu_dai;
        const char *platform_name;
        int i;
 
        dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
 
-       /* Find CPU DAI from registered DAIs*/
-       list_for_each_entry(component, &component_list, list) {
-               if (dai_link->cpu_of_node &&
-                       component->dev->of_node != dai_link->cpu_of_node)
-                       continue;
-               if (dai_link->cpu_name &&
-                       strcmp(dev_name(component->dev), dai_link->cpu_name))
-                       continue;
-               list_for_each_entry(cpu_dai, &component->dai_list, list) {
-                       if (dai_link->cpu_dai_name &&
-                               strcmp(cpu_dai->name, dai_link->cpu_dai_name))
-                               continue;
-
-                       rtd->cpu_dai = cpu_dai;
-               }
-       }
-
+       cpu_dai_component.name = dai_link->cpu_name;
+       cpu_dai_component.of_node = dai_link->cpu_of_node;
+       cpu_dai_component.dai_name = dai_link->cpu_dai_name;
+       rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);
        if (!rtd->cpu_dai) {
                dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
                        dai_link->cpu_dai_name);
@@ -963,15 +924,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
 
        /* Find CODEC from registered CODECs */
        for (i = 0; i < rtd->num_codecs; i++) {
-               struct snd_soc_codec *codec;
-               codec = soc_find_codec(codecs[i].of_node, codecs[i].name);
-               if (!codec) {
-                       dev_err(card->dev, "ASoC: CODEC %s not registered\n",
-                               codecs[i].name);
-                       return -EPROBE_DEFER;
-               }
-
-               codec_dais[i] = soc_find_codec_dai(codec, codecs[i].dai_name);
+               codec_dais[i] = snd_soc_find_dai(&codecs[i]);
                if (!codec_dais[i]) {
                        dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
                                codecs[i].dai_name);
@@ -1012,68 +965,46 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
        return 0;
 }
 
-static int soc_remove_platform(struct snd_soc_platform *platform)
+static void soc_remove_component(struct snd_soc_component *component)
 {
-       int ret;
-
-       if (platform->driver->remove) {
-               ret = platform->driver->remove(platform);
-               if (ret < 0)
-                       dev_err(platform->dev, "ASoC: failed to remove %d\n",
-                               ret);
-       }
-
-       /* Make sure all DAPM widgets are freed */
-       snd_soc_dapm_free(&platform->component.dapm);
-
-       soc_cleanup_platform_debugfs(platform);
-       platform->probed = 0;
-       module_put(platform->dev->driver->owner);
-
-       return 0;
-}
+       if (!component->probed)
+               return;
 
-static void soc_remove_codec(struct snd_soc_codec *codec)
-{
-       int err;
+       /* This is a HACK and will be removed soon */
+       if (component->codec)
+               list_del(&component->codec->card_list);
 
-       if (codec->driver->remove) {
-               err = codec->driver->remove(codec);
-               if (err < 0)
-                       dev_err(codec->dev, "ASoC: failed to remove %d\n", err);
-       }
+       if (component->remove)
+               component->remove(component);
 
-       /* Make sure all DAPM widgets are freed */
-       snd_soc_dapm_free(&codec->dapm);
+       snd_soc_dapm_free(snd_soc_component_get_dapm(component));
 
-       soc_cleanup_codec_debugfs(codec);
-       codec->probed = 0;
-       list_del(&codec->card_list);
-       module_put(codec->dev->driver->owner);
+       soc_cleanup_component_debugfs(component);
+       component->probed = 0;
+       module_put(component->dev->driver->owner);
 }
 
-static void soc_remove_codec_dai(struct snd_soc_dai *codec_dai, int order)
+static void soc_remove_dai(struct snd_soc_dai *dai, int order)
 {
        int err;
 
-       if (codec_dai && codec_dai->probed &&
-                       codec_dai->driver->remove_order == order) {
-               if (codec_dai->driver->remove) {
-                       err = codec_dai->driver->remove(codec_dai);
+       if (dai && dai->probed &&
+                       dai->driver->remove_order == order) {
+               if (dai->driver->remove) {
+                       err = dai->driver->remove(dai);
                        if (err < 0)
-                               dev_err(codec_dai->dev,
+                               dev_err(dai->dev,
                                        "ASoC: failed to remove %s: %d\n",
-                                       codec_dai->name, err);
+                                       dai->name, err);
                }
-               codec_dai->probed = 0;
+               dai->probed = 0;
        }
 }
 
 static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       int i, err;
+       int i;
 
        /* unregister the rtd device */
        if (rtd->dev_registered) {
@@ -1085,22 +1016,9 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
 
        /* remove the CODEC DAI */
        for (i = 0; i < rtd->num_codecs; i++)
-               soc_remove_codec_dai(rtd->codec_dais[i], order);
+               soc_remove_dai(rtd->codec_dais[i], order);
 
-       /* remove the cpu_dai */
-       if (cpu_dai && cpu_dai->probed &&
-                       cpu_dai->driver->remove_order == order) {
-               if (cpu_dai->driver->remove) {
-                       err = cpu_dai->driver->remove(cpu_dai);
-                       if (err < 0)
-                               dev_err(cpu_dai->dev,
-                                       "ASoC: failed to remove %s: %d\n",
-                                       cpu_dai->name, err);
-               }
-               cpu_dai->probed = 0;
-               if (!cpu_dai->codec)
-                       module_put(cpu_dai->dev->driver->owner);
-       }
+       soc_remove_dai(rtd->cpu_dai, order);
 }
 
 static void soc_remove_link_components(struct snd_soc_card *card, int num,
@@ -1109,29 +1027,24 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num,
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_platform *platform = rtd->platform;
-       struct snd_soc_codec *codec;
+       struct snd_soc_component *component;
        int i;
 
        /* remove the platform */
-       if (platform && platform->probed &&
-           platform->driver->remove_order == order) {
-               soc_remove_platform(platform);
-       }
+       if (platform && platform->component.driver->remove_order == order)
+               soc_remove_component(&platform->component);
 
        /* remove the CODEC-side CODEC */
        for (i = 0; i < rtd->num_codecs; i++) {
-               codec = rtd->codec_dais[i]->codec;
-               if (codec && codec->probed &&
-                   codec->driver->remove_order == order)
-                       soc_remove_codec(codec);
+               component = rtd->codec_dais[i]->component;
+               if (component->driver->remove_order == order)
+                       soc_remove_component(component);
        }
 
        /* remove any CPU-side CODEC */
        if (cpu_dai) {
-               codec = cpu_dai->codec;
-               if (codec && codec->probed &&
-                   codec->driver->remove_order == order)
-                       soc_remove_codec(codec);
+               if (cpu_dai->component->driver->remove_order == order)
+                       soc_remove_component(cpu_dai->component);
        }
 }
 
@@ -1173,137 +1086,78 @@ static void soc_set_name_prefix(struct snd_soc_card *card,
        }
 }
 
-static int soc_probe_codec(struct snd_soc_card *card,
-                          struct snd_soc_codec *codec)
+static int soc_probe_component(struct snd_soc_card *card,
+       struct snd_soc_component *component)
 {
-       int ret = 0;
-       const struct snd_soc_codec_driver *driver = codec->driver;
+       struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
        struct snd_soc_dai *dai;
+       int ret;
 
-       codec->component.card = card;
-       codec->dapm.card = card;
-       soc_set_name_prefix(card, &codec->component);
+       if (component->probed)
+               return 0;
+
+       component->card = card;
+       dapm->card = card;
+       soc_set_name_prefix(card, component);
 
-       if (!try_module_get(codec->dev->driver->owner))
+       if (!try_module_get(component->dev->driver->owner))
                return -ENODEV;
 
-       soc_init_codec_debugfs(codec);
+       soc_init_component_debugfs(component);
 
-       if (driver->dapm_widgets) {
-               ret = snd_soc_dapm_new_controls(&codec->dapm,
-                                               driver->dapm_widgets,
-                                               driver->num_dapm_widgets);
+       if (component->dapm_widgets) {
+               ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets,
+                       component->num_dapm_widgets);
 
                if (ret != 0) {
-                       dev_err(codec->dev,
+                       dev_err(component->dev,
                                "Failed to create new controls %d\n", ret);
                        goto err_probe;
                }
        }
 
-       /* Create DAPM widgets for each DAI stream */
-       list_for_each_entry(dai, &codec->component.dai_list, list) {
-               ret = snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
-
+       list_for_each_entry(dai, &component->dai_list, list) {
+               ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
                if (ret != 0) {
-                       dev_err(codec->dev,
+                       dev_err(component->dev,
                                "Failed to create DAI widgets %d\n", ret);
                        goto err_probe;
                }
        }
 
-       codec->dapm.idle_bias_off = driver->idle_bias_off;
-
-       if (driver->probe) {
-               ret = driver->probe(codec);
+       if (component->probe) {
+               ret = component->probe(component);
                if (ret < 0) {
-                       dev_err(codec->dev,
-                               "ASoC: failed to probe CODEC %d\n", ret);
+                       dev_err(component->dev,
+                               "ASoC: failed to probe component %d\n", ret);
                        goto err_probe;
                }
-               WARN(codec->dapm.idle_bias_off &&
-                       codec->dapm.bias_level != SND_SOC_BIAS_OFF,
+
+               WARN(dapm->idle_bias_off &&
+                       dapm->bias_level != SND_SOC_BIAS_OFF,
                        "codec %s can not start from non-off bias with idle_bias_off==1\n",
-                       codec->component.name);
+                       component->name);
        }
 
-       if (driver->controls)
-               snd_soc_add_codec_controls(codec, driver->controls,
-                                    driver->num_controls);
-       if (driver->dapm_routes)
-               snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes,
-                                       driver->num_dapm_routes);
-
-       /* mark codec as probed and add to card codec list */
-       codec->probed = 1;
-       list_add(&codec->card_list, &card->codec_dev_list);
-       list_add(&codec->dapm.list, &card->dapm_list);
-
-       return 0;
-
-err_probe:
-       soc_cleanup_codec_debugfs(codec);
-       module_put(codec->dev->driver->owner);
-
-       return ret;
-}
-
-static int soc_probe_platform(struct snd_soc_card *card,
-                          struct snd_soc_platform *platform)
-{
-       int ret = 0;
-       const struct snd_soc_platform_driver *driver = platform->driver;
-       struct snd_soc_component *component;
-       struct snd_soc_dai *dai;
-
-       platform->component.card = card;
-       platform->component.dapm.card = card;
+       if (component->controls)
+               snd_soc_add_component_controls(component, component->controls,
+                                    component->num_controls);
+       if (component->dapm_routes)
+               snd_soc_dapm_add_routes(dapm, component->dapm_routes,
+                                       component->num_dapm_routes);
 
-       if (!try_module_get(platform->dev->driver->owner))
-               return -ENODEV;
-
-       soc_init_platform_debugfs(platform);
+       component->probed = 1;
+       list_add(&dapm->list, &card->dapm_list);
 
-       if (driver->dapm_widgets)
-               snd_soc_dapm_new_controls(&platform->component.dapm,
-                       driver->dapm_widgets, driver->num_dapm_widgets);
-
-       /* Create DAPM widgets for each DAI stream */
-       list_for_each_entry(component, &component_list, list) {
-               if (component->dev != platform->dev)
-                       continue;
-               list_for_each_entry(dai, &component->dai_list, list)
-                       snd_soc_dapm_new_dai_widgets(&platform->component.dapm,
-                               dai);
-       }
-
-       platform->component.dapm.idle_bias_off = 1;
-
-       if (driver->probe) {
-               ret = driver->probe(platform);
-               if (ret < 0) {
-                       dev_err(platform->dev,
-                               "ASoC: failed to probe platform %d\n", ret);
-                       goto err_probe;
-               }
-       }
-
-       if (driver->controls)
-               snd_soc_add_platform_controls(platform, driver->controls,
-                                    driver->num_controls);
-       if (driver->dapm_routes)
-               snd_soc_dapm_add_routes(&platform->component.dapm,
-                       driver->dapm_routes, driver->num_dapm_routes);
-
-       /* mark platform as probed and add to card platform list */
-       platform->probed = 1;
-       list_add(&platform->component.dapm.list, &card->dapm_list);
+       /* This is a HACK and will be removed soon */
+       if (component->codec)
+               list_add(&component->codec->card_list, &card->codec_dev_list);
 
        return 0;
 
 err_probe:
-       soc_cleanup_platform_debugfs(platform);
-       module_put(platform->dev->driver->owner);
+       soc_cleanup_component_debugfs(component);
+       module_put(component->dev->driver->owner);
 
        return ret;
 }
@@ -1342,17 +1196,21 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
        }
        rtd->dev_registered = 1;
 
-       /* add DAPM sysfs entries for this codec */
-       ret = snd_soc_dapm_sys_add(rtd->dev);
-       if (ret < 0)
-               dev_err(rtd->dev,
-                       "ASoC: failed to add codec dapm sysfs entries: %d\n", ret);
+       if (rtd->codec) {
+               /* add DAPM sysfs entries for this codec */
+               ret = snd_soc_dapm_sys_add(rtd->dev);
+               if (ret < 0)
+                       dev_err(rtd->dev,
+                               "ASoC: failed to add codec dapm sysfs entries: %d\n",
+                               ret);
 
-       /* add codec sysfs entries */
-       ret = device_create_file(rtd->dev, &dev_attr_codec_reg);
-       if (ret < 0)
-               dev_err(rtd->dev,
-                       "ASoC: failed to add codec sysfs files: %d\n", ret);
+               /* add codec sysfs entries */
+               ret = device_create_file(rtd->dev, &dev_attr_codec_reg);
+               if (ret < 0)
+                       dev_err(rtd->dev,
+                               "ASoC: failed to add codec sysfs files: %d\n",
+                               ret);
+       }
 
        return 0;
 }
@@ -1361,33 +1219,31 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
                                     int order)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_component *component;
        int i, ret;
 
        /* probe the CPU-side component, if it is a CODEC */
-       if (cpu_dai->codec &&
-           !cpu_dai->codec->probed &&
-           cpu_dai->codec->driver->probe_order == order) {
-               ret = soc_probe_codec(card, cpu_dai->codec);
+       component = rtd->cpu_dai->component;
+       if (component->driver->probe_order == order) {
+               ret = soc_probe_component(card, component);
                if (ret < 0)
                        return ret;
        }
 
        /* probe the CODEC-side components */
        for (i = 0; i < rtd->num_codecs; i++) {
-               if (!rtd->codec_dais[i]->codec->probed &&
-                   rtd->codec_dais[i]->codec->driver->probe_order == order) {
-                       ret = soc_probe_codec(card, rtd->codec_dais[i]->codec);
+               component = rtd->codec_dais[i]->component;
+               if (component->driver->probe_order == order) {
+                       ret = soc_probe_component(card, component);
                        if (ret < 0)
                                return ret;
                }
        }
 
        /* probe the platform */
-       if (!platform->probed &&
-           platform->driver->probe_order == order) {
-               ret = soc_probe_platform(card, platform);
+       if (platform->component.driver->probe_order == order) {
+               ret = soc_probe_component(card, &platform->component);
                if (ret < 0)
                        return ret;
        }
@@ -1482,18 +1338,12 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
        /* probe the cpu_dai */
        if (!cpu_dai->probed &&
                        cpu_dai->driver->probe_order == order) {
-               if (!cpu_dai->codec) {
-                       if (!try_module_get(cpu_dai->dev->driver->owner))
-                               return -ENODEV;
-               }
-
                if (cpu_dai->driver->probe) {
                        ret = cpu_dai->driver->probe(cpu_dai);
                        if (ret < 0) {
                                dev_err(cpu_dai->dev,
                                        "ASoC: failed to probe CPU DAI %s: %d\n",
                                        cpu_dai->name, ret);
-                               module_put(cpu_dai->dev->driver->owner);
                                return ret;
                        }
                }
@@ -1654,17 +1504,24 @@ static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
        struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
-       const char *codecname = aux_dev->codec_name;
+       const char *name = aux_dev->codec_name;
 
-       rtd->codec = soc_find_codec(aux_dev->codec_of_node, codecname);
-       if (!rtd->codec) {
+       rtd->component = soc_find_component(aux_dev->codec_of_node, name);
+       if (!rtd->component) {
                if (aux_dev->codec_of_node)
-                       codecname = of_node_full_name(aux_dev->codec_of_node);
+                       name = of_node_full_name(aux_dev->codec_of_node);
 
-               dev_err(card->dev, "ASoC: %s not registered\n", codecname);
+               dev_err(card->dev, "ASoC: %s not registered\n", name);
                return -EPROBE_DEFER;
        }
 
+       /*
+        * Some places still reference rtd->codec, so we have to keep that
+        * initialized if the component is a CODEC. Once all those references
+        * have been removed, this code can be removed as well.
+        */
+        rtd->codec = rtd->component->codec;
+
        return 0;
 }
 
@@ -1674,18 +1531,13 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
        struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
        int ret;
 
-       if (rtd->codec->probed) {
-               dev_err(rtd->codec->dev, "ASoC: codec already probed\n");
-               return -EBUSY;
-       }
-
-       ret = soc_probe_codec(card, rtd->codec);
+       ret = soc_probe_component(card, rtd->component);
        if (ret < 0)
                return ret;
 
        /* do machine specific initialization */
        if (aux_dev->init) {
-               ret = aux_dev->init(&rtd->codec->dapm);
+               ret = aux_dev->init(rtd->component);
                if (ret < 0) {
                        dev_err(card->dev, "ASoC: failed to init %s: %d\n",
                                aux_dev->name, ret);
@@ -1699,7 +1551,7 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
 static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
-       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_component *component = rtd->component;
 
        /* unregister the rtd device */
        if (rtd->dev_registered) {
@@ -1708,8 +1560,8 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
                rtd->dev_registered = 0;
        }
 
-       if (codec && codec->probed)
-               soc_remove_codec(codec);
+       if (component && component->probed)
+               soc_remove_component(component);
 }
 
 static int snd_soc_init_codec_cache(struct snd_soc_codec *codec)
@@ -2107,19 +1959,14 @@ static struct platform_driver soc_driver = {
 int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
        struct snd_ac97_bus_ops *ops, int num)
 {
-       mutex_lock(&codec->mutex);
-
        codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
-       if (codec->ac97 == NULL) {
-               mutex_unlock(&codec->mutex);
+       if (codec->ac97 == NULL)
                return -ENOMEM;
-       }
 
        codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
        if (codec->ac97->bus == NULL) {
                kfree(codec->ac97);
                codec->ac97 = NULL;
-               mutex_unlock(&codec->mutex);
                return -ENOMEM;
        }
 
@@ -2132,7 +1979,6 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
         */
        codec->ac97_created = 1;
 
-       mutex_unlock(&codec->mutex);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
@@ -2302,7 +2148,6 @@ EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
  */
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
 {
-       mutex_lock(&codec->mutex);
 #ifdef CONFIG_SND_SOC_AC97_BUS
        soc_unregister_ac97_codec(codec);
 #endif
@@ -2310,7 +2155,6 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
        kfree(codec->ac97);
        codec->ac97 = NULL;
        codec->ac97_created = 0;
-       mutex_unlock(&codec->mutex);
 }
 EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
 
@@ -3027,9 +2871,10 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
        unsigned int val, val_mask;
        int ret;
 
-       val = ((ucontrol->value.integer.value[0] + min) & mask);
        if (invert)
-               val = max - val;
+               val = (max - ucontrol->value.integer.value[0]) & mask;
+       else
+               val = ((ucontrol->value.integer.value[0] + min) & mask);
        val_mask = mask << shift;
        val = val << shift;
 
@@ -3038,9 +2883,10 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
                return ret;
 
        if (snd_soc_volsw_is_stereo(mc)) {
-               val = ((ucontrol->value.integer.value[1] + min) & mask);
                if (invert)
-                       val = max - val;
+                       val = (max - ucontrol->value.integer.value[1]) & mask;
+               else
+                       val = ((ucontrol->value.integer.value[1] + min) & mask);
                val_mask = mask << shift;
                val = val << shift;
 
@@ -3085,8 +2931,9 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
        if (invert)
                ucontrol->value.integer.value[0] =
                        max - ucontrol->value.integer.value[0];
-       ucontrol->value.integer.value[0] =
-               ucontrol->value.integer.value[0] - min;
+       else
+               ucontrol->value.integer.value[0] =
+                       ucontrol->value.integer.value[0] - min;
 
        if (snd_soc_volsw_is_stereo(mc)) {
                ret = snd_soc_component_read(component, rreg, &val);
@@ -3097,8 +2944,9 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
                if (invert)
                        ucontrol->value.integer.value[1] =
                                max - ucontrol->value.integer.value[1];
-               ucontrol->value.integer.value[1] =
-                       ucontrol->value.integer.value[1] - min;
+               else
+                       ucontrol->value.integer.value[1] =
+                               ucontrol->value.integer.value[1] - min;
        }
 
        return 0;
@@ -3928,8 +3776,11 @@ EXPORT_SYMBOL_GPL(snd_soc_register_card);
  */
 int snd_soc_unregister_card(struct snd_soc_card *card)
 {
-       if (card->instantiated)
+       if (card->instantiated) {
+               card->instantiated = false;
+               snd_soc_dapm_shutdown(card);
                soc_cleanup_card_resources(card);
+       }
        dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name);
 
        return 0;
@@ -4116,6 +3967,8 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
 
        component->dev = dev;
        component->driver = driver;
+       component->probe = component->driver->probe;
+       component->remove = component->driver->remove;
 
        if (!component->dapm_ptr)
                component->dapm_ptr = &component->dapm;
@@ -4124,19 +3977,42 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
        dapm->dev = dev;
        dapm->component = component;
        dapm->bias_level = SND_SOC_BIAS_OFF;
+       dapm->idle_bias_off = true;
        if (driver->seq_notifier)
                dapm->seq_notifier = snd_soc_component_seq_notifier;
        if (driver->stream_event)
                dapm->stream_event = snd_soc_component_stream_event;
 
+       component->controls = driver->controls;
+       component->num_controls = driver->num_controls;
+       component->dapm_widgets = driver->dapm_widgets;
+       component->num_dapm_widgets = driver->num_dapm_widgets;
+       component->dapm_routes = driver->dapm_routes;
+       component->num_dapm_routes = driver->num_dapm_routes;
+
        INIT_LIST_HEAD(&component->dai_list);
        mutex_init(&component->io_mutex);
 
        return 0;
 }
 
+static void snd_soc_component_init_regmap(struct snd_soc_component *component)
+{
+       if (!component->regmap)
+               component->regmap = dev_get_regmap(component->dev, NULL);
+       if (component->regmap) {
+               int val_bytes = regmap_get_val_bytes(component->regmap);
+               /* Errors are legitimate for non-integer byte multiples */
+               if (val_bytes > 0)
+                       component->val_bytes = val_bytes;
+       }
+}
+
 static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
 {
+       if (!component->write && !component->read)
+               snd_soc_component_init_regmap(component);
+
        list_add(&component->list, &component_list);
 }
 
@@ -4225,22 +4101,18 @@ found:
 }
 EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
 
-static int snd_soc_platform_drv_write(struct snd_soc_component *component,
-       unsigned int reg, unsigned int val)
+static int snd_soc_platform_drv_probe(struct snd_soc_component *component)
 {
        struct snd_soc_platform *platform = snd_soc_component_to_platform(component);
 
-       return platform->driver->write(platform, reg, val);
+       return platform->driver->probe(platform);
 }
 
-static int snd_soc_platform_drv_read(struct snd_soc_component *component,
-       unsigned int reg, unsigned int *val)
+static void snd_soc_platform_drv_remove(struct snd_soc_component *component)
 {
        struct snd_soc_platform *platform = snd_soc_component_to_platform(component);
 
-       *val = platform->driver->read(platform, reg);
-
-       return 0;
+       platform->driver->remove(platform);
 }
 
 /**
@@ -4261,10 +4133,15 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
 
        platform->dev = dev;
        platform->driver = platform_drv;
-       if (platform_drv->write)
-               platform->component.write = snd_soc_platform_drv_write;
-       if (platform_drv->read)
-               platform->component.read = snd_soc_platform_drv_read;
+
+       if (platform_drv->probe)
+               platform->component.probe = snd_soc_platform_drv_probe;
+       if (platform_drv->remove)
+               platform->component.remove = snd_soc_platform_drv_remove;
+
+#ifdef CONFIG_DEBUG_FS
+       platform->component.debugfs_prefix = "platform";
+#endif
 
        mutex_lock(&client_mutex);
        snd_soc_component_add_unlocked(&platform->component);
@@ -4315,10 +4192,10 @@ void snd_soc_remove_platform(struct snd_soc_platform *platform)
        snd_soc_component_del_unlocked(&platform->component);
        mutex_unlock(&client_mutex);
 
-       snd_soc_component_cleanup(&platform->component);
-
        dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n",
                platform->component.name);
+
+       snd_soc_component_cleanup(&platform->component);
 }
 EXPORT_SYMBOL_GPL(snd_soc_remove_platform);
 
@@ -4386,6 +4263,20 @@ static void fixup_codec_formats(struct snd_soc_pcm_stream *stream)
                        stream->formats |= codec_format_map[i];
 }
 
+static int snd_soc_codec_drv_probe(struct snd_soc_component *component)
+{
+       struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+
+       return codec->driver->probe(codec);
+}
+
+static void snd_soc_codec_drv_remove(struct snd_soc_component *component)
+{
+       struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+
+       codec->driver->remove(codec);
+}
+
 static int snd_soc_codec_drv_write(struct snd_soc_component *component,
        unsigned int reg, unsigned int val)
 {
@@ -4424,7 +4315,6 @@ int snd_soc_register_codec(struct device *dev,
 {
        struct snd_soc_codec *codec;
        struct snd_soc_dai *dai;
-       struct regmap *regmap;
        int ret, i;
 
        dev_dbg(dev, "codec register %s\n", dev_name(dev));
@@ -4434,18 +4324,37 @@ int snd_soc_register_codec(struct device *dev,
                return -ENOMEM;
 
        codec->component.dapm_ptr = &codec->dapm;
+       codec->component.codec = codec;
 
        ret = snd_soc_component_initialize(&codec->component,
                        &codec_drv->component_driver, dev);
        if (ret)
                goto err_free;
 
+       if (codec_drv->controls) {
+               codec->component.controls = codec_drv->controls;
+               codec->component.num_controls = codec_drv->num_controls;
+       }
+       if (codec_drv->dapm_widgets) {
+               codec->component.dapm_widgets = codec_drv->dapm_widgets;
+               codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets;
+       }
+       if (codec_drv->dapm_routes) {
+               codec->component.dapm_routes = codec_drv->dapm_routes;
+               codec->component.num_dapm_routes = codec_drv->num_dapm_routes;
+       }
+
+       if (codec_drv->probe)
+               codec->component.probe = snd_soc_codec_drv_probe;
+       if (codec_drv->remove)
+               codec->component.remove = snd_soc_codec_drv_remove;
        if (codec_drv->write)
                codec->component.write = snd_soc_codec_drv_write;
        if (codec_drv->read)
                codec->component.read = snd_soc_codec_drv_read;
        codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
-       codec->dapm.codec = codec;
+       codec->dapm.idle_bias_off = codec_drv->idle_bias_off;
+       codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off;
        if (codec_drv->seq_notifier)
                codec->dapm.seq_notifier = codec_drv->seq_notifier;
        if (codec_drv->set_bias_level)
@@ -4455,23 +4364,13 @@ int snd_soc_register_codec(struct device *dev,
        codec->component.val_bytes = codec_drv->reg_word_size;
        mutex_init(&codec->mutex);
 
-       if (!codec->component.write) {
-               if (codec_drv->get_regmap)
-                       regmap = codec_drv->get_regmap(dev);
-               else
-                       regmap = dev_get_regmap(dev, NULL);
-
-               if (regmap) {
-                       ret = snd_soc_component_init_io(&codec->component,
-                               regmap);
-                       if (ret) {
-                               dev_err(codec->dev,
-                                               "Failed to set cache I/O:%d\n",
-                                               ret);
-                               goto err_cleanup;
-                       }
-               }
-       }
+#ifdef CONFIG_DEBUG_FS
+       codec->component.init_debugfs = soc_init_codec_debugfs;
+       codec->component.debugfs_prefix = "codec";
+#endif
+
+       if (codec_drv->get_regmap)
+               codec->component.regmap = codec_drv->get_regmap(dev);
 
        for (i = 0; i < num_dai; i++) {
                fixup_codec_formats(&dai_drv[i].playback);
index 177bd8639ef93e6b97a20e1cd022224e62cf14bf..c61cb9cedbcd283c22625cac53367606fb0fc18a 100644 (file)
@@ -326,12 +326,13 @@ static struct list_head *dapm_kcontrol_get_path_list(
        list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \
                list_kcontrol)
 
-static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol)
+unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol)
 {
        struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
 
        return data->value;
 }
+EXPORT_SYMBOL_GPL(dapm_kcontrol_get_value);
 
 static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
        unsigned int value)
@@ -591,9 +592,9 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
        int shared;
        struct snd_kcontrol *kcontrol;
        bool wname_in_long_name, kcname_in_long_name;
-       char *long_name;
+       char *long_name = NULL;
        const char *name;
-       int ret;
+       int ret = 0;
 
        prefix = soc_dapm_prefix(dapm);
        if (prefix)
@@ -652,15 +653,17 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
 
                kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name,
                                        prefix);
-               kfree(long_name);
-               if (!kcontrol)
-                       return -ENOMEM;
+               if (!kcontrol) {
+                       ret = -ENOMEM;
+                       goto exit_free;
+               }
+
                kcontrol->private_free = dapm_kcontrol_free;
 
                ret = dapm_kcontrol_data_alloc(w, kcontrol);
                if (ret) {
                        snd_ctl_free_one(kcontrol);
-                       return ret;
+                       goto exit_free;
                }
 
                ret = snd_ctl_add(card, kcontrol);
@@ -668,17 +671,18 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
                        dev_err(dapm->dev,
                                "ASoC: failed to add widget %s dapm kcontrol %s: %d\n",
                                w->name, name, ret);
-                       return ret;
+                       goto exit_free;
                }
        }
 
        ret = dapm_kcontrol_add_widget(kcontrol, w);
-       if (ret)
-               return ret;
+       if (ret == 0)
+               w->kcontrols[kci] = kcontrol;
 
-       w->kcontrols[kci] = kcontrol;
+exit_free:
+       kfree(long_name);
 
-       return 0;
+       return ret;
 }
 
 /* create new dapm mixer control */
@@ -1683,6 +1687,22 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w,
        }
 }
 
+static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm)
+{
+       if (dapm->idle_bias_off)
+               return true;
+
+       switch (snd_power_get_state(dapm->card->snd_card)) {
+       case SNDRV_CTL_POWER_D3hot:
+       case SNDRV_CTL_POWER_D3cold:
+               return dapm->suspend_bias_off;
+       default:
+               break;
+       }
+
+       return false;
+}
+
 /*
  * Scan each dapm widget for complete audio path.
  * A complete path is a route that has valid endpoints i.e.:-
@@ -1706,7 +1726,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
        trace_snd_soc_dapm_start(card);
 
        list_for_each_entry(d, &card->dapm_list, list) {
-               if (d->idle_bias_off)
+               if (dapm_idle_bias_off(d))
                        d->target_bias_level = SND_SOC_BIAS_OFF;
                else
                        d->target_bias_level = SND_SOC_BIAS_STANDBY;
@@ -1772,7 +1792,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
                if (d->target_bias_level > bias)
                        bias = d->target_bias_level;
        list_for_each_entry(d, &card->dapm_list, list)
-               if (!d->idle_bias_off)
+               if (!dapm_idle_bias_off(d))
                        d->target_bias_level = bias;
 
        trace_snd_soc_dapm_walk_done(card);
@@ -3109,7 +3129,8 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        }
 
        w->dapm = dapm;
-       w->codec = dapm->codec;
+       if (dapm->component)
+               w->codec = dapm->component->codec;
        INIT_LIST_HEAD(&w->sources);
        INIT_LIST_HEAD(&w->sinks);
        INIT_LIST_HEAD(&w->list);
index 6307f85e871b1d33934877990275cca6c8d08acb..b329b84bc5af77d50e1fd2803252d259cabf7602 100644 (file)
@@ -336,10 +336,12 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = {
 };
 
 static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
+       .component_driver = {
+               .probe_order = SND_SOC_COMP_ORDER_LATE,
+       },
        .ops            = &dmaengine_pcm_ops,
        .pcm_new        = dmaengine_pcm_new,
        .pcm_free       = dmaengine_pcm_free,
-       .probe_order    = SND_SOC_COMP_ORDER_LATE,
 };
 
 static const char * const dmaengine_pcm_dma_channel_names[] = {
index 7767fbd73eb7a464434c805a901fa94fe64a5e04..9b3939049cefc357581200063108124c3adf8907 100644 (file)
@@ -271,31 +271,3 @@ int snd_soc_platform_write(struct snd_soc_platform *platform,
        return snd_soc_component_write(&platform->component, reg, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_platform_write);
-
-/**
- * snd_soc_component_init_io() - Initialize regmap IO
- *
- * @component: component to initialize
- * @regmap: regmap instance to use for IO operations
- *
- * Return: 0 on success, a negative error code otherwise
- */
-int snd_soc_component_init_io(struct snd_soc_component *component,
-       struct regmap *regmap)
-{
-       int ret;
-
-       if (!regmap)
-               return -EINVAL;
-
-       ret = regmap_get_val_bytes(regmap);
-       /* Errors are legitimate for non-integer byte
-        * multiples */
-       if (ret > 0)
-               component->val_bytes = ret;
-
-       component->regmap = regmap;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_init_io);
index 642c862407520db3e36b4ddadf1b5d7a6b467749..002311afdeaa8bd6ceb40d120b0c6afaa13c6119 100644 (file)
@@ -352,7 +352,7 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream)
        } else {
                for (i = 0; i < rtd->num_codecs; i++) {
                        codec_dai = rtd->codec_dais[i];
-                       if (codec_dai->driver->playback.sig_bits == 0) {
+                       if (codec_dai->driver->capture.sig_bits == 0) {
                                bits = 0;
                                break;
                        }
index b86cd9936ef19cad0c1ca04c60c52ed746f46b50..01921d7e73fa65392da04ed823a60ef0be42c34a 100644 (file)
@@ -42,6 +42,7 @@
 struct tegra_max98090 {
        struct tegra_asoc_utils_data util_data;
        int gpio_hp_det;
+       int gpio_mic_det;
 };
 
 static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream,
@@ -112,6 +113,22 @@ static struct snd_soc_jack_gpio tegra_max98090_hp_jack_gpio = {
        .invert = 1,
 };
 
+static struct snd_soc_jack tegra_max98090_mic_jack;
+
+static struct snd_soc_jack_pin tegra_max98090_mic_jack_pins[] = {
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+};
+
+static struct snd_soc_jack_gpio tegra_max98090_mic_jack_gpio = {
+       .name = "Mic detection",
+       .report = SND_JACK_MICROPHONE,
+       .debounce_time = 150,
+       .invert = 1,
+};
+
 static const struct snd_soc_dapm_widget tegra_max98090_dapm_widgets[] = {
        SND_SOC_DAPM_HP("Headphones", NULL),
        SND_SOC_DAPM_SPK("Speakers", NULL),
@@ -141,6 +158,19 @@ static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd)
                                        &tegra_max98090_hp_jack_gpio);
        }
 
+       if (gpio_is_valid(machine->gpio_mic_det)) {
+               snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
+                                &tegra_max98090_mic_jack);
+               snd_soc_jack_add_pins(&tegra_max98090_mic_jack,
+                                     ARRAY_SIZE(tegra_max98090_mic_jack_pins),
+                                     tegra_max98090_mic_jack_pins);
+
+               tegra_max98090_mic_jack_gpio.gpio = machine->gpio_mic_det;
+               snd_soc_jack_add_gpios(&tegra_max98090_mic_jack,
+                                      1,
+                                      &tegra_max98090_mic_jack_gpio);
+       }
+
        return 0;
 }
 
@@ -153,6 +183,11 @@ static int tegra_max98090_card_remove(struct snd_soc_card *card)
                                        &tegra_max98090_hp_jack_gpio);
        }
 
+       if (gpio_is_valid(machine->gpio_mic_det)) {
+               snd_soc_jack_free_gpios(&tegra_max98090_mic_jack, 1,
+                                       &tegra_max98090_mic_jack_gpio);
+       }
+
        return 0;
 }
 
@@ -201,6 +236,11 @@ static int tegra_max98090_probe(struct platform_device *pdev)
        if (machine->gpio_hp_det == -EPROBE_DEFER)
                return -EPROBE_DEFER;
 
+       machine->gpio_mic_det =
+                       of_get_named_gpio(np, "nvidia,mic-det-gpios", 0);
+       if (machine->gpio_mic_det == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
        ret = snd_soc_of_parse_card_name(card, "nvidia,model");
        if (ret)
                goto err;
index f0829de28708b20724fffddf831bec56818152d1..cd71fd889d8bf834cc71cd1e491bdd9c590d78ef 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/platform_device.h>
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
+#include <linux/dmaengine.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -137,7 +138,7 @@ txx9aclc_dma_submit(struct txx9aclc_dmadata *dmadata, dma_addr_t buf_dma_addr)
        }
        desc->callback = txx9aclc_dma_complete;
        desc->callback_param = dmadata;
-       desc->tx_submit(desc);
+       dmaengine_submit(desc);
        return desc;
 }
 
@@ -160,7 +161,7 @@ static void txx9aclc_dma_tasklet(unsigned long data)
                void __iomem *base = drvdata->base;
 
                spin_unlock_irqrestore(&dmadata->dma_lock, flags);
-               chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+               dmaengine_terminate_all(chan);
                /* first time */
                for (i = 0; i < NR_DMA_CHAIN; i++) {
                        desc = txx9aclc_dma_submit(dmadata,
@@ -169,7 +170,7 @@ static void txx9aclc_dma_tasklet(unsigned long data)
                                return;
                }
                dmadata->dmacount = NR_DMA_CHAIN;
-               chan->device->device_issue_pending(chan);
+               dma_async_issue_pending(chan);
                spin_lock_irqsave(&dmadata->dma_lock, flags);
                __raw_writel(ctlbit, base + ACCTLEN);
                dmadata->frag_count = NR_DMA_CHAIN % dmadata->frags;
@@ -188,7 +189,7 @@ static void txx9aclc_dma_tasklet(unsigned long data)
                        dmadata->frag_count * dmadata->frag_bytes);
                if (!desc)
                        return;
-               chan->device->device_issue_pending(chan);
+               dma_async_issue_pending(chan);
 
                spin_lock_irqsave(&dmadata->dma_lock, flags);
                dmadata->frag_count++;
@@ -266,7 +267,7 @@ static int txx9aclc_pcm_close(struct snd_pcm_substream *substream)
        struct dma_chan *chan = dmadata->dma_chan;
 
        dmadata->frag_count = -1;
-       chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+       dmaengine_terminate_all(chan);
        return 0;
 }
 
@@ -398,8 +399,7 @@ static int txx9aclc_pcm_remove(struct snd_soc_platform *platform)
                struct dma_chan *chan = dmadata->dma_chan;
                if (chan) {
                        dmadata->frag_count = -1;
-                       chan->device->device_control(chan,
-                                                    DMA_TERMINATE_ALL, 0);
+                       dmaengine_terminate_all(chan);
                        dma_release_channel(chan);
                }
                dev->dmadata[i].dma_chan = NULL;
index 7103b0908d130436743bceb6bf422222bd421466..272844746135763faa6425aae3fb8dc3bfc9ec50 100644 (file)
@@ -816,6 +816,11 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev)
                return -EINVAL;
        }
 
+       if (cdev->n_streams < 2) {
+               dev_err(dev, "bogus number of streams: %d\n", cdev->n_streams);
+               return -EINVAL;
+       }
+
        ret = snd_pcm_new(cdev->chip.card, cdev->product_name, 0,
                        cdev->n_audio_out, cdev->n_audio_in, &cdev->pcm);
 
index b2b6f398a4e1dfcc349bc71ef25cf060f59b1d4c..d3d49525a16bef649194ae410f64b72f243fc1f2 100644 (file)
@@ -1506,6 +1506,12 @@ static struct port_info {
        PORT_INFO(vendor, product, num, name, 0, \
                  SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \
                  SNDRV_SEQ_PORT_TYPE_HARDWARE)
+#define GM_SYNTH_PORT(vendor, product, num, name, voices) \
+       PORT_INFO(vendor, product, num, name, voices, \
+                 SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \
+                 SNDRV_SEQ_PORT_TYPE_MIDI_GM | \
+                 SNDRV_SEQ_PORT_TYPE_HARDWARE | \
+                 SNDRV_SEQ_PORT_TYPE_SYNTHESIZER)
 #define ROLAND_SYNTH_PORT(vendor, product, num, name, voices) \
        PORT_INFO(vendor, product, num, name, voices, \
                  SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \
@@ -1525,6 +1531,11 @@ static struct port_info {
                  SNDRV_SEQ_PORT_TYPE_MIDI_MT32 | \
                  SNDRV_SEQ_PORT_TYPE_HARDWARE | \
                  SNDRV_SEQ_PORT_TYPE_SYNTHESIZER)
+       /* Yamaha MOTIF XF */
+       GM_SYNTH_PORT(0x0499, 0x105c, 0, "%s Tone Generator", 128),
+       CONTROL_PORT(0x0499, 0x105c, 1, "%s Remote Control"),
+       EXTERNAL_PORT(0x0499, 0x105c, 2, "%s Thru"),
+       CONTROL_PORT(0x0499, 0x105c, 3, "%s Editor"),
        /* Roland UA-100 */
        CONTROL_PORT(0x0582, 0x0000, 2, "%s Control"),
        /* Roland SC-8850 */
index 19a921eb75f112d47cf25db29fea2ac332a69ca2..d2aa45a8d89546b378fa05fc94750a35f6fe5c18 100644 (file)
@@ -1174,5 +1174,21 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
                }
        }
 
+       /* XMOS based USB DACs */
+       switch (chip->usb_id) {
+       /* iFi Audio micro/nano iDSD */
+       case USB_ID(0x20b1, 0x3008):
+               if (fp->altsetting == 2)
+                       return SNDRV_PCM_FMTBIT_DSD_U32_LE;
+               break;
+       /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
+       case USB_ID(0x20b1, 0x2009):
+               if (fp->altsetting == 3)
+                       return SNDRV_PCM_FMTBIT_DSD_U32_LE;
+               break;
+       default:
+               break;
+       }
+
        return 0;
 }
index 74a78cedce37aad381bea0c399bcb7c5eab38307..f6ff90a76bd76c9c1fb1b56f0a60aafee130af7e 100644 (file)
@@ -13,7 +13,7 @@ CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CUR
 
 export CC CFLAGS
 
-TARGETS = pmu copyloops mm tm
+TARGETS = pmu copyloops mm tm primitives
 
 endif
 
diff --git a/tools/testing/selftests/powerpc/primitives/Makefile b/tools/testing/selftests/powerpc/primitives/Makefile
new file mode 100644 (file)
index 0000000..ea737ca
--- /dev/null
@@ -0,0 +1,17 @@
+CFLAGS += -I$(CURDIR)
+
+PROGS := load_unaligned_zeropad
+
+all: $(PROGS)
+
+$(PROGS): ../harness.c
+
+run_tests: all
+       @-for PROG in $(PROGS); do \
+               ./$$PROG; \
+       done;
+
+clean:
+       rm -f $(PROGS) *.o
+
+.PHONY: all run_tests clean
diff --git a/tools/testing/selftests/powerpc/primitives/asm/asm-compat.h b/tools/testing/selftests/powerpc/primitives/asm/asm-compat.h
new file mode 120000 (symlink)
index 0000000..b14255e
--- /dev/null
@@ -0,0 +1 @@
+../.././../../../../arch/powerpc/include/asm/asm-compat.h
\ No newline at end of file
diff --git a/tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h b/tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c
new file mode 100644 (file)
index 0000000..d1b6475
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Userspace test harness for load_unaligned_zeropad. Creates two
+ * pages and uses mprotect to prevent access to the second page and
+ * a SEGV handler that walks the exception tables and runs the fixup
+ * routine.
+ *
+ * The results are compared against a normal load that is that is
+ * performed while access to the second page is enabled via mprotect.
+ *
+ * Copyright (C) 2014 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#define FIXUP_SECTION ".ex_fixup"
+
+#include "word-at-a-time.h"
+
+#include "utils.h"
+
+
+static int page_size;
+static char *mem_region;
+
+static int protect_region(void)
+{
+       if (mprotect(mem_region + page_size, page_size, PROT_NONE)) {
+               perror("mprotect");
+               return 1;
+       }
+
+       return 0;
+}
+
+static int unprotect_region(void)
+{
+       if (mprotect(mem_region + page_size, page_size, PROT_READ|PROT_WRITE)) {
+               perror("mprotect");
+               return 1;
+       }
+
+       return 0;
+}
+
+extern char __start___ex_table[];
+extern char __stop___ex_table[];
+
+#if defined(__powerpc64__)
+#define UCONTEXT_NIA(UC)       (UC)->uc_mcontext.gp_regs[PT_NIP]
+#elif defined(__powerpc__)
+#define UCONTEXT_NIA(UC)       (UC)->uc_mcontext.uc_regs->gregs[PT_NIP]
+#else
+#error implement UCONTEXT_NIA
+#endif
+
+static int segv_error;
+
+static void segv_handler(int signr, siginfo_t *info, void *ptr)
+{
+       ucontext_t *uc = (ucontext_t *)ptr;
+       unsigned long addr = (unsigned long)info->si_addr;
+       unsigned long *ip = &UCONTEXT_NIA(uc);
+       unsigned long *ex_p = (unsigned long *)__start___ex_table;
+
+       while (ex_p < (unsigned long *)__stop___ex_table) {
+               unsigned long insn, fixup;
+
+               insn = *ex_p++;
+               fixup = *ex_p++;
+
+               if (insn == *ip) {
+                       *ip = fixup;
+                       return;
+               }
+       }
+
+       printf("No exception table match for NIA %lx ADDR %lx\n", *ip, addr);
+       segv_error++;
+}
+
+static void setup_segv_handler(void)
+{
+       struct sigaction action;
+
+       memset(&action, 0, sizeof(action));
+       action.sa_sigaction = segv_handler;
+       action.sa_flags = SA_SIGINFO;
+       sigaction(SIGSEGV, &action, NULL);
+}
+
+static int do_one_test(char *p, int page_offset)
+{
+       unsigned long should;
+       unsigned long got;
+
+       FAIL_IF(unprotect_region());
+       should = *(unsigned long *)p;
+       FAIL_IF(protect_region());
+
+       got = load_unaligned_zeropad(p);
+
+       if (should != got)
+               printf("offset %u load_unaligned_zeropad returned 0x%lx, should be 0x%lx\n", page_offset, got, should);
+
+       return 0;
+}
+
+static int test_body(void)
+{
+       unsigned long i;
+
+       page_size = getpagesize();
+       mem_region = mmap(NULL, page_size * 2, PROT_READ|PROT_WRITE,
+               MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+
+       FAIL_IF(mem_region == MAP_FAILED);
+
+       for (i = 0; i < page_size; i++)
+               mem_region[i] = i;
+
+       memset(mem_region+page_size, 0, page_size);
+
+       setup_segv_handler();
+
+       for (i = 0; i < page_size; i++)
+               FAIL_IF(do_one_test(mem_region+i, i));
+
+       FAIL_IF(segv_error);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(test_body, "load_unaligned_zeropad");
+}
diff --git a/tools/testing/selftests/powerpc/primitives/word-at-a-time.h b/tools/testing/selftests/powerpc/primitives/word-at-a-time.h
new file mode 120000 (symlink)
index 0000000..eb74401
--- /dev/null
@@ -0,0 +1 @@
+../../../../../arch/powerpc/include/asm/word-at-a-time.h
\ No newline at end of file
index 3f94e1afd6cfdf88b906d9806b7873633f5523cb..4c4b1f631ecf61f6e3048d746c23be39ea4f2ef9 100644 (file)
@@ -3,6 +3,7 @@
 CC = $(CROSS_COMPILE)gcc
 CFLAGS = -Wall
 BINARIES = hugepage-mmap hugepage-shm map_hugetlb thuge-gen hugetlbfstest
+BINARIES += transhuge-stress
 
 all: $(BINARIES)
 %: %.c
diff --git a/tools/testing/selftests/vm/transhuge-stress.c b/tools/testing/selftests/vm/transhuge-stress.c
new file mode 100644 (file)
index 0000000..fd7f1b4
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Stress test for transparent huge pages, memory compaction and migration.
+ *
+ * Authors: Konstantin Khlebnikov <koct9i@gmail.com>
+ *
+ * This is free and unencumbered software released into the public domain.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <err.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#define PAGE_SHIFT 12
+#define HPAGE_SHIFT 21
+
+#define PAGE_SIZE (1 << PAGE_SHIFT)
+#define HPAGE_SIZE (1 << HPAGE_SHIFT)
+
+#define PAGEMAP_PRESENT(ent)   (((ent) & (1ull << 63)) != 0)
+#define PAGEMAP_PFN(ent)       ((ent) & ((1ull << 55) - 1))
+
+int pagemap_fd;
+
+int64_t allocate_transhuge(void *ptr)
+{
+       uint64_t ent[2];
+
+       /* drop pmd */
+       if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE,
+                               MAP_FIXED | MAP_ANONYMOUS |
+                               MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr)
+               errx(2, "mmap transhuge");
+
+       if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE))
+               err(2, "MADV_HUGEPAGE");
+
+       /* allocate transparent huge page */
+       *(volatile void **)ptr = ptr;
+
+       if (pread(pagemap_fd, ent, sizeof(ent),
+                       (uintptr_t)ptr >> (PAGE_SHIFT - 3)) != sizeof(ent))
+               err(2, "read pagemap");
+
+       if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) &&
+           PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) &&
+           !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - PAGE_SHIFT)) - 1)))
+               return PAGEMAP_PFN(ent[0]);
+
+       return -1;
+}
+
+int main(int argc, char **argv)
+{
+       size_t ram, len;
+       void *ptr, *p;
+       struct timespec a, b;
+       double s;
+       uint8_t *map;
+       size_t map_len;
+
+       ram = sysconf(_SC_PHYS_PAGES);
+       if (ram > SIZE_MAX / sysconf(_SC_PAGESIZE) / 4)
+               ram = SIZE_MAX / 4;
+       else
+               ram *= sysconf(_SC_PAGESIZE);
+
+       if (argc == 1)
+               len = ram;
+       else if (!strcmp(argv[1], "-h"))
+               errx(1, "usage: %s [size in MiB]", argv[0]);
+       else
+               len = atoll(argv[1]) << 20;
+
+       warnx("allocate %zd transhuge pages, using %zd MiB virtual memory"
+             " and %zd MiB of ram", len >> HPAGE_SHIFT, len >> 20,
+             len >> (20 + HPAGE_SHIFT - PAGE_SHIFT - 1));
+
+       pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
+       if (pagemap_fd < 0)
+               err(2, "open pagemap");
+
+       len -= len % HPAGE_SIZE;
+       ptr = mmap(NULL, len + HPAGE_SIZE, PROT_READ | PROT_WRITE,
+                       MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0);
+       if (ptr == MAP_FAILED)
+               err(2, "initial mmap");
+       ptr += HPAGE_SIZE - (uintptr_t)ptr % HPAGE_SIZE;
+
+       if (madvise(ptr, len, MADV_HUGEPAGE))
+               err(2, "MADV_HUGEPAGE");
+
+       map_len = ram >> (HPAGE_SHIFT - 1);
+       map = malloc(map_len);
+       if (!map)
+               errx(2, "map malloc");
+
+       while (1) {
+               int nr_succeed = 0, nr_failed = 0, nr_pages = 0;
+
+               memset(map, 0, map_len);
+
+               clock_gettime(CLOCK_MONOTONIC, &a);
+               for (p = ptr; p < ptr + len; p += HPAGE_SIZE) {
+                       int64_t pfn;
+
+                       pfn = allocate_transhuge(p);
+
+                       if (pfn < 0) {
+                               nr_failed++;
+                       } else {
+                               size_t idx = pfn >> (HPAGE_SHIFT - PAGE_SHIFT);
+
+                               nr_succeed++;
+                               if (idx >= map_len) {
+                                       map = realloc(map, idx + 1);
+                                       if (!map)
+                                               errx(2, "map realloc");
+                                       memset(map + map_len, 0, idx + 1 - map_len);
+                                       map_len = idx + 1;
+                               }
+                               if (!map[idx])
+                                       nr_pages++;
+                               map[idx] = 1;
+                       }
+
+                       /* split transhuge page, keep last page */
+                       if (madvise(p, HPAGE_SIZE - PAGE_SIZE, MADV_DONTNEED))
+                               err(2, "MADV_DONTNEED");
+               }
+               clock_gettime(CLOCK_MONOTONIC, &b);
+               s = b.tv_sec - a.tv_sec + (b.tv_nsec - a.tv_nsec) / 1000000000.;
+
+               warnx("%.3f s/loop, %.3f ms/page, %10.3f MiB/s\t"
+                     "%4d succeed, %4d failed, %4d different pages",
+                     s, s * 1000 / (len >> HPAGE_SHIFT), len / s / (1 << 20),
+                     nr_succeed, nr_failed, nr_pages);
+       }
+}
index c4d6d2e20e0debb8ea3bb8638fb6fb0a757014f8..264fbc297e0b138262647e2426c059a87ff14cfe 100644 (file)
@@ -132,6 +132,7 @@ static const char * const page_flag_names[] = {
        [KPF_NOPAGE]            = "n:nopage",
        [KPF_KSM]               = "x:ksm",
        [KPF_THP]               = "t:thp",
+       [KPF_BALLOON]           = "o:balloon",
 
        [KPF_RESERVED]          = "r:reserved",
        [KPF_MLOCKED]           = "m:mlocked",
index 5819a2708d7edd5823d9e5885a6b7c3796b387ad..e05000e200d22bf7e7edf1ba6a3d8dbc1d1c9a6b 100644 (file)
@@ -302,7 +302,7 @@ static void kvm_free_assigned_device(struct kvm *kvm,
        else
                pci_restore_state(assigned_dev->dev);
 
-       assigned_dev->dev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
+       pci_clear_dev_assigned(assigned_dev->dev);
 
        pci_release_regions(assigned_dev->dev);
        pci_disable_device(assigned_dev->dev);
index 714b949323120aee855dd7e57db549cac31e8183..e723bb91aa346a3bbc0805e064a3823838d2a7c1 100644 (file)
@@ -203,7 +203,7 @@ int kvm_assign_device(struct kvm *kvm,
                        goto out_unmap;
        }
 
-       pdev->dev_flags |= PCI_DEV_FLAGS_ASSIGNED;
+       pci_set_dev_assigned(pdev);
 
        dev_info(&pdev->dev, "kvm assign device\n");
 
@@ -229,7 +229,7 @@ int kvm_deassign_device(struct kvm *kvm,
 
        iommu_detach_device(domain, &pdev->dev);
 
-       pdev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
+       pci_clear_dev_assigned(pdev);
 
        dev_info(&pdev->dev, "kvm deassign device\n");